在 TensorFlow.org 上查看 | 在 Google Colab 运行 | 在 GitHub 上查看源代码 | Download notebook | 查看 TF Hub 模型 |
尽管全整数量化能够改进模型大小和延迟,但量化后的模型并不总是像预期的那样工作。通常预计模型质量(例如准确率、mAP、WER)略低于原始浮点模型。但是,在某些情况下,模型质量可能会低于您的预期,或者生成完全错误的结果。
发生这种问题时,找出量化错误的根本原因既棘手又痛苦。为了协助这一模型检查过程,可使用量化调试器来识别有问题的层,而选择性量化可将这些有问题的层保留为浮点,以便以减少量化带来的好处为代价来恢复模型准确率。
注:此 API 是实验性的,在改进过程中可能会有一些突破性变化。
量化调试器
量化调试器使得在现有模型中进行量化质量指标分析成为可能。量化调试器可以自动化过程,以便使用调试数据集运行模型,并收集每个张量的量化质量指标。
注:量化调试器和选择性量化目前仅适用于使用 int8 激活的全整数量化。
先决条件
如果您已经有了量化模型的流水线,那么您就有了运行量化调试器所需的所有组件!
- 要量化的模型
- 有代表性的数据集
除了模型和数据之外,您还需要使用数据处理框架(例如 pandas、Google Sheet)来分析导出的结果。
安装
本部分会准备各种库、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
Boilerplates and helpers
MODEL_URI = 'https://tfhub.dev/google/imagenet/mobilenet_v3_small_100_224/classification/5'
def process_image(data):
data['image'] = tf.image.resize(data['image'], (224, 224)) / 255.0
return data
# Representative dataset
def representative_dataset(dataset):
def _data_gen():
for data in dataset.batch(1):
yield [data['image']]
return _data_gen
def eval_tflite(tflite_model, dataset):
"""Evaluates tensorflow lite classification model with the given dataset."""
interpreter = tf.lite.Interpreter(model_content=tflite_model)
interpreter.allocate_tensors()
input_idx = interpreter.get_input_details()[0]['index']
output_idx = interpreter.get_output_details()[0]['index']
results = []
for data in representative_dataset(dataset)():
interpreter.set_tensor(input_idx, data[0])
interpreter.invoke()
results.append(interpreter.get_tensor(output_idx).flatten())
results = np.array(results)
gt_labels = np.array(list(dataset.map(lambda data: data['label'] + 1)))
accuracy = (
np.sum(np.argsort(results, axis=1)[:, -5:] == gt_labels.reshape(-1, 1)) /
gt_labels.size)
print(f'Top-5 accuracy (quantized): {accuracy * 100:.2f}%')
model = tf.keras.Sequential([
tf.keras.layers.Input(shape=(224, 224, 3), batch_size=1),
hub.KerasLayer(MODEL_URI)
])
model.compile(
loss='sparse_categorical_crossentropy',
metrics='sparse_top_k_categorical_accuracy')
model.build([1, 224, 224, 3])
# Prepare dataset with 100 examples
ds = tfds.load('imagenet_v2', split='test[:1%]')
ds = ds.map(process_image)
converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.representative_dataset = representative_dataset(ds)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
quantized_model = converter.convert()
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}%')
eval_tflite(quantized_model, ds)
我们可以看到,对于我们的小数据集,原始模型具有更高的 top-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))
第 2 步:运行调试器并获取结果
当您调用 QuantizationDebugger.run()
时,调试器将记录相同运算位置的浮点张量和量化张量之间的差异,并使用给定的指标来处理它们。
debugger.run()
可以使用 QuantizationDebugger.layer_statistics
来访问处理后的指标,也可以使用 QuantizationDebugger.layer_statistics_dump()
将其转储为 CSV 格式的文本文件。
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
) - RMSE / 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()
有许多范围很广的层,还有一些层的 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、Edge TPU)上运行量化模型,选择性量化将导致模型碎片化,并将导致较慢的推断延迟,这主要是由 CPU 和这些加速器之间的数据传输开销引起的。为防止出现这种情况,您可以考虑运行量化感知训练以保持所有层为整数,同时保持模型准确率。
量化调试器的选项接受 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)
selective_quantized_model = debugger.get_nondebug_quantized_model()
eval_tflite(selective_quantized_model, ds)
与原始浮点模型相比,准确率仍然较低,但我们通过跳过 111 层中的约 10 层的量化,使整个量化模型有了显著的改善。
您还可以尝试不量化同一类中的所有运算。例如,要跳过所有均值运算的量化,可以将 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)
selective_quantized_model = debugger.get_nondebug_quantized_model()
eval_tflite(selective_quantized_model, ds)
通过这些技术,我们能够提高量化后的 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)
debugger.run()
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
使用(内部)mlir_quantize API 访问深度功能
注:以下部分的某些功能,TFLiteConverter._experimental_calibrate_only
和 converter.mlir_quantize
为实验性内部 API,可能会有非向后兼容的更改。
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()
# 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))
来自已经校准的模型的选择性量化
您可以直接调用 convert.mlir_quantize
从已校准的模型中获取选择性量化模型。当您想要校准一次模型,并尝试各种拒绝列表组合时,这将特别有用。
selective_quantized_model = convert.mlir_quantize(
calibrated_model, denylisted_nodes=suspected_layers)
eval_tflite(selective_quantized_model, ds)