2. 模型量化基础

模型量化,说白了就是给模型「减肥」。

我刚开始接触这个领域时,觉得量化就是个简单的精度转换——把32位浮点数变成16位或者8位整数。后来踩了不少坑才发现,这里面的门道比想象中深得多。

今天咱们就聊聊量化的基本原理,以及FP16、INT8这些常见量化方式。还有两个绕不开的话题:量化感知训练(QAT)和训练后量化(PTQ)。最后我会说说量化对精度到底有多大影响。

模型量化 基本原理 FP16 / INT8 QAT vs PTQ 精度影响 映射关系 缩放因子 动态范围 精度损失 训练时模拟 后训练转换 精度-速度权衡

量化的基本原理

量化是什么?简单说,就是把连续的浮点数映射到离散的整数上。

你想想看,一个FP32的数值范围大约是±3.4×10³⁸,而INT8只有-128到127。我们要把这么大一个范围压缩到这么小的空间里,肯定得丢点东西。

核心公式其实就一个:

Q = round(r / S) + Z

其中:

  • r 是原始浮点值
  • S 是缩放因子(scale)
  • Z 是零点偏移(zero point)
  • Q 是量化后的整数值

反量化就是反过来:

r ≈ (Q - Z) × S

关键点:量化不是简单的截断,而是带缩放和偏移的映射。缩放因子S决定了每个整数步长代表多少浮点数,零点Z则解决了浮点0和整数0不对齐的问题。

我在项目中遇到过一个问题:有些模型的权重分布特别不均匀,比如大部分值集中在[-0.1, 0.1]之间,但有几个异常值跑到±10去了。这时候如果直接用全局的min/max做量化,小值区域的精度会损失得非常厉害。

FP16与INT8量化

FP16和INT8是目前最主流的两种量化方式。它们的目标不同,适用场景也不同。

FP16量化

FP16,也叫半精度浮点。它用16位表示一个数:1位符号位、5位指数位、10位尾数位。

FP16的好处是:

  • 内存占用直接减半(相比FP32)
  • 计算速度提升明显(现代GPU对FP16有专门优化)
  • 精度损失很小,基本不影响模型效果

我个人的习惯是:只要硬件支持,推理时优先用FP16。它几乎是个「免费午餐」——不需要额外调参,直接转换就行。

小技巧:在PyTorch中,用model.half()就能把模型参数转成FP16。但要注意,某些算子(比如LayerNorm)在FP16下可能不稳定,需要保留FP32计算。

INT8量化

INT8就狠多了。它把每个数压缩到8位,内存占用只有FP32的1/4。

但代价也很明显:精度损失大,而且需要仔细处理。

INT8量化的常见策略有两种:

  1. 对称量化:零点Z=0,只计算缩放因子S。适合权重分布对称的情况。
  2. 非对称量化:零点Z不为0,可以更好地拟合非对称分布。适合激活值(通常是ReLU后的正数)。

举个例子,假设我们要量化一个权重矩阵:

# 对称量化示例
import numpy as np

weights = np.array([-1.2, 0.5, 2.3, -0.8, 1.1])
max_abs = np.max(np.abs(weights))  # 2.3
scale = max_abs / 127  # 0.0181

quantized = np.round(weights / scale).astype(np.int8)
# 结果: [-66, 28, 127, -44, 61]

dequantized = quantized * scale
# 近似: [-1.19, 0.50, 2.30, -0.80, 1.10]

注意:INT8量化对异常值非常敏感。如果权重中有个别很大的值,会导致整个量化范围被拉宽,小值区域的精度会严重下降。我曾经在一个NLP模型上遇到过这种情况,最后不得不做剪枝把异常值去掉。

量化感知训练(QAT)与训练后量化(PTQ)

这两个是量化的两种主要实现路径。说白了,一个是在训练时就考虑量化,一个是训练完了再转。

训练后量化(PTQ)

PTQ是最简单的方式。模型训练完成后,直接对权重和激活值做量化。

步骤大致是:

  1. 收集少量校准数据(几百张图片或几十个batch)
  2. 跑一遍前向推理,统计每层的激活值分布
  3. 根据统计结果计算每层的缩放因子和零点
  4. 把权重和激活值转成INT8

PTQ的优点很明显:不需要重新训练,几分钟就能搞定。但缺点也突出:精度损失可能比较大,尤其是对小的模型或者敏感的任务。

我记得有一次给一个目标检测模型做PTQ,mAP直接从0.72掉到了0.65。嗯,这个损失太大了,客户接受不了。

量化感知训练(QAT)

QAT的思路是:在训练过程中模拟量化操作,让模型自己学会适应量化带来的误差。

具体做法是:

  • 在前向传播时,插入伪量化节点(fake quantization node)
  • 这些节点模拟量化和反量化的过程
  • 反向传播时,使用直通估计器(STE)来近似梯度
  • 训练完成后,移除伪量化节点,导出真正的INT8模型

QAT的效果通常比PTQ好很多。还是刚才那个目标检测模型,用QAT后mAP只掉了0.01,几乎可以忽略。

我的建议:如果模型对精度要求很高(比如医疗影像、自动驾驶),或者模型本身比较小(MobileNet、TinyBERT这类),优先考虑QAT。如果模型很大、冗余度高(比如ResNet-152、GPT系列),PTQ通常就够了。

量化对精度的影响

量化一定会损失精度,这是物理规律。但损失多少,取决于几个因素:

因素 影响程度 说明
量化位宽 高 INT8损失通常大于FP16,INT4损失更大
模型大小 中 大模型冗余度高,量化损失相对较小
任务敏感度 高 分类任务相对鲁棒,检测/分割任务更敏感
量化方法 高 QAT通常优于PTQ,逐层量化优于全局量化
校准数据 中 校准数据分布越接近真实数据,效果越好

我个人的经验是:FP16量化几乎不影响精度,可以无脑用。INT8量化需要评估,但大部分场景下精度损失在1%以内。如果超过2%,就要考虑换QAT或者做其他优化了。

避坑指南:我曾经在一个语音识别模型上做INT8量化,结果WER(词错误率)从8%飙到了15%。排查了半天,发现是某些层的激活值范围特别大,导致量化后信息丢失严重。后来对这些层单独做了per-channel量化,才把WER降回8.5%。

量化不是银弹。它是在精度、速度和内存之间做权衡。理解了这个权衡,你就能在合适的场景下做出正确的选择。

好了,这一章的内容就到这里。量化是个实践性很强的话题,建议你找个模型亲手试试PTQ和QAT,感受一下精度和速度的变化。