使用量化调试器检查量化误差

在 TensorFlow.org 上查看 在 Google Colab 中运行 在 GitHub 上查看源代码 下载笔记本 查看 TF Hub 模型

虽然全整数量化可以提高模型大小和延迟,但量化后的模型并不总是按预期工作。通常情况下,模型质量(例如准确率、mAP、WER)会略低于原始浮点模型。但是,在某些情况下,模型质量可能会低于您的预期,或者生成完全错误的结果。

当出现此问题时,很难找到量化误差的根本原因,更难修复量化误差。为了帮助进行此模型检查过程,可以使用 **量化调试器** 来识别有问题的层,并且可以使用 **选择性量化** 将这些有问题的层保留为浮点型,以便以降低量化收益为代价来恢复模型准确率。

量化调试器

量化调试器使您能够在现有模型中进行量化质量指标分析。量化调试器可以自动执行使用调试数据集运行模型并收集每个张量的量化质量指标的过程。

先决条件

如果您已经拥有量化模型的管道,那么您拥有运行量化调试器所需的所有必要组件!

  • 要量化的模型
  • 代表性数据集

除了模型和数据,您还需要使用数据处理框架(例如 pandas、Google 表格)来分析导出的结果。

设置

本节准备库、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

样板代码和辅助函数

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)

我们可以看到,原始模型在我们的小型数据集上具有更高的前 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、EdgeTPU)上运行量化模型,选择性量化会导致模型碎片化,并导致推理延迟变慢,这主要是由 CPU 和这些加速器之间的数据传输成本造成的。为了防止这种情况,您可以考虑运行 量化感知训练,以将所有层保持为整数,同时保留模型精度。

量化调试器的选项接受 denylisted_nodesdenylisted_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()

可以从 debugger.model_statistics 中单独看到 model_debug_metrics 的结果。

debugger.model_statistics

使用(内部)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)