3. 模型量化原理:从FP32到INT4,一场精度与速度的博弈
量化,说白了就是把模型从「高精度浮点」搬到「低精度整数」的过程。你想想看,FP32 的模型跑在 NPU 上,就像开着一辆满载的卡车——稳是稳,但跑不快。而 INT8 甚至 INT4,就像把货卸下来换成了小跑车,速度上去了,但得小心别把东西颠碎了。
我在项目中遇到过不少团队,一上来就追求 INT4 量化,结果精度掉得一塌糊涂。后来我总结了一个原则:能用 INT8 就别碰 INT4,除非你实在被带宽逼急了。下面我们一步步拆解这些量化方案。
3.1 量化本质:把连续值映射到离散值
量化的数学本质很简单:
Q = round( (x - zero_point) / scale )
其中 x 是原始浮点值,scale 是缩放因子,zero_point 是零点偏移。反量化就是逆过程:
x_approx = Q * scale + zero_point
嗯,这里要注意:round 操作会引入信息损失。你想想看,FP32 有约 4.2e9 个可表示的值,INT8 只有 256 个。这就像把整个图书馆的书塞进一个小书架——总得扔掉一些。
3.2 FP32、FP16、INT8、INT4 量化原理
3.2.1 FP32 → FP16:最温和的量化
FP16 其实不算严格意义上的量化,它只是把浮点数的位宽砍了一半。FP32 有 8 位指数 + 23 位尾数,FP16 是 5 位指数 + 10 位尾数。好处是:
- 显存/带宽减半
- 计算吞吐翻倍(很多 NPU 对 FP16 有专门的加速单元)
- 精度损失极小,通常 < 0.1%
我曾经在一个语音识别模型上试过,FP16 直接跑,WER(词错误率)只涨了 0.05%。这种方案我建议作为量化的「保底选项」——如果 INT8 搞不定,至少 FP16 能让你先跑起来。
3.2.2 FP32 → INT8:最主流的量化方案
INT8 是目前工业界的主力。为什么?因为大部分 NPU 对 INT8 的计算效率最高,功耗也最低。
INT8 量化需要确定两个参数:scale 和 zero_point。这里就引出了对称量化和非对称量化的区别。
3.2.3 INT4 量化:极限压缩
INT4 只有 16 个可表示的值。说实话,这玩意儿挺难伺候的。我在一个 NLP 模型上试过 INT4 量化,精度直接掉了 5 个点,后来用了混合精度(敏感层保留 INT8)才勉强拉回来。
INT4 的适用场景:
- 对带宽极度敏感的端侧模型
- 模型本身冗余度很高(比如大语言模型)
- 可以接受一定精度损失的场景
3.3 对称量化 vs 非对称量化
这两种方案的区别,说白了就是零点要不要偏移。
| 特性 | 对称量化 | 非对称量化 |
|---|---|---|
| zero_point | 固定为 0 | 可偏移 |
| 表示范围 | [-127, 127](INT8) | [0, 255](INT8) |
| 计算复杂度 | 低(不需要零点修正) | 高(需要零点修正) |
| 适用场景 | 权重、分布对称的激活值 | 分布不对称的激活值(如 ReLU) |
我个人习惯:权重用对称,激活用非对称。为什么?因为权重的分布通常比较规整,对称量化省去了零点修正的计算开销。而激活值(尤其是经过 ReLU 的)全是正数,非对称量化能更好地利用 INT8 的 256 个 bin。
3.4 量化感知训练(QAT) vs 训练后量化(PTQ)
这是量化领域最经典的选择题。我直接说结论:能 PTQ 就别 QAT,除非 PTQ 搞不定。
3.4.1 训练后量化(PTQ)
PTQ 就是模型训练完了,直接拿权重和激活值的统计信息来算 scale 和 zero_point。流程很简单:
- 收集校准数据(几百到几千张图片/样本)
- 跑一遍推理,记录每层的激活值分布
- 根据分布计算量化参数
- 把模型转成 INT8
PTQ 的优点:
- 不需要重新训练,省时省力
- 对大部分模型效果不错
- 工具链成熟(TensorRT、ONNX Runtime 都支持)
缺点:
- 对分布敏感的模型(比如小模型、稀疏模型)效果差
- 无法补偿量化带来的误差
3.4.2 量化感知训练(QAT)
QAT 是在训练过程中模拟量化操作,让模型学会「适应」量化误差。核心思想是:在 forward 时插入 fake quantization 节点,backward 时用 STE(Straight-Through Estimator)近似梯度。
# 伪代码示例:QAT 中的 fake quantization
def fake_quantize(x, scale, zero_point):
# 量化
q = torch.round(x / scale + zero_point)
# 反量化(模拟量化后的精度损失)
x_hat = (q - zero_point) * scale
# STE:梯度直接通过
return x_hat.detach() + x - x.detach()
QAT 的流程:
- 加载预训练模型
- 插入 fake quantization 节点
- 用较小的学习率 fine-tune 几个 epoch
- 导出 INT8 模型
QAT 的优点:
- 精度通常比 PTQ 高 1-3%
- 对敏感模型特别有效
缺点:
- 需要重新训练,耗时
- 调参复杂(学习率、量化起始 epoch 都要调)
3.5 知识体系总览
下面这张图总结了量化方案的选择逻辑:
3.6 实战建议
最后,我总结几条实战经验:
- 先做 PTQ:90% 的模型 PTQ 就够了,别一上来就 QAT 浪费时间。
- 校准集要选对:校准集要覆盖真实场景的分布。我曾经用 ImageNet 的校准集量化了一个医疗模型,结果精度掉了 5%,换成医疗数据后就好了。
- 混合精度是王道:如果 INT8 搞不定,别硬上 INT4。试试敏感层保留 FP16,其他层用 INT8。我在多个项目上验证过,混合精度比纯 INT4 效果好得多。
- 量化后的模型要验证:不要只看 top-1 accuracy,还要看具体场景的指标。比如检测模型要看 mAP,语音模型要看 WER。
好了,这一章的内容就到这里。量化是个实践性很强的工作,建议你拿一个实际模型跑一遍 PTQ 和 QAT,感受一下精度的变化。纸上得来终觉浅,绝知此事要躬行。