3. PTQ与QAT:两种量化方案的实战抉择

聊到模型量化,圈子里最常争论的就是PTQ和QAT谁更好。说实话,这问题没有标准答案。我做了几年端侧部署,两种方案都踩过坑,今天就把我的经验掰开揉碎讲给你听。

3.1 Post-Training Quantization(PTQ)原理

PTQ,全称是训练后量化。说白了,就是模型训练完了,我再动手脚。你训练好的浮点模型,我直接拿过来,通过一些统计手段,把权重和激活值从FP32映射到INT8。

它的核心流程其实就三步:

  1. 收集统计量:跑一小批校准数据,观察每一层激活值的分布范围(min/max或者百分位)
  2. 计算缩放因子:根据统计到的范围,算出scale和zero_point
  3. 执行量化:把浮点数值映射到定点整数空间

这里有个关键点——校准数据集的选择。我在项目中遇到过,有人随便拿了几张图就跑校准,结果模型精度直接崩了。校准数据最好能覆盖真实场景的分布,数量不用多,几百张就够了,但一定要有代表性。

PTQ的数学本质

量化过程可以理解为:q = round(r / scale) + zero_point

其中r是浮点值,q是量化后的整数,scale和zero_point就是我们要找的参数。

3.2 PTQ的优缺点

PTQ最大的优点就是快。真的快。我试过对一个ResNet50做PTQ,从准备校准数据到导出量化模型,前后不到半小时。不需要重新训练,不需要改网络结构,甚至不需要原始训练代码。

但缺点也很明显——精度损失不可控。尤其是对小型模型或者敏感层,PTQ可能会带来1%-3%的精度下降。我曾经在一个语音唤醒模型上试过PTQ,结果唤醒率直接掉了5个点,排查了半天才发现是某个卷积层的激活值分布太宽,量化后信息丢失严重。

维度 PTQ表现
部署速度 极快,几小时搞定
精度损失 中等,大模型损失小,小模型损失大
需要的数据 少量校准数据(无标签也可)
工程复杂度 低,几乎不需要改代码

我的建议:如果你的模型比较大(比如超过100M参数),或者对精度要求不那么苛刻,优先尝试PTQ。它是最省力的方案。

3.3 Quantization-Aware Training(QAT)原理

QAT就不一样了。它是在训练过程中就模拟量化效果,让模型自己去适应量化带来的误差。你想想看,模型在训练时就知道自己将来要被量化,它就会主动调整权重分布,让量化后的损失最小化。

QAT的核心是伪量化节点(Fake Quantize)。这些节点插入在网络的每一层前后,在前向传播时模拟量化和反量化的过程,但反向传播时仍然使用浮点梯度更新权重。

# 伪代码示例:QAT中的前向传播
def forward(x):
    # 模拟量化
    x_q = quantize(x, scale, zero_point)
    # 模拟反量化(保持浮点计算)
    x_fake = dequantize(x_q, scale, zero_point)
    # 后续计算
    y = conv(x_fake)
    return y

这里有个细节很多人会忽略——直通估计器(STE)。因为量化操作不可导,QAT用STE来近似梯度:前向传播走量化路径,反向传播时直接把梯度传过去,忽略量化带来的梯度截断。嗯,这招虽然粗糙,但实践中效果出奇的好。

注意:QAT训练时,学习率要调小。我一般设置为原始训练学习率的1/10到1/100。学习率太大,模型容易在量化边界附近震荡,收敛不了。

3.4 QAT的优缺点

QAT的优点就是精度高。我做过对比,同样一个语音识别模型,PTQ后WER(词错误率)上升了2.3%,而QAT只上升了0.4%。对于生产环境来说,这个差距可能就是能不能上线的问题。

但代价也很明显——训练成本高。你需要完整的训练流程,需要带标签的数据,需要调参。而且训练时间会变长,因为伪量化节点增加了计算量。我记得有一次做QAT,一个模型训了3天才收敛,而PTQ只用了2小时。

维度 QAT表现
部署速度 慢,需要完整训练流程
精度损失 极小,通常<1%
需要的数据 大量带标签训练数据
工程复杂度 高,需要修改训练代码

3.5 两种方法的优劣对比

好了,咱们把PTQ和QAT放在一起比比看。我直接说结论:

  • 精度优先:选QAT。尤其是语音模型、小模型、对精度敏感的模型。
  • 速度优先:选PTQ。快速验证、原型开发、大模型场景。
  • 折中方案:先用PTQ快速评估,如果精度不达标,再针对关键层做QAT微调。

我曾经在一个项目里就是这么干的。先跑PTQ,发现某个注意力层的量化误差特别大,然后只对这一层做QAT微调,其他层保持PTQ结果。最后精度恢复了1.8%,而训练时间只增加了半天。这种混合方案,我个人觉得是工程上最实用的。

避坑指南

我曾经犯过一个错误——在QAT训练时忘了冻结BN层的统计量。结果模型在验证集上精度很高,但部署到端侧后完全跑偏。后来排查才发现,BN层的running_mean和running_var在QAT训练时被更新了,导致推理时的分布和训练时不一致。

所以记住:QAT训练时,BN层要设置为eval模式,或者冻结其统计量。

3.6 实战选择建议

最后给个实操建议。如果你刚接触量化,我建议你:

  1. 先跑PTQ:花半天时间,看看精度损失多少。如果损失在可接受范围内,直接上线。
  2. 如果精度不够:分析哪些层量化误差大(可以用余弦相似度或者SNR来评估),对这些层做QAT微调。
  3. 如果还不行:考虑换模型结构,或者用更宽的位宽(比如INT16混合精度)。

记住,量化不是银弹。有时候模型本身设计就不适合量化,比如用了太多激活值分布不均匀的激活函数(像Swish、GELU)。这种情况下,再好的量化方案也救不了。嗯,这就是另一个话题了。

一句话总结:PTQ是快刀斩乱麻,QAT是慢工出细活。根据你的场景选,别盲目跟风。