核心问题: 量化时,究竟应该让整个权重矩阵共享一套 scalezero 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. $$

量化时,所有元素都使用相同的 scalezero point

$$ W_{ij}\longrightarrow Q_{ij}, $$$$ Q_{ij} =\operatorname{round}\left(\frac{W_{ij}}{s_W}\right)+z_W. $$

对于前面的 $4\times 8$ 矩阵,整个 $W$ 只共用一个 scale

W[0, :] ┐
W[1, :] │
W[2, :] ├── scale_0
W[3, :] ┘

因此,scale 集合只有一个元素:

$$ S=[s_0]. $$

示例

假设采用 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. $$

也就是说,小数值通道中的信息可能完全消失。

优缺点

优点:

  • 只需要保存一套 scalezero point
  • 实现简单;
  • 量化元数据很少;
  • 计算内核容易优化;
  • 通常速度较高。

缺点:

  • 容易受到异常值和大数值通道影响;
  • 不同通道数值分布差异较大时,量化误差明显;
  • 低比特量化,尤其是 INT4 时,精度通常较差。

适用情况

逐层量化适合:

  • 数值分布比较均匀的张量;
  • 对精度要求不高的场景;
  • INT8 激活量化;
  • 硬件只支持较粗量化粒度的场景。

逐通道量化

逐通道量化也叫 Per-channel quantization。对权重而言,通常是每个输出通道使用一套量化参数。

对于线性层权重:

$$ W\in\mathbb{R}^{C_{\mathrm{out}}\times C_{\mathrm{in}}}, $$

通常每一行对应一个输出通道,因此每行分别计算 scale

$$ s_0,s_1,\ldots,s_{C_{\mathrm{out}}-1}. $$

量化公式变为:

$$ 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}, $$

为了广播,也可以写成:

$$ 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

$$ W_{ij}\approx s_iQ_{ij}, $$

那么:

$$ 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}}}, $$

通常的做法是:

  1. 每个输出通道单独处理;
  2. 再把一行中的权重沿输入维度切成若干组;
  3. 每组使用一套量化参数。

假设:

$$ 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}$ 所在的组为:

$$ g=\left\lfloor\frac{j}{G}\right\rfloor. $$

量化公式是:

$$ 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 为:

$$ s=\frac{12}{7}\approx 1.714. $$

这样,$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 包括:

$$ 32,\quad 64,\quad 128,\quad 256. $$

例如某个权重矩阵为:

$$ W\in\mathbb{R}^{4096\times 4096}. $$

使用 group size = 128 时,每个输出通道包含:

$$ \frac{4096}{128}=32 $$

个组。总 scale 数量为:

$$ 4096\times 32=131072. $$

相比之下,逐通道量化只有:

$$ 4096 $$

scale。因此,逐组量化的精度更高,但需要保存更多量化参数。

三种量化方法的直接对比

仍然假设权重矩阵为:

$$ W\in\mathbb{R}^{4096\times 4096}, $$

并且逐组量化的 group size 为 128:

量化粒度一套参数覆盖范围scale 数量
逐层整个 $4096\times4096$ 矩阵$1$
逐通道每个输出通道的一整行$4096$
逐组每行每 128 个权重一组$4096\times32=131072$

从粗到细排列:

逐层  →  逐通道  →  逐组  →  逐元素

粒度越细:

  • scale 越多;
  • 对局部数值分布的拟合越准确;
  • 量化误差越小;
  • 元数据和计算复杂度越高。

总结

量化粒度本质上决定了多少个权重共享一套量化参数:

  • 逐层量化开销最小、实现最简单,但最容易被跨通道的数值范围差异影响;
  • 逐通道量化为每个输出通道保留独立参数,在精度和元数据之间取得了较好的平衡;
  • 逐组量化继续沿输入维度拆分权重,尤其适合 INT4 等低比特大模型权重量化,但会引入更多 scale、元数据访问和反量化计算。

实际选择不能只看精度,还要同时考虑目标位宽、权重分布、硬件支持以及计算内核能否高效消费相应粒度的量化参数。