Проверка ошибок квантования с помощью отладчика квантования

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

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

Когда возникает эта проблема, сложно и болезненно определить основную причину ошибки квантования, и еще сложнее исправить ошибку квантования. Для того, чтобы способствовать этому процессу инспекции модели, квантование отладчик может быть использован для идентификации проблемных слоев, и избирательное квантование может оставить эти проблемные слои в обращении , так что точность модели может быть восстановлена за счет снижения выгоды от квантования.

Отладчик квантования

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

Предпосылки

Если у вас уже есть конвейер для квантования модели, у вас есть все необходимые части для запуска отладчика квантования!

  • Модель для квантования
  • Репрезентативный набор данных

В дополнение к модели и данным вам необходимо будет использовать структуру обработки данных (например, pandas, Google Sheets) для анализа экспортированных результатов.

Настраивать

В этом разделе подготавливаются библиотеки, модель MobileNet v3 и тестовый набор данных из 100 изображений.

# Quantization debugger is available from TensorFlow 2.7.0
pip uninstall -y tensorflow
pip install tf-nightly
pip install tensorflow_datasets --upgrade  # imagenet_v2 needs latest checksum
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import tensorflow as tf
import tensorflow_datasets as tfds
import tensorflow_hub as hub

Котелки и помощники

2021-10-30 11:57:45.262002: W tensorflow/python/util/util.cc:368] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.
INFO:tensorflow:Assets written to: /tmp/tmp_3ry7zon/assets
INFO:tensorflow:Assets written to: /tmp/tmp_3ry7zon/assets
2021-10-30 11:57:52.134354: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:363] Ignored output_format.
2021-10-30 11:57:52.134407: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:366] Ignored drop_control_dependency.
fully_quantize: 0, inference_type: 6, input_inference_type: 0, output_inference_type: 0
test_ds = ds.map(lambda data: (data['image'], data['label'] + 1)).batch(16)
loss, acc = model.evaluate(test_ds)
print(f'Top-5 accuracy (float): {acc * 100:.2f}%')
7/7 [==============================] - 6s 33ms/step - loss: 88.6092 - sparse_top_k_categorical_accuracy: 11.7143
Top-5 accuracy (float): 1171.43%
eval_tflite(quantized_model, ds)
Top-5 accuracy (quantized): 51.00%

Мы видим, что исходная модель имеет гораздо более высокую точность из топ-5 для нашего небольшого набора данных, в то время как квантованная модель имеет значительную потерю точности.

Шаг 1. Подготовка отладчика

Самый простой способ использовать отладчик квантования является обеспечение tf.lite.TFLiteConverter , что вы использовали проквантовать модель.

converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.representative_dataset = representative_dataset(ds)

# my_debug_dataset should have the same format as my_representative_dataset
debugger = tf.lite.experimental.QuantizationDebugger(
    converter=converter, debug_dataset=representative_dataset(ds))
INFO:tensorflow:Assets written to: /tmp/tmpoa_5gejn/assets
INFO:tensorflow:Assets written to: /tmp/tmpoa_5gejn/assets
2021-10-30 11:58:34.006052: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:363] Ignored output_format.
2021-10-30 11:58:34.006103: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:366] Ignored drop_control_dependency.
fully_quantize: 0, inference_type: 6, input_inference_type: 0, output_inference_type: 0

Шаг 2. Запуск отладчика и получение результатов

При вызове QuantizationDebugger.run() , отладчик будет регистрировать различия между поплавком тензорами и квантованными тензорами для того же место оп, и обрабатывать их с заданными показателями.

debugger.run()

Обработанные показатели могут быть доступны с QuantizationDebugger.layer_statistics , или могут быть сброшены в текстовый файл в формате CSV с QuantizationDebugger.layer_statistics_dump() .

RESULTS_FILE = '/tmp/debugger_results.csv'
with open(RESULTS_FILE, 'w') as f:
  debugger.layer_statistics_dump(f)
head /tmp/debugger_results.csv

Для каждой строки в отвал, название и индекс оп приходит первым, а затем параметры квантования и метрик ошибок ( в том числе пользовательских метрик ошибок , если таковые имеются). Полученный файл CSV можно использовать для выбора проблемных слоев с большими показателями ошибок квантования.

С помощью pandas или других библиотек обработки данных мы можем проверить подробные метрики ошибок для каждого уровня.

layer_stats = pd.read_csv(RESULTS_FILE)
layer_stats.head()

Шаг 3. Анализ данных

Есть разные способы проанализировать результат. Во-первых, давайте добавим некоторые полезные метрики, полученные на основе выходных данных отладчика. ( scale означает , что масштабный коэффициент квантования для каждого тензора.)

  • Диапазон ( 256 / scale )
  • СКО / шкала ( sqrt(mean_squared_error) / scale )

RMSE / scale близка к 1 / sqrt(12) (~ 0,289) , когда квантуется распределение аналогичен исходному распределению поплавка, что указывает на хорошую квантованной модели. Чем больше значение, тем более вероятно, что слой недостаточно хорошо квантован.

layer_stats['range'] = 255.0 * layer_stats['scale']
layer_stats['rmse/scale'] = layer_stats.apply(
    lambda row: np.sqrt(row['mean_squared_error']) / row['scale'], axis=1)
layer_stats[['op_name', 'range', 'rmse/scale']].head()
plt.figure(figsize=(15, 5))
ax1 = plt.subplot(121)
ax1.bar(np.arange(len(layer_stats)), layer_stats['range'])
ax1.set_ylabel('range')
ax2 = plt.subplot(122)
ax2.bar(np.arange(len(layer_stats)), layer_stats['rmse/scale'])
ax2.set_ylabel('rmse/scale')
plt.show()

png

Есть много слоев с широкими диапазонами, и некоторые слои , которые имеют высокую RMSE/scale значения. Получим слои с высокими показателями ошибок.

layer_stats[layer_stats['rmse/scale'] > 0.7][[
    'op_name', 'range', 'rmse/scale', 'tensor_name'
]]

С этими слоями вы можете попробовать выборочное квантование, чтобы убедиться, что квантование этих слоев не улучшает качество модели.

suspected_layers = list(
    layer_stats[layer_stats['rmse/scale'] > 0.7]['tensor_name'])

В дополнение к этому, пропуск квантования для первых нескольких слоев также помогает улучшить качество квантованной модели.

suspected_layers.extend(list(layer_stats[:5]['tensor_name']))

Селективное квантование

Селективное квантование пропускает квантование для некоторых узлов, так что вычисление может происходить в исходной области с плавающей запятой. Когда правильные слои пропускаются, мы можем ожидать некоторого восстановления качества модели за счет увеличения задержки и размера модели.

Однако, если вы планируете запускать квантованные модели на ускорителях только для целых чисел (например, Hexagon DSP, EdgeTPU), выборочное квантование вызовет фрагментацию модели и приведет к более медленной задержке вывода, в основном из-за стоимости передачи данных между ЦП и этими ускорителями. . Чтобы предотвратить это, вы можете рассмотреть возможность запуска квантования осведомленного обучения , чтобы сохранить все слои в целом при сохранении точности модели.

Опция квантования отладчика принимает denylisted_nodes и denylisted_ops варианты для пропуска квантования для определенных слоев, или всех экземпляров определенных ОПСОВ. Используя suspected_layers мы , полученную из предыдущего шага, мы можем использовать отладчик квантования для получения селективно квантованной модели.

debug_options = tf.lite.experimental.QuantizationDebugOptions(
    denylisted_nodes=suspected_layers)
debugger = tf.lite.experimental.QuantizationDebugger(
    converter=converter,
    debug_dataset=representative_dataset(ds),
    debug_options=debug_options)
INFO:tensorflow:Assets written to: /tmp/tmpqqc57uli/assets
INFO:tensorflow:Assets written to: /tmp/tmpqqc57uli/assets
2021-10-30 11:59:13.603355: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:363] Ignored output_format.
2021-10-30 11:59:13.603400: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:366] Ignored drop_control_dependency.
fully_quantize: 0, inference_type: 6, input_inference_type: 0, output_inference_type: 0
selective_quantized_model = debugger.get_nondebug_quantized_model()
eval_tflite(selective_quantized_model, ds)
fully_quantize: 0, inference_type: 6, input_inference_type: 0, output_inference_type: 0
Top-5 accuracy (quantized): 64.00%

Точность все еще ниже по сравнению с исходной моделью с плавающей запятой, но мы получили заметное улучшение по сравнению с всей квантованной моделью за счет пропуска квантования для ~ 10 слоев из 111 слоев.

Вы также можете попытаться не квантовать все операции в одном классе. Например, чтобы пропустить квантования для все среднего опса, вы можете передать MEAN в denylisted_ops .

debug_options = tf.lite.experimental.QuantizationDebugOptions(
    denylisted_ops=['MEAN'])
debugger = tf.lite.experimental.QuantizationDebugger(
    converter=converter,
    debug_dataset=representative_dataset(ds),
    debug_options=debug_options)
INFO:tensorflow:Assets written to: /tmp/tmpxltlornb/assets
INFO:tensorflow:Assets written to: /tmp/tmpxltlornb/assets
2021-10-30 11:59:44.677473: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:363] Ignored output_format.
2021-10-30 11:59:44.677519: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:366] Ignored drop_control_dependency.
fully_quantize: 0, inference_type: 6, input_inference_type: 0, output_inference_type: 0
selective_quantized_model = debugger.get_nondebug_quantized_model()
eval_tflite(selective_quantized_model, ds)
fully_quantize: 0, inference_type: 6, input_inference_type: 0, output_inference_type: 0
Top-5 accuracy (quantized): 54.00%

С помощью этих методов мы можем повысить точность квантованной модели MobileNet V3. Далее мы исследуем продвинутые методы, чтобы еще больше повысить точность модели.

Расширенное использование

Следующие функции позволяют дополнительно настроить конвейер отладки.

Пользовательские метрики

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

  • layer_debug_metrics : вычислить метрику на основе дифференциала для каждого из выходов оп поплавка и квантованных выходов оп.
  • layer_direct_compare_metrics : а не получать только диф, это будет вычислять метрику на основе сырого поплавка и квантованных тензоров и его параметров квантования (шкала, нулевая точка)
  • model_debug_metrics : используется только тогда , когда float_model_(path|content) передается в отладчик. Помимо показателей операционного уровня, выходные данные окончательного уровня сравниваются с эталонными выходными данными исходной модели с плавающей запятой.
debug_options = tf.lite.experimental.QuantizationDebugOptions(
    layer_debug_metrics={
        'mean_abs_error': (lambda diff: np.mean(np.abs(diff)))
    },
    layer_direct_compare_metrics={
        'correlation':
            lambda f, q, s, zp: (np.corrcoef(f.flatten(),
                                             (q.flatten() - zp) / s)[0, 1])
    },
    model_debug_metrics={
        'argmax_accuracy': (lambda f, q: np.mean(np.argmax(f) == np.argmax(q)))
    })

debugger = tf.lite.experimental.QuantizationDebugger(
    converter=converter,
    debug_dataset=representative_dataset(ds),
    debug_options=debug_options)
INFO:tensorflow:Assets written to: /tmp/tmpm7cb9qcd/assets
INFO:tensorflow:Assets written to: /tmp/tmpm7cb9qcd/assets
2021-10-30 12:00:18.502193: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:363] Ignored output_format.
2021-10-30 12:00:18.502238: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:366] Ignored drop_control_dependency.
INFO:tensorflow:Assets written to: /tmp/tmpzkg3ny_8/assets
INFO:tensorflow:Assets written to: /tmp/tmpzkg3ny_8/assets
2021-10-30 12:00:28.401195: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:363] Ignored output_format.
2021-10-30 12:00:28.401241: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:366] Ignored drop_control_dependency.
fully_quantize: 0, inference_type: 6, input_inference_type: 0, output_inference_type: 0
debugger.run()
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/numpy/lib/function_base.py:2691: RuntimeWarning: invalid value encountered in true_divide
  c /= stddev[:, None]
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/numpy/lib/function_base.py:2692: RuntimeWarning: invalid value encountered in true_divide
  c /= stddev[None, :]
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/lite/tools/optimize/debugging/python/debugger.py:382: RuntimeWarning: Mean of empty slice
  metrics[metric_name] = np.nanmean(metrics[metric_name])
CUSTOM_RESULTS_FILE = '/tmp/debugger_results.csv'
with open(CUSTOM_RESULTS_FILE, 'w') as f:
  debugger.layer_statistics_dump(f)

custom_layer_stats = pd.read_csv(CUSTOM_RESULTS_FILE)
custom_layer_stats[['op_name', 'mean_abs_error', 'correlation']].tail()

Результат model_debug_metrics может быть отдельно видно из debugger.model_statistics .

debugger.model_statistics
{'argmax_accuracy': 0.36}

Использование (внутреннего) API mlir_quantize для доступа к подробным функциям

from tensorflow.lite.python import convert

Режим проверки всей модели

Поведение по умолчанию для создания модели отладки - проверка на уровне каждого уровня. В этом режиме вход для пары операций с плавающей запятой и операции квантования поступает из одного источника (предыдущая операция квантования). Другой режим - проверка всей модели, где модели с плавающей точкой и квантование разделены. Этот режим был бы полезен для наблюдения за тем, как ошибка распространяется вниз по модели. Чтобы включить, enable_whole_model_verify=True для convert.mlir_quantize при создании модели отладки вручную.

converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.representative_dataset = representative_dataset(ds)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter._experimental_calibrate_only = True
calibrated_model = converter.convert()
INFO:tensorflow:Assets written to: /tmp/tmp2oa0sp06/assets
INFO:tensorflow:Assets written to: /tmp/tmp2oa0sp06/assets
2021-10-30 12:01:33.233118: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:363] Ignored output_format.
2021-10-30 12:01:33.233171: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:366] Ignored drop_control_dependency.
# Note that enable_numeric_verify and enable_whole_model_verify are set.
quantized_model = convert.mlir_quantize(
    calibrated_model,
    enable_numeric_verify=True,
    enable_whole_model_verify=True)
debugger = tf.lite.experimental.QuantizationDebugger(
    quant_debug_model_content=quantized_model,
    debug_dataset=representative_dataset(ds))
fully_quantize: 0, inference_type: 6, input_inference_type: 0, output_inference_type: 0

Выборочное квантование из уже откалиброванной модели

Вы можете напрямую позвонить convert.mlir_quantize получить селективную квантованную модель от уже калиброванной модели. Это будет особенно полезно, если вы хотите один раз откалибровать модель и поэкспериментировать с различными комбинациями денилистов.

selective_quantized_model = convert.mlir_quantize(
    calibrated_model, denylisted_nodes=suspected_layers)
eval_tflite(selective_quantized_model, ds)
fully_quantize: 0, inference_type: 6, input_inference_type: 0, output_inference_type: 0
Top-5 accuracy (quantized): 64.00%