量化粒度与策略:Per-tensor、Per-channel、Per-group量化;对称量化与非对称量化
聊到量化,很多人第一反应就是「把 FP16 变成 INT8」。但真正上手做的时候你会发现,怎么量化比量化成什么格式更关键。量化粒度选错了,模型精度可能直接崩掉。今天我就把这块掰开揉碎了讲清楚。
一、量化粒度:从粗到细的三个层次
量化粒度,说白了就是多少个参数共享同一个缩放因子。粒度越粗,计算越简单,但精度损失越大;粒度越细,精度保留越好,但计算开销也上去了。这是个典型的 trade-off。
1. Per-tensor 量化(最粗粒度)
整个张量只用一个 scale 和 zero_point。比如一个 4x4 的权重矩阵,所有 16 个元素都用同一个缩放因子。
# 伪代码示意
scale = max(abs(tensor)) / 127.0
quantized_tensor = round(tensor / scale)
我刚开始做量化时,第一个版本就用的 per-tensor。当时觉得「一个 scale 搞定多省事」。结果跑起来发现,某些层的输出误差大得离谱。后来一查,原来是权重分布不均匀——大部分值集中在 0 附近,但有几个 outlier 把 scale 撑得很大,导致小值被严重压缩。
2. Per-channel 量化(中等粒度)
每个输出通道独立计算 scale。对于权重矩阵来说,就是每一行(或每一列)都有自己的缩放因子。
# 假设权重 shape 为 [out_channels, in_channels]
for c in range(out_channels):
scale[c] = max(abs(weight[c, :])) / 127.0
quantized_weight[c, :] = round(weight[c, :] / scale[c])
这是目前工业界最常用的方案。为什么?因为卷积层和线性层的权重,不同通道的分布差异往往很大。你想想看,一个卷积核负责检测边缘,另一个负责检测纹理,它们的数值范围能一样吗?
我个人习惯是:权重用 per-channel,激活用 per-tensor。激活的分布相对稳定,per-tensor 就够了。当然,如果你追求极致精度,激活也可以做 per-channel,但计算复杂度会上升。
3. Per-group 量化(最细粒度)
把通道再分成若干组,每组独立量化。比如 128 个通道一组,或者 32 个一组。
# group_size = 128
num_groups = in_channels // group_size
for g in range(num_groups):
group_data = weight[:, g*group_size : (g+1)*group_size]
scale[g] = max(abs(group_data)) / 127.0
quantized_group = round(group_data / scale[g])
这个方案在低比特量化(比如 4-bit)时特别有用。我记得有一次做 4-bit 量化,per-channel 精度已经不行了,换成 group_size=32 的 per-group,硬是把精度拉回来了。
二、对称量化 vs 非对称量化
这两个概念其实很简单,但很多人容易搞混。我直接上公式:
| 类型 | 公式 | zero_point | 适用场景 |
|---|---|---|---|
| 对称量化 | q = round(x / scale) | 固定为 0 | 权重(通常对称分布) |
| 非对称量化 | q = round(x / scale) + zero_point | 可偏移 | 激活(ReLU 后全正数) |
对称量化:简单粗暴
输入范围是 [-max, max],映射到 [-127, 127](INT8)。zero_point 永远是 0,计算时少一次加减法。
为什么权重适合对称量化?因为训练好的权重,正负值分布通常是对称的。你想想看,一个卷积核的权重,正数和负数的最大值差不多。这时候用对称量化,不会浪费量化区间。
非对称量化:灵活适配
输入范围是 [min, max],映射到 [0, 255](UINT8)。zero_point 可以偏移,用来适配非对称分布。
激活函数比如 ReLU,输出全是非负数。这时候用对称量化,一半的量化区间(负数部分)就浪费了。非对称量化可以把整个 [0, max] 映射到 [0, 255],精度更高。
三、知识体系总览
下面这张图把量化粒度和策略的关系梳理清楚了。你可以把它当作一个决策树:
四、实际项目中的选择策略
说了这么多理论,到底怎么选?我直接给结论:
- 8-bit 量化:权重 per-channel + 对称,激活 per-tensor + 非对称。这是最稳的组合,90% 的场景都适用。
- 4-bit 量化:权重 per-group(group_size=32 或 64)+ 对称,激活 per-tensor + 非对称。精度不够时,把 group_size 调小。
- 混合精度:敏感层用高精度(比如 8-bit),不敏感层用低精度(4-bit)。我有个项目就是这么干的,精度只掉了 0.1%,但推理速度提升了 40%。
嗯,量化粒度和策略这块就讲到这里。记住一句话:没有最好的方案,只有最适合你模型的方案。多试几次,你就能找到那个平衡点。
公众号:蓝海资料掘金营,微信deep3321