模型校准

在量化中,一个重要的步骤是确定量化参数,合理的初始量化参数能够显著提升模型精度并加快模型的收敛速度。

模型改造完成并完成浮点训练后,便可进行 Calibration。Calibration 就是在浮点模型中插入 Observer,使用少量训练数据,在模型 forward 过程中统计各处的数据分布,以确定合理的量化参数的过程。 虽然不做 Calibration 也可以进行量化感知训练,但一般来说,它对量化感知训练有益无害,所以推荐您将此步骤作为必选项:

  • 对于部分模型,仅通过 Calibration 便可使精度达到要求,不必进行比较耗时的量化感知训练。

  • 即使模型经过量化校准后无法满足精度要求,此过程也可降低后续量化感知训练的难度,缩短训练时间,提升最终的训练精度。

流程和示例

Calibration 与 QAT 的整体流程如下图所示:

calibration_v2_workflow
注解

模型经过 Calibration 后的量化精度若已满足要求,便可直接进行定点模型转换的步骤,否则需要进行量化感知训练进一步提升精度。

下面分别介绍各个步骤:

  1. 构建并训练浮点模型,详细可参考 构建浮点模型 章节的相关介绍。

  2. 在浮点模型上插入 Observer 节点,使用 prepare 方法转化浮点模型前,需要为模型配置 qconfig,详细内容可参考 构建QConfig 小节的相关介绍。

  3. 设置 fake quantize 状态为 CALIBRATION

    horizon.quantization.set_fake_quantize(model, horizon.quantization.FakeQuantState.CALIBRATION)

    fake quantize 一共有三种状态,分别需要在 QATcalibrationvalidation 前将模型的 fake quantize 设置为对应的状态。

    • 在 calibration 状态下,仅观测各算子输入输出的统计量。

    • 在 QAT 状态下,会观测各算子的输入输出统计量,以及进行伪量化操作。

    • 在 validation 状态下,仅进行伪量化操作,不会对算子的输入输出统计量进行观测。

    class FakeQuantState(Enum):
        QAT = "qat"
        CALIBRATION = "calibration"
        VALIDATION = "validation"
注意

设置模型状态为 FakeQuantState.CALIBRATION 之后,请勿再使用 model.eval(),否则将无法正常进行校准!

  1. calibration。把准备好的校准数据喂给模型,模型在 forward 过程中由 observer 观测相关统计量。

  2. 设置模型状态为 eval 并设置 fake quantize 状态为 VALIDATION

    model.eval()
    horizon.quantization.set_fake_quantize(model, horizon.quantization.FakeQuantState.VALIDATION)
  3. 验证 calibration 效果。如果效果满意,则可以直接将模型转为定点或在此基础上进行量化感知训练,不满意则进行精度调优流程(参考 精度调优指导)。

常用算法介绍

我们推荐在校准时使用 HistogramObserver。此 Observer 会统计各个 feature 在全量校准集上的整体分布,自动搜索全局最优的 scale。

当前支持的搜索方式有:

搜索方式说明
mse (default)根据数据分布计算不同 scale 时伪量化前后的 mse,取损失最小的 scale
percentile控制有效值在总体数据中的占比,percentile=1.0 时退化为 minmax
otherswasserstein,kl,js,cdf_kl,cdf_js。不建议使用,在此不做详细介绍

在使用默认的搜索方式完成模型校准后,若精度不满足预期,可以通过 HistogramObserver.reset_scale 接口使用其他方式重新计算 scale,此过程无需重新校准(注意:scale 会保存在 state_dict 中,所以在 scale 更新后需要重新保存 state_dict)。

class HistogramObserver:
    @classmethod
    def reset_scale(
        cls,
        model: torch.nn.Module,
        method: str,
        method_kwargs: Dict = None,
        prefix: Tuple[str] = None,
        dtype=None,
    ):
        """Reset scales in model by specified method.

        Args:
            model (torch.nn.Module): Model calibrated with HistogramObserver.
            method (str): Chosen from ('mse', 'percentile').
            method_kwargs (Dict, optional): For 'percentile' method, user can set `{'percentile': 0.0~1.0}` to control the proportion of numerical values included in the quantization interval. Defaults to None.
            prefix (Tuple[str], optional): Only reset scale in specified scopes. Defaults to None.
            dtype (_type_, optional): Only reset scale of specified quant dtype. Defaults to None.
        """
注解

HistogramObserver 相比,其他 Observer 一般采用了逐帧计算+滑动平均的做法,在预期精度和结果稳定性上都会差一些,不再推荐使用

调参技巧

  1. calibration 数据越多越好,但因为边际效应的存在,当数据量大到一定程度后,对精度的提升将非常有限。如果训练集较小,可以全部用来 calibration,如果训练集较大,可以结合 calibration 耗时挑选大小合适的子集,建议至少进行 10 - 100 个 step 的校准。挑选的校准数据要尽量覆盖模型的典型输入分布,各个场景都要包含,否则校准会出现偏差。比如:应用场景包含白天 / 夜晚 / 城区 / 高速等多种情况,那么校准数据应当包含所有情况。

  2. 在做图像增强时,可以进行水平翻转类增强,避免进行马赛克这类增强,且尽量使用 infer 阶段的前处理 + 训练数据进行校准。