Quantizzazione intera post-addestramento

Visualizza su TensorFlow.org Esegui in Google Colab Visualizza la fonte su GitHub Scarica taccuino

Panoramica

La quantizzazione intera è una strategia di ottimizzazione che converte i numeri a virgola mobile a 32 bit (come pesi e uscite di attivazione) nei numeri a virgola fissa a 8 bit più vicini. Questo si traduce in un modello più piccolo e maggiore velocità di inferenza, che è utile per dispositivi a bassa potenza, come microcontrollori . Questo formato di dati è richiesta anche da interi solo acceleratori come il TPU bordo .

In questo tutorial, si allena un modello MNIST da zero, convertirlo in un file tensorflow Lite, e quantizzare utilizzando quantizzazione post-allenamento . Infine, controllerai l'accuratezza del modello convertito e lo confronterai con il modello float originale.

In realtà hai diverse opzioni su quanto vuoi quantizzare un modello. In questo tutorial, eseguirai la "quantizzazione intera intera", che converte tutti i pesi e gli output di attivazione in dati interi a 8 bit, mentre altre strategie potrebbero lasciare una certa quantità di dati in virgola mobile.

Per saperne di più sulle varie strategie di quantizzazione, letto circa l'ottimizzazione del modello tensorflow Lite .

Impostare

Per quantizzare sia i tensori di input che quelli di output, dobbiamo utilizzare le API aggiunte in 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 modello TensorFlow

Costruiremo un semplice modello a numeri Classificare dal set di dati MNIST .

Questo addestramento non richiederà molto tempo perché stai addestrando il modello per solo 5 epoche, che si addestra con una precisione di circa il 98% circa.

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

Converti in un modello TensorFlow Lite

Ora è possibile convertire il modello addestrato in formato tensorflow Lite utilizzando l' TFLiteConverter API, e applicare vari gradi di quantizzazione.

Attenzione che alcune versioni di quantizzazione lasciano alcuni dati in formato float. Quindi le sezioni seguenti mostrano ogni opzione con quantità crescenti di quantizzazione, fino a quando non otteniamo un modello che è interamente di dati int8 o uint8. (Nota che duplichiamo del codice in ogni sezione in modo da poter vedere tutti i passaggi di quantizzazione per ciascuna opzione.)

Innanzitutto, ecco un modello convertito senza quantizzazione:

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.

Ora è un modello TensorFlow Lite, ma utilizza ancora valori float a 32 bit per tutti i dati dei parametri.

Converti usando la quantizzazione della gamma dinamica

Ora diamo consentono di default optimizations bandiera per quantizzare tutti i parametri fissi (come pesi):

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.

Il modello è ora un po' più piccolo con pesi quantizzati, ma altri dati variabili sono ancora in formato float.

Converti usando la quantizzazione di fallback float

Per quantizzare i dati variabili (come input del modello / uscita e intermedi tra strati), è necessario fornire un RepresentativeDataset . Questa è una funzione generatore che fornisce un insieme di dati di input sufficientemente grande da rappresentare valori tipici. Consente al convertitore di stimare un intervallo dinamico per tutti i dati variabili. (Il set di dati non deve essere univoco rispetto al set di dati di addestramento o valutazione.) Per supportare più input, ogni punto dati rappresentativo è un elenco e gli elementi nell'elenco vengono inseriti nel modello in base ai loro indici.

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

Ora tutti i pesi e i dati variabili sono quantizzati e il modello è significativamente più piccolo rispetto al modello TensorFlow Lite originale.

Tuttavia, per mantenere la compatibilità con le applicazioni che tradizionalmente utilizzano tensori di input e output del modello float, il convertitore TensorFlow Lite lascia i tensori di input e output del modello in 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'>

Di solito è un bene per la compatibilità, ma non sarà compatibile con i dispositivi che eseguono solo operazioni basate su numeri interi, come Edge TPU.

Inoltre, il processo di cui sopra può lasciare un'operazione in formato float se TensorFlow Lite non include un'implementazione quantizzata per tale operazione. Questa strategia consente il completamento della conversione in modo da avere un modello più piccolo ed efficiente, ma, ancora una volta, non sarà compatibile con l'hardware solo intero. (Tutte le operazioni in questo modello MNIST hanno un'implementazione quantizzata.)

Quindi, per garantire un modello solo intero end-to-end, hai bisogno di un paio di parametri in più...

Converti usando la quantizzazione solo intera

Per quantizzare i tensori di input e output e fare in modo che il convertitore generi un errore se incontra un'operazione che non può quantizzare, convertire nuovamente il modello con alcuni parametri aggiuntivi:

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 quantizzazione interna rimane la stessa di sopra, ma puoi vedere che i tensori di input e output sono ora in formato intero:

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

Ora avete un modello intero quantizzato che usi interi dati per tensori di input e output del modello, in modo che sia compatibile con l'hardware intero solo come il TPU bordo .

Salva i modelli come file

Avrete bisogno di un .tflite file per distribuire il proprio modello su altri dispositivi. Quindi salviamo i modelli convertiti in file e poi li carichiamo quando eseguiamo le inferenze di seguito.

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

Eseguire i modelli TensorFlow Lite

Ora faremo correre inferenze che utilizzano il tensorflow Lite Interpreter per confrontare le precisioni modello.

Innanzitutto, abbiamo bisogno di una funzione che esegua l'inferenza con un dato modello e immagini, quindi restituisca le previsioni:

# 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

Prova i modelli su un'immagine

Ora confronteremo le prestazioni del modello float e del modello quantizzato:

  • tflite_model_file è il modello originale tensorflow Lite con i dati in virgola mobile.
  • tflite_model_quant_file è l'ultimo modello abbiamo convertito utilizzando quantizzazione interi solo (utilizza i dati uint8 per l'input e l'output).

Creiamo un'altra funzione per stampare le nostre previsioni:

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)

Ora prova il modello float:

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

png

E prova il modello quantizzato:

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

png

Valuta i modelli su tutte le immagini

Ora eseguiamo entrambi i modelli utilizzando tutte le immagini di prova che abbiamo caricato all'inizio di questo 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)))

Valutare il modello float:

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

Valutare il modello quantizzato:

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

Quindi ora hai un intero quantizzato un modello con quasi nessuna differenza nella precisione, rispetto al modello float.

Per saperne di più su altre strategie di quantizzazione, letto circa l'ottimizzazione del modello tensorflow Lite .