4. 量化方法

聊到具体怎么下手做量化,你会发现市面上就两条主流路:训练后量化(PTQ)和量化感知训练(QAT)。说白了,一个是对训练好的模型直接动手,另一个是让模型在训练过程中就学会适应低精度。

我刚开始接触量化那会儿,也觉得QAT听起来更高级,肯定效果更好。但实际项目做多了才发现——没有银弹。选哪个,完全看你的场景、资源和容忍度。

一句话总结:PTQ是快刀斩乱麻,QAT是慢工出细活。前者适合快速部署,后者适合精度敏感场景。

PTQ vs QAT 决策流程 训练好的浮点模型 PTQ:直接校准 + 量化 QAT:插入伪量化节点再训练 少量校准数据 计算缩放因子/零点 输出量化模型 插入伪量化节点 反向传播微调 输出量化模型

4.1 训练后量化(PTQ)—— 拿来就用

PTQ的思路很直接:模型已经训练好了,我拿一小部分数据跑一遍推理,统计出每层激活值和权重的分布范围,然后算出缩放因子和零点,直接完成量化。

你想想看,这就像你做好了一锅汤,出锅前尝一口,觉得咸淡刚好,然后直接分装。整个过程不需要重新开火。

我的经验:PTQ最爽的地方在于——你不需要原始训练代码,不需要训练环境,甚至不需要懂模型内部结构。拿一个ONNX或Caffe模型,跑几百张图片的校准集,半小时就能出量化结果。

PTQ的核心步骤

  1. 准备校准数据集——通常几百到几千张样本,覆盖典型场景即可
  2. 前向传播收集统计量——记录每层激活值的min/max或直方图
  3. 计算量化参数——根据统计量确定scale和zero_point
  4. 转换权重和激活——将浮点数值映射到INT8/INT4
  5. 验证精度——对比量化前后的指标差异

这里有个关键点:校准数据集的质量直接影响量化效果。我在一个安防项目里踩过坑——用了1000张白天场景的图片做校准,结果模型在夜间场景下精度直接掉了5个点。后来把校准集改成白天黑夜各一半,问题就解决了。

PTQ的优缺点

优势 劣势
无需训练,部署极快 大模型或小模型精度损失可能较大
不需要原始训练数据(少量校准数据即可) 对异常值敏感,分布偏移时效果差
计算资源要求低,CPU都能跑 无法恢复因量化丢失的信息
适合快速迭代和实验 某些层(如Depthwise Conv)量化后精度下降明显

注意:PTQ不是万能的。我曾经在MobileNetV3上做PTQ,INT8量化后精度掉了3%。后来换成QAT,精度只掉了0.5%。如果你的模型本身就很紧凑(比如轻量级网络),PTQ可能hold不住。

4.2 量化感知训练(QAT)—— 让模型学会适应

QAT的思路完全不同。它在训练过程中就模拟了量化行为——在模型里插入伪量化节点(Fake Quantize Node),前向传播时把浮点值量化再反量化,让模型在反向传播时感知到量化误差,从而调整权重去适应这种精度损失。

说白了,就是让模型在娘胎里就学会怎么在低精度下好好干活。

QAT的工作流程

  1. 加载预训练浮点模型
  2. 在关键位置插入伪量化节点——通常在权重和激活之后
  3. 使用原始训练数据继续训练——通常需要几个epoch的微调
  4. 冻结量化参数——训练完成后固定scale和zero_point
  5. 移除伪量化节点,导出INT8模型

这里有个细节:QAT训练时用的学习率要比正常训练小很多。我一般设置成原始学习率的1/10到1/100,不然模型容易震荡。

伪量化节点到底干了什么?

伪量化节点做的事情很简单:

# 伪量化操作示意
def fake_quantize(x, scale, zero_point, bit_width=8):
    # 量化
    x_int = torch.round(x / scale + zero_point)
    # 钳制到有效范围
    x_int = torch.clamp(x_int, 0, 2**bit_width - 1)
    # 反量化(模拟量化后的精度损失)
    x_fake = (x_int - zero_point) * scale
    return x_fake

你看,前向传播时它做了量化再反量化,引入了量化误差。但反向传播时,它用的是直通估计器(STE)——直接把梯度传过去,忽略量化操作的不可导性。这样模型就能正常更新权重了。

我的习惯:QAT微调时,我会先用PTQ跑一遍,看看哪些层精度掉得厉害。然后针对这些层单独调整量化配置,或者给它们分配更多的比特数。这样比全模型QAT更高效。

4.3 PTQ vs QAT:怎么选?

这个问题没有标准答案。我根据项目经验总结了一个决策矩阵:

场景 推荐方法 原因
快速原型验证 PTQ 几分钟出结果,够用就行
大模型(ResNet-50以上) PTQ 模型冗余度高,量化损失小
轻量级模型(MobileNet、SqueezeNet) QAT 模型紧凑,PTQ容易崩
精度要求极高(医疗、金融) QAT 能多挽回0.5%就值得
没有训练数据/训练环境 PTQ QAT需要完整训练流程
部署到边缘设备(INT4/混合精度) QAT 低比特量化PTQ几乎不可用

我个人习惯是:先PTQ,再决定要不要QAT。PTQ如果精度损失在可接受范围内(比如1%以内),那就直接用PTQ,省时省力。如果PTQ掉点太多,再上QAT。

一个真实案例:去年我做一个人脸识别模型,ResNet-50 backbone,PTQ后精度掉了0.8%,客户能接受,直接上线。但另一个项目是MobileNetV2做手势识别,PTQ后精度掉了4.2%,完全不能用。后来用QAT微调了3个epoch,精度恢复到只掉0.3%。

4.4 混合方案:PTQ + 部分QAT

有没有折中方案?有。我经常用的一种策略是:

  • 大部分层用PTQ——快速搞定
  • 敏感层用QAT微调——针对性修复

怎么找敏感层?很简单:逐层做PTQ,看哪一层量化后精度掉得最多。然后只对这些层做QAT微调,其他层保持PTQ结果。

这样做的好处是:训练时间只有全QAT的1/5,但精度恢复效果接近全QAT的80%。我在好几个项目里都用这招,效果不错。

避坑指南:我曾经在一个项目里偷懒,PTQ后精度掉了2%,我觉得还行就直接部署了。结果上线后用户反馈在某些场景下识别率明显下降。后来排查发现,那些场景的数据分布和校准集差异很大。所以——校准集一定要覆盖真实部署场景,不然PTQ就是自欺欺人。

4.5 小结

PTQ和QAT不是对立关系,而是工具链上的两个选项。PTQ是快车道,适合大多数场景;QAT是精细活,适合精度敏感或低比特场景。我的建议是:先跑PTQ,看结果,再决定要不要上QAT。别一上来就QAT,那是在浪费算力。