Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
tensorflow
GitHub Repository: tensorflow/docs-l10n
Path: blob/master/site/zh-cn/lite/performance/quantization_debugger.ipynb
25118 views
Kernel: Python 3
#@title Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License.

利用量化调试器检查量化错误

尽管全整数量化能够改进模型大小和延迟,但量化后的模型并不总是像预期的那样工作。通常预计模型质量(例如准确率、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
#@title 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_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()

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

debugger.model_statistics

使用(内部)mlir_quantize API 访问深度功能

注:以下部分的某些功能,TFLiteConverter._experimental_calibrate_onlyconverter.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)