LLM WIKI · 课程精读

LEARNING UNIT · 15

量化与误差修正

比较线性量化、聚类量化、SmoothQuant、OBQ、QAT 与残差修正的目标和硬件代价。

已整理章节
15 节
单元来源
14 条视频
总时长
40:29
状态
已发布
学习位置
15 / 20
01

主题讲解 · 02:42

K-means 聚类量化与线性量化有何不同

学习目标

  • 能区分 codebook、索引矩阵、scale 与 zero-point 的角色。
  • 能写出 K-means 聚类量化的分配与重建公式。
  • 能写出 affine 线性量化的量化与反量化公式。
  • 能比较两种方法的存储开销、误差结构与硬件执行方式。
  • 能解释聚类中心数与索引位宽的关系。

前置与衔接

需要理解浮点权重、整数编码和有损压缩。

量化资料中常同时出现 codebook、scale 和 zero-point,但它们并不一定属于同一种表示。

本课用同一个 3×3 权重矩阵说明:聚类量化把数值分配给学习到的中心,线性量化则把数值映射到规则整数网格。

核心讲解

1. 两种量化都在做“编码 + 重建”

给定浮点权重集合

W={w1,w2,,wn},W=\{w_1,w_2,\ldots,w_n\},

量化器生成更紧凑的编码,解码后得到近似权重

W^.\hat W.

二者都会丢失一部分信息,目标都是在更少存储或更快计算下控制误差。

图 1

聚类量化保存索引矩阵与 codebook,线性量化保存规则整数码与 scale、zero-point;两者的重建结构不同。

原视频 · 00:00 ↗

真正的区别在于编码空间怎样构造、元数据存什么,以及推理时怎样恢复或计算。

2. K-means 量化学习任意聚类中心

设要把权重分成 KK 个聚类,中心为

C={c0,c1,,cK1}.C=\{c_0,c_1,\ldots,c_{K-1}\}.

K-means 的典型目标是最小化平方重建误差:

minC,zj=1n(wjczj)2,\min_{C,z} \sum_{j=1}^{n} \left(w_j-c_{z_j}\right)^2,

其中

zj{0,1,,K1}z_j\in\{0,1,\ldots,K-1\}

是第 jj 个权重所属的聚类编号。

中心位置由数据分布学习得到,不要求等间距。

3. 视频中的两个中心例子

视频权重大致分成两组:

  • 一组接近 2.0;
  • 另一组接近 5.0。

于是可以使用两个中心

c0=2.0,c1=5.0.c_0=2.0, \qquad c_1=5.0.

例如 2.1、2.3、1.9 更接近 c0c_0,而 4.7、5.1、5.3 更接近 c1c_1

每个原权重只保存 0 或 1 这一索引。

4. 索引矩阵与 codebook 各存什么

对原矩阵中的每个位置,保存聚类编号组成索引矩阵 ZZ

另存映射表

02.0,15.0.0\mapsto2.0, \qquad 1\mapsto5.0.

这张中心表就是 codebook,也常译为码本。

图 2

K-means 量化把每个权重替换为聚类编号,并用 codebook 保存编号到聚类中心的映射。

原视频 · 00:40 ↗

索引本身不是近似浮点权重;只有结合 codebook 才能解释它代表哪个值。

5. 聚类量化如何重建权重

解码公式非常直接:

w^j=czj.\hat w_j=c_{z_j}.

索引为 0 的位置全部重建成 2.0,索引为 1 的位置全部重建成 5.0。

图 3

解码时按索引查找中心值,例如 0→2.0、1→5.0,从而得到只含共享中心的近似权重。

原视频 · 01:00 ↗

例如原来的 2.1 变成 2.0,原来的 4.8 变成 5.0。

误差分别是 0.1 与 0.2,但大量权重开始共享少数中心值。

6. 聚类数决定索引位宽

若 codebook 有 KK 个中心,每个索引至少需要

bidx=log2Kb_{idx}=\lceil\log_2K\rceil

bit。

图 4

若 codebook 有 K 个中心,每个索引至少需要 ceil(log₂K) bit;2、4、8 个中心分别对应 1、2、3 bit。

原视频 · 01:20 ↗

常见例子:

  • K=2K=2,每个索引 1 bit;
  • K=4K=4,每个索引 2 bit;
  • K=8K=8,每个索引 3 bit;
  • K=16K=16,每个索引 4 bit。

KK 不是 2 的幂,理论编码位数仍向上取整,实际打包还可能有对齐开销。

7. 聚类量化的存储账本

原来 nn 个 FP32 权重需要

32n32n

bit。

聚类量化大致需要

nlog2K+Kbcn\lceil\log_2K\rceil+Kb_c

bit,其中 bcb_c 是每个中心的存储位宽。

视频中的 9 个权重、2 个中心例子,索引只需

9×1=9 bit,9\times1=9\text{ bit},

但还必须加上两个中心的 codebook。

对只有 9 个元素的玩具矩阵,codebook 开销占比很高;对数百万权重,码本成本才容易被摊薄。

8. 线性量化使用统一 affine 规则

常见 affine 线性量化写成

q=clip(round(w/s)+z,qmin,qmax),q =\operatorname{clip}\left( \operatorname{round}(w/s)+z, q_{min},q_{max} \right),

其中:

  • s>0s>0 是 scale;
  • zz 是 zero-point;
  • qq 是规则整数码。

反量化为

w^=s(qz).\hat w=s(q-z).

所有权重共享某个 tensor、channel 或 group 对应的同一套 affine 参数。

9. 视频中的乘 200、加 10 例子

视频用极简规则说明线性映射:

q=round(200w+10).q=\operatorname{round}(200w+10).

它等价于

s=1200,z=10s=\frac1{200}, \qquad z=10

且暂不考虑 clipping。

例如

w=0.3w=-0.3

映射为

q=round(60+10)=50.q=\operatorname{round}(-60+10)=-50.
图 5

线性量化用统一 affine 规则和取整把浮点数映射到整数格点,而不是查找任意学习中心。

原视频 · 01:40 ↗

反量化得到

w^=5010200=0.3.\hat w=\frac{-50-10}{200}=-0.3.

若原值不恰好落在网格上,就会出现舍入误差。

10. 线性量化的格点必须等间距

忽略 clipping 时,相邻整数码 qqq+1q+1 的反量化值相差固定 scale:

s((q+1)z)s(qz)=s.s((q+1)-z)-s(q-z)=s.

因此线性均匀量化的重建值构成等间距网格。

K-means 中心则可以集中在权重密集区域,间距不规则。

这使聚类量化能更贴合非均匀分布,但也失去简单 affine 算术结构。

11. INT8 范围与元数据

有符号 INT8 通常提供整数范围

[128,127].[-128,127].
图 6

INT8 线性量化把值编码到 -128 至 127,并用 scale 与 zero-point 近似反量化回浮点域。

原视频 · 02:20 ↗

若共有 9 个 INT8 编码,主体存储是

9×8=72 bit,9\times8=72\text{ bit},

此外还要存 scale 和 zero-point。

这些元数据的数量取决于粒度:per-tensor 只有一组,per-channel 或 per-group 会有更多组。

12. 两种“码”的语义不同

聚类量化中的索引 zjz_j 表示“去 codebook 的第几项取中心”。

线性量化中的整数码 qjq_j 表示“在 affine 网格上的第几个整数位置”。

虽然二者都可能用少量 bit 保存,但解码方式不同:

聚类:w^j=C[zj],\text{聚类:}\quad \hat w_j=C[z_j],
线性:w^j=s(qjz).\text{线性:}\quad \hat w_j=s(q_j-z).

不能把 codebook 索引直接当作普通 INT8 数值参与同一套整数乘加。

13. 误差结构比较

聚类量化的误差是

ej=wjczj.e_j=w_j-c_{z_j}.

若中心通过当前权重分布学习,密集区域通常能获得更细致的表示。

线性量化的误差是

ej=wjs(qjz).e_j=w_j-s(q_j-z).

在 clipping 范围内,它主要来自舍入;超出范围还会产生饱和误差。

线性网格简单、规则,但可能把许多码分配给几乎没有权重的区间。

14. 硬件执行方式比较

线性量化的规则整数码更容易直接送入 INT8/INT4 矩阵乘 kernel,再通过 scale 反量化或 requantize。

聚类量化需要从 codebook 取中心,可能涉及:

  • 解包低比特索引;
  • 查表或 gather;
  • 将中心展开后计算;
  • 或使用支持 LUT 的专用 kernel。

所以更低的索引位宽不自动等于更快的端到端推理。

具体速度取决于 codebook 大小、缓存命中、索引打包、kernel 和硬件支持。

15. 两者并非完全互斥

“聚类量化”和“线性量化”描述的是两种基本编码结构,但实际系统可以组合:

  • 对不同 group 分别学习 codebook;
  • 让中心本身再做线性量化;
  • 用残差码本叠加多级近似;
  • 在不同层采用不同量化器。

因此课程中的二分是概念对照,不表示每个工程方案必须纯粹属于一侧。

跟练与练习

原视频练习

编者练习

有 1,000,000 个权重,使用 K=16K=16 的聚类量化,中心以 FP16 保存。忽略对齐,主体索引与 codebook 各需要多少 bit?

查看参考答案

索引位宽为
log216=4 bit.\lceil\log_2 16\rceil=4\text{ bit}.
主体索引需要
1,000,000×4=4,000,000 bit.1{,}000{,}000\times4=4{,}000{,}000\text{ bit}.
codebook 需要
16×16=256 bit.16\times16=256\text{ bit}.
在大权重集合中,码本相对主体索引很小。

编者练习 2

为什么不能只比较“4-bit 索引”和“8-bit 整数码”就断言聚类量化一定更快?

查看参考答案

存储位宽只是性能的一部分。聚类量化还需解包索引、访问 codebook 并把中心送入计算;线性 INT8 则更容易直接使用硬件整数乘加和规则缩放。
端到端速度还受内存访问、缓存、kernel、并行度、元数据和硬件指令支持影响,因此必须测量完整实现。

常见误区

  • 误区:codebook 就是 zero-point。纠正:codebook 保存多个任意中心;zero-point 是 affine 网格中表示实数零的整数偏移。
  • 误区:聚类索引可以直接当普通整数权重相乘。纠正:索引要先解释为 codebook 中的中心,除非专用 kernel 把查表与计算融合。
  • 误区:两个中心时总存储只有每权重 1 bit。纠正:还必须保存 codebook,并考虑打包和对齐。
  • 误区:线性量化所有格点由数据自由学习。纠正:均匀线性量化的格点由 scale 和 zero-point 定义,间距固定。
  • 误区:INT8 一定是无符号 0 到 255。纠正:本课使用有符号范围 -128 到 127;具体 dtype 与约定需看实现。
  • 误区:位宽更低就一定精度更差。纠正:误差还取决于权重分布、中心或 scale 的选择、粒度和校准目标。

本课小结

  • K-means 量化保存低比特索引和 codebook,重建为 w^j=czj\hat w_j=c_{z_j}
  • KK 个中心至少需要每索引 log2K\lceil\log_2K\rceil bit,码本成本需单独计入。
  • 线性量化用 scale、zero-point 和取整把浮点值映射到规则整数网格。
  • 聚类中心可非均匀地贴合数据,线性网格则更容易映射到标准整数硬件。
  • 两种方案要同时比较误差、主体位宽、元数据、解码和实际 kernel,不能只看每权重 bit 数。
02

主题讲解 · 03:08

LLM.int8() 如何用外积拆出激活离群通道

学习目标

  • 能区分深度学习语境中的向量外积与三维向量叉积。
  • 能把矩阵乘法写成共享维上若干外积之和。
  • 能解释一个极大激活值为何会损伤正常值的 INT8 分辨率。
  • 能按激活列与权重对应行,把矩阵乘法拆成正常通道和离群通道两部分。
  • 能区分拆分恒等式的严格等价与量化计算带来的近似误差。
  • 能说明 LLM.int8() 中 INT8 分支与高精度离群分支各自承担什么工作。

前置与衔接

需要知道线性层的基本形式

Y=XW,Y=XW,

以及均匀量化会用有限整数格点逼近连续浮点数。

本课位于“量化与误差修正”单元。

前面的课程说明了离群值会拉大量化范围;本课进一步回答:为什么可以把有离群值的 channel 单独拿出来算,而且不会破坏矩阵乘法的结构?

答案来自一个很朴素的线性代数视角:矩阵乘法就是一组外积的和。

核心讲解

1. 本课中的“外积”到底是什么

设列向量

x=[123]R3×1x= \begin{bmatrix} 1\\ 2\\ 3 \end{bmatrix} \in\mathbb{R}^{3\times1}

与行向量

w=[456]R1×3.w^\top= \begin{bmatrix} 4&5&6 \end{bmatrix} \in\mathbb{R}^{1\times3}.

二者相乘得到

xw=[45681012121518].xw^\top= \begin{bmatrix} 4&5&6\\ 8&10&12\\ 12&15&18 \end{bmatrix}.
图 1

深度学习语境中的向量外积由列向量乘行向量得到矩阵;每个列向量元素都会广播乘整行。

原视频 · 00:20 ↗

可以把这个过程理解为:列向量的每个标量分别广播乘整行,再把得到的行堆叠起来。

也可以反过来理解为:行向量的每个标量分别广播乘整列,再把得到的列拼接起来。

这里的 outer product 输出一个矩阵。

它不是三维几何中输出另一个向量的 cross product,也不是把两个向量做逐元素乘法。

2. 矩阵乘法可以写成外积之和

XRM×K,WRK×N.X\in\mathbb{R}^{M\times K}, \qquad W\in\mathbb{R}^{K\times N}.

XX 按列拆开:

X=[x1x2xK],xkRM×1.X= \begin{bmatrix} x_1&x_2&\cdots&x_K \end{bmatrix}, \qquad x_k\in\mathbb{R}^{M\times1}.

WW 按行拆开:

W=[w1w2wK],wkR1×N.W= \begin{bmatrix} w_1^\top\\ w_2^\top\\ \vdots\\ w_K^\top \end{bmatrix}, \qquad w_k^\top\in\mathbb{R}^{1\times N}.

那么

XW=k=1Kxkwk.XW= \sum_{k=1}^{K}x_kw_k^\top.
图 2

矩阵乘法可以沿共享维拆成若干列向量与行向量的外积,再把这些矩阵逐项相加。

原视频 · 00:40 ↗

每一项

xkwkRM×Nx_kw_k^\top\in\mathbb{R}^{M\times N}

都是一个矩阵,所有外积矩阵相加后仍是 M×NM\times N 的输出。

共享维索引 kk 同时选择:

  • XX 的第 kk 列;
  • WW 的第 kk 行。

这对索引必须保持配对。

若只挑 XX 的一列,却没有挑 WW 的对应行,就不再是在拆原来的矩阵乘法。

3. 离群值为什么会压缩正常值的量化分辨率

先考虑一组普通激活值,例如

0.1,0.7.0.1, \qquad 0.7.

若量化范围只需覆盖这两个数,INT8 的许多格点都能用来区分它们。

现在加入一个极大的离群值:

600.600.

若三者共用一个对称量化 scale,可粗略写为

s600127.s\approx\frac{600}{127}.

量化整数为

q=round(x/s).q=\operatorname{round}(x/s).

此时 0.1/s0.1/s0.7/s0.7/s 都非常接近零,可能被映射到同一个整数格点。

图 3

当 0.1、0.7 与 600 共用同一 INT8 动态范围时,小值可能落入同一量化格点而失去差异。

原视频 · 01:20 ↗

问题不是小值真的等于零,而是有限的 256 个整数码需要覆盖过大的动态范围。

量化步长被离群值拉大后,小值之间原本有意义的差异无法在整数编码中保留。

视频中的映射数字只是直觉示例,不是固定的 LLM.int8() 量化参数。

实际 scale、阈值、是否对称、累加精度和 kernel 都由具体实现决定。

4. 按共享维挑出离群通道

设离群激活出现在 XX 的第 2 列和第 4 列。

因为矩阵乘法沿 KK 维配对,所以必须同时选出 WW 的第 2 行和第 4 行。

图 4

挑出激活矩阵中的离群列时,必须同时挑出权重矩阵共享维上的对应行,才能保持乘积配对。

原视频 · 01:40 ↗

记离群索引集合为

O{1,2,,K},\mathcal{O}\subseteq\{1,2,\ldots,K\},

正常索引集合为其补集

N={1,2,,K}O.\mathcal{N}=\{1,2,\ldots,K\}\setminus\mathcal{O}.

则原矩阵乘法可以重写为

XW=kNxkwk+kOxkwk.XW = \sum_{k\in\mathcal{N}}x_kw_k^\top + \sum_{k\in\mathcal{O}}x_kw_k^\top.

把两组列、行分别重新拼成子矩阵:

XNRM×N,WNRN×N,X_{\mathcal{N}}\in\mathbb{R}^{M\times|\mathcal{N}|}, \qquad W_{\mathcal{N}}\in\mathbb{R}^{|\mathcal{N}|\times N},
XORM×O,WORO×N.X_{\mathcal{O}}\in\mathbb{R}^{M\times|\mathcal{O}|}, \qquad W_{\mathcal{O}}\in\mathbb{R}^{|\mathcal{O}|\times N}.

于是

XW=XNWN+XOWO.XW=X_{\mathcal{N}}W_{\mathcal{N}}+X_{\mathcal{O}}W_{\mathcal{O}}.

5. 两路拆分在量化前严格等价

假设 K=4K=4,外积和为

x1w1+x2w2+x3w3+x4w4.x_1w_1^\top+x_2w_2^\top+x_3w_3^\top+x_4w_4^\top.

若第 2、4 个通道属于离群集合,只需重新分组:

(x1w1+x3w3)+(x2w2+x4w4).\left(x_1w_1^\top+x_3w_3^\top\right) + \left(x_2w_2^\top+x_4w_4^\top\right).
图 5

将外积和重新分组为正常通道之和与离群通道之和,量化前仍严格等于原矩阵乘法。

原视频 · 02:20 ↗

这里使用的是矩阵加法的结合律和交换律。

在精确算术下,这个拆分没有改变函数,也没有丢弃任何 channel。

浮点计算调整求和顺序时,末位可能因舍入产生微小差异;这与随后把正常分支量化为 INT8 的主要近似要分开看。

因此应区分两层结论:

  1. 分块恒等式在数学上严格成立;
  2. INT8 分支会引入量化误差,所以最终混合精度结果通常只是原全精度输出的近似。

6. 正常通道走 INT8,离群通道保留高精度

拆分后可以让正常分支使用 INT8 量化计算:

Y^NXNWN.\hat Y_{\mathcal{N}} \approx X_{\mathcal{N}}W_{\mathcal{N}}.

离群分支保留 FP16 等较高精度:

YO=XOWO.Y_{\mathcal{O}} = X_{\mathcal{O}}W_{\mathcal{O}}.

最后相加:

Y^=Y^N+YO.\hat Y=\hat Y_{\mathcal{N}}+Y_{\mathcal{O}}.
图 6

正常通道分支用 INT8 近似计算,离群通道分支保留 FP16,最后相加得到混合精度输出。

原视频 · 02:40 ↗

这样做的核心收益是:极大激活值不再参与正常分支的量化范围估计,因此正常值可以使用更细的 INT8 格点。

同时,离群贡献没有被粗糙压进同一套 INT8 范围,而是由高精度分支单独保留。

7. 它没有把所有计算都变成 INT8

LLM.int8() 的这个思想本质上是 mixed precision。

它用高精度离群分支换取正常分支更稳定的低比特量化。

若离群集合很小,高精度分支的计算量通常只占共享维的一小部分。

但真实性能仍取决于:

  • 离群通道比例;
  • 两路 kernel 的实现;
  • 子矩阵收集和布局开销;
  • INT8 GEMM 的硬件吞吐;
  • 两路结果合并的调度成本。

因此“数学上能拆”不自动等于“任何硬件上都一定更快”。

8. 离群阈值属于实现策略,不属于恒等式

外积拆分对任意索引集合都成立。

至于哪些通道应进入 O\mathcal{O},需要额外的离群检测规则。

常见实现会根据激活绝对值阈值或统计量判断,但阈值、统计范围和版本可能不同。

阈值太低会让高精度分支过大,吞掉 INT8 的速度与存储收益。

阈值太高则可能漏掉真正拉大量化范围的通道,精度仍会受损。

视频重点是“如何利用外积拆分”,没有给出一个可跨实现照搬的固定阈值。

9. 用 shape 检查拆分是否正确

给定

XRM×K,WRK×N,X\in\mathbb{R}^{M\times K}, \qquad W\in\mathbb{R}^{K\times N},

如果选出 rr 个离群通道,则

XORM×r,WORr×N,X_{\mathcal{O}}\in\mathbb{R}^{M\times r}, \qquad W_{\mathcal{O}}\in\mathbb{R}^{r\times N},

所以

XOWORM×N.X_{\mathcal{O}}W_{\mathcal{O}}\in\mathbb{R}^{M\times N}.

正常分支也输出 M×NM\times N,两者才能逐元素相加。

若两路输出 shape 不同,通常说明列与对应行没有沿同一个共享维索引拆分。

跟练与练习

原视频练习

编者练习

XR32×4096,WR4096×11008.X\in\mathbb{R}^{32\times4096}, \qquad W\in\mathbb{R}^{4096\times11008}. 如果检测到 12 个离群通道,写出正常分支和离群分支四个子矩阵的 shape,以及两路输出的 shape。

查看参考答案

离群分支为 XOR32×12X_{\mathcal{O}}\in\mathbb{R}^{32\times12}WOR12×11008W_{\mathcal{O}}\in\mathbb{R}^{12\times11008},输出为 32×1100832\times11008。正常分支为 XNR32×4084X_{\mathcal{N}}\in\mathbb{R}^{32\times4084}WNR4084×11008W_{\mathcal{N}}\in\mathbb{R}^{4084\times11008},输出同样是 32×1100832\times11008。两路输出 shape 相同,因而可以逐元素相加。

编者练习 2

为什么不能说“把离群通道拆出来后,最终结果与原 FP16 矩阵乘法严格相同”?

查看参考答案

通道拆分与重分组在量化前是严格等价的;但正常分支随后使用 INT8 表示与算术,会产生量化和浮点累加误差。因此最终输出通常只是原全精度矩阵乘法的近似。高精度离群分支减少了最危险的误差来源,却没有消除正常分支的全部误差。

常见误区

  • 误区:外积就是三维向量叉积。纠正:本课指列向量乘行向量,输出矩阵。
  • 误区:矩阵乘法只能看成各行与各列的内积。纠正:它也可等价地看成共享维上的外积之和。
  • 误区:激活离群列只需从 XX 中挑出。纠正:必须同时挑出 WW 在共享维上的对应行。
  • 误区:拆出离群分支等于删除离群值。纠正:离群贡献仍完整参与输出,只是改用高精度计算。
  • 误区:两路拆分本身会产生量化误差。纠正:分块恒等式严格成立,误差来自具体低比特表示与数值计算。
  • 误区:混合精度输出与原 FP16 输出逐位相同。纠正:正常通道的 INT8 分支仍是近似。
  • 误区:离群阈值是一个固定的数学常数。纠正:检测规则和阈值依实现、模型与版本而定。
  • 误区:只要离群通道少,部署就必然更快。纠正:还要看数据布局、kernel、硬件与分支合并开销。

本课小结

  • 深度学习语境中的外积由 M×1M\times1 列向量与 1×N1\times N 行向量相乘,得到 M×NM\times N 矩阵。
  • 矩阵乘法满足 XW=kxkwkXW=\sum_kx_kw_k^\top,因此可以沿共享维重新分组。
  • 极大激活值会拉大量化 scale,让普通值落入相同或相邻过粗的 INT8 格点。
  • 选择激活离群列时,必须同步选择权重的对应行。
  • 数学拆分 XW=XNWN+XOWOXW=X_{\mathcal{N}}W_{\mathcal{N}}+X_{\mathcal{O}}W_{\mathcal{O}} 在量化前严格等价。
  • LLM.int8() 让正常通道走 INT8、离群通道保留高精度,再合并两路输出;最终结果通常是全精度输出的近似。
03

主题讲解 · 02:51

SmoothQuant 的对角缩放为何保持矩阵乘法不变

学习目标

  • 能用 S1S=IS^{-1}S=I 证明 SmoothQuant 的全精度等价变换。
  • 能写出 XXWWSS 的 shape,并判断缩放作用在哪个 channel 维。
  • 能说明激活为何按列统计、权重为何按行统计。
  • 能写出 SmoothQuant 的逐通道缩放公式。
  • 能复算视频中 α=1/2\alpha=1/2 时的数值示例。
  • 能区分等价缩放本身与后续量化误差。

前置与衔接

需要理解矩阵乘法、单位阵、可逆矩阵和对角矩阵。

前一课说明了 LLM.int8() 如何把离群 channel 拆成高精度分支。

SmoothQuant 采取另一条路线:不拆出一条高精度分支,而是在激活和权重之间重新分配逐通道尺度,让两边都更容易量化。

本课只回答两个核心问题:

  1. 插入缩放为什么不会改变原线性层?
  2. 对角线上的每个缩放系数怎样由激活与权重统计得到?

核心讲解

1. 单位阵不会改变矩阵乘积

对任意维度匹配的矩阵 AA,都有

AI=A,IA=A.AI=A, \qquad IA=A.

如果 SS 可逆,则

S1S=I,SS1=I.S^{-1}S=I, \qquad SS^{-1}=I.

因此在矩阵乘法的合适位置插入 S1SS^{-1}S,本质上只是插入单位映射。

2. 对角阵的逆可以逐元素计算

S=diag(s1,s2,,sD).S=\operatorname{diag}(s_1,s_2,\ldots,s_D).

只要每个 sj0s_j\neq0,就有

S1=diag(1s1,1s2,,1sD).S^{-1} = \operatorname{diag} \left( \frac1{s_1}, \frac1{s_2}, \ldots, \frac1{s_D} \right).

例如

S=[2003],S1=[1/2001/3].S= \begin{bmatrix} 2&0\\ 0&3 \end{bmatrix}, \qquad S^{-1}= \begin{bmatrix} 1/2&0\\ 0&1/3 \end{bmatrix}.

二者相乘为

S1S=[1001].S^{-1}S= \begin{bmatrix} 1&0\\ 0&1 \end{bmatrix}.
图 1

非零对角阵 S 的逆由各对角元素取倒数得到,因此 S⁻¹S=I,可在矩阵乘法中插入而不改变全精度结果。

原视频 · 00:20 ↗

对角结构还有一个计算意义:右乘 SSS1S^{-1} 只会逐列缩放,不会在 channel 之间做稠密混合。

3. 在线性层中插入 S⁻¹S

设一批 token 的激活为

XRT×D,X\in\mathbb{R}^{T\times D},

权重按右乘约定写为

WRD×K.W\in\mathbb{R}^{D\times K}.

原始线性层为

Y=XW.Y=XW.

构造可逆对角阵

SRD×D.S\in\mathbb{R}^{D\times D}.

XXWW 的共享维之间插入单位阵:

Y=XW=X(S1S)W=(XS1)(SW).Y =XW =X(S^{-1}S)W =(XS^{-1})(SW).
图 2

在线性层 XW 的共享维插入 S⁻¹S 后,可结合为 (XS⁻¹)(SW),其中 S 的 shape 为 D×D。

原视频 · 00:40 ↗

定义

X~=XS1,W~=SW,\tilde X=XS^{-1}, \qquad \tilde W=SW,

X~W~=XW.\tilde X\tilde W=XW.

这个等式在未量化的精确算术下严格成立。

4. 对角缩放沿输入 channel 成对迁移尺度

对激活有

X~t,j=Xt,j/sj.\tilde X_{t,j}=X_{t,j}/s_j.

也就是说,XX 的第 jj 列被缩小 sjs_j 倍。

对权重有

W~j,k=sjWj,k.\tilde W_{j,k}=s_jW_{j,k}.

也就是说,WW 的第 jj 行被放大 sjs_j 倍。

两次缩放在乘积中互相抵消:

(Xt,jsj)(sjWj,k)=Xt,jWj,k.\left(\frac{X_{t,j}}{s_j}\right) \left(s_jW_{j,k}\right) =X_{t,j}W_{j,k}.

因此同一个输入 channel 在激活侧除以 sjs_j,在权重侧乘以 sjs_j

尺度被“迁移”了,完整线性映射却没有变化。

5. 为什么激活按列求最大绝对值

XX 的行对应 token,列对应输入 channel。

SmoothQuant 需要为每个输入 channel 构造一个缩放系数,所以激活侧统计量沿 token 维归约:

aj=maxtXt,j.a_j=max_t|X_{t,j}|.
图 3

激活矩阵按输入 channel 求每列绝对值最大值,得到长度为 D 的校准统计向量。

原视频 · 01:00 ↗

所有 DD 个 channel 的统计量组成向量

a=[a1a2aD].a= \begin{bmatrix} a_1&a_2&\cdots&a_D \end{bmatrix}.

真实部署中,aja_j 通常由一组校准样本统计,而不是只看单个在线请求。

这样得到的 SS 可以在部署前固定,并把 SWSW 提前合并进权重。

6. 激活侧的数值例子

视频给出一个两行四列的激活矩阵:

X=[116262819].X= \begin{bmatrix} 1&-16&2&6\\ -2&8&-1&9 \end{bmatrix}.

逐列取绝对值最大值得到

a=[21629].a= \begin{bmatrix} 2&16&2&9 \end{bmatrix}.
图 4

示例激活矩阵每列取绝对值最大值后得到 [2,16,2,9],保留了各 channel 的动态范围差异。

原视频 · 01:20 ↗

第二列的统计量 16 明显大于第一、第三列的 2,说明它对共享量化范围更不友好。

7. 为什么权重按行求最大绝对值

在右乘约定下,WW 的行与 XX 的列共享同一个 DD 维索引。

因此权重侧对每一行求绝对值最大值:

bj=maxkWj,k.b_j=max_k|W_{j,k}|.
图 5

权重矩阵按共享输入维求每行绝对值最大值,示例得到 [2,1,2,1],与激活列统计逐通道配对。

原视频 · 01:40 ↗

视频示例得到

b=[2121].b= \begin{bmatrix} 2&1&2&1 \end{bmatrix}.

aja_jbjb_j 必须逐通道对应。

若把权重错误地按列统计,就会得到长度为 KK 的向量,无法构造作用于共享输入维的 D×DD\times D 对角阵。

8. SmoothQuant 的一般缩放公式

一种标准写法为

sj=ajαbj1α,0α1.s_j = \frac{a_j^{\alpha}} {b_j^{1-\alpha}}, \qquad 0\leq\alpha\leq1.

其中:

  • aja_j 是第 jj 个激活 channel 的绝对值最大值;
  • bjb_j 是权重对应行的绝对值最大值;
  • α\alpha 控制尺度迁移的强弱。

再构造

S=diag(s1,s2,,sD).S=\operatorname{diag}(s_1,s_2,\ldots,s_D).

sjs_j 较大时,激活列 X:,jX_{:,j} 被除以更大的数,其动态范围缩小;权重行 Wj,:W_{j,:} 则相应放大。

这就是把量化难度从激活迁移到权重的数学形式。

9. α=1/2 时变成开平方

视频选择

α=12.\alpha=\frac12.

此时

sj=ajbj.s_j = \sqrt{\frac{a_j}{b_j}}.

将示例向量代入:

ab=[2/216/12/29/1]=[11619].\frac{a}{b} = \begin{bmatrix} 2/2&16/1&2/2&9/1 \end{bmatrix} = \begin{bmatrix} 1&16&1&9 \end{bmatrix}.

逐元素开平方:

s=[1413].s= \begin{bmatrix} 1&4&1&3 \end{bmatrix}.

最终

S=diag(1,4,1,3).S=\operatorname{diag}(1,4,1,3).
图 6

当 α=1/2 时,逐通道 scale 为 sqrt(max|X|/max|W|),示例得到 s=[1,4,1,3] 并对角化为 S。

原视频 · 02:20 ↗

第二个激活 channel 的范围最大,所以得到最大的缩放系数 4。

它在 XS1XS^{-1} 中被除以 4,同时对应权重行在 SWSW 中乘以 4。

10. α 不是固定等于 1/2

α=1/2\alpha=1/2 让示例容易手算,但不是数学恒等式要求的唯一取值。

只要所有 sjs_j 非零,S1S=IS^{-1}S=I 就成立。

α\alpha 决定的是量化误差在激活与权重之间怎样折中:

  • α\alpha 较大,更积极地压缩激活动态范围;
  • α\alpha 较小,权重放大更温和,但激活侧仍可能难量化。

具体取值需要结合模型、校准数据和量化配置选择。

11. 等价缩放不等于量化无误差

在量化前,

(XS1)(SW)=XW(XS^{-1})(SW)=XW

是严格恒等式。

XS1XS^{-1}SWSW 取整到有限整数格点后,得到的是

QX(XS1)QW(SW),Q_X(XS^{-1})Q_W(SW),

它通常只近似原始 XWXW

SmoothQuant 的作用是让两侧的动态范围更均衡,从而减少给定位宽下的量化误差,而不是消灭所有数值误差。

另外,有限精度浮点运算改变结合顺序也可能带来末位舍入差异。

12. 零统计量需要实现保护

公式含有 bjb_j 的除法,并要求 sjs_j 可逆。

若某个 channel 的校准最大值为零,直接套公式可能产生除零或零缩放。

真实实现通常会使用很小的正数、clamp 或其他数值保护。

这是部署实现边界;视频的正数示例没有遇到这个问题。

跟练与练习

原视频练习

编者练习

XR64×4096,WR4096×11008.X\in\mathbb{R}^{64\times4096}, \qquad W\in\mathbb{R}^{4096\times11008}. 写出 aabbssSSXS1XS^{-1}SWSW 的 shape,并说明 aja_jbjb_j 分别沿哪个轴归约。

查看参考答案

a,b,sR4096a,b,s\in\mathbb{R}^{4096}SR4096×4096S\in\mathbb{R}^{4096\times4096}XS1R64×4096XS^{-1}\in\mathbb{R}^{64\times4096}SWR4096×11008SW\in\mathbb{R}^{4096\times11008}aj=maxtXt,ja_j=\max_t|X_{t,j}|,沿 64 个 token 归约;bj=maxkWj,kb_j=\max_k|W_{j,k}|,沿 11008 个输出维归约。二者都为第 jj 个输入 channel 生成一个统计量。

编者练习 2

如果某 channel 的 aj=25a_j=25bj=4b_j=4,且 α=1/2\alpha=1/2,求 sjs_j,并说明激活列和权重行分别怎样变化。

查看参考答案

sj=25/4=2.5s_j=\sqrt{25/4}=2.5。激活第 jj 列除以 2.5,权重第 jj 行乘以 2.5。二者在矩阵乘积中的缩放互相抵消;量化前该 channel 对输出的贡献不变。

常见误区

  • 误区:任何对角阵都可直接求逆。纠正:所有对角元素都必须非零。
  • 误区:SmoothQuant 在 XX 左侧乘 S1S^{-1}。纠正:右乘 S1S^{-1} 才沿输入 channel 缩放列。
  • 误区:激活与权重都按列求最大值。纠正:右乘约定下,激活按列、权重按对应行统计。
  • 误区:SS 的 shape 与输出维 KK 相同。纠正:它作用在共享输入维,shape 是 D×DD\times D
  • 误区:α\alpha 必须等于 1/21/2。纠正:这是视频的手算示例,一般公式允许其他折中值。
  • 误区:插入 S1SS^{-1}S 会引入量化误差。纠正:等价缩放本身严格成立,误差来自后续有限精度量化。
  • 误区:缩放后模型输出必然逐位一致。纠正:浮点结合顺序和实际 kernel 可能造成末位差异。
  • 误区:每个在线请求都重新统计并重写权重。纠正:典型部署用校准数据预先确定缩放并折叠固定权重。

本课小结

  • 对非零对角阵,S1S^{-1} 由各对角元素取倒数得到,且 S1S=IS^{-1}S=I
  • SmoothQuant 用 XW=(XS1)(SW)XW=(XS^{-1})(SW) 在激活列与对应权重行之间迁移尺度。
  • 激活侧按列统计 aj=maxtXt,ja_j=\max_t|X_{t,j}|,权重侧按行统计 bj=maxkWj,kb_j=\max_k|W_{j,k}|
  • 一般缩放为 sj=ajα/bj1αs_j=a_j^\alpha/b_j^{1-\alpha};视频在 α=1/2\alpha=1/2 时得到 s=[1,4,1,3]s=[1,4,1,3]
  • 缩放恒等式在量化前严格成立,后续整数取整仍会产生近似误差。
  • 校准统计、α\alpha 选择和零值保护都属于实际实现需要明确的边界。
04

主题讲解 · 03:31

SmoothQuant 隐含的外积结构:右乘缩列,左乘缩行

学习目标

  • 能把任意矩阵乘法写成共享维上的外积之和。
  • 能用外积展开证明右乘对角阵只缩放矩阵的列。
  • 能用外积展开证明左乘对角阵只缩放矩阵的行。
  • 能解释对角阵为何不会混合不同 channel。
  • 能把 XS1XS^{-1} 的列缩放与 SWSW 的行缩放一一配对。
  • 能说明 SmoothQuant 的 scale 公式为何是激活范围除以权重范围,而不是反过来。

前置与衔接

需要理解外积、矩阵乘法和对角矩阵。

上一课已经从单位阵角度证明

XW=(XS1)(SW).XW=(XS^{-1})(SW).

本课换一个视角:把两个对角矩阵乘法都展开成外积和,观察每一项究竟把哪一列、哪一行放大或缩小。

这个视角能解释 SmoothQuant 为什么“用到了外积思想,但不像 LLM.int8() 那样直接”。

核心讲解

1. 本课所说的外积

对列向量

aRM×1a\in\mathbb{R}^{M\times1}

与行向量

bR1×N,b^\top\in\mathbb{R}^{1\times N},

外积为

abRM×N.ab^\top\in\mathbb{R}^{M\times N}.

它可以理解为:

  • aa 的每个标量广播乘整行 bb^\top
  • 或用 bb^\top 的每个标量广播乘整列 aa
  • 再把产生的行或列拼接成矩阵。

这里不是三维向量叉积,也不是逐元素乘法。

2. LLM.int8() 中外积是显式的

X=[x1x2x3],X= \begin{bmatrix} x_1&x_2&x_3 \end{bmatrix},
W=[w1w2w3].W= \begin{bmatrix} w_1^\top\\ w_2^\top\\ w_3^\top \end{bmatrix}.

矩阵乘法直接展开为

XW=x1w1+x2w2+x3w3.XW = x_1w_1^\top +x_2w_2^\top +x_3w_3^\top.
图 1

LLM.int8() 显式把 X 的列与 W 的对应行组成外积,再把各外积矩阵相加。

原视频 · 00:20 ↗

LLM.int8() 可以把其中含离群激活的外积项归到高精度分支,其余项归到 INT8 分支。

所以外积在这里是“直接拆计算项”的工具。

3. SmoothQuant 的外积藏在对角矩阵乘法中

SmoothQuant 使用

XW=(XS1)(SW).XW=(XS^{-1})(SW).

表面上看,XS1XS^{-1}SWSW 只是两个普通矩阵乘法。

但任何矩阵乘法都能写成外积和,所以可以继续展开它们。

外积展开后,对角阵每行或每列只有一个非零元素,很多外积矩阵会天然只占一列或一行。

这就是“隐蔽的外积思想”。

4. 右乘对角阵:从 shape 开始

X=[x1x2x3]RM×3,X= \begin{bmatrix} x_1&x_2&x_3 \end{bmatrix} \in\mathbb{R}^{M\times3},

并设

S1=diag(12,3,14).S^{-1} = \operatorname{diag} \left( \frac12, 3, \frac14 \right).

写成按行排列的形式:

S1=[r1r2r3],S^{-1} = \begin{bmatrix} r_1^\top\\ r_2^\top\\ r_3^\top \end{bmatrix},

其中

r1=[1/200],r_1^\top= \begin{bmatrix} 1/2&0&0 \end{bmatrix},
r2=[030],r_2^\top= \begin{bmatrix} 0&3&0 \end{bmatrix},
r3=[001/4].r_3^\top= \begin{bmatrix} 0&0&1/4 \end{bmatrix}.

于是

XS1=x1r1+x2r2+x3r3.XS^{-1} = x_1r_1^\top+x_2r_2^\top+x_3r_3^\top.

5. 每个外积项只占一列

第一项为

x1r1=x1[1/200]=[(1/2)x100].x_1r_1^\top = x_1 \begin{bmatrix} 1/2&0&0 \end{bmatrix} = \begin{bmatrix} (1/2)x_1&0&0 \end{bmatrix}.
图 2

把 XS⁻¹ 展开为外积和时,S⁻¹ 的第 j 行只在第 j 个位置非零,因此只把 X 的第 j 列放回对应输出列。

原视频 · 01:00 ↗

同理,

x2r2=[03x20],x_2r_2^\top = \begin{bmatrix} 0&3x_2&0 \end{bmatrix},
x3r3=[00(1/4)x3].x_3r_3^\top = \begin{bmatrix} 0&0&(1/4)x_3 \end{bmatrix}.

三项相加后非零列错位拼回:

XS1=[(1/2)x13x2(1/4)x3].XS^{-1} = \begin{bmatrix} (1/2)x_1&3x_2&(1/4)x_3 \end{bmatrix}.
图 3

每个激活列与对角阵对应行做外积,产生只占一个输出列的矩阵;错位相加后得到逐列缩放结果。

原视频 · 01:20 ↗

因此,矩阵右乘对角阵等价于逐列缩放。

6. 为什么没有 channel 混合

如果 S1S^{-1} 不是对角阵,第 jj 行可能有多个非零元素。

那么外积

xjrjx_jr_j^\top

就会把 xjx_j 同时写入多个输出列,产生 channel 混合。

对角结构使第 jj 行只有第 jj 个位置非零,所以:

  • 输入第 jj 列仍落在输出第 jj 列;
  • 只改变这一列的尺度;
  • 不把它加到其他列中。

这比“对角阵计算简单”更具体地解释了它的语义。

7. 左乘对角阵也可展开为外积和

现在看权重侧。

S=diag(2,1/3,4),S=\operatorname{diag}(2,1/3,4),
W=[w1w2w3].W= \begin{bmatrix} w_1^\top\\ w_2^\top\\ w_3^\top \end{bmatrix}.

SS 按列写为

S=[s1s2s3],S= \begin{bmatrix} s_1&s_2&s_3 \end{bmatrix},

其中

s1=[200],s2=[01/30],s3=[004].s_1= \begin{bmatrix} 2\\0\\0 \end{bmatrix}, \quad s_2= \begin{bmatrix} 0\\1/3\\0 \end{bmatrix}, \quad s_3= \begin{bmatrix} 0\\0\\4 \end{bmatrix}.

那么

SW=s1w1+s2w2+s3w3.SW = s_1w_1^\top +s_2w_2^\top +s_3w_3^\top.
图 4

把 SW 展开为外积和时,每个对角元素与 W 的对应行配对,非对角零项阻止不同行之间混合。

原视频 · 02:00 ↗

8. 每个外积项只占一行

第一项为

s1w1=[2w100].s_1w_1^\top = \begin{bmatrix} 2w_1^\top\\ 0\\ 0 \end{bmatrix}.

第二、三项分别只在第二、三行非零。

相加得到

SW=[2w1(1/3)w24w3].SW= \begin{bmatrix} 2w_1^\top\\ (1/3)w_2^\top\\ 4w_3^\top \end{bmatrix}.
图 5

左乘 S 后,W 的各行分别变为 s₁w₁、s₂w₂、s₃w₃;这与 XS⁻¹ 的逐列反向缩放成对抵消。

原视频 · 02:20 ↗

因此,矩阵左乘对角阵等价于逐行缩放。

9. 左右两次缩放为何成对抵消

对原矩阵乘法的第 jj 个共享维贡献:

xjwj.x_jw_j^\top.

SmoothQuant 后,激活列变为

x~j=xj/sj,\tilde x_j=x_j/s_j,

权重行变为

w~j=sjwj.\tilde w_j^\top=s_jw_j^\top.

二者外积为

x~jw~j=(xjsj)(sjwj)=xjwj.\tilde x_j\tilde w_j^\top = \left(\frac{x_j}{s_j}\right) \left(s_jw_j^\top\right) =x_jw_j^\top.

所以不只是总矩阵乘法相等,精确算术下每个共享 channel 的外积贡献都分别保持不变。

这也解释了为什么 S1S^{-1} 必须作用在 XX,而 SS 必须作用在 WW

10. 为什么 scale 是激活范围除以权重范围

SmoothQuant 的一种标准缩放为

sj=ajαbj1α,s_j = \frac{a_j^\alpha}{b_j^{1-\alpha}},

其中

aj=maxtXt,j,bj=maxkWj,k.a_j=\max_t|X_{t,j}|, \qquad b_j=\max_k|W_{j,k}|.

α=1/2\alpha=1/2 时,

sj=ajbj.s_j=\sqrt{\frac{a_j}{b_j}}.

假设激活第 1 列含有很大的离群值,a1a_1 增大。

那么 s1s_1 增大,于是:

x~1=x1/s1\tilde x_1=x_1/s_1

被压低,而

w~1=s1w1\tilde w_1^\top=s_1w_1^\top

被提高。

图 6

当激活第一列含大离群值时,s₁ 随 max|x₁| 增大,XS⁻¹ 中该列被压低,而 SW 中对应权重行被提升。

原视频 · 03:00 ↗

这正是把激活离群尺度迁移给权重的方向。

11. 如果把比值反过来会怎样

若错误地令 sjbj/ajs_j\propto\sqrt{b_j/a_j},激活离群值越大,sjs_j 反而越小。

由于激活实际乘的是 1/sj1/s_j,小 sjs_j 会进一步放大激活列,与压制离群值的目标相反。

所以“激活范围在分子、权重范围在分母”不是随意记号,而是由 XS1XS^{-1} 中的反向缩放决定的。

12. 外积视角解释结构,不决定全部算法

外积展开能证明右乘缩列、左乘缩行以及两侧同一 channel 的缩放互相抵消。

它不能单独决定校准集、α\alpha、clipping、量化粒度和 kernel 融合;这些属于 SmoothQuant 的统计与系统实现层。

跟练与练习

原视频练习

编者练习

A=[a1a2],D=diag(3,1/2).A= \begin{bmatrix} a_1&a_2 \end{bmatrix}, \qquad D=\operatorname{diag}(3,1/2).ADAD 展开为两个外积之和,并写出最终结果。

查看参考答案

DD 的两行分别为 [3,0][3,0][0,1/2][0,1/2],所以 AD=a1[3,0]+a2[0,1/2]=[3a1,(1/2)a2]AD=a_1[3,0]+a_2[0,1/2]=[3a_1,(1/2)a_2]。每个外积项只占一个输出列,因此右乘对角阵逐列缩放。

编者练习 2

为什么说 SmoothQuant 的每个 channel 外积贡献在量化前都保持不变?

查看参考答案

原贡献为 xjwjx_jw_j^\top。变换后激活列为 xj/sjx_j/s_j,权重行为 sjwjs_jw_j^\top,所以 (xj/sj)(sjwj)=xjwj(x_j/s_j)(s_jw_j^\top)=x_jw_j^\top。等价性逐 channel 成立;量化后仍可能因取整而产生误差。

常见误区

  • 误区:SmoothQuant 没有用到外积,因为公式中没有外积符号。纠正:对角矩阵乘法可展开为稀疏外积之和。
  • 误区:右乘对角阵缩放矩阵的行。纠正:右乘缩放列,左乘才缩放行。
  • 误区:对角阵会把多个 channel 混到一起。纠正:每行每列只有一个非零项,不产生跨 channel 混合。
  • 误区:XS1XS^{-1}SWSW 的缩放方向相同。纠正:同一 channel 一边除以 sjs_j,另一边乘以 sjs_j
  • 误区:离群值越大,sjs_j 应越小。纠正:激活含离群值时 sjs_j 增大,才能让 1/sj1/s_j 压低该激活列。
  • 误区:外积证明说明量化后也严格相等。纠正:它只证明量化前的代数等价,取整仍引入误差。
  • 误区:外积视角已经决定完整部署方案。纠正:校准、阈值、位宽和 kernel 仍需单独设计。

本课小结

  • LLM.int8() 直接把矩阵乘法分成外积项;SmoothQuant 的外积结构隐藏在两个对角矩阵乘法里。
  • XS1XS^{-1} 的每个外积项只占一个输出列,因此右乘对角阵等价于逐列缩放。
  • SWSW 的每个外积项只占一个输出行,因此左乘对角阵等价于逐行缩放。
  • 对角结构让相同 channel 保持对齐,不发生跨 channel 混合。
  • 每个通道满足 (xj/sj)(sjwj)=xjwj(x_j/s_j)(s_jw_j^\top)=x_jw_j^\top,量化前贡献不变。
  • 激活范围位于 scale 公式分子,使离群激活对应的 sjs_j 增大,再由 1/sj1/s_j 将该列压低。
05

主题讲解 · 03:09

SmoothQuant 的缩放为何不会增加推理算子

学习目标

  • 能写出 SmoothQuant 在 XWXW 中插入 S1SS^{-1}S 的等价变换。
  • 能解释对角缩放为什么比通用矩阵乘简单。
  • 能说明校准集为何让缩放矩阵在部署前就固定下来。
  • 能分别解释 SWSWXS1XS^{-1} 如何从推理计算图中消失。
  • 能用张量 shape 检查缩放究竟作用在 token 维还是 channel 维。

前置与衔接

需要知道线性层可写成 Y=XWY=XW,并了解激活量化容易被少数离群 channel 拉大动态范围。

本课位于“量化与误差修正”单元。

它不讨论如何选择量化 bit 数,而是回答一个部署问题:SmoothQuant 引入了缩放矩阵 SS,为什么推理时没有因此多做两次矩阵乘?

核心讲解

1. 先把线性层的 shape 写清楚

设一个线性层接收 TT 个 token,每个 token 有 CinC_{in} 个输入 channel:

XRT×Cin,WRCin×Cout.X\in\mathbb{R}^{T\times C_{in}},\qquad W\in\mathbb{R}^{C_{in}\times C_{out}}.

原始输出是

Y=XW.Y=XW.

SmoothQuant 关注的是不同输入 channel 的数值尺度不均衡,而不是 token 之间的长度差异。

因此它构造的缩放矩阵为

S=diag(s1,s2,,sCin),S=\operatorname{diag}(s_1,s_2,\ldots,s_{C_{in}}),

shape 为 Cin×CinC_{in}\times C_{in}

图 1

SmoothQuant 把激活与权重之间插入的对角缩放写成等价变换,目标是避免推理期新增矩阵乘。

原视频 · 00:00 ↗

板书把最终结论分成两半:右侧的 SWSW 可以提前并入权重,左侧的 XS1XS^{-1} 可以利用对角结构并进一步并入前置归一化层。

2. 插入的是恒等映射,不是近似

因为 SS 可逆,所以

S1S=I.S^{-1}S=I.

在线性层中插入它:

Y=XW=XS1SW=(XS1)(SW).Y=XW=XS^{-1}SW=(XS^{-1})(SW).
图 2

在 XW 中插入 S^{-1}S,把同一个线性映射改写为 (XS^{-1})(SW)。

原视频 · 00:40 ↗

定义

X=XS1,W=SW,X'=XS^{-1},\qquad W'=SW,

就仍有

Y=XW.Y=X'W'.

这一变换本身没有量化误差;误差来自随后分别量化 XX'WW'

它的目的,是把激活中难量化的离群尺度转移给通常更容易离线处理的权重。

3. 为什么校准集是“算子消失”的前提

如果 SS 要根据每一次在线输入重新计算,那么 W=SWW'=SW 也会随请求变化,就不能提前保存。

SmoothQuant 的做法是先用有代表性的校准数据统计各 channel 的激活范围,再确定缩放系数。

图 3

缩放矩阵 S 由校准集在推理前确定,而不是根据每次在线输入重新计算。

原视频 · 01:00 ↗

部署阶段开始时,模型权重 WW 已固定,校准得到的 SS 也已固定。

于是 SS 不再是请求相关的动态张量,而是一次离线模型变换的参数。

这里要区分两件事:

  • 校准阶段需要统计并计算 SS
  • 在线推理阶段只使用变换后的模型参数。

若部署数据与校准数据分布差异很大,量化精度可能下降,但这不改变算子融合的代数条件。

4. 右侧的 SW 直接折叠进权重

因为 SSWW 都固定,可以在导出或加载模型时计算

W=SW.W'=SW.
图 4

固定的 S 与 W 可预先合并成新权重 W'=SW,推理时直接使用 W'。

原视频 · 01:20 ↗

推理时执行的仍是一个线性层:

Y=XW.Y=X'W'.

运行时不会先启动一个 kernel 计算 SWSW,再启动另一个 GEMM 计算 XWX'W'

板书把它类比为 LoRA 合并:训练或适配过程可以保留多个参数分支,部署前把固定分支合成一份新权重。

类比只说明“固定参数可离线合并”,并不表示 SmoothQuant 与 LoRA 的训练目标相同。

5. 左侧的 XS⁻¹ 本来就是按 channel 缩放

右乘对角阵不会形成稠密的全连接混合。

对每个元素有

Xt,j=Xt,jsj1.X'_{t,j}=X_{t,j}s_j^{-1}.
图 5

右乘对角阵 S^{-1} 等价于按 channel 缩放激活的各列,不需要通用矩阵乘。

原视频 · 02:00 ↗

同一列中的所有 token 共享一个缩放系数,而不同列使用不同系数。

因此即使单独执行,它也只需要逐元素乘或广播,而不需要通用 GEMM。

shape 是最可靠的检查方法:

  • XX 的行是 token;
  • XX 的列是输入 channel;
  • S1S^{-1} 位于 XX 的右侧;
  • 所以它缩放的是列,也就是 channel。

不要因为板书把张量画成若干格,就把缩放误解为对 token 做重加权。

6. 常见 Transformer 中还能并入前置归一化层

若线性层前有带仿射参数的 LayerNorm:

LN(h)=γh^+β,\operatorname{LN}(h)=\gamma\odot\hat h+\beta,

而下一步需要

LN(h)s1,\operatorname{LN}(h)\odot s^{-1},

则可预先更新

γ=γs1,β=βs1.\gamma'=\gamma\odot s^{-1},\qquad \beta'=\beta\odot s^{-1}.
图 6

固定的按通道缩放还能吸收到前置 LayerNorm 的仿射参数中,从计算图里消去额外逐元素乘。

原视频 · 02:40 ↗

这样归一化 kernel 直接输出已经缩放的 XX',后面不再单独乘 s1s^{-1}

若使用没有偏置项的 RMSNorm,只需按实现处理 scale 参数;若前置算子没有可吸收缩放的仿射参数,则可能保留一个轻量逐元素操作或与相邻 kernel 融合。

所以准确说法不是“任何计算图都天然没有缩放”,而是:固定的对角缩放可以通过参数折叠或 kernel fusion 消除额外调度,通常不会增加新的 GEMM。

7. 编者补充:性能收益要看部署图,而不是纸面公式

纸面上出现 S1S^{-1}SS,不代表线上一定多两个算子。

判断部署开销时应追踪三个阶段:

  1. 校准阶段如何得到 SS
  2. 模型导出阶段如何生成 WW' 和更新归一化参数;
  3. 推理图中最终保留哪些 kernel。

若导出器只改写权重、却没有同步改写归一化层或做算子融合,左侧缩放仍可能以逐元素 kernel 出现。

这属于实现是否完整的问题,不是 SmoothQuant 等价变换失效。

跟练与练习

原视频练习

编者练习

XR32×4096X\in\mathbb{R}^{32\times4096}WR4096×11008W\in\mathbb{R}^{4096\times11008}。写出 SSXS1XS^{-1}SWSW 的 shape,并说明 SS 的第 jj 个对角元素影响哪些数据。

查看参考答案

SR4096×4096S\in\mathbb{R}^{4096\times4096}XS1R32×4096XS^{-1}\in\mathbb{R}^{32\times4096}SWR4096×11008SW\in\mathbb{R}^{4096\times11008}sj1s_j^{-1} 缩放 XX 的第 jj 列,也就是 32 个 token 的第 jj 个输入 channel;sjs_j 同时缩放 WW 的第 jj 行,从而保持乘积不变。

编者练习 2

为什么“SS 是对角阵”只能说明它不需要 GEMM,却不能单独说明它在推理图里完全没有额外 kernel?

查看参考答案

对角结构只把运算降为按 channel 的逐元素缩放。如果没有把固定缩放折叠进权重、归一化参数或相邻 fused kernel,运行时仍可能单独启动一次逐元素 kernel。真正让额外算子消失的是“离线固定”与“参数折叠或 kernel fusion”共同成立。

常见误区

  • 误区:SmoothQuant 通过删掉离群值获得收益。纠正:它用等价缩放把量化难度从激活迁移到权重。
  • 误区:插入 S1SS^{-1}S 会改变全精度模型输出。纠正:在量化前它严格等于单位阵。
  • 误区:SS 每次根据当前 prompt 计算。纠正:典型部署使用校准集在推理前确定它。
  • 误区:右乘 S1S^{-1} 是按 token 缩放。纠正:它按 XX 的列,也就是输入 channel 缩放。
  • 误区:只要是对角阵,计算就自动从图中消失。纠正:还需要离线折叠或算子融合。
  • 误区:LayerNorm 与 RMSNorm 的折叠参数完全相同。纠正:是否有 bias、仿射参数形式及算子位置都要按具体实现检查。

本课小结

  • SmoothQuant 用 XW=(XS1)(SW)XW=(XS^{-1})(SW) 在激活与权重之间重新分配尺度。
  • 校准集让 SS 在部署前固定,因而 SWSW 可以离线合并为新权重。
  • XS1XS^{-1} 是按 channel 广播的逐元素缩放,不是稠密矩阵乘。
  • 在常见计算图中,这个缩放还能并入前置归一化参数或 fused kernel。
  • 判断是否“零额外开销”要查看最终推理图,而不能只数公式中的矩阵符号。
06

主题讲解 · 03:04

LLM.int8() 与 SmoothQuant 如何处理激活离群值

学习目标

  • 能解释大模型激活离群值主要出现在哪个张量维度。
  • 能写出按收缩维拆分矩阵乘的等价公式。
  • 能说明 LLM.int8() 为什么需要混合精度路径。
  • 能说明 SmoothQuant 如何把量化难度从激活迁移到权重。
  • 能从“共同对象、处理动作、运行时结构”三个层次比较两种方法。

前置与衔接

需要知道线性层 Y=XWY=XW 中,XX 的列与 WW 的行共享同一个收缩索引。

上一课解释了 SmoothQuant 的固定缩放如何折叠进部署图;本课把它与 LLM.int8() 放在同一张矩阵图上比较。

核心讲解

1. 先定位离群值:它主要沿 channel 出现

XRT×Cin,WRCin×Cout.X\in\mathbb{R}^{T\times C_{in}},\qquad W\in\mathbb{R}^{C_{in}\times C_{out}}.

XX 的一行对应一个 token,一列对应一个 feature 或 channel。

图 1

大模型激活离群值通常集中在 feature/channel 维,而不是由某几个 token 位置固定产生。

原视频 · 00:00 ↗

课程强调,大幅值激活常集中在少数 channel 上。

也就是说,不是“某个 token 的全部 feature 都异常大”,而是“许多 token 在某几个 feature 位置上容易出现大值”。

这种轴向结构决定了后续方法都要成对处理:

  • XX 中的离群列;
  • WW 中相同收缩索引对应的行。

若只处理激活列而不处理权重行,就无法保持原矩阵乘的贡献。

2. 离群 channel 有三个关键性质

板书把经验现象概括为:相对固定、数量较少、但很重要。

图 2

离群 channel 往往在校准集与推理中位置相对固定、数量较少,却不能简单裁掉。

原视频 · 00:20 ↗

“相对固定”指校准数据中反复出现大值的 feature 位置,在相似推理分布中往往仍值得重点处理。

“数量较少”意味着可以为这些维度使用特殊路径,而不必让全部计算都保持高精度。

“很重要”意味着不能粗暴裁剪这些维度;把大值直接截断可能造成明显精度损失。

这里的固定性是统计规律,不是对每个模型、每层和每个请求都绝对不变的保证。

实际实现仍需根据模型、阈值和校准分布验证。

3. LLM.int8():把收缩维拆成普通集与离群集

令离群 channel 索引集合为 OO,其余索引集合为 RR

按输入 channel 拆分后:

X=[XR  XO],X=[X_R\;X_O],

而权重沿对应行拆分:

W=[WRWO].W= \begin{bmatrix} W_R\\ W_O \end{bmatrix}.

矩阵乘自然分解为

XW=XRWR+XOWO.XW=X_RW_R+X_OW_O.
图 3

LLM.int8() 把离群激活列及对应权重行放入 FP16 分支,其余部分走 INT8 路径。

原视频 · 01:20 ↗

LLM.int8() 的核心直觉是:

  • 普通部分 XRWRX_RW_R 使用 INT8 路径;
  • 离群部分 XOWOX_OW_O 保持 FP16 等较高精度;
  • 最后把两部分结果相加。

因为离群集合较小,高精度分支只承担少量收缩维,整体仍能获得量化收益。

4. 为什么必须抽取“X 列 + W 行”

矩阵乘的单个输出为

Yt,k=j=1CinXt,jWj,k.Y_{t,k}=\sum_{j=1}^{C_{in}}X_{t,j}W_{j,k}.

若第 jj 个激活 channel 被判定为离群,相关贡献是

Xt,jWj,k.X_{t,j}W_{j,k}.

所以需要保留 XX 的第 jj 列和 WW 的第 jj 行。

图 4

抽取激活的离群列时,必须同步抽取 W 中相同收缩索引对应的行。

原视频 · 01:40 ↗

“列—行配对”来自收缩维的定义,不是实现上的偶然约定。

一个常见错误是按可视化方向机械切块,却没有检查共享索引;只要回到求和公式就能纠正。

5. SmoothQuant:不拆分高精度分支,而是迁移尺度

SmoothQuant 选择另一种动作。

它构造可逆对角矩阵 SS,将

XWXW

改写为

(XS1)(SW).(XS^{-1})(SW).
图 5

SmoothQuant 不拆出高精度分支,而是压低离群激活 channel,并把尺度压力转移给对应权重行。

原视频 · 02:00 ↗

若某个激活 channel 数值特别大,就用较强的 sj1s_j^{-1} 把该列压低。

为保持乘积不变,同时用 sjs_j 放大 WW 的对应行。

这样激活的动态范围更平滑,更适合统一的低比特量化;代价是权重范围变大一些。

由于权重通常是静态参数,且量化行为更容易离线校准,这种“压力转移”往往比直接量化激活离群值更可控。

6. S⁻¹S 保证迁移前后的全精度函数相同

SmoothQuant 的尺度迁移不是删值或近似:

S1S=I.S^{-1}S=I.

因此在量化之前

XW=XS1SWXW=XS^{-1}SW

严格成立。

图 6

SmoothQuant 利用 S^{-1}S=I 保持 XW 不变,同时重新分配激活列与权重行的数值范围。

原视频 · 02:20 ↗

实际误差来自变换后对 X=XS1X'=XS^{-1}W=SWW'=SW 的量化。

缩放系数的任务是平衡两边的量化难度,而不是凭空消除信息。

上一课进一步说明:固定的 SWSW 可离线合入权重,XS1XS^{-1} 可折叠进相邻归一化或 fused kernel。

7. 两种方法“同”在哪里

两者的共同点不只是“都处理离群值”。

更精确地说,它们都利用同一个结构事实:问题集中在少数激活 channel,以及与这些 channel 对应的权重行。

它们都保留这些维度的贡献,而不是直接裁掉。

它们的目标都是让大部分计算进入更高效的低精度路径,同时控制由激活离群值造成的误差。

8. 两种方法“异”在哪里

LLM.int8() 的动作是分流:

  • 普通维走 INT8;
  • 离群维走高精度;
  • 运行时合并两个结果。

SmoothQuant 的动作是重参数化:

  • 所有相关维度仍在统一矩阵乘中;
  • 先把激活范围压平,把尺度转给权重;
  • 部署前折叠固定缩放,再执行量化线性层。

因此两者的工程取舍不同:

  • 混合精度分流更直接地保护离群贡献,但需要额外的数据选择、两条计算路径和结果归并;
  • 离线平滑有利于更规整的 W8A8 kernel,但依赖校准质量与缩放参数的选择。

不能只问“哪个精度更高”,还要结合目标硬件是否高效支持相应 kernel、模型规模、延迟与吞吐要求来判断。

跟练与练习

原视频练习

编者练习

Cin=8C_{in}=8,离群集合 O={2,7}O=\{2,7\}XOX_OWOW_OXRX_RWRW_R 分别是什么 shape?

查看参考答案

XRT×8X\in\mathbb{R}^{T\times8}WR8×CoutW\in\mathbb{R}^{8\times C_{out}},则 XORT×2X_O\in\mathbb{R}^{T\times2}WOR2×CoutW_O\in\mathbb{R}^{2\times C_{out}}。其余 6 个 channel 构成 XRRT×6X_R\in\mathbb{R}^{T\times6}WRR6×CoutW_R\in\mathbb{R}^{6\times C_{out}}。两条分支输出 shape 都是 T×CoutT\times C_{out},所以可以相加。

编者练习 2

为什么把激活第 jj 列乘以 1/81/8 时,必须把权重第 jj 行乘以 88

查看参考答案

该收缩维对输出的贡献是 X:,jWj,:X_{:,j}W_{j,:}。变换后变成 (X:,j/8)(8Wj,:)(X_{:,j}/8)(8W_{j,:}),乘积不变。若只压缩激活列而不补偿权重行,这一维的全部输出贡献都会缩小为原来的 1/81/8

常见误区

  • 误区:离群值等于少数异常 token。纠正:这里重点是少数 feature/channel 在多个 token 上出现大激活。
  • 误区:离群 channel 数量少,所以可以裁掉。纠正:它们往往对模型输出很重要。
  • 误区:LLM.int8() 只抽取 X 的列。纠正:必须同步使用 W 的对应行。
  • 误区:SmoothQuant 与 LLM.int8() 都建立独立 FP16 分支。纠正:SmoothQuant 的核心是离线尺度迁移与重参数化。
  • 误区:SmoothQuant 压小激活后自然保持输出不变。纠正:必须用相反尺度放大对应权重行。
  • 误区:某个校准集上的固定离群位置对任何分布都不变。纠正:这是需验证的统计稳定性,不是数学定理。

本课小结

  • 两种方法都聚焦少数激活离群 channel 与对应权重行。
  • LLM.int8() 把普通维和离群维拆成低精度与高精度两条路径。
  • SmoothQuant 用 S1S=IS^{-1}S=I 把激活尺度转移到权重,争取统一的低精度计算。
  • 前者强调混合精度保护,后者强调离线平滑与规整 kernel。
  • 比较量化方法时,要同时看误差、校准假设和最终硬件执行图。
07

主题讲解 · 02:58

聚类量化后如何微调而不破坏权重共享

学习目标

  • 能说明聚类量化如何用索引矩阵和 codebook 表示权重。
  • 能解释逐元素微调为何会让共享权重“分家”。
  • 能推导聚类中心的梯度为何是同簇权重梯度之和。
  • 能区分固定的簇分配与可训练的聚类中心。
  • 能描述更新中心后如何重建量化权重。

前置与衔接

需要理解 K-means 的“样本—簇中心”关系,并会写最基本的梯度下降更新。

本课讨论聚类量化后的微调约束。

它与普通后训练量化不同:这里不仅要压缩一次模型,还希望在训练过程中保持“许多权重共享少量中心”的表示。

核心讲解

1. 聚类量化保存的不是每个浮点权重

设原始权重展平后为 w1,,wNw_1,\ldots,w_N,聚类得到 KK 个中心:

c1,c2,,cK.c_1,c_2,\ldots,c_K.

每个位置只保存一个簇索引

ai{1,2,,K}.a_i\in\{1,2,\ldots,K\}.

反量化权重为

w^i=cai.\hat w_i=c_{a_i}.
图 1

聚类量化把稠密权重表示为低比特索引矩阵与少量聚类中心组成的 codebook。

原视频 · 00:20 ↗

KK 很小,索引只需少量 bit;codebook 只保存 KK 个浮点中心。

压缩收益来自大量位置复用同一中心,而不是把每个权重换成另一个独立浮点数。

2. 索引负责“属于哪一簇”,码本负责“这一簇是多少”

板书例子把接近 2.0 的权重归入 0 号簇,把接近 5.0 的权重归入 1 号簇。

例如原值 2.1 的编码过程是:

2.1ai=0c0=2.0.2.1\longrightarrow a_i=0\longrightarrow c_0=2.0.
图 2

每个原始权重只保存所属簇的索引,反量化时用索引查找对应中心值。

原视频 · 00:40 ↗

量化误差为原权重与中心的差,例如 2.12.0=0.12.1-2.0=0.1

只要索引矩阵和 codebook 仍然存在,就可以随时重建同 shape 的量化权重矩阵。

3. 朴素逐元素微调会破坏共享关系

普通梯度下降把每个权重当成独立参数:

winew=wioldηgi,w_i^{new}=w_i^{old}-\eta g_i,

其中

gi=Lwi.g_i=\frac{\partial L}{\partial w_i}.

即使两个位置原本都等于中心 2.0,只要它们的梯度不同,更新后就会得到两个不同值。

图 3

若量化后直接逐元素微调,原先共享同一中心的权重会各自漂移,无法继续只用索引与码本表示。

原视频 · 01:20 ↗

这样一来,同一个索引不再能同时还原这两个位置。

若为每个新值都新增中心,codebook 会不断膨胀,最终退化为保存全部独立权重。

所谓“权重分家”,本质是参数化方式错了:量化模型宣称同簇位置共享一个参数,优化器却仍把这些位置视为独立参数。

4. 正确参数化:训练中心,不训练解码后的每个位置

量化权重应写成中心的函数:

w^i=cai.\hat w_i=c_{a_i}.

在一次固定簇分配的微调中,aia_i 保持不变,真正可训练的是 c1,,cKc_1,\ldots,c_K

于是同一簇的所有位置永远读取同一个参数。

中心改变后,这些位置会一起改变;共享约束由参数化自动保证,而不是更新后再手工把相近权重聚回来。

5. 中心梯度来自同簇位置的梯度归约

由链式法则:

Lck=i=1NLw^iw^ick.\frac{\partial L}{\partial c_k} =\sum_{i=1}^{N} \frac{\partial L}{\partial \hat w_i} \frac{\partial \hat w_i}{\partial c_k}.

因为当 ai=ka_i=k 时,w^i=ck\hat w_i=c_k,所以

w^ick={1,ai=k,0,aik.\frac{\partial \hat w_i}{\partial c_k} = \begin{cases} 1,&a_i=k,\\ 0,&a_i\ne k. \end{cases}

最终得到

Lck=i:ai=kLw^i.\frac{\partial L}{\partial c_k} =\sum_{i:a_i=k} \frac{\partial L}{\partial \hat w_i}.
图 4

依据固定簇索引,把每个权重位置的更新量按颜色分组并对同簇位置求和。

原视频 · 02:00 ↗

板书用颜色标出两簇:所有粉色位置归约到粉色中心,所有蓝色位置归约到蓝色中心。

这正是一个按簇索引的 scatter-add 或 segment-sum。

6. 更新量求和与梯度求和不要混淆符号

若先定义逐位置的梯度下降增量

Δwi=ηLw^i,\Delta w_i=-\eta\frac{\partial L}{\partial \hat w_i},

则中心增量可写为

Δck=i:ai=kΔwi.\Delta c_k=\sum_{i:a_i=k}\Delta w_i.

等价地,直接用中心梯度更新:

cknew=ckoldηi:ai=kLw^i.c_k^{new} =c_k^{old} -\eta\sum_{i:a_i=k} \frac{\partial L}{\partial \hat w_i}.
图 5

同簇更新量汇总后只更新对应聚类中心,索引矩阵保持不变。

原视频 · 02:20 ↗

板书把 ηW-\eta\nabla W 作为“已经包含负号与学习率的更新量”再求和,因此示例里中心直接加上汇总值。

若手里的是原始梯度 W\nabla W,则仍要使用减号和学习率;不要把两套记号混在一起。

7. 索引不变,新 codebook 产生新的共享权重

中心更新后,仍用原索引解码:

w^inew=cainew.\hat w_i^{new}=c_{a_i}^{new}.
图 6

固定索引矩阵查找更新后的中心值,可重建仍然保持权重共享的新矩阵。

原视频 · 02:40 ↗

原来属于蓝色簇的全部位置一起变为新的蓝色中心,粉色簇同理。

因此训练后的模型仍可保存为:

  • 一个低比特索引张量;
  • 一个小型 codebook。

权重共享关系没有被逐元素更新破坏。

8. 编者补充:求和、平均与簇大小

把共享中心视为计算图里的真实参数时,严格链式法则给出的是梯度之和。

有些工程实现会按簇大小做平均或其他归一化:

gˉk=1Cki:ai=kgi.\bar g_k=\frac{1}{|C_k|}\sum_{i:a_i=k}g_i.

这会改变有效学习率,使大簇不会仅因成员更多而获得更大更新。

它可以是合理的优化器设计,但不应与“原损失对共享参数的原始梯度”混为一谈。

实践中还可能周期性重新分配簇、使用直通估计器或联合学习索引;这些属于更复杂的量化感知训练策略,本视频聚焦的是固定索引、只更新中心这一核心机制。

跟练与练习

原视频练习

编者练习

某中心 c0c_0 被 3 个权重位置共享,这 3 个位置的原始梯度分别为 0.2,0.1,0.40.2,-0.1,0.4。学习率为 0.010.01,求一次梯度下降后的中心变化。

查看参考答案

中心梯度为 0.20.1+0.4=0.50.2-0.1+0.4=0.5。梯度下降增量为 Δc0=0.01×0.5=0.005\Delta c_0=-0.01\times0.5=-0.005,所以 c0new=c0old0.005c_0^{new}=c_0^{old}-0.005。若先计算每个位置的更新量再求和,也会得到同一结果。

编者练习 2

为什么“逐元素更新后再把每个权重舍入到最近中心”与“直接训练中心”不是同一个优化过程?

查看参考答案

前者先允许共享位置独立移动,再执行离散投影,投影可能改变簇分配并丢弃部分更新;后者从一开始就把同簇位置绑定到一个参数,中心梯度由所有成员共同决定。二者的可行参数空间、梯度路径和优化轨迹都不同。

常见误区

  • 误区:聚类量化只保存中心值。纠正:还必须保存每个权重位置的簇索引。
  • 误区:量化后可以照常逐元素微调,再沿用旧索引。纠正:独立更新会破坏共享关系。
  • 误区:中心梯度取同簇梯度中的任意一个。纠正:共享参数收到所有使用位置的梯度贡献。
  • 误区:板书中的更新量求和等于原始梯度直接相加后再加到中心。纠正:要检查负号与学习率是否已包含在 ΔW\Delta W 中。
  • 误区:固定索引意味着模型无法学习。纠正:codebook 中心仍可学习,并同时改变同簇全部位置。
  • 误区:梯度平均就是链式法则的唯一结果。纠正:原始共享参数梯度是求和,平均属于额外的优化归一化。

本课小结

  • 聚类量化以 w^i=cai\hat w_i=c_{a_i} 表示大量权重共享少量中心。
  • 逐元素微调会让同簇权重分裂,破坏索引加 codebook 的压缩形式。
  • 固定簇索引、只训练中心可以从参数化上保持权重共享。
  • 中心梯度等于所有同簇权重位置梯度之和。
  • 更新 codebook 后用原索引重建,就得到新的、仍可压缩的量化权重。
08

主题讲解 · 02:48

为什么共享聚类中心的梯度必须求和

学习目标

  • 能辨认聚类量化微调中的自变量与中间变量。
  • 能画出“中心→解码权重→输出→损失”的计算图。
  • 能对一个共享中心完整展开多路径链式法则。
  • 能解释同簇权重的局部导数为何为 1,其他位置为何为 0。
  • 能把标量推导推广为按索引 scatter-add 的张量实现。

前置与衔接

上一课已经得到操作结论:固定索引,只更新 codebook 中的聚类中心。

本课不再讨论这种做法是否保持压缩,而是证明中心梯度为什么等于所有同簇权重梯度的和。

核心讲解

1. 先复盘更新规则,但不要把结论当证明

聚类量化把每个解码权重写成

Wi=Cai,W_i=C_{a_i},

其中 aia_i 是固定簇索引,CkC_k 是第 kk 个中心。

反向传播会先得到每个解码位置的梯度 L/Wi\partial L/\partial W_i

实现随后按索引把同簇项相加,用来更新对应中心。

图 1

聚类量化微调把同簇位置的更新量求和,用来更新 codebook 中的共享中心。

原视频 · 00:00 ↗

板书例子中,蓝色位置的更新量和为 1.5,粉色位置的更新量和为 -0.9;两个数分别修改两个中心,再由旧索引重建整张权重矩阵。

问题是:这种求和是经验规则,还是链式法则的必然结果?

2. 自变量是 C,W 是由 C 解码得到的变量

普通全精度训练常把 WW 直接视作参数。

固定索引的聚类量化则改变了参数化:

Clookup by aWlinear layerYL.C\xrightarrow{\text{lookup by }a}W\xrightarrow{\text{linear layer}}Y\longrightarrow L.
图 2

固定簇分配下,聚类中心 C 是自变量,解码权重 W 是由 C 赋值得到的中间变量。

原视频 · 01:40 ↗

这里真正交给优化器的参数是 CC

WW 虽然参与前向计算,却是索引查表的输出,是 CC 的函数。

若把这一层依赖关系漏掉,就会错误地问“为什么不直接更新 WW”;因为直接更新中间结果会脱离它的共享参数化。

3. 一个中心会通过多条路径影响损失

假设中心 C2C_2 被权重位置 W3,W5,W6,W7,W9W_3,W_5,W_6,W_7,W_9 共同使用。

损失对 C2C_2 的总导数必须包含每一条路径:

LC2=LW3W3C2+LW5W5C2++LW9W9C2.\frac{\partial L}{\partial C_2} = \frac{\partial L}{\partial W_3} \frac{\partial W_3}{\partial C_2} + \frac{\partial L}{\partial W_5} \frac{\partial W_5}{\partial C_2} +\cdots+ \frac{\partial L}{\partial W_9} \frac{\partial W_9}{\partial C_2}.
图 3

损失对中心 C₂ 的梯度需要汇总所有依赖 C₂ 的权重位置对应的 ∂L/∂Wᵢ。

原视频 · 02:00 ↗

自动微分遇到“一个节点被多个下游节点使用”时,本来就会把各路径贡献累加。

聚类梯度求和不是额外发明的量化规则,而是共享参数反向传播的标准行为。

4. 查表赋值的局部导数是 1 或 0

固定索引下,若 ai=2a_i=2,就有

Wi=C2.W_i=C_2.

所以

WiC2=1.\frac{\partial W_i}{\partial C_2}=1.

ai2a_i\ne2WiW_i 来自其他中心,与 C2C_2 无关,因此

WiC2=0.\frac{\partial W_i}{\partial C_2}=0.
图 4

当某个解码权重 Wᵢ 直接等于中心 C₂ 时,局部导数 ∂Wᵢ/∂C₂=1;其他簇的局部导数为 0。

原视频 · 02:20 ↗

代回链式法则,所有属于 C2C_2 的项保留,其他项乘 0 消失:

LC2=i:ai=2LWi.\frac{\partial L}{\partial C_2} = \sum_{i:a_i=2} \frac{\partial L}{\partial W_i}.

这就是“对聚类梯度求和”的数学依据。

5. 用指示函数可以一次写完所有中心

定义指示函数

1[ai=k]={1,ai=k,0,aik.\mathbf{1}[a_i=k]= \begin{cases} 1,&a_i=k,\\ 0,&a_i\ne k. \end{cases}

Wi=k=1K1[ai=k]Ck.W_i=\sum_{k=1}^{K}\mathbf{1}[a_i=k]C_k.

因此

WiCk=1[ai=k],\frac{\partial W_i}{\partial C_k}=\mathbf{1}[a_i=k],

以及

LCk=iLWi1[ai=k].\frac{\partial L}{\partial C_k} =\sum_i \frac{\partial L}{\partial W_i} \mathbf{1}[a_i=k].

指示函数把“按颜色挑出同簇位置”写成了明确代数。

6. 张量实现就是按索引做 scatter-add

设解码权重梯度张量为 GWG_W,其 shape 与 WW 相同;索引张量 AA 也与 WW 同 shape。

将二者展平后,可以执行:

  1. 初始化 GCRKG_C\in\mathbb{R}^{K} 为零;
  2. 对每个位置 ii,读取簇索引 k=Aik=A_i
  3. 累加 GC[k]+=GW[i]G_C[k]\mathrel{+}=G_W[i]

这就是 segment-sum 或 scatter-add。

无需显式构造巨大的 Jacobian W/C\partial W/\partial C;索引已经隐式编码了 Jacobian 中 1 的位置。

7. 为什么不是“任选一个梯度”或“默认平均”

一个中心被多个权重位置复用,意味着它同时影响所有这些位置。

只取一个位置的梯度会丢失其他计算路径对损失的贡献。

链式法则给出的原始中心梯度是求和:

GC[k]=i:ai=kGW[i].G_C[k]=\sum_{i:a_i=k}G_W[i].

若为了优化稳定性再除以簇大小,得到的是人为归一化后的更新方向:

G~C[k]=GC[k]{i:ai=k}.\tilde G_C[k]=\frac{G_C[k]}{|\{i:a_i=k\}|}.

它可能有用,但已经改变了各中心的有效学习率,不能说是链式法则本身要求的平均。

8. 推导依赖“索引固定”这一前提

本课把 aia_i 当作常量,因此查表对中心是简单线性赋值。

若训练期间还允许权重切换簇,aia_i 是离散变量,最近中心选择在边界处不可导,通常需要交替优化、直通估计或软分配等额外机制。

所以本结论的精确适用范围是:一次反向传播中簇分配固定,中心是连续可训练参数。

跟练与练习

原视频练习

编者练习

W1=C1W_1=C_1W2=C2W_2=C_2W3=C1W_3=C_1W4=C1W_4=C_1,且 L/W=[0.2,0.4,0.7,0.1]\partial L/\partial W=[0.2,-0.4,0.7,-0.1]。求 L/C1\partial L/\partial C_1L/C2\partial L/\partial C_2

查看参考答案

C1C_1 被位置 1、3、4 使用,所以 L/C1=0.2+0.70.1=0.8\partial L/\partial C_1=0.2+0.7-0.1=0.8C2C_2 只被位置 2 使用,所以 L/C2=0.4\partial L/\partial C_2=-0.4

编者练习 2

若中心 CkC_k 当前没有任何权重位置使用,它在这一次前向—反向中的梯度是多少?这意味着什么?

查看参考答案

求和集合为空,因此梯度为 0。仅靠当前损失的反向传播,该中心不会更新;实现可能需要重新初始化空簇、重新分配索引或加入其他正则机制,才能让它重新参与表示。

常见误区

  • 误区:量化微调中 W 仍是独立优化参数。纠正:固定索引时 W 是由中心查表得到的中间变量。
  • 误区:一个中心只有一条梯度路径。纠正:每个共享它的权重位置都贡献一条路径。
  • 误区:所有 Wi/Ck\partial W_i/\partial C_k 都等于 1。纠正:只有 ai=ka_i=k 的位置为 1,其他位置为 0。
  • 误区:中心梯度求和是为了方便实现。纠正:它直接来自多路径链式法则。
  • 误区:梯度平均与梯度求和完全等价。纠正:平均改变了与簇大小相关的有效步长。
  • 误区:这套推导自动覆盖动态重分簇。纠正:离散索引变化需要额外估计或优化策略。

本课小结

  • 固定簇分配时,中心 C 是参数,解码权重 W 是 C 的函数。
  • 一个中心通过所有同簇权重位置共同影响损失。
  • 同簇查表的局部导数为 1,异簇位置的局部导数为 0。
  • 链式法则因此化为对同簇权重梯度求和。
  • 工程上可以用索引驱动的 scatter-add 高效得到全部中心梯度。
09

主题讲解 · 02:59

量化感知训练如何在浮点训练中模拟整数误差

学习目标

  • 能区分量化、反量化与伪量化三种运算。
  • 能解释 QAT 为什么仍保留浮点主权重。
  • 能说明 fake-quant 节点如何把部署误差带进训练前向。
  • 能用数值例子比较浮点、PTQ 与 QAT 三条路径。
  • 能指出 rounding 不可导,并解释直通估计器的角色。

前置与衔接

需要理解均匀量化中的 scale、整数码与反量化,并知道梯度下降更新的是可训练参数。

本课回答“感知”二字的具体含义:模型不是直接把整数当作连续参数训练,而是在浮点训练图中看到量化—反量化后的前向误差。

核心讲解

1. QAT 训练的主权重仍然是浮点数

量化感知训练(Quantization-Aware Training, QAT)并不通常把优化器状态和主权重都改成低比特整数。

训练时保留浮点主权重 WW,梯度与优化器更新也作用在这份浮点参数上。

图 1

QAT 训练期间保留浮点主权重并用浮点梯度更新,只在前向中模拟部署量化。

原视频 · 00:00 ↗

低精度效果主要出现在前向模拟中;真正部署时才把参数编码为目标整数格式并交给对应 kernel。

这样既能使用成熟的连续优化器,又能让损失函数看到量化造成的扰动。

2. 用统一公式定义量化与反量化

忽略零点并使用对称均匀量化时,可写成

q=clip(round(xs),qmin,qmax),q=\operatorname{clip}\left(\operatorname{round}\left(\frac{x}{s}\right),q_{min},q_{max}\right),

其中 s>0s>0 是 scale,qq 是整数码。

反量化为

x^=sq.\hat x=sq.

视频采用 s=0.1s=0.1 且暂不考虑截断范围的简化例子:

q=round(10x),x^=0.1q.q=\operatorname{round}(10x),\qquad \hat x=0.1q.

这个例子等价于把数值舍入到一位小数的网格。

3. 伪量化是一次量化—反量化往返

定义

fakequant(x)=sclip(round(xs),qmin,qmax).\operatorname{fakequant}(x) =s\cdot\operatorname{clip}\left( \operatorname{round}\left(\frac{x}{s}\right), q_{min},q_{max} \right).
图 2

伪量化先量化到离散格点再反量化回浮点,在简化例子中等价于只保留一位小数。

原视频 · 00:40 ↗

它的输出仍用浮点 dtype 存放,但数值只能落在量化格点上。

例如

x=1.2345,quads=0.1x=1.2345,quad s=0.1

得到

q=round(12.345)=12,qquadx^=1.2.q=\operatorname{round}(12.345)=12,qquad \hat x=1.2.

因此 fake-quant 可以在普通浮点算子中模拟舍入和截断误差,不要求训练硬件真的执行整数 kernel。

4. 推理时量化谁,训练前向就模拟谁

若部署计划做权重量化,就在训练前向对权重插入 fake-quant。

若部署计划做激活和权重的 W8A8 量化,就在对应激活 XX 与权重 WW 两处模拟:

Yqat=fakequant(X)fakequant(W).Y_{qat}=\operatorname{fakequant}(X) \operatorname{fakequant}(W).
图 3

部署时计划量化的激活与权重,在 QAT 前向中也插入对应 fake-quant 节点。

原视频 · 01:00 ↗

“一般推理时对谁量化,训练时就对谁伪量化”是一条有用检查规则。

但实际计算图还需处理 bias、累加精度、残差、算子融合与 requantization,不能只在模型入口和最终权重各放一次 fake-quant 就认为完全一致。

5. 浮点基线看不到量化台阶

视频令

X=1.24,W=1.24.X=1.24,\qquad W=1.24.

纯浮点前向得到

Yfp=1.24×1.24=1.5376.Y_{fp}=1.24\times1.24=1.5376.

如果训练始终用这条路径,损失只观察连续浮点函数,不会直接知道部署时 1.241.24 会被映射到 1.2。

模型可能在浮点评估中很好,转换为低精度后却出现精度下降。

6. 整数推理路径产生 1.44

sx=sw=0.1s_x=s_w=0.1 的简化对称量化中:

qx=round(1.24/0.1)=12,q_x=\operatorname{round}(1.24/0.1)=12,
qw=round(1.24/0.1)=12.q_w=\operatorname{round}(1.24/0.1)=12.

整数乘加域得到

qxqw=144.q_xq_w=144.

输出对应的组合 scale 为

sxsw=0.01,s_xs_w=0.01,

所以反量化结果是

Yq=144×0.01=1.44.Y_q=144\times0.01=1.44.
图 4

后训练量化把两个 1.24 编码成整数 12,整数乘积 144 再乘组合 scale 还原为 1.44。

原视频 · 01:20 ↗

它与浮点结果 1.5376 有差距,这就是部署量化误差的一个最小例子。

7. QAT 用 fake-quant 在训练前向复现同一差距

训练时仍执行浮点乘法,但输入数值先落到格点:

fakequant(1.24)=1.2.\operatorname{fakequant}(1.24)=1.2.

因此

Yqat=1.2×1.2=1.44.Y_{qat}=1.2\times1.2=1.44.

这个前向输出与上面的简化整数推理结果一致。

损失函数现在能看到“量化后只得到 1.44”,从而对浮点主权重产生适应量化误差的训练信号。

这就是“量化感知”的核心,而不是给模型附加一个写着 quantization 的标签。

8. 浮点权重可被推到更合适的量化格点

假设一次更新把浮点主权重从 1.24 调整到 1.26。

它在浮点域只移动了 0.02,但跨过了舍入边界:

fakequant(1.26)=1.3.\operatorname{fakequant}(1.26)=1.3.

于是前向变成

1.2×1.3=1.56.1.2\times1.3=1.56.
图 5

当浮点权重从 1.24 更新到 1.26 后,伪量化值跨到 1.3,使量化输出从 1.44 变为更接近目标的 1.56。

原视频 · 02:20 ↗

1.56 比 1.44 更接近原浮点基线 1.5376。

真实部署时,1.26 编码为整数 13,12×13=15612\times13=156,乘 0.01 后仍为 1.56。

因此训练中学到的改进能迁移到目标量化路径。

9. 编者补充:round 不可导,反向通常依赖 STE

只看前向还不够。

舍入函数在几乎处处导数为 0,在跳变点不可导;若严格使用真实导数,梯度很难穿过 fake-quant 回到浮点主权重。

QAT 常用直通估计器(Straight-Through Estimator, STE),在有效量化范围内近似令

fakequant(x)x1.\frac{\partial\operatorname{fakequant}(x)}{\partial x}\approx1.
图 6

浮点主权重连续更新,但只有跨过 rounding 边界时 fake-quant 前向值才从一个离散格点跳到下一个。

原视频 · 02:40 ↗

这样前向使用离散化数值,反向则把上游梯度近似传给浮点参数。

超出 clipping 范围时,具体框架可能把梯度置零或采用其他近似;scale 与 zero-point 也可能固定、校准或参与学习。

所以 QAT 的完整机制是:浮点主参数 + fake-quant 前向 + 近似反向,而不只是“先取整再乘回来”。

跟练与练习

原视频练习

编者练习

sx=0.2s_x=0.2sw=0.05s_w=0.05x=0.74x=0.74w=0.13w=0.13,忽略 clipping。求整数码、整数乘积和反量化输出。

查看参考答案

qx=round(0.74/0.2)=4q_x=\operatorname{round}(0.74/0.2)=4qw=round(0.13/0.05)=3q_w=\operatorname{round}(0.13/0.05)=3。整数乘积为 12,组合 scale 为 0.2×0.05=0.010.2\times0.05=0.01,所以反量化输出为 12×0.01=0.1212\times0.01=0.12

编者练习 2

为什么 QAT 既需要 fake-quant 前向,又常需要 STE 反向?

查看参考答案

fake-quant 前向让损失看到舍入与截断误差,但 round 的真实导数几乎处处为 0,无法提供实用的连续优化信号。STE 在反向近似传递梯度,使浮点主权重可以根据量化后的损失更新。两者分别解决“模拟部署数值”和“让优化可进行”两个问题。

常见误区

  • 误区:QAT 直接对整数权重做普通梯度下降。纠正:通常保留浮点主权重并模拟低精度前向。
  • 误区:fake-quant 的输出 dtype 必须是整数。纠正:它通常仍是浮点 dtype,只是数值落在量化格点。
  • 误区:两个量化输入相乘后只乘一个输入 scale。纠正:输出需要组合 scale sxsws_xs_w
  • 误区:在训练中模拟权重量化就自动覆盖 W8A8 部署。纠正:若激活也量化,对应位置也需 fake-quant。
  • 误区:round 可以直接按真实导数反传。纠正:QAT 常借助 STE 等近似梯度。
  • 误区:QAT 一定恢复全部浮点精度。纠正:它提供适应量化误差的机会,结果仍受 bit 数、范围、校准和优化影响。

本课小结

  • QAT 的主权重与优化器更新通常保持浮点精度。
  • fake-quant 在训练前向中执行量化—反量化,显式暴露部署误差。
  • 对计划量化的权重和激活,应在相应训练位置模拟量化。
  • 模型可通过连续更新把参数推到更有利的离散格点。
  • 完整 QAT 还需要 STE 等近似反向,让梯度穿过不可导的舍入操作。
10

主题讲解 · 01:54

OBQ 如何把最小剪枝损失推广为最小量化损失

学习目标

  • 能写出 OBS 与 OBQ 共享的局部二次损失目标。
  • 能区分“剪成零”和“量化到格点”对应的坐标约束。
  • 能从拉格朗日乘子法推导 OBQ 的最优权重补偿。
  • 能解释 OBQ 分数为什么同时依赖量化误差与逆 Hessian。
  • 能说明剪枝为何是量化目标取零时的特殊情形。

前置与衔接

需要理解二阶 Taylor 近似、Hessian、标准基向量 eie_i,以及 OBS 在剪掉一个权重后对其余权重做补偿的思想。

上一单元的 OBS 约束是把第 ii 个权重变为零。

本课只改变这个目标值:不再强制到零,而是强制到某个量化格点 qiq_i

这个看似很小的替换,会把“最小剪枝损失”自然推广为“最小量化损失”。

核心讲解

1. OBS 与 OBQ 使用同一个局部二次目标

在当前参数附近,并假设一阶项可以忽略时,权重变化 DeltawDelta w 引起的损失变化近似为

ΔLf(Δw)=12ΔwTHΔw.\Delta L\approx f(\Delta w) =\frac12\Delta w^TH\Delta w.

其中 HH 是损失对权重的 Hessian。

OBS 和 OBQ 都希望在满足一个坐标约束的前提下,使这个二次目标尽可能小。

图 1

OBS 与 OBQ 使用同一个二阶损失目标,区别集中在单个权重被强制到 0 还是量化值 qᵢ。

原视频 · 00:00 ↗

二者的差别不在目标函数,而在“第 ii 个权重最终必须落到哪里”。

2. OBS 的约束来自把权重剪成零

原权重为 wiw_i

若剪枝后它必须变为零,那么更新量满足

wi+Δwi=0,w_i+\Delta w_i=0,

所以

Δwi=wi.\Delta w_i=-w_i.

用第 ii 个标准基向量 eie_i 抽取该坐标,可写成

eiTΔw=wi,e_i^T\Delta w=-w_i,

或等价地

eiTΔw+wi=0.e_i^T\Delta w+w_i=0.
图 2

OBS 剪枝把第 i 个权重强制到 0,因此该坐标的变化量满足 Δwᵢ=-wᵢ。

原视频 · 00:20 ↗

这条约束把“剪掉一个权重”的离散操作写成了连续二次优化中的等式条件。

3. OBS 的拉格朗日形式

先回顾 OBS,构造拉格朗日函数

J(Δw,λ)=12ΔwTHΔw+λ(eiTΔw+wi).\mathcal{J}(\Delta w,\lambda) =\frac12\Delta w^TH\Delta w +\lambda(e_i^T\Delta w+w_i).

DeltawDelta w 求梯度并令其为零:

ΔwJ=HΔw+λei=0.\nabla_{\Delta w}\mathcal{J} =H\Delta w+\lambda e_i=0.

HH 可逆,则

Δw=λH1ei.\Delta w=-\lambda H^{-1}e_i.
图 3

拉格朗日一阶条件给出 Δw 沿 H⁻¹eᵢ 方向补偿,其系数由坐标约束决定。

原视频 · 00:40 ↗

H1eiH^{-1}e_i 是逆 Hessian 的第 ii 列。

因此一般情况下,强制改变一个坐标会沿着多个权重坐标传播补偿,而不是只修改 wiw_i

4. 用坐标约束解出 OBS 的系数

Δw=λH1ei\Delta w=-\lambda H^{-1}e_i

代回

eiTΔw=wi,e_i^T\Delta w=-w_i,

得到

λeiTH1ei=wi.-\lambda e_i^TH^{-1}e_i=-w_i.

因为

eiTH1ei=[H1]ii,e_i^TH^{-1}e_i=[H^{-1}]_{ii},

所以

λ=wi[H1]ii.\lambda=\frac{w_i}{[H^{-1}]_{ii}}.

最终得到 OBS 更新

ΔwOBS=wi[H1]iiH1ei.\Delta w_{\mathrm{OBS}}^* =-\frac{w_i}{[H^{-1}]_{ii}}H^{-1}e_i.

相应的最小二阶损失增量为

ΔLOBS=wi22[H1]ii.\Delta L_{\mathrm{OBS}}^* =\frac{w_i^2}{2[H^{-1}]_{ii}}.

5. OBQ 只把目标零替换为量化格点

量化不要求 wiw_i 变为零,而要求它变为一个可表示的量化值 qiq_i

因此新约束是

wi+Δwi=qi.w_i+\Delta w_i=q_i.

移项得到

Δwi=qiwi.\Delta w_i=q_i-w_i.

向量形式为

eiTΔw+wiqi=0.e_i^T\Delta w+w_i-q_i=0.
图 4

OBQ 把剪枝目标 0 替换为量化格点 qᵢ,使约束变为 Δwᵢ=qᵢ-wᵢ。

原视频 · 01:00 ↗

注意 qiq_i 在这次条件优化中被视为已经选定的目标格点。

如何决定量化网格、scale、zero-point 或候选 qiq_i,属于量化器设计的上游步骤。

6. OBQ 的拉格朗日推导

把新约束放入拉格朗日函数:

JOBQ(Δw,λ)=12ΔwTHΔw+λ(eiTΔw+wiqi).\mathcal{J}_{\mathrm{OBQ}}(\Delta w,\lambda) =\frac12\Delta w^TH\Delta w +\lambda(e_i^T\Delta w+w_i-q_i).

目标函数没有改变,因此对 DeltawDelta w 的一阶条件仍是

HΔw+λei=0,H\Delta w+\lambda e_i=0,

从而

Δw=λH1ei.\Delta w=-\lambda H^{-1}e_i.

唯一需要重算的是由新坐标约束确定的系数。

代入

eiTΔw=qiwie_i^T\Delta w=q_i-w_i

得到

λ[H1]ii=qiwi.-\lambda[H^{-1}]_{ii}=q_i-w_i.

因此

λ=wiqi[H1]ii.\lambda=\frac{w_i-q_i}{[H^{-1}]_{ii}}.

7. 最优补偿由量化误差驱动

lambdalambda 代回更新式:

ΔwOBQ=wiqi[H1]iiH1ei.\Delta w_{\mathrm{OBQ}}^* =-\frac{w_i-q_i}{[H^{-1}]_{ii}}H^{-1}e_i.
图 5

OBQ 的补偿幅度由量化误差 wᵢ-qᵢ 决定,而不是由原权重 wᵢ 本身决定。

原视频 · 01:20 ↗

与 OBS 相比,公式中所有决定补偿幅度的 wiw_i 都被替换成了量化误差 wiqiw_i-q_i

这很符合直觉:

  • qiq_iwiw_i 很近,需要强制施加的坐标变化很小;
  • qiq_iwiw_i 很远,必须做更大的变化,并让其他权重承担更多补偿;
  • 补偿传播到哪些坐标,仍由 H1eiH^{-1}e_i 决定。

8. OBQ 的最小局部损失分数

把最优更新代回二次目标,可得

ΔLOBQ=(wiqi)22[H1]ii.\Delta L_{\mathrm{OBQ}}^* =\frac{(w_i-q_i)^2}{2[H^{-1}]_{ii}}.
图 6

最优补偿为 -(wᵢ-qᵢ)H⁻¹eᵢ/[H⁻¹]ᵢᵢ,最小二阶损失增量与 (wᵢ-qᵢ)² 成正比。

原视频 · 01:40 ↗

这个分数同时包含两类信息:

  1. 分子 (wiqi)2(w_i-q_i)^2 衡量权重到量化格点的几何误差;
  2. 分母 [H1]ii[H^{-1}]_{ii} 反映模型沿相关耦合方向吸收该误差的能力。

只按最近格点的欧氏距离判断,会忽略模型损失曲面的曲率与权重耦合。

OBQ 的二阶分数正是把这两部分结合起来。

9. 剪枝是量化目标 qᵢ=0 的特殊情形

qi=0,q_i=0,

则 OBQ 约束退化为

Δwi=wi.\Delta w_i=-w_i.

最优更新退化为

Δw=wi[H1]iiH1ei,\Delta w^* =-\frac{w_i}{[H^{-1}]_{ii}}H^{-1}e_i,

最小损失也退化为

ΔL=wi22[H1]ii.\Delta L^* =\frac{w_i^2}{2[H^{-1}]_{ii}}.

这说明在当前二阶局部模型下,剪枝可以看成量化到零这一特殊目标值。

10. 推导成立的边界

视频用公式替换快速说明了推广关系,但这些公式并不是无条件的全局保证。

需要注意:

  • 二阶 Taylor 模型只在当前参数附近近似损失;
  • 推导假设一阶项可忽略,通常对应训练后的近似驻点;
  • 直接写 H1H^{-1} 需要 Hessian 可逆,工程中常用阻尼或近似逆提高稳定性;
  • qiq_i 被视为本步固定目标,整组权重的量化顺序仍会影响后续状态;
  • 一次量化多个坐标时,不能机械地把多个单坐标最优解独立相加;
  • 真正算法还要在精度、计算量、分组方式与硬件格式之间折中。

因此本课给出的是 OBQ 单坐标更新的核心数学骨架,而不是全部工程实现细节。

跟练与练习

原视频练习

编者练习

给定 H1=[2113],w1=0.7,q1=0.5.H^{-1}=\begin{bmatrix}2&1\\1&3\end{bmatrix}, \qquad w_1=0.7, \qquad q_1=0.5. 求把第一个权重量化到 q1q_1 时的最优 DeltawDelta w

查看参考答案

先计算量化误差 w1q1=0.2w_1-q_1=0.2,并取
[H1]11=2,H1e1=[21].[H^{-1}]_{11}=2, \qquad H^{-1}e_1=\begin{bmatrix}2\\1\end{bmatrix}.
所以
Δw=0.22[21]=[0.20.1].\Delta w^* =-\frac{0.2}{2} \begin{bmatrix}2\\1\end{bmatrix} =\begin{bmatrix}-0.2\\-0.1\end{bmatrix}.
第一个坐标更新后为 0.70.2=0.5=q10.7-0.2=0.5=q_1,第二个坐标同时补偿 0.1-0.1

编者练习 2

为什么不能只用 (wiqi)2(w_i-q_i)^2 选择优先量化的权重?

查看参考答案

平方距离只反映权重与量化格点之间的数值误差,没有反映损失曲率和权重耦合。
OBQ 分数还除以 [H1]ii[H^{-1}]_{ii}:即使两个权重到格点的距离相同,模型沿相关方向吸收误差的能力也可能不同,因此真实的局部损失增量不同。

常见误区

  • 误区:OBQ 把所有权重都直接取最近格点,不需要补偿。纠正:本课核心正是量化一个坐标后,利用逆 Hessian 调整其余坐标。
  • 误区:qiq_i 是训练前的原始权重。纠正:qiq_i 表示第 ii 个权重选定的量化后目标值。
  • 误区:wiqiw_i-q_iqiwiq_i-w_i 可在更新式中随意交换。纠正:坐标变化是 qiwiq_i-w_i,公式前的负号把它写成 (wiqi)-(w_i-q_i)
  • 误区:分数小只因为量化误差小。纠正:逆 Hessian 对角项同样参与排序。
  • 误区:剪枝与量化是完全无关的优化。纠正:在该局部二阶推导里,剪枝就是令量化目标 qi=0q_i=0
  • 误区:二阶近似得到的最小值就是原始非线性损失的全局最小值。纠正:它只是在假设成立时的局部近似结果。

本课小结

  • OBS 与 OBQ 共享目标 12ΔwTHΔw\frac12\Delta w^TH\Delta w
  • OBS 约束是 Δwi=wi\Delta w_i=-w_i,OBQ 约束是 Δwi=qiwi\Delta w_i=q_i-w_i
  • OBQ 的最优补偿由量化误差 wiqiw_i-q_i 和方向 H1eiH^{-1}e_i 共同决定。
  • 最小局部损失为 (wiqi)2/(2[H1]ii)(w_i-q_i)^2/(2[H^{-1}]_{ii})
  • qi=0q_i=0 即恢复 OBS 的剪枝更新与损失分数。
  • 下一课可继续追问:怎样把单坐标二阶更新扩展到一组权重和实际量化顺序。
11

主题讲解 · 01:55

为什么量化会把连续损失山谷切成台阶

学习目标

  • 能解释浮点参数空间中的损失为什么通常可局部看成连续曲面。
  • 能证明多个浮点权重会被量化到同一个整数码。
  • 能说明量化损失沿单个参数方向为什么呈分段常数平台。
  • 能区分真实量化、伪量化与量化感知训练中的浮点主权重。
  • 能解释“把损失踢下台阶”这一 QAT 直觉的适用边界。

前置与衔接

需要知道均匀量化的 scale、round、整数码和反量化,并理解模型损失由前向预测与目标共同决定。

上一课从二阶优化角度研究把权重移动到量化格点所造成的局部损失。

本课换一个几何视角:固定其他条件,只沿一个或少数权重方向移动时,量化映射如何把连续参数区间压到相同的可表示状态。

核心讲解

1. 浮点模型的损失山谷

把所有权重组成向量

w=(w1,w2,,wd).w=(w_1,w_2,\ldots,w_d).

给定数据与模型结构后,前向预测和训练目标定义了损失函数

L(w).L(w).

在没有量化时,神经网络常由矩阵乘、加法、激活等连续运算组成。

除少数不可导点外,参数发生很小变化时,输出和损失通常也连续变化。

图 1

未量化时,参数向量 w 到损失 L(w) 的映射可在局部看成连续曲面。

原视频 · 00:20 ↗

在二维切片上,人们常把它画成山谷;在真实模型中,它是高维参数空间上的复杂损失景观。

“山谷”是一种局部几何直觉,不意味着真实损失一定是凸碗或只有一个极小值。

2. 量化先把连续权重映射到离散码

以忽略 zero-point 与 clipping 的对称均匀量化为例:

q(w)=round(ws),q(w)=\operatorname{round}\left(\frac{w}{s}\right),

其中 s>0s>0 是 scale,qq 是整数码。

反量化后的可计算权重是

w^=sq(w).\hat w=sq(w).

因此量化后的损失可以写成

Lq(w)=L(w^)=L ⁣(sround(w/s)).L_q(w)=L(\hat w)=L\!\left(s\operatorname{round}(w/s)\right).

它不是直接把原浮点 ww 送进模型,而是先经过一个多对一映射。

3. 同一个量化区间内,整数码保持不变

对普通四舍五入,若整数 kk 固定,则当

w[(k12)s,(k+12)s)w\in\left[(k-\tfrac12)s,(k+\tfrac12)s\right)

时,通常有

q(w)=k.q(w)=k.

在这个区间内部移动 ww,整数码不会改变,反量化值 hatw=skhat w=sk 也不会改变。

只要其他权重、输入与量化参数固定,模型前向输出就相同,因此损失也相同。

这就是一个量化平台。

4. 视频中的 1.26 与 1.34 例子

视频把量化简化成“乘 10 后取整”:

q(w)=round(10w).q(w)=\operatorname{round}(10w).

此时 scale 相当于 s=0.1s=0.1

对于两个浮点权重

wa=1.26,wb=1.34,w_a=1.26, \qquad w_b=1.34,

q(wa)=round(12.6)=13,q(w_a)=\operatorname{round}(12.6)=13,
q(wb)=round(13.4)=13.q(w_b)=\operatorname{round}(13.4)=13.
图 2

在乘 10 后取整的简化量化中,1.26 与 1.34 都编码为整数 13,因而得到相同量化前向与损失。

原视频 · 00:40 ↗

两者反量化后都得到

w^=0.1×13=1.3.\hat w=0.1\times13=1.3.

模型实际参与前向的权重相同,因而在其他条件完全相同的前提下得到相同损失。

5. 连续山谷被切成离散台阶

浮点损失 L(w)L(w) 可以随 ww 连续变化。

量化损失却是复合函数

Lq(w)=L(Q(w)).L_q(w)=L(Q(w)).

只要 ww 仍处于同一个量化区间,Q(w)Q(w) 就保持不变,所以 Lq(w)L_q(w) 是常数。

ww 穿过舍入边界时,量化码从 kk 跳到 k+1k+1,前向权重和损失才会跳到下一个平台。

图 3

浮点权重对应连续损失曲面,量化后的可表示状态则把这一山谷切成离散台阶。

原视频 · 00:00 ↗

因此沿一条权重切片观察,原来的平滑山谷会被离散量化格点采样,并呈现为阶梯状。

在多维参数空间里,更准确的说法是:参数空间被划分为许多量化单元,每个单元内部映射到同一组量化参数。

6. 反向看:量化模型不能唯一确定原浮点权重

若只拿到已经量化好的整数码 q=13q=13,并知道 s=0.1s=0.1,可以确定部署权重是 1.3。

但无法唯一恢复它量化前的浮点值。

按照给定舍入约定,某个连续区间中的许多浮点数都可能映射到整数 13。

图 4

同一个量化权重可以由一段浮点权重区间映射而来,因此量化模型不能唯一反推出原浮点参数。

原视频 · 01:00 ↗

这说明量化是信息丢失的多对一映射。

它不仅降低比特数,还抹去了同一个量化单元内部的细微浮点差异。

7. 伪量化让训练前向看见平台

伪量化通常执行一次量化—反量化往返:

fakequant(w)=sclip(round(w/s),qmin,qmax).\operatorname{fakequant}(w) =s\cdot\operatorname{clip}\left( \operatorname{round}(w/s),q_{min},q_{max} \right).

输出仍由浮点 dtype 保存,但数值被限制在目标量化网格上。

图 5

伪量化让不同浮点权重映射到同一个可表示值,使浮点训练前向感受到量化平台。

原视频 · 01:20 ↗

因此 QAT 的前向损失能看到:同一量化区间内部的不同主权重会产生相同的伪量化权重。

这就是“让模型感受到台阶”的具体含义。

8. QAT 为什么仍保留浮点主权重

若直接把可训练参数永久限制为离散整数,普通梯度下降难以产生连续的小步更新。

QAT 通常保留浮点主权重 wfpw_{fp},优化器更新的也是这份连续参数。

前向则使用

w^=fakequant(wfp).\hat w=\operatorname{fakequant}(w_{fp}).

这样,浮点主权重可以在同一个量化区间内积累变化,并最终跨过量化边界。

一旦跨界,伪量化前向切换到新的整数码,损失也可能跳到另一个平台。

9. “把损失踢下台阶”的直觉

视频用

wnew=woldηwLw_{new}=w_{old}-\eta\nabla_wL

表示浮点权重更新。

若更新把主权重从一个量化区间推到相邻区间,部署前向使用的量化值就发生改变。

图 6

QAT 保留浮点更新,并尝试推动主权重跨过量化边界,从一个损失平台进入更低的平台。

原视频 · 01:40 ↗

如果新平台的任务损失更低,就可以直观地说训练把损失“踢下了一个台阶”。

但梯度方向并不保证每次跨界都会进入更低平台;它仍受数据、小批量噪声、优化器和近似梯度影响。

10. 真正台阶函数的梯度问题

严格的 round 在每个量化区间内部是常数,因此真实导数几乎处处为零,在边界处不可导。

如果完全使用这个真实导数,优化器很难得到推动浮点主权重跨越边界的信号。

QAT 常使用直通估计器(Straight-Through Estimator, STE),在反向传播时近似令

fakequant(w)w1\frac{\partial\operatorname{fakequant}(w)}{\partial w}\approx1

或采用带 clipping 掩码的近似。

因此“沿台阶做梯度更新”并不是对台阶函数真实梯度的直接下降,而是用代理梯度优化真实的量化前向目标。

11. 台阶直觉的适用边界

这一图像很有用,但需要附带几个条件:

  • 固定其他权重、输入、量化 scale 与 zero-point 后,单个权重切片才容易画成一维台阶;
  • 多个权重同时变化时,参数空间被切成高维量化单元,而不是一排简单楼梯;
  • 激活量化会让平台还依赖输入样本与中间激活;
  • clipping 会在范围两端形成饱和区,不能只考虑相邻舍入边界;
  • stochastic rounding 等非确定性量化下,单次损失或期望损失的形状会不同;
  • batch normalization、scale 学习和图优化也可能改变前向状态。

所以“山谷变台阶”是确定性量化下理解多对一映射的局部模型,不是对所有量化训练景观的完整描述。

跟练与练习

原视频练习

编者练习

设对称均匀量化 scale 为 s=0.2s=0.2,忽略 clipping,并使用普通四舍五入。判断 w=0.31w=0.310.390.390.410.41 分别映射到哪个整数码与反量化值。

查看参考答案

q(0.31)=round(1.55)=2,w^=0.4.q(0.31)=\operatorname{round}(1.55)=2, \qquad \hat w=0.4.
q(0.39)=round(1.95)=2,w^=0.4.q(0.39)=\operatorname{round}(1.95)=2, \qquad \hat w=0.4.
q(0.41)=round(2.05)=2,w^=0.4.q(0.41)=\operatorname{round}(2.05)=2, \qquad \hat w=0.4.
三者都落在同一量化单元;若其他条件不变,量化前向和损失相同。

编者练习 2

既然量化损失在平台内部不变,QAT 为什么还能更新浮点主权重?

查看参考答案

严格 round 的真实梯度几乎处处为零,因此 QAT 通常不直接使用它。
前向通过 fake-quant 模拟量化台阶,反向通过 STE 或其他代理梯度把信号传给浮点主权重。浮点参数可以连续累积更新,并最终跨过量化边界,使前向切换到新平台。

常见误区

  • 误区:量化后没有损失函数。纠正:量化模型仍能前向预测,当然也能与目标比较并计算任务损失。
  • 误区:1.26 与 1.34 本身相等。纠正:它们只是经过指定量化器后得到相同整数码和反量化值。
  • 误区:任何神经网络损失都是规则的凸山谷。纠正:山谷只是低维切片的直觉,真实景观通常高维且非凸。
  • 误区:QAT 直接对整数参数做普通梯度下降。纠正:常见实现保留浮点主权重,前向插入 fake-quant。
  • 误区:台阶函数在平台内部有可用的真实梯度。纠正:QAT 通常依赖 STE 等代理梯度。
  • 误区:每次跨过量化边界都一定降低损失。纠正:更新只是在代理优化下寻找更好量化状态,并无单步必降保证。

本课小结

  • 浮点损失 L(w)L(w) 在局部通常随参数连续变化。
  • 量化映射把一个连续参数区间压到同一整数码,因此量化损失在区间内部形成平台。
  • 穿过舍入边界时,量化码与前向结果才会跳到新平台。
  • fake-quant 让训练前向感受到这些平台,浮点主权重则保留连续更新能力。
  • STE 提供穿过不可导量化节点的代理梯度。
  • “把损失踢下台阶”适合建立直觉,但真实高维 QAT 还受量化器、数据和优化器共同影响。
12

主题讲解 · 03:29

为什么 QAT 需要 STE 穿过 round 的零梯度

学习目标

  • 能画出浮点训练与伪量化训练的两条简化计算图。
  • 能用链式法则定位 round 节点造成的梯度阻断。
  • 能解释 STE 为什么把 round 的反向导数近似为 1。
  • 能复现视频中浮点梯度 1 与 QAT 梯度 1.2 的数值例子。
  • 能说明 STE 等于 1 为什么不代表 QAT 与浮点训练相同。

前置与衔接

需要掌握标量乘法的链式法则,并知道 QAT 在前向使用 fake-quant、在优化器侧保留浮点主权重。

上一课说明,round 把连续参数区间压成量化平台。

这会立刻带来一个反向传播问题:平台内部的真实导数为零,普通梯度无法推动主权重继续移动。

本课用最小标量例子说明直通估计器(Straight-Through Estimator, STE)如何解决这个问题。

核心讲解

1. 先建立两条最简计算图

没有量化时,令

y=xw,y=xw,

再由预测 yy 与目标计算损失 L(y)L(y)

简化后的伪量化路径则令

q=round(w),q=\operatorname{round}(w),
y=xq,y=xq,

再计算同一个任务损失。

图 1

QAT 的简化计算图把浮点路径 y=xw 改为 w→round→q→xq→loss,并在反向对 round 使用代理梯度。

原视频 · 00:00 ↗

真实 fake-quant 还会包含 scale、zero-point、clipping 和反量化。

视频故意把它缩成 round,是为了只观察不可导节点怎样影响链式法则。

2. 浮点路径的梯度

y=xwy=xw

应用链式法则:

Lw=Lyyw.\frac{\partial L}{\partial w} =\frac{\partial L}{\partial y} \frac{\partial y}{\partial w}.

因为

yw=x,\frac{\partial y}{\partial w}=x,

所以

Lw=Lyx.\frac{\partial L}{\partial w} =\frac{\partial L}{\partial y}x.

3. 视频中的浮点数值例子

视频设

x=2,w=1.24,x=2, \qquad w=1.24,

得到

y=2×1.24=2.48.y=2\times1.24=2.48.

再假设这个预测点上的上游梯度为

Ly=0.5.\frac{\partial L}{\partial y}=0.5.

于是

Lw=0.5×2=1.\frac{\partial L}{\partial w} =0.5\times2=1.
图 2

无量化路径中 y=xw,因此 ∂L/∂w=(∂L/∂y)x;示例取 x=2、上游梯度 0.5,得到梯度 1。

原视频 · 01:00 ↗

这是后面比较 QAT 梯度的浮点基线。

4. 伪量化路径多了一个 round 节点

wq=round(w)y=xqLw\longrightarrow q=\operatorname{round}(w) \longrightarrow y=xq \longrightarrow L

应用链式法则:

Lw=Lyyqqw.\frac{\partial L}{\partial w} =\frac{\partial L}{\partial y} \frac{\partial y}{\partial q} \frac{\partial q}{\partial w}.

其中乘法节点仍然容易求:

yq=x.\frac{\partial y}{\partial q}=x.
图 3

伪量化路径把 q=round(w) 送入 y=xq,其中 ∂y/∂q=x=2,剩下的难点是 ∂q/∂w。

原视频 · 01:40 ↗

真正的困难集中在

qw=round(w)w.\frac{\partial q}{\partial w} =\frac{\partial\operatorname{round}(w)}{\partial w}.

5. round 的真实导数为何无法训练

round 是阶梯函数。

在每个舍入区间内部,输出整数码保持不变,因此

round(w)w=0\frac{\partial\operatorname{round}(w)}{\partial w}=0

几乎处处成立。

在半整数等跳变边界处,它又不可导。

如果严格把真实导数代入链式法则,则

Lw=Lyx×0=0.\frac{\partial L}{\partial w} =\frac{\partial L}{\partial y} x\times0 =0.

浮点主权重收不到梯度,就无法根据量化后的损失调整。

6. STE 用代理导数让梯度直通

STE 的核心做法是:前向仍执行离散的 round,反向则使用一个容易优化的代理导数。

最简单的版本令

qw1.\frac{\partial q}{\partial w} \approx1.
图 4

round 是阶梯函数,真实导数几乎处处为 0;STE 在反向用代理导数 1 传递梯度。

原视频 · 02:00 ↗

这样链式法则变为

LwLyx.\frac{\partial L}{\partial w} \approx \frac{\partial L}{\partial y}x.

“直通”指上游梯度在反向时近似直接穿过离散节点,而不是说 round 在数学上真的变成了恒等函数。

7. 前向离散、反向连续是有意的不一致

STE 不是 round 的真实导数。

它是一种代理梯度估计:

  • 前向使用 q=round(w)q=\operatorname{round}(w),让损失看到量化误差;
  • 反向近似使用 dq/dw=1dq/dw=1,让浮点主权重得到可用更新信号。

这两个规则有意不一致。

若前向也把 round 改成恒等映射,模型就看不到目标量化路径;若反向坚持真实零梯度,模型又无法训练。

STE 在二者之间做了实用折中。

8. 为什么 STE=1 不会让 QAT 退化为浮点训练

一个自然疑问是:

qw1\frac{\partial q}{\partial w}\approx1

以后,QAT 的梯度公式看起来又变成

LwLyx.\frac{\partial L}{\partial w} \approx\frac{\partial L}{\partial y}x.

它是否就与无量化训练完全相同?

答案是否定的,因为两条路径中的

Ly\frac{\partial L}{\partial y}

不一定在同一个预测点上计算。

9. 伪量化先改变预测,再改变上游梯度

浮点路径使用

yfp=xw.y_{fp}=xw.

伪量化路径使用

yq=xround(w).y_q=x\operatorname{round}(w).

w=1.24w=1.24x=2x=2 时,视频的极简 round 得到

q=1,yq=2.q=1, \qquad y_q=2.

而浮点路径是

yfp=2.48.y_{fp}=2.48.
图 5

浮点前向使用 w,伪量化前向使用 round(w);两条路径产生不同预测,因此损失与上游梯度也可能不同。

原视频 · 02:40 ↗

预测不同,损失函数的取值点也不同,因此上游梯度通常不同:

Lyy=yfpLyy=yq.\left.\frac{\partial L}{\partial y}\right|_{y=y_{fp}} \ne \left.\frac{\partial L}{\partial y}\right|_{y=y_q}.

10. 复现 QAT 梯度 1.2

视频继续假设伪量化预测点上的上游梯度变为

Ly=0.6.\frac{\partial L}{\partial y}=0.6.

又有

yq=x=2,\frac{\partial y}{\partial q}=x=2,

STE 取

qw1.\frac{\partial q}{\partial w}\approx1.

所以

Lw0.6×2×1=1.2.\frac{\partial L}{\partial w} \approx0.6\times2\times1 =1.2.
图 6

即使 STE 令 ∂q/∂w≈1,伪量化仍会先改变预测和损失,继而改变上游梯度 ∂L/∂y。

原视频 · 03:00 ↗

它与浮点路径的梯度 1 不同。

差异并非来自 STE 的局部因子,而是来自量化前向改变了模型输出、损失和上游梯度。

11. 用平方损失把“0.5 变 0.6”具体化

视频直接假设两条路径的上游梯度分别为 0.5 与 0.6。

可以用平方损失说明这种变化为什么合理。

L(y)=12(yy)2,L(y)=\frac12(y-y^*)^2,

Ly=yy.\frac{\partial L}{\partial y}=y-y^*.

只要伪量化使预测从 yfpy_{fp} 变到 yqy_q,上游梯度就同步从 yfpyy_{fp}-y^* 变到 yqyy_q-y^*

因此即使下游局部雅可比使用相同的代理值,整条链上的梯度仍可能不同。

12. 更真实的 fake-quant STE

实际 fake-quant 常写成

w^=sclip(round(w/s),qmin,qmax).\hat w=s\cdot\operatorname{clip}\left( \operatorname{round}(w/s),q_{min},q_{max} \right).

一种常见反向近似是在可表示范围内令

w^w1,\frac{\partial\hat w}{\partial w}\approx1,

在超出 clipping 范围时令梯度为零。

不同框架还可能学习 scale、使用 per-channel 参数,或为量化边界设计其他代理梯度。

所以“STE 就是导数恒等于 1”是本课最简版本,不应当作所有 QAT 实现的固定 API。

13. STE 提供方向,但不保证无偏或最优

因为 STE 使用的不是 round 真实导数,它通常不是严格意义上的精确梯度。

它可能有偏,且训练效果依赖:

  • 代理导数的形状;
  • clipping 范围;
  • scale 是否参与学习;
  • 权重与激活的量化粒度;
  • 优化器、学习率与校准数据。

STE 的价值是把不可微的离散前向转化为可训练问题,而不是提供普适的收敛保证。

跟练与练习

原视频练习

编者练习

设伪量化路径中 x=3x=3,当前量化预测点的上游梯度为 0.4-0.4,STE 取 dq/dw=1dq/dw=1。求 dL/dwdL/dw

查看参考答案


LwLyyqqw\frac{\partial L}{\partial w} \approx \frac{\partial L}{\partial y} \frac{\partial y}{\partial q} \frac{\partial q}{\partial w}
dy/dq=x=3dy/dq=x=3,得到
Lw(0.4)×3×1=1.2.\frac{\partial L}{\partial w} \approx(-0.4)\times3\times1=-1.2.

编者练习 2

两条路径都把最后一个局部导数设为 1,为什么上游梯度仍可能不同?

查看参考答案

STE 只规定离散节点在反向时怎样近似传递梯度,不会撤销前向量化。
浮点路径使用 yfp=xwy_{fp}=xw,伪量化路径使用 yq=xQ(w)y_q=xQ(w)。只要 Q(w)wQ(w)\ne w,两条路径就在不同预测点上计算损失,dL/dydL/dy 也可能不同,最终参数梯度自然不同。

常见误区

  • 误区:round 在数学上的导数是 1。纠正:真实导数几乎处处为 0;1 是 STE 选择的代理导数。
  • 误区:STE 会把前向 round 也替换成恒等函数。纠正:常见 QAT 前向仍执行量化模拟,只在反向使用代理梯度。
  • 误区:STE=1 所以 QAT 与浮点训练完全相同。纠正:量化前向改变预测、损失和上游梯度。
  • 误区:视频中的 round(w) 就是完整 fake-quant。纠正:它省略了 scale、zero-point、clipping 与反量化。
  • 误区:所有框架在整个实数轴上都用梯度 1。纠正:许多实现会对 clipping 区间外的梯度做掩码,也可能使用其他估计器。
  • 误区:STE 是精确无偏梯度。纠正:它是实用代理,效果与量化配置和优化过程有关。

本课小结

  • round 的阶梯结构使真实导数几乎处处为零,阻断普通反向传播。
  • STE 保留离散前向,并在反向用代理导数近似穿过量化节点。
  • 浮点路径的示例梯度是 0.5×2=10.5\times2=1
  • QAT 路径的示例梯度是 0.6×2×1=1.20.6\times2\times1=1.2
  • 二者不同的关键是伪量化先改变了预测和上游梯度,而不是 STE 局部因子不同。
  • 实际 fake-quant 的 STE 还可能包含 clipping 掩码、可学习 scale 与实现相关边界。
13

主题讲解 · 02:46

为什么激活 Per-Channel 量化比 Per-Token 更难加速

学习目标

  • 能区分激活的 token 维度、输入 channel 维度与权重的输出 channel 维度。
  • 能解释 per-channel 激活量化为何更能隔离少数离群通道。
  • 能推导 per-token 激活 scale 如何从 GEMM 的归约维度中提出。
  • 能说明 token scale 与输出通道 scale 为什么形成外积后处理。
  • 能定位 per-channel 激活 scale 落在点积求和内部造成的加速困难。

前置与衔接

需要理解矩阵乘法

Y=XWY=XW

的元素级点积、均匀量化的 scale,以及 INT8 GEMM 通常先进行整数乘加、再做反量化缩放。

前面的 SmoothQuant 课程说明,激活离群值常集中在少数 channel 上。

这使 per-channel 激活量化在精度上很有吸引力,但量化粒度还必须与矩阵乘归约结构匹配,才能转化为实际速度。

核心讲解

1. 先固定矩阵维度与术语

令激活矩阵

XRM×K,X\in\mathbb{R}^{M\times K},

权重矩阵

WRK×N,W\in\mathbb{R}^{K\times N},

输出为

Y=XWRM×N.Y=XW\in\mathbb{R}^{M\times N}.

这里:

  • MM 是 token 或展平后的行维度;
  • KK 是输入 channel,也是 GEMM 的归约维度;
  • NN 是输出 channel。

m,nm,n 个输出元素为

Ymn=k=1KXmkWkn.Y_{mn}=\sum_{k=1}^{K}X_{mk}W_{kn}.

后面所有“scale 能否提出”的判断,都取决于它是否随归约索引 kk 变化。

2. 离群值为什么指向 per-channel 粒度

在不少 Transformer 激活中,大幅离群值并非均匀出现在所有位置,而是集中在某些隐藏 channel。

同一个离群 channel 可能跨多个 token 持续出现大值。

图 1

激活离群值常集中在特定 channel;per-channel 有利于精度,但 per-token 的缩放因子更容易从 GEMM 归约中提出。

原视频 · 00:00 ↗

如果每个输入 channel 分别选择 scale,普通通道不需要与离群通道共享同一个巨大动态范围。

这就是 per-channel 激活量化在数值精度上的直接动机。

3. Per-token 量化会让离群值主导整行范围

per-token 激活量化为第 mm 个 token 的整行 Xm,:X_{m,:} 使用一个 scale ama_m

若一行中同时有普通值

0.1, 0.2, 0.3, 0.40.1,\ 0.2,\ 0.3,\ 0.4

和离群值

60, 70,60,\ 70,

覆盖 70 的 scale 会非常大。

普通值除以这个 scale 后可能接近零,舍入后丢失有效信息。

图 2

per-token 量化把同一 token 内的普通值与 60、70 共用 scale,普通值可能被舍入到 0。

原视频 · 01:00 ↗

因此,per-token 虽然工程友好,却可能在离群通道存在时牺牲更多精度。

4. Per-channel 量化把离群通道单独处理

per-channel 激活量化为第 kk 个输入 channel 使用 scale ckc_k

普通通道可以使用较小 scale,离群通道使用较大 scale。

图 3

per-channel 量化把离群通道 60、70 与普通通道分开确定 scale,避免它们共同压缩普通值的有效整数分辨率。

原视频 · 00:40 ↗

于是 0.1、0.3 等普通值不必与 60、70 争夺同一组整数码。

视频据 SmoothQuant 的讨论指出,这种粒度在数值上本可以缓解 channel-wise 离群问题。

但“量化误差更小”不等于“端到端更快”。

5. Per-token 激活量化的矩阵形式

记量化后的激活整数矩阵为

XqZM×K,X_q\in\mathbb{Z}^{M\times K},

mm 个 token 的 scale 为 ama_m

忽略 zero-point,可写成

Xdiag(a)Xq,X\approx\operatorname{diag}(a)X_q,

其中

a=(a1,a2,,aM)T.a=(a_1,a_2,\ldots,a_M)^T.

也就是说,XqX_q 的每一行只乘一个标量。

这个标量与该行内部的归约索引 kk 无关。

6. 权重 Per-output-channel 量化

权重常按输出 channel 量化。

令整数权重为

WqZK×N,W_q\in\mathbb{Z}^{K\times N},

nn 个输出通道的 scale 为 bnb_n,则

WWqdiag(b).W\approx W_q\operatorname{diag}(b).

权重的一整列共享 bnb_n

对固定输出元素 YmnY_{mn}bnb_n 同样不随求和索引 kk 变化。

7. 两个 scale 都能从整数点积提出

代入矩阵乘:

Ydiag(a)XqWqdiag(b).Y \approx \operatorname{diag}(a) X_qW_q \operatorname{diag}(b).

元素级写成

Ymnambnk=1K(Xq)mk(Wq)kn.Y_{mn} \approx a_mb_n \sum_{k=1}^{K}(X_q)_{mk}(W_q)_{kn}.
图 4

当激活按 token、权重按输出通道缩放时,每个输出元素只需在整数点积外乘 aₘbₙ。

原视频 · 01:40 ↗

ama_mbnb_n 都不依赖 kk,所以可以整体提出求和。

中间的

Z=XqWqZ=X_qW_q

可以由标准 INT8 GEMM 进行整数乘加,并通常以更高精度整数累加器保存。

8. 外积缩放适合作为 GEMM 后处理

所有输出元素需要的总 scale 构成矩阵

S=abT,S=ab^T,

其中

Smn=ambn.S_{mn}=a_mb_n.

所以反量化可写成

YZ(abT),Y\approx Z\odot(ab^T),

这里 \odot 表示逐元素乘法。

图 5

整张输出的缩放矩阵是 token scale 列向量与输出通道 scale 行向量的外积,可在 INT8 GEMM 后逐元素应用。

原视频 · 02:00 ↗

这条路径把高成本归约留给标准 INT8 GEMM,再在输出端做规则的逐元素缩放。

实现可以把反量化、bias、激活等后处理进一步融合,减少中间内存流量。

9. Per-channel 激活量化的矩阵形式

若激活按输入 channel 量化,第 kk 个 channel 使用 ckc_k,则

XXqdiag(c).X\approx X_q\operatorname{diag}(c).

继续假设权重按输出 channel 量化:

WWqdiag(b).W\approx W_q\operatorname{diag}(b).

于是

YXqdiag(c)Wqdiag(b).Y \approx X_q\operatorname{diag}(c) W_q\operatorname{diag}(b).

关键的 diag(c)\operatorname{diag}(c) 位于两个整数矩阵之间。

10. Channel scale 落在 GEMM 归约内部

元素级展开:

Ymnbnk=1Kck(Xq)mk(Wq)kn.Y_{mn} \approx b_n\sum_{k=1}^{K} c_k(X_q)_{mk}(W_q)_{kn}.

这里 ckc_k 随求和索引 kk 改变。

因此不能写成某个统一标量乘

k(Xq)mk(Wq)kn.\sum_k(X_q)_{mk}(W_q)_{kn}.
图 6

激活按输入 channel 缩放时,每个归约项带不同 cₖ,scale 留在求和内部,不能从普通整数点积整体提出。

原视频 · 02:20 ↗

若仍先计算普通整数点积,已经失去了每一项对应哪个 ckc_k 的信息,无法在输出端补回正确结果。

11. 为什么这会妨碍标准 INT8 GEMM

标准 INT8 GEMM 擅长计算

Zmn=kxmk(q)wkn(q)Z_{mn}=\sum_k x_{mk}^{(q)}w_{kn}^{(q)}

并把大量整数乘加映射到优化硬件单元。

per-channel 激活量化要求的却是

kckxmk(q)wkn(q).\sum_k c_kx_{mk}^{(q)}w_{kn}^{(q)}.

每个归约项多了一个通常为浮点的、随 kk 变化的系数。

这可能需要:

  • 在归约前反量化或重缩放部分数据;
  • 把额外乘法嵌入定制 kernel;
  • 预处理或重新编码权重;
  • 使用更复杂的累加与 requantization 路径。

这些工作会削弱直接调用高吞吐标准 INT8 GEMM 的优势,并增加融合难度。

12. 一个 2×2 数值对照

Xq=[10203040],Wq=[50706080].X_q= \begin{bmatrix} 10&20\\ 30&40 \end{bmatrix}, \qquad W_q= \begin{bmatrix} 50&70\\ 60&80 \end{bmatrix}.

若激活按 token 缩放,取

a=[0.10.2],a= \begin{bmatrix} 0.1\\0.2 \end{bmatrix},

权重按输出 channel 缩放,取

b=[0.30.4].b= \begin{bmatrix} 0.3\\0.4 \end{bmatrix}.

第一个输出元素可写成

Y110.1×0.3(10×50+20×60).Y_{11} \approx0.1\times0.3 (10\times50+20\times60).

两个 scale 可一起放在整数点积外。

若激活改为 per-channel scale

c=[0.10.2],c= \begin{bmatrix} 0.1\\0.2 \end{bmatrix},

Y110.3(0.1×10×50+0.2×20×60).Y_{11} \approx0.3 \left( 0.1\times10\times50 +0.2\times20\times60 \right).

两个归约项使用不同 ckc_k,无法统一提出。

13. SmoothQuant 的折中思路

per-channel 激活量化数值合理但算子不友好,per-token 或 per-tensor 路径算子更规整但更怕离群值。

SmoothQuant 的核心思路之一,是在保持线性层数学输出不变的前提下,把激活的 per-channel 难度平滑迁移到权重侧。

若用对角缩放矩阵 DD,则

XW=(XD1)(DW).XW=(XD^{-1})(DW).

通过选择 DD,可以降低激活 channel 间动态范围差异,同时让权重吸收相反缩放。

这样推理时更容易采用硬件友好的激活量化粒度,并保留权重 per-channel 的高效路径。

14. “难加速”不是“绝对不能加速”

本课结论针对直接复用普通 INT8 GEMM 与简单后处理的情形。

它不表示 per-channel 激活量化在任何硬件和任何 kernel 上都不可能高效。

专用 kernel、预折叠、分组量化、整数近似 scale 或新硬件指令都可能改善性能。

但相比 scale 可从归约中整体提出的 per-token 路径,它需要更复杂的数据流和算子设计,这就是“更难加速”的准确含义。

跟练与练习

原视频练习

编者练习

给定 Ymn=kamxmk(q)bnwkn(q),Y_{mn}=\sum_k a_mx_{mk}^{(q)}b_nw_{kn}^{(q)}, 把 scale 提出求和,并写成矩阵形式。

查看参考答案

因为 ama_mbnb_n 对固定 m,nm,n 都不随 kk 变化,
Ymn=ambnkxmk(q)wkn(q).Y_{mn}=a_mb_n\sum_kx_{mk}^{(q)}w_{kn}^{(q)}.
矩阵形式为
Y=diag(a)(XqWq)diag(b)=(XqWq)(abT).Y=\operatorname{diag}(a)(X_qW_q)\operatorname{diag}(b) =(X_qW_q)\odot(ab^T).

编者练习 2

为什么表达式 kckxmk(q)wkn(q)\sum_k c_kx_{mk}^{(q)}w_{kn}^{(q)} 一般不能化成 ckxmk(q)wkn(q)c\sum_kx_{mk}^{(q)}w_{kn}^{(q)}

查看参考答案

ckc_k 随归约索引 kk 变化,每一项使用不同 scale。除非所有 ckc_k 恰好相等,或存在额外结构可利用,否则不存在一个统一标量 cc 能同时代表全部归约项。
因此普通整数点积完成后再乘单个输出 scale,无法恢复 per-channel 激活缩放的结果。

常见误区

  • 误区:per-channel 中的 channel 总是输出通道。纠正:对激活 XX,这里指输入 channel KK;对权重 WW,常指输出 channel NN
  • 误区:per-token 精度一定更高。纠正:同一 token 中的离群通道可能主导 scale,压缩普通值的分辨率。
  • 误区:per-channel 激活量化的 scale 也能统一放到 GEMM 输出端。纠正:它随归约索引 kk 变化,位于求和内部。
  • 误区:INT8 GEMM 输出直接就是最终浮点结果。纠正:整数累加后还需应用相应 scale,并可能融合 bias、激活与 requantization。
  • 误区:难融合等于数学上算不出来。纠正:结果当然可计算,只是不能直接复用最简单的标准整数 GEMM 加统一后缩放路径。
  • 误区:SmoothQuant 删除了离群信息。纠正:它通过成对逆缩放在激活与权重间迁移数值难度,理想线性层输出保持不变。

本课小结

  • per-channel 激活量化能把离群通道与普通通道分开确定 scale,通常更有利于精度。
  • per-token 激活 scale ama_m 与权重输出通道 scale bnb_n 都不随归约索引 kk 变化。
  • 因此可先计算标准整数 GEMM,再用外积 abTab^T 做逐元素反量化缩放。
  • per-channel 激活 scale ckc_k 随归约索引变化,必须留在点积求和内部。
  • 这使其更难直接映射到普通 INT8 GEMM 与轻量融合后处理。
  • SmoothQuant 等方法通过重参数化把激活离群难度迁移到更易处理的权重侧。
14

主题讲解 · 03:15

DecDEC 如何用 CPU 残差动态修正低比特量化误差

学习目标

  • 能把权重量化误差写成残差矩阵 R=WW^R=W-\hat W
  • 能解释激活离群 channel 为什么会放大对应权重行的量化误差。
  • 能推导选中通道残差修正的代数正确性。
  • 能说明低比特权重常驻 GPU、残差按需从 CPU 传输的分工。
  • 能比较静态校准通道与运行时近似 top-k 选择的差别。

前置与衔接

需要理解线性层的向量—矩阵乘法、权重量化与 decode 阶段的 GEMV。

前几课通过 SmoothQuant、OBQ 与 QAT 尽量在量化前或训练时降低误差。

本课讨论另一条系统路线:低比特权重已经确定后,把原权重与量化权重的差保存在 CPU 内存中,只在当前 token 真正需要时取回少量残差做修正。

视频以 DecDEC 为例介绍这一思路;具体论文版本、实现参数与硬件结果应以原论文和代码为准。

核心讲解

1. 权重量化残差是什么

令浮点权重矩阵为

WRK×N,W\in\mathbb{R}^{K\times N},

量化并反量化后的近似权重为

W^.\hat W.

定义残差

R=WW^.R=W-\hat W.

于是有精确分解

W=W^+R.W=\hat W+R.
图 1

权重量化残差定义为 R=W-W_hat;低比特 W_hat 常驻 GPU,而被选中的残差行可按需从 CPU 取回。

原视频 · 00:20 ↗

若把完整 RR 都保留在 GPU 并参与每次计算,就会削弱低比特量化节省显存和带宽的意义。

DecDEC 类方法的关键是:主模型只常驻低比特 W^\hat W,完整或压缩后的残差信息放在容量更大的 CPU 内存中。

2. 为什么单请求 decode 常画成 GEMV

自回归 decode 每一步通常只为一个请求新增一个 token。

在最简 batch size 为 1 的分析中,当前激活是行向量

x=(x1,x2,,xK),x=(x_1,x_2,\ldots,x_K),

线性层输出为

y=xW.y=xW.

这是向量乘矩阵,即 GEMV。

图 2

单请求 decode 先用激活向量 x 与 GPU 上的量化权重 W_hat 做主 GEMV,得到低比特近似输出。

原视频 · 01:00 ↗

若服务系统把多个请求或 token 合批,计算可重新表现为 GEMM;因此“decode 是 GEMV”需要限定在单请求、单 token 的简化场景。

3. 量化主路径的输出误差

使用量化权重时,主路径输出为

y^=xW^.\hat y=x\hat W.

浮点输出是

y=xW.y=xW.

二者之差为

yy^=x(WW^)=xR.y-\hat y =x(W-\hat W) =xR.

按输入 channel 展开:

xR=i=1KxiRi,xR=\sum_{i=1}^{K}x_iR_i,

其中 RiR_i 是残差矩阵第 ii 行,对应输入 channel ii

4. 激活离群值会放大特定残差行

量化输出可写成

y^=i=1KxiW^i.\hat y=\sum_{i=1}^{K}x_i\hat W_i.

ii 个 channel 对输出误差的贡献是

xi(WiW^i)=xiRi.x_i(W_i-\hat W_i)=x_iR_i.

即使不同残差行的范数相近,绝对值很大的 xix_i 也会放大对应误差。

图 3

量化输出包含 xᵢW_hatᵢ;当 x₂、x₄ 是大激活时,对应权重行的量化误差被显著放大。

原视频 · 01:20 ↗

所以在有限带宽下,优先修正当前激活绝对值最大的 channel,通常比平均处理所有 channel 更有针对性。

5. 运行时选择修正集合

假设当前 token 的离群位置是第 2 和第 4 个 channel。

记选中集合为

S={2,4}.S=\{2,4\}.
图 4

单 token 激活向量中的大值位置决定本次最值得修正的输入 channel。

原视频 · 00:40 ↗

系统不需要取回整张残差矩阵,只需从 CPU 侧取回

R2=W2W^2,R_2=W_2-\hat W_2,
R4=W4W^4.R_4=W_4-\hat W_4.

这些行与当前大激活位置一一对应。

6. 两条并行计算路径

GPU 主路径计算

y^=xW^.\hat y=x\hat W.

残差路径对选中通道计算

yR=iSxiRi.y_R=\sum_{i\in S}x_iR_i.

最终修正输出为

ycorr=y^+yR.y_{corr}=\hat y+y_R.

S={2,4}S=\{2,4\} 时:

yR=x2(W2W^2)+x4(W4W^4).y_R=x_2(W_2-\hat W_2) +x_4(W_4-\hat W_4).

这同样是一个规模较小的向量—矩阵乘或稀疏行组合。

7. 选中通道为何能恢复原权重贡献

展开第 2 个通道在最终输出中的两项:

x2W^2+x2(W2W^2).x_2\hat W_2+x_2(W_2-\hat W_2).

合并后得到

x2W2.x_2W_2.

第 4 个通道同理:

x4W^4+x4(W4W^4)=x4W4.x_4\hat W_4+x_4(W_4-\hat W_4) =x_4W_4.
图 5

加入 x₂(W₂-W_hat₂) 和 x₄(W₄-W_hat₄) 后,选中项中的量化权重被代数抵消并恢复为原权重贡献。

原视频 · 02:20 ↗

因此在残差未再次近似、传输和计算均精确的理想情况下,选中 channel 的量化误差被完全抵消。

8. 未选中通道仍保持低比特近似

对一般选中集合 SS,修正输出为

ycorr=iSxiWi+iSxiW^i.y_{corr} =\sum_{i\in S}x_iW_i +\sum_{i\notin S}x_i\hat W_i.

这揭示了方法的本质:

  • 重要通道使用原权重贡献;
  • 其他通道继续使用低比特权重;
  • 用少量 CPU—GPU 数据交换换取主要误差的恢复。

SS 包含全部 channel,就恢复完整浮点输出,但传输与计算代价也失去优势。

SS 为空,就退化为普通量化推理。

9. 为什么要动态检测而不是固定通道

静态量化方法常使用校准集统计哪些 channel 容易出现离群值。

但校准分布与真实推理请求不一定完全一致;不同 token 的大激活位置也可能变化。

DecDEC 的视频示例采用运行时检测:根据当前 xx 近似选择绝对值最大的若干 channel。

这使残差取回集合随 token 改变,而不是永久固定为校准集上的同一批索引。

10. 近似 top-k 控制检测开销

精确排序所有 KK 个激活以取 top-k 可能产生额外延迟。

视频强调使用近似 top-k 检测,让系统以较低开销找到足够重要的通道。

近似选择可能漏掉真正最大值,但只要捕获大部分主导误差,就可能取得精度—延迟折中。

这是一种系统目标,不是要求通道集合在数学上精确等于全排序 top-k。

11. 残差本身也可以压缩

完整浮点残差行从 CPU 传到 GPU 仍可能很贵。

视频指出残差本身也进行量化,以降低 CPU 内存占用和总线传输字节数。

图 6

DecDEC 以运行时近似 top-k 选通道,并对 CPU 侧 residual 再压缩,以控制检测与数据传输成本。

原视频 · 03:00 ↗

一旦 RiR_i 被再次量化,前面的完全相消就变成近似相消。

若 CPU 侧保存的是 R~iRi\tilde R_i\approx R_i,则选中通道仍留下

xi(RiR~i)x_i(R_i-\tilde R_i)

这一二次压缩误差。

因此真实实现不是“免费恢复浮点精度”,而是在残差位宽、选中数量和传输延迟间折中。

12. 成本模型:省显存不等于没有带宽代价

这种设计利用 CPU 内存容量大、成本低的优势,但 CPU 与 GPU 之间的互连远慢于 GPU 片上和显存带宽。

主要成本包括:

  • 在当前激活上进行近似 top-k 检测;
  • 把索引发送给残差存储侧;
  • 从 CPU 内存定位并读取选中行;
  • 通过 PCIe 或其他互连传输残差;
  • 在 GPU 上解码残差并完成修正计算;
  • 把修正结果与主 GEMV 输出同步相加。

若不能与主路径重叠,这些步骤可能进入 decode 的关键延迟路径。

13. 为什么这种思路更适合低 batch decode

单请求 decode 的主计算常呈 GEMV,算术强度低,性能容易受权重读取带宽限制。

低比特 W^\hat W 显著减少每步从显存读取的权重字节数。

只取少量残差行,则仍可能维持较小的附加流量。

但随着 batch 增大:

  • 多个请求可能选中不同 channel;
  • 残差行集合的并集扩大;
  • 主路径逐渐变为 GEMM;
  • 数据复用和调度关系改变。

因此单用户、低 batch 的视频场景不能无条件外推到高吞吐服务。

14. 与 SmoothQuant 的差别

SmoothQuant 倾向于使用校准统计,把激活离群难度通过静态重参数化迁移到权重侧。

本课的残差修正路线则在运行时观察当前激活,并动态选择要恢复的权重行。

二者优化位置不同:

  • SmoothQuant 在部署前改变权重—激活缩放分配;
  • 动态残差修正在部署时为少量通道追加高精度信息。

它们并非简单互斥;是否组合取决于实现、精度目标和带宽预算。

15. 事实与版本边界

视频板书将 DecDEC 描述为 OSDI 2025 低比特量化工作,并展示在 RTX 4050 上运行量化 Llama 3 8B 的语境。

本课只使用视频给出的机制做教学推导,不把单一板书扩展为对所有模型、硬件、batch size 或当前代码版本的性能承诺。

具体位宽、top-k 比例、残差编码、对比基线和吞吐数据需要回到对应论文与实现核对。

跟练与练习

原视频练习

编者练习

x=(1,5,2),x=(1,5,2), 只选择第 2 个 channel 修正。用 WiW_iW^i\hat W_i 写出 ycorry_{corr},并说明哪个通道恢复为浮点贡献。

查看参考答案

量化主路径是
y^=W^1+5W^2+2W^3.\hat y=\hat W_1+5\hat W_2+2\hat W_3.
残差路径是
5(W2W^2).5(W_2-\hat W_2).
相加得到
ycorr=W^1+5W2+2W^3.y_{corr} =\hat W_1+5W_2+2\hat W_3.
只有第 2 个 channel 恢复为原权重贡献,第 1、3 个 channel 仍使用量化权重。

编者练习 2

若取回的是近似残差 R~i\tilde R_i,选中 channel 还剩什么误差?

查看参考答案

理想残差为 Ri=WiW^iR_i=W_i-\hat W_i
使用 R~i\tilde R_i 后,修正项与真实浮点贡献之差为
xiRixiR~i=xi(RiR~i).x_iR_i-x_i\tilde R_i =x_i(R_i-\tilde R_i).
所以残差量化越精确,选中通道越接近完全恢复;更低位宽则减少传输但增大二次误差。

常见误区

  • 误区:残差是模型输出误差。纠正:本课先定义权重残差 R=WW^R=W-\hat W,输出误差由 xRxR 产生。
  • 误区:所有 residual 都必须传到 GPU。纠正:系统只按当前激活选择少量残差行。
  • 误区:选中 channel 后整层都恢复成浮点。纠正:只有选中行的贡献被修正,其他行仍使用量化权重。
  • 误区:残差量化后仍能数学上完全相消。纠正:若取回的是近似 R~\tilde R,会留下二次压缩误差。
  • 误区:decode 永远是 GEMV。纠正:单请求单 token 是 GEMV;连续 batching 或多请求可形成 GEMM。
  • 误区:CPU 内存容量大,所以传输没有成本。纠正:总线带宽、同步和随机行访问可能直接影响 token 延迟。
  • 误区:校准集找到的离群通道必然覆盖线上推理。纠正:动态选择的动机正是运行时通道可能变化。

本课小结

  • 权重量化残差是 R=WW^R=W-\hat W,完整浮点输出满足 xW=xW^+xRxW=x\hat W+xR
  • 大激活 xix_i 会放大对应残差行 RiR_i 对输出误差的影响。
  • 动态选中集合 SS 后,只需取回并计算 iSxiRi\sum_{i\in S}x_iR_i
  • 理想残差可让选中通道从 xiW^ix_i\hat W_i 精确恢复为 xiWix_iW_i
  • 近似 top-k 控制检测成本,残差再量化控制 CPU 内存与传输成本。
  • 实际收益取决于选中比例、残差位宽、CPU—GPU 带宽、batch 和计算重叠。
15

单元综合

从表示压缩到误差补偿:大模型量化方法的统一决策框架

单元能力目标

完成本单元后,应能把一种量化方法放进以下四层账本,而不是只记论文名:

  1. 表示层:浮点值被编码成规则整数格点,还是索引加 codebook?
  2. 误差层:误差主要来自权重、激活离群值、舍入边界,还是局部曲率?
  3. 补偿层:方法使用混合精度、等价缩放、训练适应、二阶更新,还是动态残差?
  4. 系统层:最终能否落到标准整数 GEMM,是否需要额外高精度分支、查表、CPU 回传或自定义 kernel?

可检验的能力包括:

  • 从 scale 的作用轴判断一种方案是否能使用标准 INT8 GEMM;
  • 用 shape 和外积展开检查 channel 拆分、列缩放与行缩放是否配对;
  • 区分量化前严格恒等式与量化后近似结果;
  • 为给定误差来源选择合适的补偿方法;
  • 同时核算主体位宽、元数据、残差、分支和数据搬运成本。

概念连接

1. 量化首先是“编码—重建”问题

任意量化器都可以抽象为

q=Q(w),w^=D(q),q=Q(w), \qquad \hat w=D(q),

其中 QQ 产生紧凑编码,DD 产生部署时使用的近似值。

线性量化使用规则网格:

q=clip(round(w/s)+z,qmin,qmax),q=\operatorname{clip} \left( \operatorname{round}(w/s)+z, q_{\min},q_{\max} \right),
w^=s(qz).\hat w=s(q-z).

它的优势是算术规则、容易映射到整数硬件;代价是统一网格不一定贴合非均匀分布。

聚类量化则学习 codebook:

w^i=cai.\hat w_i=c_{a_i}.

它能把中心放到数据密集区域,但还要保存索引与中心,并处理查表、解码或专用 kernel。

所以比较“几 bit”时,必须把 codebook、scale、zero-point、分组描述和对齐开销一起计入。

2. 激活离群值把量化问题变成矩阵结构问题

若同一量化组内同时存在普通值与极大值,scale 会被极大值拉大。

普通值因此落入过粗的整数格点,差异被压缩甚至消失。

对线性层

Y=XW,Y=XW,

矩阵乘法的外积和为

XW=k=1Kxkwk.XW=\sum_{k=1}^{K}x_kw_k^\top.

这个形式把“第 kk 个激活 channel”与“权重第 kk 行”明确配对。

任何面向激活 channel 的处理都必须同时追踪权重共享维上的对应行。

3. LLM.int8():把困难项隔离到高精度分支

将共享维分成普通集合 N\mathcal N 与离群集合 O\mathcal O

XW=XNWN+XOWO.XW = X_{\mathcal N}W_{\mathcal N} + X_{\mathcal O}W_{\mathcal O}.

拆分在量化前严格等价。

部署时让普通分支使用 INT8,离群分支保留 FP16 等较高精度,再相加两路输出。

它的核心决策是:用一小部分高精度计算保护最危险的离群贡献。

结果是否更快取决于离群比例、子矩阵布局、kernel 与分支合并,而不只取决于代数可拆性。

4. SmoothQuant:不拆分,而是迁移尺度

SmoothQuant 插入可逆对角阵:

XW=X(S1S)W=(XS1)(SW).XW = X(S^{-1}S)W =(XS^{-1})(SW).

S=diag(s1,,sD),S=\operatorname{diag}(s_1,\ldots,s_D),

则第 jj 个激活列与对应权重行变为

x~j=xj/sj,w~j=sjwj.\tilde x_j=x_j/s_j, \qquad \tilde w_j^\top=s_jw_j^\top.

逐 channel 外积贡献保持不变:

x~jw~j=xjwj.\tilde x_j\tilde w_j^\top=x_jw_j^\top.

一般缩放为

sj=(maxxj)α(maxwj)1α.s_j = \frac{ (\max|x_j|)^\alpha }{ (\max|w_j|)^{1-\alpha} }.

激活范围越大,sjs_j 越大,xj/sjx_j/s_j 越小;困难尺度被迁移到较容易离线处理的权重侧。

5. 外积展开解释“右乘缩列、左乘缩行”

右乘对角阵 XS1XS^{-1} 可以写成 XX 的列与 S1S^{-1} 的行所构成的外积和。

由于对角阵第 jj 行只有第 jj 个位置非零,第 jj 个外积项只占输出第 jj 列,因此右乘只缩放列。

左乘对角阵 SWSW 可以写成 SS 的列与 WW 的行所构成的外积和。

每个外积项只占一行,因此左乘只缩放行。

这个结构保证 channel 不互相混合,也给 scale 公式的方向提供了 shape 级解释。

6. 等价变换怎样落到推理图

校准数据使 SS 在部署前固定。

因此

W=SWW'=SW

可以离线写回权重。

XS1XS^{-1} 本来就是逐 channel 缩放;在常见计算图中还可并入前置归一化参数或相邻 fused kernel。

“公式中出现两个矩阵”不代表线上一定新增两个 GEMM。

是否真正零额外调度,应检查导出后的部署图,而不是纸面符号。

7. QAT:让训练过程提前感受部署误差

后训练量化固定一个已经训练好的模型,再寻找合适的量化器。

量化感知训练在前向中插入 fake-quant:

wfq=D(Q(w)).w_{fq}=D(Q(w)).

损失由量化后的前向结果计算,但优化器通常更新浮点主权重。

模型因此能在训练过程中适应舍入与 clipping 误差。

8. 为什么 fake-quant 需要 STE

round 的真实导数几乎处处为零,直接反传会阻断梯度。

STE 保留离散前向,却在反向使用代理导数:

round(u)u1\frac{\partial\,\operatorname{round}(u)}{\partial u} \approx1

或带 clipping 掩码的变体。

STE 不是说 round 真的可导,而是一种优化估计器。

即使 STE 的局部代理因子为 1,QAT 梯度也不必等于全精度训练梯度,因为 fake-quant 已经改变预测和上游梯度。

9. 聚类量化微调:保持共享结构

聚类量化保存索引 aia_i 与中心 ckc_k

若逐元素更新解码后的 w^i\hat w_i,同簇权重会分裂,压缩结构被破坏。

正确的约束式微调是固定簇分配,只更新中心:

Lck=i:ai=kLw^i.\frac{\partial L}{\partial c_k} = \sum_{i:a_i=k} \frac{\partial L}{\partial\hat w_i}.

这就是共享参数的链式法则,也可由 scatter-add 实现。

10. OBQ:用局部曲率衡量量化损失

QAT 通过数据与训练适应误差;OBQ 则从局部二阶近似出发:

ΔL12ΔwTHΔw.\Delta L \approx \frac12\Delta w^TH\Delta w.

将第 ii 个权重量化到 qiq_i,约束为

Δwi=qiwi.\Delta w_i=q_i-w_i.

最优补偿沿 H1eiH^{-1}e_i 调整其他权重,最小局部损失为

ΔLi=(wiqi)22[H1]ii.\Delta L_i^* = \frac{(w_i-q_i)^2} {2[H^{-1}]_{ii}}.

qi=0q_i=0 就退化到 OBS 剪枝,因此剪枝与量化可放在同一个“离散约束加二阶补偿”框架中理解。

11. DecDEC:在运行时按输入修复残差

权重量化残差为

R=WW^.R=W-\hat W.

完整输出满足

xW=xW^+xR.xW=x\hat W+xR.

大激活会放大对应残差行的贡献,所以可以动态选择重要索引集合 SS,只计算

iSxiRi.\sum_{i\in S}x_iR_i.

这种方案把部分误差修正放到运行时,并可能用 CPU 保存或计算残差。

它的瓶颈不只在算术,还包括 top-k 选择、残差位宽、CPU—GPU 带宽、batch 和计算重叠。

对比与决策

1. 遇到激活离群值,先问能否规整成标准 kernel

  • 希望保护少数离群 channel,且可接受两路 mixed precision:考虑 LLM.int8() 式拆分。
  • 希望把计算规整为统一低精度 GEMM,且有代表性校准集:考虑 SmoothQuant 式离线迁移。
  • 离群位置依输入强烈变化,且可接受运行时筛选与残差搬运:考虑动态残差修正思路。

2. per-token 与 per-channel 的硬件差别

对整数点积的归约维 kk,若激活 scale 不依赖 kk,它可以移到求和外部。

per-token 激活 scale ama_m 与权重输出通道 scale bnb_n 可形成外积 abab^\top,在整数 GEMM 后统一反量化。

per-channel 激活 scale ckc_k 随归约索引变化,必须留在每一项乘积内部,更难直接映射到标准 INT8 GEMM。

因此精度更细的粒度不自动等于系统吞吐更高。

3. 训练时与训练后方法怎样选

  • 有训练数据、算力和足够迭代预算:QAT 可让模型主动适应量化误差。
  • 只能在已训练模型上工作:需要校准、二阶近似、混合精度或残差补偿。
  • 使用聚类表示且必须保持压缩结构:只更新共享中心,不能无约束逐元素微调。

4. 始终分开三种“正确性”

  1. 代数正确性:如 S1S=IS^{-1}S=I、外积重分组,量化前严格成立。
  2. 数值近似质量:取决于 bit、scale、clipping、校准和误差补偿。
  3. 系统收益:取决于 kernel、数据布局、分支比例、内存流量和硬件。

一个方法在第一层正确,不代表第二层无损,更不保证第三层更快。

综合训练

编者练习

某线性层的少数激活 channel 偶尔达到 500,而其余值大多在 [1,1][-1,1]。部署目标是单卡低延迟,硬件对标准 INT8 GEMM 支持很好,但不希望长期保留额外 FP16 分支。请提出一条量化路径,并列出至少三项验证。

查看参考答案

可优先尝试 SmoothQuant 式路径:用代表性校准集按激活列与权重对应行统计范围,构造非零对角缩放 SS,离线折叠 SWSW,并将 XS1XS^{-1} 融入前置归一化或 fused kernel,再做低比特量化。至少验证:一是校准集是否覆盖离群模式;二是变换前后的全精度输出是否在数值容差内一致;三是量化后误差与任务指标;四是导出图是否确实落到预期 INT8 kernel;五是移动 scale 后权重范围是否出现新的 clipping。若校准无法覆盖动态离群,再比较混合精度或动态残差方案。

编者练习 2

一个聚类量化模型保存 16 个中心和每权重 4-bit 索引。微调时,有人把解码后的每个权重都作为独立参数训练。指出问题,并给出保持压缩结构的梯度更新。

查看参考答案

独立更新会让同一个中心解码出的权重产生不同值,原来的“索引加 codebook”表示失效。应固定簇索引,只把 16 个中心作为可训练参数。对中心 ckc_k,梯度为所有满足 ai=ka_i=k 的解码权重梯度之和:L/ck=i:ai=kL/w^i\partial L/\partial c_k=\sum_{i:a_i=k}\partial L/\partial\hat w_i。更新中心后仍用原索引重建权重,从而保持共享结构。

编者练习 3

判断下列推理是否成立:“因为 XW=(XS1)(SW)XW=(XS^{-1})(SW) 严格成立,所以把两边都量化为 INT8 后,输出与原 FP16 逐位相同。”

查看参考答案

不成立。恒等式只证明未量化的代数变换保持函数。将 XS1XS^{-1}SWSW 映射到有限整数格点会引入舍入、clipping、scale 近似和累加误差;浮点结合顺序也可能造成末位差异。SmoothQuant 的目标是重新分配动态范围以降低误差并改善 kernel 规整性,不是证明 INT8 无损。

进入下一单元前

  • 已能从 shape 判断 scale 是否位于归约维,并预测它能否移到整数 GEMM 之外。
  • 已能用外积和检查激活列与权重行的 channel 配对。
  • 已能区分 LLM.int8() 的高精度隔离、SmoothQuant 的等价迁移、QAT 的训练适应、OBQ 的二阶补偿与 DecDEC 的动态残差修正。
  • 已能为聚类量化写出中心梯度求和,并说明逐元素微调为何破坏共享结构。
  • 已能分别审计代数正确性、量化误差与系统性能。
  • 如果仍会把“公式等价”误判为“量化无损”,回看 P57、P58 与 P72。
  • 如果仍无法判断量化粒度的硬件代价,回看 P76,并明确 scale 是否依赖归约索引。