Ver en TensorFlow.org | Ejecutar en Google Colab | Ver fuente en GitHub | Descargar cuaderno |
Descripción general
La cuantificación de enteros es una estrategia de optimización que convierte números de coma flotante de 32 bits (como pesos y salidas de activación) a los números de coma fija de 8 bits más cercanos. Esto resulta en un modelo más pequeño y mayor velocidad de la inferencia, que es valiosa para dispositivos de baja potencia tales como los microcontroladores . Este formato de datos también es requerido por número entero de sólo aceleradores tales como el Edge TPU .
En este tutorial, entrenar un modelo MNIST desde cero, convertirlo en un archivo Tensorflow Lite, y cuantifica usando cuantificación posterior al entrenamiento . Por último, comprobará la precisión del modelo convertido y lo comparará con el modelo flotante original.
De hecho, tiene varias opciones en cuanto a cuánto desea cuantificar un modelo. En este tutorial, realizará una "cuantificación de enteros completa", que convierte todos los pesos y salidas de activación en datos enteros de 8 bits, mientras que otras estrategias pueden dejar cierta cantidad de datos en punto flotante.
Para obtener más información sobre las diversas estrategias de cuantificación, lee la optimización modelo TensorFlow Lite .
Configuración
Para cuantificar los tensores de entrada y salida, necesitamos usar las API agregadas en TensorFlow r2.3:
import logging
logging.getLogger("tensorflow").setLevel(logging.DEBUG)
import tensorflow as tf
import numpy as np
assert float(tf.__version__[:3]) >= 2.3
Genera un modelo de TensorFlow
Vamos a construir un modelo simple de números classify del conjunto de datos MNIST .
Este entrenamiento no tomará mucho tiempo porque está entrenando el modelo por solo 5 épocas, lo que entrena a aproximadamente ~ 98% de precisión.
# Load MNIST dataset
mnist = tf.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.astype(np.float32) / 255.0
test_images = test_images.astype(np.float32) / 255.0
# Define the model architecture
model = tf.keras.Sequential([
tf.keras.layers.InputLayer(input_shape=(28, 28)),
tf.keras.layers.Reshape(target_shape=(28, 28, 1)),
tf.keras.layers.Conv2D(filters=12, kernel_size=(3, 3), activation='relu'),
tf.keras.layers.MaxPooling2D(pool_size=(2, 2)),
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(10)
])
# Train the digit classification model
model.compile(optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(
from_logits=True),
metrics=['accuracy'])
model.fit(
train_images,
train_labels,
epochs=5,
validation_data=(test_images, test_labels)
)
Epoch 1/5 1875/1875 [==============================] - 5s 2ms/step - loss: 0.2519 - accuracy: 0.9311 - val_loss: 0.1106 - val_accuracy: 0.9664 Epoch 2/5 1875/1875 [==============================] - 3s 2ms/step - loss: 0.0984 - accuracy: 0.9724 - val_loss: 0.0828 - val_accuracy: 0.9743 Epoch 3/5 1875/1875 [==============================] - 3s 2ms/step - loss: 0.0746 - accuracy: 0.9785 - val_loss: 0.0640 - val_accuracy: 0.9795 Epoch 4/5 1875/1875 [==============================] - 3s 2ms/step - loss: 0.0620 - accuracy: 0.9814 - val_loss: 0.0620 - val_accuracy: 0.9793 Epoch 5/5 1875/1875 [==============================] - 3s 2ms/step - loss: 0.0540 - accuracy: 0.9837 - val_loss: 0.0624 - val_accuracy: 0.9795 <keras.callbacks.History at 0x7fb44c988c90>
Convertir a un modelo de TensorFlow Lite
Ahora se puede convertir el modelo entrenado a formato TensorFlow Lite utilizando el TFLiteConverter
API, y aplicar diferentes grados de cuantificación.
Tenga en cuenta que algunas versiones de cuantificación dejan algunos datos en formato flotante. Entonces, las siguientes secciones muestran cada opción con cantidades crecientes de cuantificación, hasta que obtenemos un modelo que es completamente de datos int8 o uint8. (Observe que duplicamos algo de código en cada sección para que pueda ver todos los pasos de cuantificación para cada opción).
Primero, aquí hay un modelo convertido sin cuantificación:
converter = tf.lite.TFLiteConverter.from_keras_model(model)
tflite_model = converter.convert()
2021-10-30 12:04:56.623151: 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/tmp3os2tr3n/assets 2021-10-30 12:04:57.031317: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:351] Ignored output_format. 2021-10-30 12:04:57.031355: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:354] Ignored drop_control_dependency.
Ahora es un modelo de TensorFlow Lite, pero todavía usa valores flotantes de 32 bits para todos los datos de los parámetros.
Convertir usando cuantificación de rango dinámico
Ahora vamos a por defecto permiten la optimizations
la bandera para cuantificar todos los parámetros fijos (como pesos):
converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
tflite_model_quant = converter.convert()
INFO:tensorflow:Assets written to: /tmp/tmpi7xibvaj/assets INFO:tensorflow:Assets written to: /tmp/tmpi7xibvaj/assets 2021-10-30 12:04:57.597982: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:351] Ignored output_format. 2021-10-30 12:04:57.598020: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:354] Ignored drop_control_dependency.
El modelo ahora es un poco más pequeño con pesos cuantificados, pero otros datos variables todavía están en formato flotante.
Convierta usando cuantización de retroceso flotante
Para cuantificar los datos variables (tales como entrada de modelo / salida y los intermedios entre las capas), es necesario proporcionar un RepresentativeDataset
. Esta es una función generadora que proporciona un conjunto de datos de entrada que es lo suficientemente grande como para representar valores típicos. Permite al convertidor estimar un rango dinámico para todos los datos variables. (No es necesario que el conjunto de datos sea único en comparación con el conjunto de datos de entrenamiento o evaluación). Para admitir múltiples entradas, cada punto de datos representativo es una lista y los elementos de la lista se alimentan al modelo de acuerdo con sus índices.
def representative_data_gen():
for input_value in tf.data.Dataset.from_tensor_slices(train_images).batch(1).take(100):
# Model has only one input so each data point has one element.
yield [input_value]
converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.representative_dataset = representative_data_gen
tflite_model_quant = converter.convert()
INFO:tensorflow:Assets written to: /tmp/tmp3gwloj7n/assets INFO:tensorflow:Assets written to: /tmp/tmp3gwloj7n/assets 2021-10-30 12:04:58.159142: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:351] Ignored output_format. 2021-10-30 12:04:58.159181: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:354] Ignored drop_control_dependency. fully_quantize: 0, inference_type: 6, input_inference_type: 0, output_inference_type: 0
Ahora todos los pesos y datos variables están cuantificados, y el modelo es significativamente más pequeño en comparación con el modelo original de TensorFlow Lite.
Sin embargo, para mantener la compatibilidad con las aplicaciones que tradicionalmente usan tensores de entrada y salida del modelo flotante, TensorFlow Lite Converter deja los tensores de entrada y salida del modelo en flotación:
interpreter = tf.lite.Interpreter(model_content=tflite_model_quant)
input_type = interpreter.get_input_details()[0]['dtype']
print('input: ', input_type)
output_type = interpreter.get_output_details()[0]['dtype']
print('output: ', output_type)
input: <class 'numpy.float32'> output: <class 'numpy.float32'>
Eso suele ser bueno para la compatibilidad, pero no será compatible con dispositivos que realizan solo operaciones basadas en números enteros, como Edge TPU.
Además, el proceso anterior puede dejar una operación en formato flotante si TensorFlow Lite no incluye una implementación cuantificada para esa operación. Esta estrategia permite que la conversión se complete para que tenga un modelo más pequeño y eficiente, pero nuevamente, no será compatible con hardware de solo enteros. (Todas las operaciones en este modelo MNIST tienen una implementación cuantificada).
Entonces, para garantizar un modelo de solo enteros de extremo a extremo, necesita un par de parámetros más ...
Convierta usando la cuantificación de números enteros
Para cuantificar los tensores de entrada y salida, y hacer que el convertidor arroje un error si encuentra una operación que no puede cuantificar, convierta el modelo nuevamente con algunos parámetros adicionales:
def representative_data_gen():
for input_value in tf.data.Dataset.from_tensor_slices(train_images).batch(1).take(100):
yield [input_value]
converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.representative_dataset = representative_data_gen
# Ensure that if any ops can't be quantized, the converter throws an error
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
# Set the input and output tensors to uint8 (APIs added in r2.3)
converter.inference_input_type = tf.uint8
converter.inference_output_type = tf.uint8
tflite_model_quant = converter.convert()
INFO:tensorflow:Assets written to: /tmp/tmp8ygc2_3y/assets INFO:tensorflow:Assets written to: /tmp/tmp8ygc2_3y/assets 2021-10-30 12:04:59.308505: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:351] Ignored output_format. 2021-10-30 12:04:59.308542: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:354] Ignored drop_control_dependency. fully_quantize: 0, inference_type: 6, input_inference_type: 3, output_inference_type: 3 WARNING:absl:For model inputs containing unsupported operations which cannot be quantized, the `inference_input_type` attribute will default to the original type.
La cuantificación interna sigue siendo la misma que la anterior, pero puede ver que los tensores de entrada y salida ahora tienen formato entero:
interpreter = tf.lite.Interpreter(model_content=tflite_model_quant)
input_type = interpreter.get_input_details()[0]['dtype']
print('input: ', input_type)
output_type = interpreter.get_output_details()[0]['dtype']
print('output: ', output_type)
input: <class 'numpy.uint8'> output: <class 'numpy.uint8'>
Ahora usted tiene un modelo cuantizado número entero que utiliza enteros de datos para tensores de entrada y salida del modelo, por lo que es compatible con el hardware entero de sólo como el borde de TPU .
Guarde los modelos como archivos
Usted necesitará un .tflite
archivo para desplegar su modelo en otros dispositivos. Así que guardemos los modelos convertidos en archivos y luego cargámoslos cuando hagamos inferencias a continuación.
import pathlib
tflite_models_dir = pathlib.Path("/tmp/mnist_tflite_models/")
tflite_models_dir.mkdir(exist_ok=True, parents=True)
# Save the unquantized/float model:
tflite_model_file = tflite_models_dir/"mnist_model.tflite"
tflite_model_file.write_bytes(tflite_model)
# Save the quantized model:
tflite_model_quant_file = tflite_models_dir/"mnist_model_quant.tflite"
tflite_model_quant_file.write_bytes(tflite_model_quant)
24280
Ejecute los modelos de TensorFlow Lite
Ahora vamos a ejecutar inferencias utilizando el TensorFlow Lite Interpreter
para comparar la precisión de los modelos.
Primero, necesitamos una función que ejecute inferencia con un modelo e imágenes dados, y luego devuelva las predicciones:
# Helper function to run inference on a TFLite model
def run_tflite_model(tflite_file, test_image_indices):
global test_images
# Initialize the interpreter
interpreter = tf.lite.Interpreter(model_path=str(tflite_file))
interpreter.allocate_tensors()
input_details = interpreter.get_input_details()[0]
output_details = interpreter.get_output_details()[0]
predictions = np.zeros((len(test_image_indices),), dtype=int)
for i, test_image_index in enumerate(test_image_indices):
test_image = test_images[test_image_index]
test_label = test_labels[test_image_index]
# Check if the input type is quantized, then rescale input data to uint8
if input_details['dtype'] == np.uint8:
input_scale, input_zero_point = input_details["quantization"]
test_image = test_image / input_scale + input_zero_point
test_image = np.expand_dims(test_image, axis=0).astype(input_details["dtype"])
interpreter.set_tensor(input_details["index"], test_image)
interpreter.invoke()
output = interpreter.get_tensor(output_details["index"])[0]
predictions[i] = output.argmax()
return predictions
Pruebe los modelos en una imagen
Ahora compararemos el rendimiento del modelo flotante y el modelo cuantificado:
-
tflite_model_file
es el modelo original TensorFlow Lite con datos de coma flotante. -
tflite_model_quant_file
es el último modelo que convierte usando cuantificación número entero de sólo (que utiliza datos uint8 para entrada y salida).
Creemos otra función para imprimir nuestras predicciones:
import matplotlib.pylab as plt
# Change this to test a different image
test_image_index = 1
## Helper function to test the models on one image
def test_model(tflite_file, test_image_index, model_type):
global test_labels
predictions = run_tflite_model(tflite_file, [test_image_index])
plt.imshow(test_images[test_image_index])
template = model_type + " Model \n True:{true}, Predicted:{predict}"
_ = plt.title(template.format(true= str(test_labels[test_image_index]), predict=str(predictions[0])))
plt.grid(False)
Ahora prueba el modelo de flotador:
test_model(tflite_model_file, test_image_index, model_type="Float")
Y pruebe el modelo cuantificado:
test_model(tflite_model_quant_file, test_image_index, model_type="Quantized")
Evaluar los modelos en todas las imágenes.
Ahora ejecutemos ambos modelos usando todas las imágenes de prueba que cargamos al comienzo de este tutorial:
# Helper function to evaluate a TFLite model on all images
def evaluate_model(tflite_file, model_type):
global test_images
global test_labels
test_image_indices = range(test_images.shape[0])
predictions = run_tflite_model(tflite_file, test_image_indices)
accuracy = (np.sum(test_labels== predictions) * 100) / len(test_images)
print('%s model accuracy is %.4f%% (Number of test samples=%d)' % (
model_type, accuracy, len(test_images)))
Evalúe el modelo de flotador:
evaluate_model(tflite_model_file, model_type="Float")
Float model accuracy is 97.9500% (Number of test samples=10000)
Evalúe el modelo cuantificado:
evaluate_model(tflite_model_quant_file, model_type="Quantized")
Quantized model accuracy is 97.9300% (Number of test samples=10000)
Así que ahora tiene un modelo cuantificado entero con casi ninguna diferencia en la precisión, en comparación con el modelo flotante.
Para obtener más información sobre otras estrategias de cuantificación, lee la optimización modelo TensorFlow Lite .