Cuantización de enteros posterior al entrenamiento con activaciones int16

Ver en TensorFlow.org Ejecutar en Google Colab Ver fuente en GitHub Descargar cuaderno

Visión general

TensorFlow Lite ahora soporta la conversión de activaciones para valores enteros de 16 bits y los pesos de 8-bit número entero valores durante la conversión de modelo de TensorFlow a formato tampón plana de TensorFlow Lite. Nos referimos a este modo como el "modo de cuantificación 16x8". Este modo puede mejorar la precisión del modelo cuantificado de manera significativa, cuando las activaciones son sensibles a la cuantificación, mientras se logra una reducción de casi 3-4x en el tamaño del modelo. Además, este modelo totalmente cuantificado puede ser consumido por aceleradores de hardware de solo enteros.

Algunos ejemplos de modelos que se benefician de este modo de cuantificación posterior al entrenamiento incluyen:

  • súper resolución,
  • procesamiento de señales de audio, como cancelación de ruido y formación de haces,
  • eliminación de ruido en la imagen,
  • Reconstrucción HDR a partir de una sola imagen

En este tutorial, entrena un modelo MNIST desde cero, verifica su precisión en TensorFlow y luego convierte el modelo en un búfer plano de Tensorflow Lite usando este modo. Al final, verifica la precisión del modelo convertido y lo compara con el modelo float32 original. Tenga en cuenta que este ejemplo demuestra el uso de este modo y no muestra beneficios sobre otras técnicas de cuantificación disponibles en TensorFlow Lite.

Construye un modelo MNIST

Configuración

import logging
logging.getLogger("tensorflow").setLevel(logging.DEBUG)

import tensorflow as tf
from tensorflow import keras
import numpy as np
import pathlib

Compruebe que el modo de cuantificación 16x8 esté disponible

tf.lite.OpsSet.EXPERIMENTAL_TFLITE_BUILTINS_ACTIVATIONS_INT16_WEIGHTS_INT8
<OpsSet.EXPERIMENTAL_TFLITE_BUILTINS_ACTIVATIONS_INT16_WEIGHTS_INT8: 'EXPERIMENTAL_TFLITE_BUILTINS_ACTIVATIONS_INT16_WEIGHTS_INT8'>

Entrene y exporte el modelo

# Load MNIST dataset
mnist = keras.datasets.mnist
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()

# Normalize the input image so that each pixel value is between 0 to 1.
train_images = train_images / 255.0
test_images = test_images / 255.0

# Define the model architecture
model = keras.Sequential([
  keras.layers.InputLayer(input_shape=(28, 28)),
  keras.layers.Reshape(target_shape=(28, 28, 1)),
  keras.layers.Conv2D(filters=12, kernel_size=(3, 3), activation=tf.nn.relu),
  keras.layers.MaxPooling2D(pool_size=(2, 2)),
  keras.layers.Flatten(),
  keras.layers.Dense(10)
])

# Train the digit classification model
model.compile(optimizer='adam',
              loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True),
              metrics=['accuracy'])
model.fit(
  train_images,
  train_labels,
  epochs=1,
  validation_data=(test_images, test_labels)
)
2021-09-12 11:14:10.127487: E tensorflow/stream_executor/cuda/cuda_driver.cc:271] failed call to cuInit: CUDA_ERROR_SYSTEM_DRIVER_MISMATCH: system has unsupported display driver / cuda driver combination
2021-09-12 11:14:10.127636: E tensorflow/stream_executor/cuda/cuda_diagnostics.cc:313] kernel version 470.57.2 does not match DSO version 470.63.1 -- cannot find working devices in this configuration
1875/1875 [==============================] - 10s 5ms/step - loss: 0.2675 - accuracy: 0.9250 - val_loss: 0.1118 - val_accuracy: 0.9679
<keras.callbacks.History at 0x7f67d972ddd0>

Por ejemplo, entrenó el modelo solo durante una época, por lo que solo entrena con una precisión de ~ 96%.

Convertir a un modelo de TensorFlow Lite

Utilizando el pitón TFLiteConverter , ahora se puede convertir el modelo entrenado en un modelo TensorFlow Lite.

Ahora, convertir el modelo usando TFliteConverter en formato float32 por defecto:

converter = tf.lite.TFLiteConverter.from_keras_model(model)
tflite_model = converter.convert()
2021-09-12 11:14:21.178121: W tensorflow/python/util/util.cc:348] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.
INFO:tensorflow:Assets written to: /tmp/tmpvxhwo_ax/assets
2021-09-12 11:14:21.627612: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:351] Ignored output_format.
2021-09-12 11:14:21.627654: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:354] Ignored drop_control_dependency.

Escribirlo a un .tflite archivo:

tflite_models_dir = pathlib.Path("/tmp/mnist_tflite_models/")
tflite_models_dir.mkdir(exist_ok=True, parents=True)
tflite_model_file = tflite_models_dir/"mnist_model.tflite"
tflite_model_file.write_bytes(tflite_model)
84500

Para cuantificar lugar el modelo de 16x8 modo de cuantificación, establecido por primera vez la optimizations la bandera de optimizaciones uso por defecto. Luego, especifique que el modo de cuantificación 16x8 es la operación admitida requerida en la especificación de destino:

converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_ops = [tf.lite.OpsSet.EXPERIMENTAL_TFLITE_BUILTINS_ACTIVATIONS_INT16_WEIGHTS_INT8]

Como en el caso de int8 cuantificación post-formación, es posible producir un modelo totalmente número entero cuantificado mediante el establecimiento de opciones del convertidor inference_input(output)_type a tf.int16.

Configure los datos de calibración:

mnist_train, _ = tf.keras.datasets.mnist.load_data()
images = tf.cast(mnist_train[0], tf.float32) / 255.0
mnist_ds = tf.data.Dataset.from_tensor_slices((images)).batch(1)
def representative_data_gen():
  for input_value in mnist_ds.take(100):
    # Model has only one input so each data point has one element.
    yield [input_value]
converter.representative_dataset = representative_data_gen

Finalmente, convierta el modelo como de costumbre. Tenga en cuenta que, de forma predeterminada, el modelo convertido seguirá utilizando entradas y salidas flotantes para la conveniencia de la invocación.

tflite_16x8_model = converter.convert()
tflite_model_16x8_file = tflite_models_dir/"mnist_model_quant_16x8.tflite"
tflite_model_16x8_file.write_bytes(tflite_16x8_model)
INFO:tensorflow:Assets written to: /tmp/tmp02ui82cp/assets
INFO:tensorflow:Assets written to: /tmp/tmp02ui82cp/assets
2021-09-12 11:14:22.649577: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:351] Ignored output_format.
2021-09-12 11:14:22.649619: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:354] Ignored drop_control_dependency.
24768

Nótese cómo el archivo resultante es aproximadamente 1/3 del tamaño.

ls -lh {tflite_models_dir}
total 180K
-rw-rw-r-- 1 kbuilder kbuilder 83K Sep 12 11:14 mnist_model.tflite
-rw-rw-r-- 1 kbuilder kbuilder 24K Sep 12 11:13 mnist_model_quant.tflite
-rw-rw-r-- 1 kbuilder kbuilder 25K Sep 12 11:14 mnist_model_quant_16x8.tflite
-rw-rw-r-- 1 kbuilder kbuilder 44K Sep 12 11:11 mnist_model_quant_f16.tflite

Ejecute los modelos de TensorFlow Lite

Ejecute el modelo de TensorFlow Lite con el intérprete de Python TensorFlow Lite.

Cargue el modelo en los intérpretes

interpreter = tf.lite.Interpreter(model_path=str(tflite_model_file))
interpreter.allocate_tensors()
interpreter_16x8 = tf.lite.Interpreter(model_path=str(tflite_model_16x8_file))
interpreter_16x8.allocate_tensors()

Pruebe los modelos en una imagen

test_image = np.expand_dims(test_images[0], axis=0).astype(np.float32)

input_index = interpreter.get_input_details()[0]["index"]
output_index = interpreter.get_output_details()[0]["index"]

interpreter.set_tensor(input_index, test_image)
interpreter.invoke()
predictions = interpreter.get_tensor(output_index)
import matplotlib.pylab as plt

plt.imshow(test_images[0])
template = "True:{true}, predicted:{predict}"
_ = plt.title(template.format(true= str(test_labels[0]),
                              predict=str(np.argmax(predictions[0]))))
plt.grid(False)

png

test_image = np.expand_dims(test_images[0], axis=0).astype(np.float32)

input_index = interpreter_16x8.get_input_details()[0]["index"]
output_index = interpreter_16x8.get_output_details()[0]["index"]

interpreter_16x8.set_tensor(input_index, test_image)
interpreter_16x8.invoke()
predictions = interpreter_16x8.get_tensor(output_index)
plt.imshow(test_images[0])
template = "True:{true}, predicted:{predict}"
_ = plt.title(template.format(true= str(test_labels[0]),
                              predict=str(np.argmax(predictions[0]))))
plt.grid(False)

png

Evaluar los modelos

# A helper function to evaluate the TF Lite model using "test" dataset.
def evaluate_model(interpreter):
  input_index = interpreter.get_input_details()[0]["index"]
  output_index = interpreter.get_output_details()[0]["index"]

  # Run predictions on every image in the "test" dataset.
  prediction_digits = []
  for test_image in test_images:
    # Pre-processing: add batch dimension and convert to float32 to match with
    # the model's input data format.
    test_image = np.expand_dims(test_image, axis=0).astype(np.float32)
    interpreter.set_tensor(input_index, test_image)

    # Run inference.
    interpreter.invoke()

    # Post-processing: remove batch dimension and find the digit with highest
    # probability.
    output = interpreter.tensor(output_index)
    digit = np.argmax(output()[0])
    prediction_digits.append(digit)

  # Compare prediction results with ground truth labels to calculate accuracy.
  accurate_count = 0
  for index in range(len(prediction_digits)):
    if prediction_digits[index] == test_labels[index]:
      accurate_count += 1
  accuracy = accurate_count * 1.0 / len(prediction_digits)

  return accuracy
print(evaluate_model(interpreter))
0.9679

Repita la evaluación en el modelo cuantificado 16x8:

# NOTE: This quantization mode is an experimental post-training mode,
# it does not have any optimized kernels implementations or
# specialized machine learning hardware accelerators. Therefore,
# it could be slower than the float interpreter.
print(evaluate_model(interpreter_16x8))
0.9679

En este ejemplo, ha cuantificado un modelo a 16x8 sin diferencia en la precisión, pero con el tamaño reducido 3x.