2. 量化类型
聊到量化,你最先要搞清楚的,就是「量化成什么样子」。
我刚开始接触模型量化时,以为就是把 float32 转成 int8 就完事了。结果一上手,发现里面门道不少。同样是 int8,有的是对称的,有的是非对称的;有的只量化权重,有的连激活值一起动。
这一节,咱们就把这两组概念彻底掰扯清楚。
对称量化 vs 非对称量化
先看一个最基础的问题:量化后的整数范围,怎么映射到原始的浮点数范围?
对称量化,说白了就是让量化后的零点(zero-point)等于 0。原始浮点数的 0,量化后还是 0。正负范围是对称的。
举个例子,int8 对称量化,范围是 [-127, 127](有的实现用 [-128, 127])。原始浮点数的最大值和最小值,对称地映射到这个范围的两端。
对称量化的核心公式:
scale = max(|x_max|, |x_min|) / 127
x_quant = round(x / scale)
零点固定为 0,不需要存储。
非对称量化就不一样了。它的零点可以不是 0。原始浮点数的 0,量化后可能对应一个非零的整数。
非对称量化的范围是 [0, 255](uint8)。它能把浮点数的整个范围「塞」进这个区间,不管正负比例如何。
非对称量化的核心公式:
scale = (x_max - x_min) / 255
zero_point = round(-x_min / scale)
x_quant = round(x / scale) + zero_point
需要存储 scale 和 zero_point 两个参数。
你可能会问:那到底用哪个?
我个人习惯这样判断:
- 如果数据分布接近对称(比如权重经过训练后,正负值差不多),用对称量化。计算简单,硬件友好。
- 如果数据分布明显偏斜(比如 ReLU 后的激活值,全是非负的),用非对称量化。能充分利用量化范围,精度损失更小。
我在项目中遇到过一件事:有个模型,激活值全是正数,我一开始用了对称量化,结果精度掉了 2 个点。换成非对称量化后,精度只掉了 0.3 个点。差别就这么大。
我的建议:
权重一般用对称量化,激活值一般用非对称量化。这不是绝对的,但 80% 的场景都适用。
权重量化 vs 激活量化
接下来是第二个维度:量化对象。
权重量化,量化的是模型里那些固定的参数。权重在推理时是不变的,所以你可以离线算好 scale 和 zero_point,推理时直接用。
激活量化,量化的是每一层的输出(也就是下一层的输入)。激活值在推理时是动态变化的,你得在线统计它的范围,或者提前用校准数据集跑一遍,算出统计值。
这两者的区别,我用一个表格说清楚:
| 对比维度 | 权重量化 | 激活量化 |
|---|---|---|
| 数据特性 | 静态,推理时不变 | 动态,随输入变化 |
| 量化时机 | 离线,一次搞定 | 需校准集或在线统计 |
| 精度影响 | 相对较小 | 相对较大 |
| 实现难度 | 低 | 高 |
| 硬件加速收益 | 中等 | 高(减少访存) |
你想想看,只量化权重,不量化激活值,那计算时还是 float32 的乘法,加速效果有限。只有把激活值也量化了,才能用上 int8 的矩阵乘,速度才能起飞。
但激活量化有个坑:分布不稳定。同一个模型,输入不同的图片,某一层的激活值范围可能差好几倍。如果你用固定的 scale,遇到 outlier 就会截断,精度直接崩掉。
我曾经踩过的坑:
有个检测模型,激活量化后精度掉了 5 个点。排查了半天,发现是某一层的激活值偶尔会出现一个很大的值(比校准集里的最大值大 10 倍)。校准集没覆盖到这种情况,scale 设小了,大量正常值被截断。
后来我改用 per-tensor + 动态范围裁剪,才把精度拉回来。
四种组合,一张图看懂
对称/非对称、权重/激活,两两组合,一共四种情况。我画了张图,帮你理清关系:
实际部署时怎么选?
说了这么多,你肯定想知道:真正干活的时候,到底怎么配?
我直接给你一个参考配置:
# 常见的量化配置(以 TensorRT 为例)
量化策略:
- 权重:对称量化,per-channel
- 激活:非对称量化,per-tensor
- 校准方法:熵校准(Entropy Calibration)
# 为什么这么配?
# 1. 权重分布通常对称,对称量化省掉 zero_point,计算更快
# 2. 激活值多为非负,非对称量化能更好利用 uint8 范围
# 3. per-channel 对权重更精细,减少精度损失
# 4. 熵校准能自动找到最优截断阈值
当然,这不是银弹。我遇到过一些特殊情况:
- 某些轻量模型(如 MobileNet),激活值分布很窄,对称量化反而更好。因为非对称量化的 zero_point 会引入额外的计算开销。
- 某些 NLP 模型,权重和激活的分布都很奇怪,需要逐个 layer 调试,不能一刀切。
我的调试流程:
- 先用默认配置(权重对称 + 激活非对称)跑一遍
- 看精度损失,如果 < 0.5%,直接收工
- 如果损失大,逐层分析,看哪层是瓶颈
- 对瓶颈层单独调整量化方式或校准方法
80% 的情况,第一步就够了。
小结一下
对称量化和非对称量化,本质是「零点要不要偏移」的问题。权重量化和激活量化,本质是「量化谁」的问题。
这两组概念组合起来,就是你在实际部署中要做的选择。没有绝对的对错,只有适合不适合。
嗯,记住一句话:权重用对称,激活用非对称,先跑起来再说。