Целочисленное квантование после обучения

Посмотреть на TensorFlow.org Запускаем в Google Colab Посмотреть исходный код на GitHub Скачать блокнот

Обзор

Целочисленное квантование - это стратегия оптимизации, которая преобразует 32-битные числа с плавающей запятой (такие как веса и выходные данные активации) в ближайшие 8-битные числа с фиксированной запятой. Это приводит к меньшей модели и увеличена скорость логического вывода, который является ценным для маломощных устройств , таких как микроконтроллеры . Этот формат данные также требуется целочисленным только ускорители , такие как пограничный ТП .

В этом уроке, вы будете тренировать модель MNIST с нуля, преобразовать его в файл Tensorflow Lite и квантование его использование после подготовки квантования . Наконец, вы проверите точность преобразованной модели и сравните ее с исходной моделью с плавающей запятой.

На самом деле у вас есть несколько вариантов того, насколько вы хотите квантовать модель. В этом руководстве вы выполните «полное целочисленное квантование», которое преобразует все веса и выходные данные активации в 8-битные целочисленные данные, тогда как другие стратегии могут оставить некоторый объем данных в виде с плавающей запятой.

Для более узнать о различных стратегиях квантования, прочитанных об оптимизации модели TensorFlow Lite .

Настраивать

Чтобы квантовать как входные, так и выходные тензоры, нам нужно использовать API, добавленные в 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

Создайте модель TensorFlow

Мы будем строить простую модель для классификации чисел из MNIST набора данных .

Это обучение не займет много времени, потому что вы тренируете модель всего за 5 эпох, что дает точность около 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 [==============================] - 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>

Преобразование в модель TensorFlow Lite

Теперь вы можете конвертировать обученную модель в формат TensorFlow Lite с использованием TFLiteConverter API, а также применять различную степень квантования.

Помните, что некоторые версии квантования оставляют некоторые данные в формате с плавающей запятой. Таким образом, в следующих разделах каждый вариант показан с увеличивающимся объемом квантования, пока мы не получим модель, полностью состоящую из данных int8 или uint8. (Обратите внимание, что мы дублируем некоторый код в каждом разделе, чтобы вы могли видеть все шаги квантования для каждой опции.)

Во-первых, вот преобразованная модель без квантования:

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.

Теперь это модель TensorFlow Lite, но она по-прежнему использует 32-битные значения с плавающей запятой для всех данных параметров.

Преобразование с использованием квантования динамического диапазона

Теперь давайте включить по умолчанию optimizations флага проквантовать все фиксированные параметры (например, веса):

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.

Модель теперь немного меньше с квантованными весами, но другие данные переменных все еще находятся в формате с плавающей запятой.

Преобразование с использованием резервного квантования с плавающей запятой

Для того, чтобы квантовать данные переменной (например, модель ввода / вывода и промежуточных между слоями), необходимо обеспечить RepresentativeDataset . Это функция генератора, которая предоставляет набор входных данных, достаточно больших для представления типичных значений. Это позволяет конвертеру оценивать динамический диапазон для всех переменных данных. (Набор данных не обязательно должен быть уникальным по сравнению с набором данных для обучения или оценки.) Для поддержки нескольких входных данных каждая репрезентативная точка данных представляет собой список, а элементы в списке передаются в модель в соответствии с их индексами.

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

Теперь все веса и переменные данные квантованы, и модель значительно меньше по сравнению с исходной моделью TensorFlow Lite.

Однако для обеспечения совместимости с приложениями, которые традиционно используют тензоры ввода и вывода модели с плавающей запятой, TensorFlow Lite Converter оставляет тензоры ввода и вывода модели в виде числа с плавающей запятой:

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

Обычно это хорошо для совместимости, но не будет совместимо с устройствами, которые выполняют только операции с целыми числами, такими как Edge TPU.

Кроме того, вышеупомянутый процесс может оставить операцию в формате с плавающей запятой, если TensorFlow Lite не включает квантованную реализацию для этой операции. Эта стратегия позволяет завершить преобразование, так что у вас есть более компактная и более эффективная модель, но, опять же, она не будет совместима с оборудованием, поддерживающим только целые числа. (Все операции в этой модели MNIST имеют квантованную реализацию.)

Итак, чтобы обеспечить сквозную целочисленную модель, вам понадобится еще пара параметров ...

Преобразование с использованием только целочисленного квантования

Чтобы квантовать входные и выходные тензоры и заставить преобразователь выдавать ошибку, если он обнаруживает операцию, которую не может квантовать, снова преобразуйте модель с некоторыми дополнительными параметрами:

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.

Внутреннее квантование остается таким же, как указано выше, но вы можете видеть, что входные и выходные тензоры теперь имеют целочисленный формат:

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

Теперь у вас есть целое квантуется модель , которая использует целые данные для входных и выходных тензоров модели, поэтому он совместит с целочисленными только аппаратными средствами , такими как пограничный ТП .

Сохраните модели как файлы

Вам потребуется .tflite файл для развертывания модели на других устройствах. Итак, давайте сохраним преобразованные модели в файлы, а затем загрузим их при выполнении выводов ниже.

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

Запустите модели TensorFlow Lite

Теперь мы будем работать умозаключение , используя TensorFlow Lite Interpreter для сравнения модели точностей.

Во-первых, нам нужна функция, которая выполняет логический вывод с заданной моделью и изображениями, а затем возвращает прогнозы:

# 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

Протестируйте модели на одном изображении

Теперь сравним производительность модели с плавающей запятой и квантованной модели:

  • tflite_model_file является оригинальной моделью TensorFlow Lite с данными с плавающей точкой.
  • tflite_model_quant_file последняя модель , которую мы преобразованы с помощью целого числа только квантования (она использует Uint8 данные для ввода и вывода).

Давайте создадим еще одну функцию для печати наших прогнозов:

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)

Теперь протестируем модель поплавка:

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

PNG

И протестируем квантованную модель:

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

PNG

Оцените модели на всех изображениях

Теперь давайте запустим обе модели, используя все тестовые изображения, которые мы загрузили в начале этого руководства:

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

Оцените модель поплавка:

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

Оцените квантованную модель:

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

Итак, теперь у вас есть целочисленная квантованная модель почти без разницы в точности по сравнению с моделью с плавающей запятой.

Для того, чтобы узнать о более других стратегиях квантования, прочитанных о оптимизации модели TensorFlow Lite .