训练后量化(PTQ)原理:校准数据集、动态量化、静态量化、量化粒度

训练后量化,英文叫 Post-Training Quantization,简称 PTQ。说白了,就是模型训练完了,我们直接拿它做手脚,把权重和激活值从 FP32 砍成 INT8 甚至更低精度。这招在工业界用得最多,因为它快,不需要重新训练。

我个人习惯把 PTQ 比作「给模型减肥」。模型训练好比练出了一身肌肉,PTQ 就是把这身肌肉里的水分挤掉,留下干货。挤得好,模型跑得快还省内存;挤不好,精度掉得你怀疑人生。

校准数据集:量化的「标尺」

做静态量化时,我们需要知道激活值的分布范围——最小值是多少,最大值是多少。这个信息从哪来?从一小部分真实数据里统计出来。这一小部分数据,就叫校准数据集。

校准数据集的选择很有讲究。我在项目中遇到过有人随便拿几百张训练集图片去校准,结果量化后的模型在测试集上直接崩了。为什么?因为训练集和真实场景的数据分布不一样。

注意:校准数据集必须代表真实推理场景的数据分布。数量一般 100-500 个样本就够了,太多反而浪费计算资源。

校准的过程是这样的:我们把校准数据喂给模型跑一遍前向推理,同时记录每一层激活值的统计信息——比如 min、max、直方图。然后根据这些统计信息,算出每个 Tensor 的量化参数(scale 和 zero_point)。

嗯,这里要注意:校准数据集不能参与训练,它只用来收集统计量。我见过有人不小心把校准数据混进训练集,结果量化后的模型过拟合了,跑新数据效果很差。

动态量化 vs 静态量化

这两个概念经常被搞混。我简单解释一下。

动态量化:权重提前量化好,但激活值在推理时动态计算量化参数。每次推理都要算一遍 scale 和 zero_point,所以速度提升有限,但精度损失小。

静态量化:权重和激活值的量化参数都提前算好,推理时直接用量化后的整数计算。速度快,但需要校准数据集来提前统计激活值分布。

对比项 动态量化 静态量化
权重 提前量化 提前量化
激活值 推理时动态计算 提前校准,固定参数
是否需要校准集 不需要 需要
推理速度提升 中等(1.5-2x) 高(2-4x)
精度损失 较小 稍大,但可控
适用场景 CPU 推理、LSTM/Transformer GPU/NPU 推理、CNN

我个人的经验是:如果你的模型跑在 CPU 上,尤其是 NLP 模型(比如 BERT),动态量化是首选。精度几乎不掉,速度还能快一倍。但如果是 CNN 模型跑在 GPU 或 NPU 上,静态量化才是王道。

小技巧:做静态量化时,校准数据集的数量不是越多越好。我试过用 1000 张图片校准,结果和 200 张的效果差不多。关键是数据要有代表性,而不是数量多。

量化粒度:per-tensor vs per-channel

量化粒度,说白了就是「一个量化参数管多大范围」。这个选择直接影响精度和计算效率的平衡。

Per-tensor 量化:整个 Tensor(比如一个卷积层的输出)共享同一个 scale 和 zero_point。简单粗暴,计算快,但精度损失大。

Per-channel 量化:每个输出通道有自己的量化参数。精度高,但计算稍微复杂一点。

为什么会这样?你想想看,一个卷积层有 64 个输出通道,每个通道的数值分布可能差别很大。如果所有通道共用一套量化参数,那些数值范围小的通道就会被「压缩」得很厉害,信息丢失严重。

我曾经在一个目标检测模型上踩过这个坑。用 per-tensor 量化后,mAP 掉了 3 个点。换成 per-channel 量化,只掉了 0.5 个点。差别就这么大。

经验总结:
  • 权重量化:强烈建议用 per-channel。精度提升明显,计算开销增加不大。
  • 激活值量化:一般用 per-tensor 就够了。因为激活值的分布通常比较均匀,per-channel 带来的收益有限。
  • 如果你用的是小模型(比如 MobileNet),per-channel 几乎是必须的。大模型(比如 ResNet-50)对量化更鲁棒,per-tensor 也能接受。

PTQ 的核心流程

下面这张图展示了 PTQ 的完整流程。我建议你仔细看看,尤其是校准和量化参数计算那两步,最容易出错。

训练后量化(PTQ)核心流程 预训练 FP32 模型 准备校准数据集(100-500样本) 前向推理,收集激活值统计量 计算 scale 和 zero_point 生成 INT8 量化模型 关键决策点 • 动态 vs 静态量化 • per-tensor vs per-channel • 校准集代表性 • 对称 vs 非对称量化 常见陷阱 • 校准集过少或过多 • 校准集分布不匹配 • 忽略异常值(outlier)

实战中的避坑指南

讲完了原理,我分享几个实战中容易踩的坑。

第一个坑:校准集和测试集分布不一致。 我曾经在一个工业检测项目上,用实验室采集的数据做校准,结果模型部署到产线上,精度直接掉了 5 个点。后来发现产线上的光照条件和实验室完全不同。解决办法很简单:从产线上实时采集几百张图片做校准。

第二个坑:忽略激活值中的异常值。 有些层的激活值会出现个别特别大的值(outlier),如果直接用 min/max 做量化,整个量化参数都会被带偏。我建议用百分位法,比如取 99.9% 的分位数作为 max,而不是最大值。

第三个坑:per-channel 量化在硬件上不支持。 有些 NPU 只支持 per-tensor 量化。做之前一定要查清楚目标硬件的文档。我吃过这个亏,模型量化好了,部署时发现硬件不支持 per-channel,只能重新来。

我的建议:做 PTQ 之前,先跑一遍精度基准测试。量化后如果精度掉得太多,先检查校准集,再检查量化粒度,最后检查是否有异常值。按这个顺序排查,90% 的问题都能解决。

好了,PTQ 的核心内容就这些。记住一句话:量化不是玄学,是统计学。只要数据选对了,参数调好了,精度损失完全可以控制在 1% 以内。