训练后量化

训练后量化是一种转换技术,它可以缩减模型大小,同时降低 CPU 和硬件加速器的延迟时间,但不会降低模型准确度。使用 LiteRT 转换器将已训练的浮点型 TensorFlow 模型转换为 LiteRT 格式时,您可以对该模型进行量化。

优化方法

有多种训练后量化选项可供选择。下表总结了各种选择及其带来的好处:

技术 优势 硬件
动态范围量化 体积缩小 4 倍,速度提升 2-3 倍 CPU
全整数量化 体积缩小 4 倍,速度提升 3 倍以上 CPU、Edge TPU、微控制器
Float16 量化 体积缩小 2 倍,支持 GPU 加速 CPU、GPU

以下决策树可帮助您确定哪种训练后量化方法最适合您的使用情形:

训练后优化选项

无量化

建议先转换为不含量化的 TFLite 模型。这样会生成一个浮点 TFLite 模型。

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
tflite_quant_model = converter.convert()

我们建议您先执行此操作,以验证原始 TF 模型的运算符是否与 TFLite 兼容,还可以将其用作基准来调试后续训练后量化方法引入的量化错误。例如,如果量化 TFLite 模型产生意外结果,而浮点 TFLite 模型准确无误,我们可以将问题范围缩小到由量化版 TFLite 运算符引入的错误。

动态范围量化

动态范围量化可减少内存用量并加快计算速度,而无需您提供用于校准的代表性数据集。这种量化类型仅在转换时将权重从浮点静态量化为整数,可提供 8 位精度:

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
tflite_quant_model = converter.convert()

为了进一步减少推理期间的延迟时间,“动态范围”运算符会根据激活值的范围将其动态量化为 8 位,并使用 8 位权重和激活值执行计算。此优化可提供接近完全定点推理的延迟时间。不过,输出仍以浮点格式存储,因此动态范围操作的提速不如完整的定点计算。

完全整数量化

通过确保所有模型数学运算都经过整数量化,您可以进一步缩短延迟时间、减少峰值内存用量,并与纯整数硬件设备或加速器兼容。

对于全整数量化,您需要校准或估计范围,即模型中所有浮点张量的(最小值、最大值)。与权重和偏差等常量张量不同,模型输入、激活(中间层的输出)和模型输出等变量张量无法进行校准,除非我们运行几个推理周期。因此,转换器需要一个具有代表性的数据集来校准它们。此数据集可以是训练或验证数据的一小部分子集(大约 100-500 个样本)。请参阅下方的 representative_dataset() 函数。

从 TensorFlow 2.7 版本开始,您可以通过签名指定代表性数据集,如以下示例所示:

def representative_dataset():
  for data in dataset:
    yield {
      "image": data.image,
      "bias": data.bias,
    }

如果给定的 TensorFlow 模型中包含多个签名,您可以通过指定签名键来指定多个数据集:

def representative_dataset():
  # Feed data set for the "encode" signature.
  for data in encode_signature_dataset:
    yield (
      "encode", {
        "image": data.image,
        "bias": data.bias,
      }
    )

  # Feed data set for the "decode" signature.
  for data in decode_signature_dataset:
    yield (
      "decode", {
        "image": data.image,
        "hint": data.hint,
      },
    )

您可以通过提供输入张量列表来生成代表性数据集:

def representative_dataset():
  for data in tf.data.Dataset.from_tensor_slices((images)).batch(1).take(100):
    yield [tf.dtypes.cast(data, tf.float32)]

自 TensorFlow 2.7 版起,我们建议使用基于签名的输入方法,而不是基于输入张量列表的方法,因为输入张量的顺序很容易颠倒。

出于测试目的,您可以使用虚拟数据集,如下所示:

def representative_dataset():
    for _ in range(100):
      data = np.random.rand(1, 244, 244, 3)
      yield [data.astype(np.float32)]
 

具有浮点回退功能的整数(使用默认的浮点输入/输出)

为了对模型进行完全整数量化,但在浮点运算符没有整数实现时使用浮点运算符(以确保顺利转换),请按以下步骤操作:

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.representative_dataset = representative_dataset
tflite_quant_model = converter.convert()

仅限整数

创建仅包含整数的模型是 LiteRT for MicrocontrollersCoral Edge TPU 的常见用例。

此外,为了确保与仅支持整数的设备(例如 8 位微控制器)和加速器(例如 Coral Edge TPU)兼容,您可以按照以下步骤对所有运算(包括输入和输出)强制执行全整数量化:

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.representative_dataset = representative_dataset
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
converter.inference_input_type = tf.int8  # or tf.uint8
converter.inference_output_type = tf.int8  # or tf.uint8
tflite_quant_model = converter.convert()

float16 量化

您可以通过将权重量化为 float16(IEEE 16 位浮点数标准)来减小浮点模型的规模。如需启用 float16 权重量化,请按以下步骤操作:

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_types = [tf.float16]
tflite_quant_model = converter.convert()

float16 量化的优势如下:

  • 它可以将模型大小缩减一半(因为所有权重都变为原来的一半)。
  • 这会导致准确率略有下降。
  • 它支持一些可直接对 float16 数据进行操作的委托(例如 GPU 委托),从而实现比 float32 计算更快的执行速度。

float16 量化的缺点如下:

  • 与量化为定点数学相比,它减少的延迟时间并不多。
  • 默认情况下,float16 量化模型在 CPU 上运行时会将权重值“反量化”为 float32。(请注意,GPU 委托不会执行此反量化,因为它可处理 float16 数据。)

仅限整数:16 位激活函数,8 位权重(实验性)

这是一种实验性量化方案。它与“仅限整数”方案类似,但激活函数根据其范围量化为 16 位,权重量化为 8 位整数,偏差量化为 64 位整数。这称为 16x8 量化。

这种量化的主要优势在于,它可以显著提高准确率,但只会略微增加模型大小。

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.representative_dataset = representative_dataset
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_ops = [tf.lite.OpsSet.EXPERIMENTAL_TFLITE_BUILTINS_ACTIVATIONS_INT16_WEIGHTS_INT8]
tflite_quant_model = converter.convert()

如果模型中的某些运算符不支持 16x8 量化,则模型仍可量化,但不支持的运算符会保留为浮点数。应将以下选项添加到 target_spec 中,以允许此操作。

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.representative_dataset = representative_dataset
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_ops = [tf.lite.OpsSet.EXPERIMENTAL_TFLITE_BUILTINS_ACTIVATIONS_INT16_WEIGHTS_INT8,
tf.lite.OpsSet.TFLITE_BUILTINS]
tflite_quant_model = converter.convert()

以下是一些应用场景示例,其中此量化方案可提高准确性:

  • 超分辨率,
  • 音频信号处理(例如降噪和波束成形),
  • 图片去噪,
  • 从单张图片重建 HDR。

这种量化的缺点是:

  • 目前,由于缺少优化的内核实现,推理速度明显慢于 8 位全整数。
  • 目前,它与现有的硬件加速 TFLite 委托不兼容。

如需查看此量化模式的教程,请点击此处

模型准确率

由于权重是在训练后量化的,因此可能会出现准确率损失,尤其是对于较小的网络。Kaggle Models 上提供了针对特定网络的预训练全量化模型。请务必检查量化模型的准确性,以验证准确性的任何下降是否在可接受的范围内。您可以使用相关工具来评估 LiteRT 模型的准确性

或者,如果准确率下降幅度过大,请考虑使用量化感知训练。不过,这样做需要在模型训练期间进行修改以添加伪量化节点,而本页上的训练后量化技术则使用现有的预训练模型。

量化张量的表示形式

8 位量化使用以下公式来近似浮点值。

\[real\_value = (int8\_value - zero\_point) \times scale\]

该表示法包含两个主要部分:

  • 每个轴(也称为每个通道)或每个张量的权重,以 int8 二进制补码值表示,范围为 [-127, 127],零点等于 0。

  • 每个张量的激活/输入由范围为 [-128, 127] 的 int8 二进制补码值表示,零点范围为 [-128, 127]。

如需详细了解我们的量化方案,请参阅我们的量化规范。我们建议希望接入 TensorFlow Lite 委托接口的硬件供应商实现其中描述的量化方案。