核心问题: 量化时,究竟应该让整个权重矩阵共享一套
scale和zero point,还是为不同通道乃至不同权重组分别计算量化参数?
假设有一个全连接层:
$$ Y=XW^{T}, $$权重矩阵 $W$ 的形状为:
$$ W\in\mathbb{R}^{4\times 8}. $$也就是说,这一层有 4 个输出通道,每个输出通道有 8 个权重,总共有 32 个权重。例如:
$$ W= \begin{bmatrix} 0.1 & 0.2 & 0.3 & 0.4 & 0.2 & 0.1 & 0.3 & 0.2\\ -1.0 & 1.5 & -2.0 & 2.2 & -1.8 & 1.2 & -2.1 & 2.0\\ 10 & 12 & 9 & 11 & 13 & 8 & 10 & 12\\ -0.01 & 0.02 & -0.03 & 0.01 & 0.02 & -0.01 & 0.03 & -0.02 \end{bmatrix}. $$不同行的数值范围差别很大:
| 输出通道 | 大致范围 |
|---|---|
| 第 0 行 | $[-0.4,0.4]$ 附近 |
| 第 1 行 | $[-2.2,2.2]$ |
| 第 2 行 | $[-13,13]$ |
| 第 3 行 | $[-0.03,0.03]$ |
问题在于:应该让这 32 个权重使用同一个 scale,还是分别使用不同的 scale?这就产生了逐层、逐通道和逐组量化。
逐层量化
逐层量化也叫:
- Per-layer quantization;
- 在很多语境中也叫 Per-tensor quantization。
对于整个权重矩阵 $W$,只计算一套量化参数:
$$ s_W,\qquad z_W. $$量化时,所有元素都使用相同的 scale 和 zero point:
对于前面的 $4\times 8$ 矩阵,整个 $W$ 只共用一个 scale:
W[0, :] ┐
W[1, :] │
W[2, :] ├── scale_0
W[3, :] ┘
因此,scale 集合只有一个元素:
示例
假设采用 INT8 对称量化,整数范围通常取:
$$ [-127,127]. $$整个矩阵中最大的绝对值为:
$$ \max(|W|)=13. $$因此:
$$ s=\frac{13}{127}\approx 0.1024. $$这意味着相邻两个可表示浮点数之间的间隔约为 $0.1024$。对于第三行中的 $10$、$12$、$13$ 来说,这个精度还可以;但是第四行的数值范围是:
$$ [-0.03,0.03]. $$其中所有数值都小于半个量化步长:
$$ \frac{s}{2}\approx 0.0512. $$因此,第四行中的权重可能全部被量化为:
$$ q=0. $$反量化后也全部变成:
$$ \widehat{W}=0. $$也就是说,小数值通道中的信息可能完全消失。
优缺点
优点:
- 只需要保存一套
scale和zero point; - 实现简单;
- 量化元数据很少;
- 计算内核容易优化;
- 通常速度较高。
缺点:
- 容易受到异常值和大数值通道影响;
- 不同通道数值分布差异较大时,量化误差明显;
- 低比特量化,尤其是 INT4 时,精度通常较差。
适用情况
逐层量化适合:
- 数值分布比较均匀的张量;
- 对精度要求不高的场景;
- INT8 激活量化;
- 硬件只支持较粗量化粒度的场景。
逐通道量化
逐通道量化也叫 Per-channel quantization。对权重而言,通常是每个输出通道使用一套量化参数。
对于线性层权重:
$$ W\in\mathbb{R}^{C_{\mathrm{out}}\times C_{\mathrm{in}}}, $$通常每一行对应一个输出通道,因此每行分别计算 scale:
量化公式变为:
$$ Q_{ij} =\operatorname{round}\left(\frac{W_{ij}}{s_i}\right)+z_i. $$注意,scale 由行号 $i$ 决定,而不是整个矩阵共用一个。
前面的矩阵有 4 行,因此有 4 套参数:
W[0, :] ─── scale_0
W[1, :] ─── scale_1
W[2, :] ─── scale_2
W[3, :] ─── scale_3
scale 张量的形状是:
为了广播,也可以写成:
$$ S\in\mathbb{R}^{4\times 1}. $$这里所谓输出通道,就是按矩阵的行进行量化。因为在 $Y=XW^T$ 的约定下,$W$ 的列数对应输入维度,行数对应输出维度。
各通道分别计算 scale
仍然采用 INT8 对称量化。
第一行最大绝对值约为 $0.4$:
$$ s_0=\frac{0.4}{127}\approx 0.00315. $$第二行最大绝对值为 $2.2$:
$$ s_1=\frac{2.2}{127}\approx 0.01732. $$第三行最大绝对值为 $13$:
$$ s_2=\frac{13}{127}\approx 0.10236. $$第四行最大绝对值为 $0.03$:
$$ s_3=\frac{0.03}{127}\approx 0.000236. $$因此:
$$ S= \begin{bmatrix} 0.00315\\ 0.01732\\ 0.10236\\ 0.000236 \end{bmatrix}. $$现在第四行不再使用由第三行大数值所决定的 $0.1024$ 步长,而是使用 $0.000236$。原来的 $0.01$ 可以量化为:
$$ q=\operatorname{round}\left(\frac{0.01}{0.000236}\right)\approx 42. $$反量化得到:
$$ \widehat{x}=42\times 0.000236\approx 0.00991. $$这个结果与原始的 $0.01$ 很接近。
为什么通常按输出通道量化
考虑线性层:
$$ y_i=\sum_j x_jW_{ij}. $$第 $i$ 行权重负责计算第 $i$ 个输出通道:
$$ W_{i,:}\longrightarrow y_i. $$如果这一行共享一个 scale:
那么:
$$ y_i =\sum_j x_jW_{ij} \approx s_i\sum_j x_jQ_{ij}. $$这样可以先执行整数或低比特矩阵乘法,再对第 $i$ 个输出通道统一乘一次 $s_i$。这种方式既符合权重分布,又比较方便计算内核实现。
优缺点
优点:
- 每个通道可以适应自己的数值范围;
- 不容易被其他通道的异常值影响;
- 通常比逐层量化精度高很多;
scale数量仍然不算太多;- 在 INT8 权重量化中非常常见。
缺点:
scale数量增加;- 反量化和矩阵乘法实现更复杂;
- 如果单个通道内部差异仍然很大,误差依然明显;
- 对 INT4 大模型权重而言,有时粒度仍然过粗。
逐组量化
逐组量化也叫:
- Per-group quantization;
- Group-wise quantization;
- Block-wise quantization。
它是介于逐层量化和逐元素量化之间的一种粒度。
对于线性层权重:
$$ W\in\mathbb{R}^{C_{\mathrm{out}}\times C_{\mathrm{in}}}, $$通常的做法是:
- 每个输出通道单独处理;
- 再把一行中的权重沿输入维度切成若干组;
- 每组使用一套量化参数。
假设:
$$ W\in\mathbb{R}^{4\times 8}, $$group size 为 4,则每一行被分成两组:
W[0, 0:4] ─── scale_00
W[0, 4:8] ─── scale_01
W[1, 0:4] ─── scale_10
W[1, 4:8] ─── scale_11
W[2, 0:4] ─── scale_20
W[2, 4:8] ─── scale_21
W[3, 0:4] ─── scale_30
W[3, 4:8] ─── scale_31
总共有:
$$ 4\times 2=8 $$个 scale。
数学表示
假设 group size 是 $G$,元素 $W_{ij}$ 所在的组为:
量化公式是:
$$ Q_{ij} =\operatorname{round}\left(\frac{W_{ij}}{s_{i,g}}\right)+z_{i,g}, \qquad g=\left\lfloor\frac{j}{G}\right\rfloor. $$此时 scale 不再只依赖输出通道 $i$,还依赖当前元素位于第几个组 $g$。
一个更有代表性的例子
假设一行权重为:
$$ W_{0,:}=[0.1,0.2,0.3,0.4,10,12,9,11]. $$如果采用逐通道量化,这一行的最大绝对值是 $12$。使用 INT8 对称量化时:
$$ s=\frac{12}{127}\approx 0.0945. $$前半部分的 $0.1$ 只能量化为:
$$ q=\operatorname{round}\left(\frac{0.1}{0.0945}\right)=1. $$反量化结果为:
$$ \widehat{x}=1\times 0.0945=0.0945. $$对于 $0.2$:
$$ q=2,\qquad \widehat{x}=0.189. $$虽然在 INT8 下还可以接受,但如果换成 INT4,问题会严重得多。INT4 对称量化通常使用:
$$ [-7,7]. $$整行共用的 scale 为:
这样,$0.1$、$0.2$、$0.3$、$0.4$ 可能全部被量化为 0。
如果每 4 个元素一组,第一组为:
$$ [0.1,0.2,0.3,0.4], $$使用:
$$ s_0=\frac{0.4}{7}\approx 0.0571. $$第二组为:
$$ [10,12,9,11], $$使用:
$$ s_1=\frac{12}{7}\approx 1.714. $$这样,小数值部分不会再被大数值部分压制。
group size 的含义
常见的 group size 包括:
例如某个权重矩阵为:
$$ W\in\mathbb{R}^{4096\times 4096}. $$使用 group size = 128 时,每个输出通道包含:
个组。总 scale 数量为:
相比之下,逐通道量化只有:
$$ 4096 $$个 scale。因此,逐组量化的精度更高,但需要保存更多量化参数。
三种量化方法的直接对比
仍然假设权重矩阵为:
$$ W\in\mathbb{R}^{4096\times 4096}, $$并且逐组量化的 group size 为 128:
| 量化粒度 | 一套参数覆盖范围 | scale 数量 |
|---|---|---|
| 逐层 | 整个 $4096\times4096$ 矩阵 | $1$ |
| 逐通道 | 每个输出通道的一整行 | $4096$ |
| 逐组 | 每行每 128 个权重一组 | $4096\times32=131072$ |
从粗到细排列:
逐层 → 逐通道 → 逐组 → 逐元素
粒度越细:
scale越多;- 对局部数值分布的拟合越准确;
- 量化误差越小;
- 元数据和计算复杂度越高。
总结
量化粒度本质上决定了多少个权重共享一套量化参数:
- 逐层量化开销最小、实现最简单,但最容易被跨通道的数值范围差异影响;
- 逐通道量化为每个输出通道保留独立参数,在精度和元数据之间取得了较好的平衡;
- 逐组量化继续沿输入维度拆分权重,尤其适合 INT4 等低比特大模型权重量化,但会引入更多
scale、元数据访问和反量化计算。
实际选择不能只看精度,还要同时考虑目标位宽、权重分布、硬件支持以及计算内核能否高效消费相应粒度的量化参数。