3、PTQ原理与流程:Post-Training Quantization基本流程、校准数据集的选择、观察者的配置

好,咱们今天聊聊PTQ。Post-Training Quantization,中文叫训练后量化。

说白了,就是模型已经训练好了,我们直接拿过来做量化。不需要重新训练,成本低,速度快。我个人觉得,这是目前工业界落地最广的方案。

PTQ的基本流程

PTQ的流程其实不复杂。我拆成几个关键步骤来讲。

  1. 加载预训练模型:先把你的浮点模型拿过来。FP32的权重和激活值。
  2. 插入观察者:在模型的关键节点(比如卷积层、全连接层后面)插入Observer。它的任务就是收集数据分布。
  3. 运行校准:喂一批校准数据,跑几次前向推理。Observer会记录下每一层的最大值、最小值、或者直方图。
  4. 计算量化参数:根据收集到的统计量,算出scale和zero_point。这一步决定了你的量化精度。
  5. 转换模型:把FP32的权重和激活值,替换成INT8的版本。同时把量化参数固化到模型里。
  6. 验证精度:跑一下验证集,看看精度掉了多少。如果掉得太多,就得回头调参数了。

嗯,这里要注意。步骤3和步骤4是核心。校准数据选不好,或者Observer配置不对,后面全白搭。

核心要点:PTQ的本质,就是用一小部分数据,去估计整个数据集的分布。估计得准,量化损失就小。

校准数据集的选择

校准数据集,说白了就是用来「探路」的。你想想看,模型没见过你的数据,它怎么知道激活值的范围?

所以,校准集必须能代表真实场景。

  • 数量不用多:我个人习惯,一般100到500张图片就够了。多了反而浪费计算资源。
  • 多样性要够:不能全是晴天,也得有阴天、雨天。不能全是猫,也得有狗。我在项目中遇到过,有人拿100张纯白背景的图片做校准,结果模型一上真实场景,精度直接崩了。
  • 避免偏差:校准集的分布,要和训练集、测试集尽量一致。如果训练集里全是高清图,校准集却用低分辨率图,那量化参数肯定不准。

我的经验:如果条件允许,从训练集中随机抽取一小部分作为校准集。这是最稳妥的做法。我曾经偷懒,直接用验证集做校准,结果过拟合了校准集,验证精度虚高,上线后被打脸。

观察者的配置

Observer,就是那个负责「看」的模块。它决定了你如何收集数据。

常见的Observer有这么几种:

Observer类型 原理 适用场景
MinMaxObserver 记录运行时的最小值和最大值 权重量化,或者激活值分布比较均匀的情况
MovingAverageMinMaxObserver 滑动平均的方式更新min/max 激活值分布有轻微波动的情况
HistogramObserver 记录直方图,然后通过KL散度等算法找到最优截断点 激活值分布有长尾或者异常值的情况
PercentileObserver 只保留一定百分比的数值,忽略极端值 数据中有少量噪声或异常点

你可能会问,到底选哪个?

我的建议是:权重用MinMax,激活值用Histogram或者Percentile。

为什么?因为权重的分布通常比较稳定,MinMax就够了。但激活值不一样,它受输入影响很大,偶尔会出现一个很大的值。如果用MinMax,整个量化范围会被这个异常值拉宽,导致精度损失。

避坑指南:我曾经在一个检测模型上,激活值用了MinMaxObserver。结果校准集里有一张过曝的图片,导致所有层的scale都偏大。最后模型输出全是0。嗯,从那以后,我激活值再也不敢用MinMax了。

下面是一个简单的配置示例,我用的是PyTorch的FX量化框架:

import torch
from torch.quantization import default_qconfig, QConfig
from torch.quantization.observer import HistogramObserver, MinMaxObserver

# 配置QConfig
my_qconfig = QConfig(
    activation=HistogramObserver.with_args(
        reduce_range=False,
        dtype=torch.quint8
    ),
    weight=MinMaxObserver.with_args(
        dtype=torch.qint8,
        qscheme=torch.per_tensor_symmetric
    )
)

# 应用到模型
model.qconfig = my_qconfig

这段代码里,激活值用了HistogramObserver,权重用了MinMaxObserver。我个人习惯,权重用对称量化(per_tensor_symmetric),激活值用非对称量化(per_tensor_affine)。这样组合效果最好。

核心逻辑流程图

为了让你更直观地理解,我画了一张流程图。它展示了PTQ从开始到结束的完整链路。

PTQ 核心流程 加载 FP32 预训练模型 插入 Observer 节点 运行校准数据集 收集统计量 (min/max/hist) 计算 scale & zero_point 转换模型为 INT8 验证精度 准备阶段 校准阶段 转换阶段

这张图把整个流程串起来了。你从顶部往下看,每一步都依赖上一步的结果。尤其是校准阶段,Observer配置得好不好,直接决定了最终精度。

一个小技巧:如果你发现量化后精度掉得厉害,先别急着换算法。检查一下校准集里有没有异常样本。我曾经排查了三天,最后发现是一张全黑的图片拉低了所有激活值的范围。

好了,PTQ的原理和流程就讲到这里。核心就是:选对校准数据,配好Observer,然后跑一遍。剩下的就是调参和验证了。

专注资料整理