RSVP pour votre événement TensorFlow Everywhere local dès aujourd'hui!
Cette page a été traduite par l'API Cloud Translation.
Switch to English

Quantification d'entiers post-entraînement

Voir sur TensorFlow.org Exécuter dans Google Colab Afficher la source sur GitHub Télécharger le carnet

Aperçu

La quantification d'entiers est une stratégie d'optimisation qui convertit les nombres à virgule flottante de 32 bits (tels que les poids et les sorties d'activation) en nombres à virgule fixe de 8 bits les plus proches. Il en résulte un modèle plus petit et une vitesse d'inférence accrue, ce qui est précieux pour les appareils à faible consommation tels que les microcontrôleurs . Ce format de données est également requis par les accélérateurs de nombres entiers uniquement tels que Edge TPU .

Dans ce didacticiel, vous allez entraîner un modèle MNIST à partir de zéro, le convertir en fichier Tensorflow Lite et le quantifier à l'aide de la quantification post-entraînement . Enfin, vous vérifierez la précision du modèle converti et le comparerez au modèle flottant d'origine.

Vous avez en fait plusieurs options quant à la quantité que vous souhaitez quantifier un modèle. Dans ce didacticiel, vous allez effectuer une «quantification complète des nombres entiers», qui convertit tous les poids et sorties d'activation en données entières 8 bits, alors que d'autres stratégies peuvent laisser une certaine quantité de données en virgule flottante.

Pour en savoir plus sur les différentes stratégies de quantification, lisez sur l' optimisation du modèle TensorFlow Lite .

Installer

Afin de quantifier à la fois les tenseurs d'entrée et de sortie, nous devons utiliser des API ajoutées dans 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

Générer un modèle TensorFlow

Nous allons construire un modèle simple pour classer les nombres de l' ensemble de données MNIST .

Cette formation ne prendra pas longtemps car vous entraînez le modèle pendant seulement 5 époques, ce qui entraîne une précision d'environ 98%.

# 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 [==============================] - 6s 2ms/step - loss: 0.4998 - accuracy: 0.8598 - val_loss: 0.1418 - val_accuracy: 0.9580
Epoch 2/5
1875/1875 [==============================] - 4s 2ms/step - loss: 0.1329 - accuracy: 0.9614 - val_loss: 0.0916 - val_accuracy: 0.9708
Epoch 3/5
1875/1875 [==============================] - 4s 2ms/step - loss: 0.0867 - accuracy: 0.9752 - val_loss: 0.0775 - val_accuracy: 0.9756
Epoch 4/5
1875/1875 [==============================] - 4s 2ms/step - loss: 0.0696 - accuracy: 0.9802 - val_loss: 0.0636 - val_accuracy: 0.9795
Epoch 5/5
1875/1875 [==============================] - 4s 2ms/step - loss: 0.0557 - accuracy: 0.9830 - val_loss: 0.0568 - val_accuracy: 0.9808

<tensorflow.python.keras.callbacks.History at 0x7ff9de3cd1d0>

Convertir en modèle TensorFlow Lite

Vous pouvez désormais convertir le modèle entraîné au format TensorFlow Lite à l'aide de l'API TFLiteConverter et appliquer différents degrés de quantification.

Attention, certaines versions de quantification laissent certaines données au format float. Ainsi, les sections suivantes montrent chaque option avec des quantités croissantes de quantification, jusqu'à ce que nous obtenions un modèle entièrement composé de données int8 ou uint8. (Notez que nous dupliquons du code dans chaque section afin que vous puissiez voir toutes les étapes de quantification pour chaque option.)

Tout d'abord, voici un modèle converti sans quantification:

converter = tf.lite.TFLiteConverter.from_keras_model(model)

tflite_model = converter.convert()
INFO:tensorflow:Assets written to: /tmp/tmp37d927ds/assets

Il s'agit désormais d'un modèle TensorFlow Lite, mais il utilise toujours des valeurs flottantes 32 bits pour toutes les données de paramètres.

Conversion à l'aide de la quantification de la plage dynamique

Maintenant, activons l'indicateur d' optimizations par défaut pour quantifier tous les paramètres fixes (tels que les poids):

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/tmpwvhlb7_l/assets

INFO:tensorflow:Assets written to: /tmp/tmpwvhlb7_l/assets

Le modèle est maintenant un peu plus petit avec des poids quantifiés, mais d'autres données variables sont toujours au format flottant.

Conversion à l'aide de la quantification de repli flottant

Pour quantifier les données variables (telles que l'entrée / sortie du modèle et les intermédiaires entre les couches), vous devez fournir un ensemble de données RepresentativeDataset . Il s'agit d'une fonction de générateur qui fournit un ensemble de données d'entrée suffisamment grand pour représenter des valeurs typiques. Il permet au convertisseur d'estimer une plage dynamique pour toutes les données variables. (Il n'est pas nécessaire que l'ensemble de données soit unique par rapport à l'ensemble de données d'entraînement ou d'évaluation.) Pour prendre en charge plusieurs entrées, chaque point de données représentatif est une liste et les éléments de la liste sont alimentés au modèle en fonction de leurs indices.

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/tmp_8xr7gn5/assets

INFO:tensorflow:Assets written to: /tmp/tmp_8xr7gn5/assets

Désormais, tous les poids et données variables sont quantifiés et le modèle est nettement plus petit que le modèle TensorFlow Lite d'origine.

Cependant, pour maintenir la compatibilité avec les applications qui utilisent traditionnellement des tenseurs d'entrée et de sortie de modèle flottant, le convertisseur TensorFlow Lite laisse les tenseurs d'entrée et de sortie du modèle en float:

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'>

C'est généralement bon pour la compatibilité, mais cela ne sera pas compatible avec les appareils qui n'effectuent que des opérations basées sur des nombres entiers, tels que Edge TPU.

De plus, le processus ci-dessus peut laisser une opération au format flottant si TensorFlow Lite n'inclut pas d'implémentation quantifiée pour cette opération. Cette stratégie permet à la conversion de se terminer afin que vous ayez un modèle plus petit et plus efficace, mais encore une fois, il ne sera pas compatible avec le matériel contenant uniquement des nombres entiers. (Toutes les opérations de ce modèle MNIST ont une implémentation quantifiée.)

Donc, pour garantir un modèle entier de bout en bout, vous avez besoin de quelques paramètres supplémentaires ...

Convertir à l'aide de la quantification en entier uniquement

Pour quantifier les tenseurs d'entrée et de sortie, et faire en sorte que le convertisseur génère une erreur s'il rencontre une opération qu'il ne peut pas quantifier, convertissez à nouveau le modèle avec quelques paramètres supplémentaires:

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/tmp1fjpjkzp/assets

INFO:tensorflow:Assets written to: /tmp/tmp1fjpjkzp/assets

La quantification interne reste la même que ci-dessus, mais vous pouvez voir que les tenseurs d'entrée et de sortie sont maintenant au format entier:

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'>

Vous disposez à présent d'un modèle quantifié en nombre entier qui utilise des données entières pour les tenseurs d'entrée et de sortie du modèle, il est donc compatible avec le matériel de type entier uniquement tel que Edge TPU .

Enregistrer les modèles sous forme de fichiers

Vous aurez besoin d'un fichier .tflite pour déployer votre modèle sur d'autres appareils. Nous allons donc enregistrer les modèles convertis dans des fichiers, puis les charger lorsque nous exécutons les inférences ci-dessous.

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)
24576

Exécutez les modèles TensorFlow Lite

Nous allons maintenant exécuter des inférences à l'aide de l' Interpreter TensorFlow Lite pour comparer les précisions du modèle.

Tout d'abord, nous avons besoin d'une fonction qui exécute l'inférence avec un modèle et des images donnés, puis renvoie les prédictions:

# 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

Testez les modèles sur une image

Nous allons maintenant comparer les performances du modèle flottant et du modèle quantifié:

  • tflite_model_file est le modèle TensorFlow Lite original avec des données à virgule flottante.
  • tflite_model_quant_file est le dernier modèle que nous avons converti en utilisant une quantification entière uniquement (il utilise des données uint8 pour l'entrée et la sortie).

Créons une autre fonction pour imprimer nos prédictions:

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)

Maintenant, testez le modèle de flotteur:

test_model(tflite_model_file, test_image_index, model_type="Float")

png

Et testez le modèle quantifié:

test_model(tflite_model_quant_file, test_image_index, model_type="Quantized")

png

Évaluez les modèles sur toutes les images

Maintenant, exécutons les deux modèles en utilisant toutes les images de test que nous avons chargées au début de ce tutoriel:

# 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)))

Évaluez le modèle de flotteur:

evaluate_model(tflite_model_file, model_type="Float")
Float model accuracy is 98.0800% (Number of test samples=10000)

Évaluez le modèle quantifié:

evaluate_model(tflite_model_quant_file, model_type="Quantized")
Quantized model accuracy is 98.1100% (Number of test samples=10000)

Vous avez donc maintenant un nombre entier quantifié un modèle avec presque aucune différence de précision, par rapport au modèle flottant.

Pour en savoir plus sur les autres stratégies de quantification, lisez sur l' optimisation du modèle TensorFlow Lite .