3. 模型量化基础:INT8/INT4量化原理、对称与非对称量化、量化感知训练
量化这个话题,说白了就是把模型从浮点数世界拽到整数世界。为什么要这么干?因为整数运算快、省带宽、省功耗。我在做边缘端部署时,一个模型从FP32压到INT8,推理速度能翻3-4倍,功耗直接砍半。今天我们就聊聊量化的核心原理和实战要点。
3.1 量化原理:从浮点到整数的映射
量化的本质就是一个线性映射。我们把浮点数的范围映射到整数的表示范围上。比如INT8,取值范围是[-128, 127](有符号)或[0, 255](无符号)。
数学上很简单:
Q = round(r / S) + Z
其中:
- r 是原始浮点值
- S 是缩放因子(scale)
- Z 是零点偏移(zero point)
- Q 是量化后的整数值
反量化就是逆过程:
r = (Q - Z) × S
嗯,这里要注意:S 和 Z 是量化参数,它们决定了映射的精度。S越小,精度越高,但能表示的范围越小。这是个典型的精度-范围权衡。
核心指标:量化误差 = |r - r̂|,其中r̂是反量化后的值。误差主要来自舍入操作。
3.2 对称量化 vs 非对称量化
这两种方式我都在项目中用过。简单说:
- 对称量化:Z = 0,浮点0映射到整数0。范围对称,[-α, α] 映射到 [-127, 127](INT8有符号)。
- 非对称量化:Z ≠ 0,浮点0可以映射到任意整数。范围灵活,[min, max] 映射到 [0, 255](INT8无符号)。
| 特性 | 对称量化 | 非对称量化 |
|---|---|---|
| 零点偏移 | Z = 0 | Z ≠ 0 |
| 实现复杂度 | 低(乘法少) | 高(需要额外减法) |
| 精度利用率 | 低(可能浪费范围) | 高(完全利用范围) |
| 适用场景 | 权重分布对称(如ReLU后) | 权重分布不对称(如sigmoid输出) |
| 硬件友好度 | 高(很多硬件只支持对称) | 低(需要额外逻辑) |
我个人习惯:权重用对称量化,激活值用非对称量化。为什么?权重分布通常接近对称(均值为0),对称量化不会损失太多精度。激活值分布往往偏斜(比如ReLU后全是非负),非对称量化能更好地利用整数范围。
实战技巧:我在部署MobileNet时发现,如果激活值也用对称量化,精度掉了2.3%。换成非对称量化后,只掉了0.7%。这个差距在敏感任务上很要命。
3.3 INT8 vs INT4:精度与效率的博弈
INT4量化是近两年的热点。它比INT8更激进:
- INT8:256个量化级别,精度损失可控,大部分模型都能接受
- INT4:16个量化级别,精度损失大,但带宽和计算量再减半
我曾经在语音识别模型上试过INT4,结果WER(词错误率)从8.5%飙到了15.2%。后来改用混合精度——敏感层用INT8,非敏感层用INT4——才把WER压回9.1%。
| 指标 | INT8 | INT4 |
|---|---|---|
| 量化级别 | 256 | 16 |
| 存储压缩比(vs FP32) | 4× | 8× |
| 典型精度损失 | < 1% | 1-5% |
| 硬件支持 | 广泛(CPU/GPU/NPU) | 有限(部分NPU/专用加速器) |
| 推荐场景 | 通用部署 | 资源极度受限 |
避坑指南:我曾经在图像超分模型上直接用INT4量化,结果输出图像全是色块。后来发现是激活值范围太大,量化步长S太大导致信息丢失。解决方案:先做激活值裁剪(clipping),把异常大值截断,再量化。
3.4 量化感知训练(QAT)
量化感知训练,说白了就是在训练过程中模拟量化误差,让模型学会适应量化。和训练后量化(PTQ)不同,QAT把量化操作插入计算图,反向传播时通过直通估计器(STE)传递梯度。
QAT的核心流程:
- 插入伪量化节点:在权重和激活值后面加fake_quant操作
- 前向传播:用量化后的值计算,但梯度还是浮点的
- 反向传播:STE把梯度直接通过量化节点(忽略舍入操作)
- 更新参数:浮点权重在后台更新,量化参数(S, Z)也同步更新
# 伪代码:QAT中的fake_quant
def fake_quantize(x, scale, zero_point, num_bits=8):
# 量化
q = torch.round(x / scale + zero_point)
# 截断到整数范围
q = torch.clamp(q, 0, 2**num_bits - 1)
# 反量化(模拟量化误差)
x_hat = (q - zero_point) * scale
return x_hat
# 前向时用x_hat,反向时梯度直接通过
关键参数:QAT的学习率通常比正常训练小10倍。我一般用1e-5起步,否则权重更新太快,量化参数跟不上。
QAT和PTQ的对比:
| 维度 | PTQ(训练后量化) | QAT(量化感知训练) |
|---|---|---|
| 是否需要训练 | 否 | 是(需要少量微调) |
| 精度恢复能力 | 有限 | 强(可接近FP32精度) |
| 部署时间 | 几分钟 | 几小时到几天 |
| 适用模型 | 大模型、鲁棒模型 | 小模型、精度敏感模型 |
| 典型精度损失 | 0.5-2% | < 0.5% |
我个人建议:先跑PTQ看看精度损失。如果损失在可接受范围内,直接用PTQ,省时省力。如果损失太大,再上QAT。我在部署YOLOv5时,PTQ掉了1.8%的mAP,QAT微调了3个epoch后只掉了0.3%。
3.5 量化参数校准
量化参数S和Z怎么确定?需要校准数据集。校准过程:
- 收集统计量:跑几百到几千张图片,记录每层激活值的min/max或分布
- 选择量化范围:可以用min/max,也可以用百分位(比如99.9%分位)
- 计算S和Z:根据选定的范围计算缩放因子和零点
校准方法对比:
| 方法 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| MinMax | 用实际min/max | 简单、无超参 | 对异常值敏感 |
| Percentile | 用指定分位值 | 抗异常值 | 需要调分位参数 |
| KL散度 | 最小化量化前后分布差异 | 精度最优 | 计算量大 |
| MSE | 最小化均方误差 | 平衡精度和计算 | 需要搜索最优范围 |
我的经验:校准集大小选500-1000张就够了。太多反而可能过拟合到校准集的分布。另外,校准集要和实际部署场景的数据分布一致,否则量化参数会偏。
3.6 量化部署实战流程
最后,给一个我常用的量化部署流程:
- 模型准备:导出FP32模型,确保计算图固定(没有动态shape)
- 校准:用校准集跑一遍,收集每层的激活值统计量
- PTQ尝试:先做训练后量化,评估精度损失
- 精度检查:如果损失 < 1%,直接部署;否则进入QAT
- QAT微调:插入伪量化节点,用低学习率微调3-5个epoch
- 导出INT8模型:冻结量化参数,导出为INT8格式
- 硬件部署:在目标硬件上跑推理,验证性能和精度
最后提醒:量化不是万能药。有些模型(比如GAN、Transformer)对量化特别敏感。我遇到过BERT量化后精度掉了5%以上,最后只能做混合精度——注意力层用FP16,其他层用INT8。所以,量化策略要因模型而异,别一刀切。