4. 量化技术基础:FP32 vs FP16 vs INT8、量化原理、对称/非对称量化
说到推理延迟优化,量化是绕不开的一关。说白了,就是用更少的数据位宽去表示模型参数和激活值。我刚开始接触这玩意儿时也觉得玄乎——把 FP32 砍成 INT8,精度不掉?后来亲手调过几个模型才明白,这里面门道不少。
4.1 数值表示:FP32、FP16、INT8 到底差在哪?
先看一张表,把三种主流数据类型的核心参数摆出来。你对比一下,心里就有数了。
| 数据类型 | 位宽 | 表示范围 | 精度(尾数位) | 典型场景 |
|---|---|---|---|---|
| FP32 | 32 bit | ~±3.4×10³⁸ | 23 bit | 训练、高精度推理 |
| FP16 | 16 bit | ~±6.5×10⁴ | 10 bit | 混合精度训练、部分推理 |
| INT8 | 8 bit | [-128, 127] 或 [0, 255] | 8 bit 均匀分布 | 推理加速、边缘部署 |
FP32 是「老大哥」,精度高但占地方。FP16 是「轻量版」,范围窄了但速度能翻倍。INT8 则是「特种兵」,位宽只有 FP32 的四分之一,计算吞吐量能提升 2-4 倍。我在项目中遇到过,把一个大模型从 FP32 压到 INT8,推理延迟从 120ms 降到了 35ms,效果立竿见影。
核心结论:量化不是简单的「砍位宽」,而是用更少的比特数去逼近原始数值分布。关键在于找到合适的映射关系。
4.2 量化原理:从浮点到整数的映射
量化本质上是一个数学映射。假设原始浮点数值范围是 [r_min, r_max],目标整型范围是 [q_min, q_max]。我们需要一个线性变换:
q = round(r / scale + zero_point)
r = (q - zero_point) * scale
这里面有两个关键参数:scale(缩放因子)和 zero_point(零点偏移)。scale 决定了每个整型步长代表多少浮点值,zero_point 则保证浮点 0 能精确映射到整型空间。
举个例子。假设某层权重在 [-2.0, 2.0] 之间,要量化到 INT8 的 [-128, 127]:
scale = (2.0 - (-2.0)) / (127 - (-128)) = 4.0 / 255 ≈ 0.0157
zero_point = round(-(-2.0) / 0.0157) + (-128) = round(127.4) - 128 = 0
嗯,这里要注意:zero_point 计算时一定要做 round 操作,否则反量化时会引入系统性偏差。我曾经因为忘记 round,导致量化后的模型精度掉了 5 个点,排查了半天才发现是零点没对齐。
4.3 对称量化 vs 非对称量化
这两种方式,说白了就是 zero_point 怎么处理。
4.3.1 对称量化
对称量化强制 zero_point = 0。浮点范围以 0 为中心对称,映射到整型范围也是对称的。公式简化为:
q = round(r / scale)
r = q * scale
优点:计算简单,硬件实现友好。很多 GPU 和 NPU 的 INT8 指令集直接支持对称量化。
缺点:如果浮点分布不对称(比如 ReLU 后的激活值全是非负的),会浪费一半的整型表示范围。
4.3.2 非对称量化
非对称量化允许 zero_point 不为 0。浮点范围可以任意偏移,整型范围也能被充分利用。
优点:表示效率高,尤其适合激活值这种非对称分布。
缺点:计算时多一步 zero_point 的加减,硬件实现稍复杂。
| 对比维度 | 对称量化 | 非对称量化 |
|---|---|---|
| zero_point | 固定为 0 | 根据数据分布计算 |
| 计算复杂度 | 低 | 中 |
| 表示效率 | 对称分布 100%,非对称分布 < 50% | 接近 100% |
| 硬件友好度 | 高 | 中 |
| 典型应用 | 权重、LayerNorm | 激活值、Embedding |
我的习惯:权重层我一般用对称量化,因为训练好的权重分布通常接近对称。激活层我倾向非对称量化,尤其是 ReLU 后的输出。这样组合下来,精度损失通常能控制在 0.5% 以内。
4.4 量化粒度:逐层 vs 逐通道
量化粒度决定了 scale 和 zero_point 的共享范围。粒度越细,精度越高,但计算开销也越大。
- 逐层量化(Per-Tensor):整层共享一组 scale/zero_point。实现简单,但遇到层内数值分布差异大时,精度损失明显。
- 逐通道量化(Per-Channel):每个输出通道独立计算 scale/zero_point。精度更好,尤其适合卷积层。我建议在卷积和全连接层用逐通道量化。
- 逐组量化(Per-Group):将通道分成若干组,每组独立量化。这是精度和计算量的折中方案,在 LLM 量化中比较常见。
避坑指南:我曾经在 MobileNet 上尝试逐层量化,结果精度掉了 8%。换成逐通道量化后,精度只掉了 1.2%。所以,轻量级网络对量化粒度更敏感,建议优先用逐通道。
4.5 量化流程全景图
下面这张图把量化流程串起来了。从原始浮点模型开始,经过校准、量化、推理,最后输出整型结果。你跟着箭头走一遍,就能理解全貌。
从图上你能看到,量化不是一锤子买卖。校准数据集的质量直接影响 scale/zero_point 的准确性。我建议校准集至少选 500-1000 张样本,覆盖模型常见的输入分布。如果校准集太偏,量化后的模型在真实场景下可能会「翻车」。
4.6 实战中的量化选择建议
说了这么多理论,最后给点实在的建议。你根据场景对号入座:
- 追求极致速度:用对称量化 + 逐层量化。虽然精度损失大一点,但计算最快。适合对延迟极度敏感的场景。
- 平衡精度与速度:权重用对称逐通道量化,激活用非对称逐层量化。这是我在大多数项目中的默认配置。
- 追求最高精度:全部用非对称逐通道量化。精度损失最小,但计算开销也最大。适合对精度要求苛刻的金融、医疗场景。
一个小技巧:量化后一定要做精度验证。我习惯用 KL 散度来评估量化前后激活值的分布差异。如果 KL 散度超过 0.01,说明量化策略需要调整。这个阈值是我在多个项目中试出来的,你可以参考。
量化技术就讲到这里。记住,没有银弹,只有最适合你场景的方案。动手试一遍,比看十遍理论都管用。