Lecture 3 · 精度、量化与稀疏 · Accuracy, Quantization, and Sparsity

Efficient ML Inference at Scale · Lecture 03。个人学习讲义与配套实验,按主题整理并持续修订。

课程源码 ZIP ↓

个人整理的学习笔记,非官方讲义;课程来源与整理说明见课程介绍。配套示例未逐一运行验证。

本篇目录

对应 Maps to:Stanford CS349E / EE339 Efficient ML Inference at Scale(Autumn 2026),Lecture 3 · Tue Sep 29 · Accuracy, Quantization, and Sparsity(Priyanka Raina)。阅读材料:T. Dettmers, M. Lewis, Y. Belkada, L. Zettlemoyer, LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale(NeurIPS 2022);W. Fedus, B. Zoph, N. Shazeer, Switch Transformers: Scaling to Trillion Parameter Models with Simple and Efficient Sparsity(JMLR 2022)。 依据说明:课程网站只公开了课表和阅读材料,没有 slides。本讲以两篇阅读材料为主线,另外用到这些公开文献:SmoothQuant(Xiao et al. 2023)、GPTQ(Frantar et al. 2023)、AWQ(Lin et al. 2024)、NVIDIA 的 2:4 结构化稀疏(Mishra et al. 2021)、DeepSeek-V3 技术报告(无辅助损失的负载均衡)、How to Scale Your Model 第 4 章(MoE 的临界批大小)。数值格式本身(INT、FP8、MX、NVFP4)在前置讲 P3 里讲过,这里直接用。补充材料是我选的,不代表课堂上实际讲了什么。 配套代码 Code:code/lecture03/(numpy;1 个共享模块 + 5 个脚本,每个都在 3 秒内跑完) 预计阅读时间:3–4 小时(含运行代码)


0. 本讲导航 · How to read this lecture

0.1 学习目标 Learning goals

学完这一讲,你应该能够:

  1. 说清楚”精度”在推理里意味着什么:每个数用几个比特,换来多少字节、多少算力、多少容量,代价是多少输出偏差;会用”每个权重平均几比特(含缩放因子)“比较不同方案。
  2. 解释仅权重量化(W8A16、W4A16)、W8A8、W4A4 和 KV cache 量化各自省的是什么,以及它们分别加速 decode 还是 prefill。
  3. 复述 LLM.int8() 的故事:离群特征在 6.7B 左右出现,为什么按 token / 按通道的缩放管不住它们(缩放因子不能沿被求和的维度变化),混合精度分解怎样解决,代价是什么;SmoothQuant 换了什么思路。
  4. 比较非结构化稀疏和 2:4 结构化稀疏:精度、存储格式、在 roofline 上省什么;解释激活稀疏为什么只帮得了小批量。
  5. 解释 MoE(Switch Transformer)的路由、专家容量、容量因子和负载均衡损失;会算 MoE 的参数量 vs 激活参数量,以及 decode 时一个批会碰到多少专家、需要多大的批才能 compute-bound。

0.2 先修与本讲范围 Prerequisites & scope

默认你已经会 Assumed本讲会讲 Covered here后面再展开 Later
第 1 讲的 roofline、临界批大小;第 2 讲的 Transformer 结构和 KV cache模型层面的量化:权重、激活、KV;离群特征;稀疏;MoE量化矩阵乘法的内核怎么写(第 10 讲)
P3 的数值格式、缩放因子、粒度、块缩放各种方案在 roofline 上的位置MoE 的专家并行与 all-to-all(第 4 讲)
—量化感知的评价方法量化训练(QAT)、FP8 训练(不覆盖)

0.3 本讲地图 Map

§1  精度意味着什么:比特、字节、算力、容量;怎样衡量"输出偏了多少"
§2  量化回顾:缩放因子、粒度、"每个权重几比特"
§3  仅权重量化与 KV 量化:格式、分组、敏感度、KV                                 ← 代码 01
§4  激活量化与离群特征:W8A8;LLM.int8();为什么 vector-wise 不够;阈值;SmoothQuant  ← 代码 02
§5  精度在 roofline 上:decode、prefill、临界批大小、容量;怎样选方案              ← 代码 03
§6  稀疏:非结构化 vs 2:4;存储;roofline;激活稀疏                              ← 代码 04
§7  混合专家(MoE):Switch 路由、容量、负载均衡、MoE 的 roofline                  ← 代码 05
§8  现状(2025–2026)
§9–13  小结、常见误区、练习(含答案)、延伸阅读、下一讲预告

0.4 记号 Notation

写法含义
WxxAyy权重 xx 位、激活 yy 位。W4A16 = 4 位权重、16 位激活(仅权重量化);W8A8 = 权重和激活都是 8 位
KVzzKV cache 每个数 zz 位
整张 / 每通道 / 每组 gg缩放因子的粒度:整个矩阵一个;每个输出通道一个;沿输入维每 gg 个数一个(P3 §6.4)
每 token / vector-wise激活每一行(每个 token)一个缩放因子;vector-wise = 激活每行 × 权重每列
RTNround-to-nearest:最简单的”就近舍入”量化
EE、kkMoE 的专家数、每个 token 选几个专家
CF容量因子(capacity factor)

“每个权重几比特”包含缩放因子的开销:INT4 每 128 个一组、每组一个 16 位缩放因子,就是 4+16/128=4.1254 + 16/128 = 4.125 位;NVFP4 每 16 个数一个 8 位缩放因子,是 4.5 位。


1. 精度意味着什么 · What “accuracy” means for inference

1.1 两个问题

第 1、2 讲反复得到同一个结论:decode 的时间几乎全花在搬字节上(权重、KV cache),prefill 的时间花在算上。量化和稀疏攻的就是这两件事:

  • 量化(quantization):每个数用更少的比特。字节少了,带宽和容量都受益;如果计算也用低精度,算力也翻倍。
  • 稀疏(sparsity):一部分数直接变成 0,不存、不算。

两者都会让模型的输出偏离原来的样子。所以每一种方案都要回答两个问题:

  1. 省了什么?字节、FLOPs、显存容量——取决于你在 roofline 的哪一边(§5)。
  2. 偏了多少?——取决于方案的细节,而且常常有意外(§3、§4)。

这一讲的标题是 “Accuracy, Quantization, and Sparsity”:准确率是代价,量化和稀疏是手段。

1.2 怎样衡量”偏了多少”

真实的评测用困惑度(perplexity)和下游任务(问答、代码、数学)。LLM.int8() 论文用的是 C4 数据集上的困惑度和一组零样本任务。

我们的模型是第 2 讲的随机初始化 Transformer,没有”正确答案”。代码里的做法是把全精度模型自己当作老师(quant_lab.py 开头):

  1. 用全精度模型按温度 1 采样出 8 个、每个 64 个 token 的序列;
  2. 在这些序列上比较量化模型和全精度模型的下一个 token 分布:
    • KL 散度 KL(p全精度p_\text{全精度} ‖ p量化p_\text{量化}):两个分布差多少,0 表示完全一样;
    • top-1 一致率:两者最可能的下一个 token 是否相同;
    • 困惑度比:量化模型在老师文字上的困惑度 ÷ 全精度的困惑度,越接近 1 越好。

“相对原模型的 KL 散度”也是 llama.cpp 等项目评价量化时常用的指标,它直接衡量”量化后还是不是同一个模型”。代价是:它不能告诉你”偏了以后答案对不对”——真实部署前还要跑下游任务。

这一讲用的小模型(QCFG):4 层、d=256d = 256、8 个 query 头、2 组 KV、dff=768d_\text{ff} = 768、词表 1024,全精度困惑度 146.6。

1.3 一个技巧:用 float64 精确地模拟 int8

int8 × int8 的乘积最大 1272=16129127^2 = 16129,几千个相加也远小于 2532^{53},而 float64 能精确表示 2532^{53} 以内的所有整数。所以把 int8 的值存成 float64 再用 BLAS 做矩阵乘法,结果和”真正的 int32 累加”逐位相同(quant_lab.py 的自检验证了这一点)。代码里的”整数矩阵乘法”都是这样实现的,既精确又快。


2. 量化回顾 · Quantization recap

这一节是 P3 §6 的速记,读过 P3 可以跳过。

对称量化:q=round(x/s)q = \text{round}(x / s),s=max⁡∣x∣/qmaxs = \max|x| / q_\text{max},反量化 x^=s⋅q\hat{x} = s \cdot q。INT8 的 qmax=127q_\text{max} = 127,INT4 是 7。

粒度:一个缩放因子管多少个数。管得越少,越能适应局部的数值范围,但缩放因子本身也要存。对权重 W[输入,输出]W[\text{输入}, \text{输出}]:

  • 整张(per-tensor):一个。
  • 每通道(per-channel):每个输出通道(每一列)一个。
  • 每组(group-wise):沿输入维每 gg 个一个。沿输入维分组的原因:一个输出是输入维上的点积,同一个点积里的数共享缩放因子,硬件就能累加完再统一乘。

块缩放格式(MX、NVFP4):同样是”沿输入维每 16 或 32 个数一个缩放因子”,只是元素和缩放因子都是浮点。NVFP4 的缩放因子用 E4M3(有尾数),比 MXFP4 的 E8M0(只能是 2 的幂)更精细(P3 §6.5)。

假量化(fake quantization):量化后立刻反量化,用浮点数参与计算。只要反量化后的值和真正低精度存储的值相同,数值上就等价。本讲的代码都这样做,因为我们关心的是”数值上偏多少”,而不是低精度内核本身。


3. 仅权重量化与 KV 量化 · Weight-only and KV quantization

代码:01_weight_and_kv_quantization.py

3.1 为什么从权重开始

仅权重量化(weight-only)只改变权重的存储:读进来以后先反量化回 BF16,再做 BF16 的矩阵乘法。它是最常用、最安全的推理量化:

  • 不改变计算方式,激活不受影响(激活难量化,§4);
  • 正好打在小批量 decode 的瓶颈上:读权重(§5.1);
  • 权重是离线量化的,可以花时间做得很仔细(GPTQ、AWQ)。

3.2 各种格式

代码 01 演示 1,所有线性层用同一种格式(lm_head 保持全精度):

格式比特 / 权重KL 散度top-1 一致困惑度比
bf1616.0000.00000100.0%1.0000
int8-tensor8.0000.0001698.0%1.0010
int8-channel8.0520.0000799.0%1.0009
fp8-tensor(E4M3)8.0000.0010196.5%1.0024
int4-tensor4.0000.0528273.2%1.0560
int4-channel4.0520.0242281.1%1.0215
int4-g1284.1250.0196484.0%1.0138
int4-g324.5000.0137586.5%1.0048
mxfp44.2500.0192983.6%1.0359
nvfp44.5000.0128687.5%1.0123

读这张表:

  • 8 位几乎无损。INT8 每通道的 KL 只有 7×10−57 \times 10^{-5}:字节减半,输出几乎不变。
  • 同是 8 位,FP8 比 INT8 差:E4M3 只有 3 位尾数,相对精度约 1/16;INT8 对”大致均匀地分布在一个范围里”的权重更细。FP8 的优势是动态范围大,对激活更友好(§4.7),而不是精度。
  • 到 4 位,粒度变得至关重要:整张一个缩放因子 KL 0.053,每通道 0.024,每 32 个一组 0.014。块缩放格式遵循同样的规律:MXFP4(4.25 位)≈ INT4 每 128 个一组,NVFP4(4.5 位)≈ INT4 每 32 个一组。
  • 困惑度比和 KL 大体同向,但只用了 504 个 token,有噪声(MXFP4 的困惑度比偏高,KL 却和 int4-g128 差不多)。

3.3 分组大小:精度 vs 开销

代码 01 演示 2(INT4,每组一个 16 位缩放因子):

组大小比特 / 权重缩放因子占比KL 散度
165.000020.0%0.01057
324.500011.1%0.01375
644.25005.9%0.01666
1284.12503.0%0.01964
2564.06251.5%0.02320

组越小越准,但收益递减:每把组大小减半,KL 只降 15%–23%,比特却在涨。业界常用 128(GPTQ、AWQ 的默认值),或块缩放格式的 16 / 32。这是一个工程折中,没有唯一正确的答案。

这里只用了最简单的就近舍入(RTN)。GPTQ 用近似的二阶信息逐列修正舍入误差(把一列的误差分摊给还没量化的列),AWQ 按激活的大小找出”重要”的通道并在量化前放大它们。同样的比特,它们能做得准得多——它们改变的是”怎么舍入”,不改变”存多少字节”。

3.4 谁最敏感:lm_head 是个例外

代码 01 演示 3,一次只量化一部分(INT4,每 128 个一组):

量化了什么占线性层权重KL 散度
只量化每层的 Wq / Wk8.7% / 2.2%0.00094 / 0.00094
只量化每层的 Wv / Wo2.2% / 8.7%0.00194 / 0.00203
只量化每层的 W_gate / W_up / W_down各 26.1%0.00485 / 0.00449 / 0.00446
只量化 lm_head(相当于 8.7%)0.02723
只量化第 0 / 1 / 2 / 3 层各 25%0.00576 / 0.00518 / 0.00466 / 0.00434
全部线性层(不含 lm_head)100%0.01964

三个发现:

  1. lm_head 一个矩阵造成的偏差,比其余全部线性层加起来还大(0.027 vs 0.020)。它的误差直接加在 logits 上,后面没有任何层来”消化”。实践中常把 lm_head(和词嵌入)留在 8 / 16 位。
  2. Wq、Wk 最不敏感:它们的误差先经过 softmax 才影响输出。
  3. 各部分的 KL 加起来约等于全部量化时的 KL(每类矩阵之和 0.0197,每层之和 0.0199,全部 0.0196):小扰动下误差近似可加。这正是混合精度策略的基础——按敏感度给不同部分分配不同的比特。

3.5 KV cache 量化

第 2 讲 §5.5 说过,长上下文、大批量时 KV cache 比权重还大。代码 01 演示 4(每个 token、每个 KV 头一个 16 位缩放因子):

KV 格式比特 / 数KV 缩小KL 散度top-1 一致
K bf16 / V bf1616.01.00×0.0000099.8%
K fp8 / V fp88.51.88×0.0001298.8%
K int8 / V int88.51.88×0.0000199.2%
K int4 / V int44.53.56×0.0020793.8%
K int4 / V bf1610.21.56×0.0006896.9%
K bf16 / V int410.21.56×0.0013895.9%

(第一行把 K、V 舍入到 BF16:KL 小到四舍五入成 0,但偶尔会让两个几乎并列的最大值换个位置,所以 top-1 一致率是 99.8% 而不是 100%。)

  • 8 位 KV 几乎无损,KV 字节和显存占用几乎减半。
  • 4 位 KV 偏差大一个数量级。在这个随机模型里 V 比 K 敏感;真实模型里常常相反——K 有离群通道(类似 §4 的激活离群),所以 KIVI 等方法让 K 按通道、V 按 token 分组。哪个更敏感取决于模型,要测,不要猜。

4. 激活量化与离群特征 · Activation quantization and outlier features

代码:02_outliers_llm_int8.py

4.1 W8A8:激活也量化

仅权重量化不改变计算:prefill 照样是 BF16 的 compute-bound。要让计算也变快,就得让激活也量化,用 8 位 Tensor Core(H100 上 INT8 / FP8 的算力是 BF16 的 2 倍)。这就是 W8A8。

整数矩阵乘法的结构:

yij≈six⋅sjw⋅∑kqikx qkjw.y_{ij} \approx s^x_i \cdot s^w_j \cdot \sum_k q^x_{ik} \, q^w_{kj} .

方括号里(求和)全是整数运算,最后乘一次缩放因子。这里有一条硬约束:缩放因子只能随 ii(token)和 jj(输出通道)变化,不能随 kk(被求和的维度)变化,否则就没法先做整数累加、再统一乘回去。允许的组合:

  • 整张 × 整张(absmax);
  • 每 token × 整张;
  • 每 token × 每通道:LLM.int8() 论文称为 vector-wise 量化,是这个约束下最细的粒度。

4.2 LLM.int8() 的发现:离群特征

LLM.int8() 论文测量了 125M 到 175B 的一系列模型,发现了一个涌现现象:

  • 模型到 6.7B 参数左右时,隐藏状态里会出现离群特征(outlier features):少数几个特征维度,数值比其他维度大得多(论文说可达 20 倍)。
  • 它们高度系统化:6.7B 模型一个 2048 token 的序列里约有 15 万个离群值,却集中在不超过 7 个特征维度上;几乎所有 token、所有层都有。
  • 只出现在注意力投影和 MLP 上投影的输入里。
  • 它们很重要:把这几维置零,注意力 softmax 的 top-1 概率质量下降 20% 以上,困惑度变差 600%–1000%;而随机置零同样数量的维度,困惑度只变差约 0.1%。

8 位量化随之崩溃。论文表 1(C4 困惑度,越低越好):

方法1.3B6.7B13B
32 位浮点15.9113.3012.45
int8 absmax(整张)16.5514.5919.08
int8 absmax vector-wise16.8214.1316.48
int8 zeropoint vector-wise15.9413.3813.47
LLM.int8()15.9313.2412.45

13B 模型用整张 int8 量化,困惑度从 12.45 变成 19.08——比 6.7B 的 8 位模型还差。只有 LLM.int8() 保持了全精度的困惑度。

4.3 在小模型里复现:注入离群特征

我们的随机小模型没有离群特征(它没有被训练过,也不够大)。代码 02 用一个不改变模型输出的办法注入它们(quant_lab.inject_outliers):在 RMSNorm 之后,把第 3、77、150 维放大 20 倍,同时把后面 Wq / Wk / Wv、W_gate / W_up 的对应行缩小 20 倍。(x⊙γ)W(x \odot \gamma) W 里 γj\gamma_j 乘 ss、WW 的第 jj 行除以 ss,乘积不变:全精度 logits 的最大变化只有 10−1410^{-14}。

但激活变了。代码 02 演示 1:

模型线性层的输入最大 |x|各维度最大值的中位数最大值 ≥ 6 的维度数
原模型q_proj4.53.120 / 256
注入后q_proj69.53.123 / 256
注入后mlp_up67.93.143 / 256
两者mlp_down11.74.1676 / 768

注入后,3 列(约 1.2% 的维度)变成了其他列的约 20 倍。顺带注意 mlp_down 的输入(SwiGLU 的输出)在这个随机模型里本来尾巴就重,§4.5 会看到它的影响。

4.4 各种 int8 方案:复现表 1 的现象

代码 02 演示 2(lm_head 保持全精度):

方案原模型 KLtop-1有离群 KLtop-1
int8 整张 × 整张(absmax)0.0008295.9%0.0226081.1%
int8 每 token × 整张0.0003397.7%0.0045493.0%
int8 每 token × 每通道(vector-wise)0.0002597.9%0.0042392.2%
LLM.int8()(vector-wise + 分解,α = 6)0.0002198.0%0.0002198.4%

模型一点没变,W8A8 却坏了:整张缩放的 KL 涨了约 28 倍,按 token 缩放和 vector-wise 也涨了十几倍。只有 LLM.int8() 回到了没有离群特征时的水平。坏的不是模型,是缩放因子被那 3 列”撑大”了。

4.5 为什么 vector-wise 管不住

代码 02 演示 3 取第 0 层 q_proj 的输入 XX(512 个 token × 256 维),只看 253 个”正常”维度的量化误差:

模型每 token 最大 |x| 的中位数每 token 缩放因子按 token 缩放的误差假想按列缩放的误差
原模型3.040.02400.70%0.72%
注入后24.790.19526.42%0.72%

按 token 缩放时,每一行的最大值几乎总落在离群列上,缩放因子变大约 8 倍,其余 253 个正常维度只能用很粗的格子量化,误差从 0.70% 涨到 6.4%。如果能每一列一个缩放因子,误差就回到 0.72%——但离群特征恰好沿 kk 方向(特征维度),按列缩放就是”缩放因子随 kk 变化”,违反 §4.1 的硬约束。这是 vector-wise 的天花板。

4.6 LLM.int8():混合精度分解

既然离群维度只有几个,就把它们整个拿出来用 16 位算:

Y≈∑h∈OXfp16h Wfp16h  +  S⋅∑h∉OXint8h Wint8h,Y \approx \sum_{h \in O} X^{h}_{\text{fp16}}\, W^{h}_{\text{fp16}} \;+\; S \cdot \sum_{h \notin O} X^{h}_{\text{int8}}\, W^{h}_{\text{int8}} ,

OO 是”至少有一个 ∣x∣≥α|x| \ge \alpha“的特征维度集合,论文取 α=6.0\alpha = 6.0。离群部分(约 0.1% 的维度)用 FP16 矩阵乘法,其余 99.9% 用 vector-wise int8。LLM.int8() = vector-wise 量化 + 混合精度分解。

阈值是一个旋钮。代码 02 演示 4(有离群特征的模型):

α走 16 位的维度占比q / up 输入的离群维度down 输入KL 散度
∞(纯 vector-wise)0.00%000.00423
120.65%300.00024
80.99%3110.00024
63.70%3760.00021
420.57%124500.00013
367.36%1777030.00004
  • α = 12 就恰好挑出注入的 3 个维度,16 位部分只占 0.65%,KL 已经回到 2.4×10−42.4 \times 10^{-4}。
  • α = 6 时,mlp_down 输入也有约 10% 的维度被挑出来:这是随机模型的 SwiGLU 输出尾巴重,不是系统性的离群特征。真实大模型的正常激活要小得多,论文报告 α = 6 时只有约 0.1% 的维度走 16 位。
  • α 再小,16 位的比例迅速上升,等于退回了 FP16 矩阵乘法。

代价(代码 02 演示 6,按论文”不超过 7 个离群维度”估算):16 位那部分的运算只占 0.06%–0.17%,离群行的 16 位拷贝只多 0.1%–0.3% 的字节。真正的代价在实现:每次矩阵乘法都要扫描激活找离群列、拆成两块、跑两个内核、再相加。论文实测 6.7B 以下的模型用 LLM.int8() 反而比 FP16 慢,175B 尺寸的大矩阵乘法约快 2 倍。LLM.int8() 的主要目标是省显存:让 OPT-175B / BLOOM 能装进一台消费级 GPU 服务器。

4.7 SmoothQuant:把难度挪给权重

另一个思路(Xiao et al. 2023):(X/s)(sW)=XW(X / s)(sW) = XW 对任意正向量 ss 都成立。取

sj=max⁡∣Xj∣αmax⁡∣Wj∣1−α,s_j = \frac{\max |X_j|^{\alpha}}{\max |W_j|^{1-\alpha}} ,

把 XX 的第 jj 列除以 sjs_j、WW 的第 jj 行乘以 sjs_j。激活的离群列被压扁,难度挪给了权重(权重本来就容易量化,§3)。1/s1/s 可以并进前一个 RMSNorm 的缩放系数,运行时零开销;之后就能用最快的”整张一个缩放因子”的静态 int8 内核。代码 02 演示 5(激活整张、权重每通道):

模型不平滑α = 0.25α = 0.5α = 0.75α = 1.0
原模型0.000740.000550.000410.000320.00028
注入后0.022470.000550.000410.000320.00028

平滑之后,有没有离群特征的结果完全相同。这不是巧合:我们注入离群特征的方式恰好是”γj\gamma_j 放大、WjW_j 缩小”,而 SmoothQuant 只看 max⁡∣Xj∣⋅max⁡∣Wj∣\max|X_j| \cdot \max|W_j| 这个乘积,注入不改变它——SmoothQuant 正好能把它”挪回去”。真实模型的离群特征不一定这么配合,论文在 OPT-175B 等模型上用 α ≈ 0.5 达到了和 FP16 相当的 W8A8 精度。α 太大时权重会出现新的离群行,所以通常取 0.5 左右。

4.8 后来的路:FP8 和块缩放

LLM.int8() 的”分解 + 两种精度”在硬件上不规整。后来更受欢迎的几条路:

  • SmoothQuant:离线平滑,单一 int8 内核。
  • FP8(E4M3):浮点的动态范围大,离群值不那么致命;H100 起有 FP8 Tensor Core。
  • 块缩放(MX、NVFP4):每 16–32 个数一个缩放因子,沿被求和的维度也分了块。离群值只影响自己那一块,而且硬件在每块的部分和上乘缩放因子——这正是绕开了 §4.1 那条硬约束的硬件方案(P3 §6.5)。

5. 精度在 roofline 上 · Precision on the roofline

代码:03_precision_on_the_roofline.py。全部是 roofline 下界;不计反量化指令、激活读写和内核开销。

5.1 decode:小批量只看权重字节

代码 03 演示 1:Llama-3.1-8B,H100,上下文 4096,一步 decode 的下界(括号里是相对 BF16 的加速):

方案B=1B = 1B=16B = 16B=64B = 64B=256B = 256
W16A16(BF16)4.64 ms7.04 ms14.74 ms45.51 ms
W8A16(INT8 仅权重)2.40 ms(1.93×)4.80 ms(1.47×)12.50 ms(1.18×)43.27 ms(1.05×)
W4A16(INT4 g128)1.32 ms(3.53×)3.72 ms(1.89×)11.41 ms(1.29×)42.18 ms(1.08×)
W8A8(INT8 或 FP8)2.40 ms(1.93×)4.80 ms(1.47×)12.50 ms(1.18×)43.27 ms(1.05×)
W8A8(FP8)+ KV FP82.32 ms(2.00×)3.52 ms(2.00×)7.37 ms(2.00×)22.75 ms(2.00×)
  • B=1B = 1 时比特数减半,时间就减半;计算用 BF16 还是 8 位无所谓(计算只占 0.4%)。小批量 decode 只看权重的字节。
  • 批变大以后,KV 的字节越来越多,权重量化的收益随之缩小:B=256B = 256 时,一步里 137 GB 是 KV、权重只有 4 GB,W4A16 只快 1.08 倍。要继续加速,就得量化 KV(最后一行每个 BB 都正好快 2 倍)。
  • W8A8 在 decode 里和 W8A16 完全一样:算力翻倍只在 compute-bound 时才用得上。

5.2 prefill:仅权重量化没用

代码 03 演示 2:Llama-3.1-8B,一个 2048 token 的提示:

芯片方案计算搬运TTFT 下界加速
H100W16A1632.19 ms4.56 ms32.19 ms1.00×
H100W4A1632.19 ms1.24 ms32.19 ms1.00×
H100W8A8(FP8)16.09 ms2.32 ms16.09 ms2.00×
Blackwell UltraW16A1612.74 ms1.91 ms12.74 ms1.00×
Blackwell UltraW8A8(FP8)6.37 ms0.97 ms6.37 ms2.00×
Blackwell UltraW4A4(NVFP4)+ KV FP82.12 ms0.54 ms2.12 ms6.00×

prefill 是 compute-bound 的:仅权重量化省了字节,可计算还是 BF16,一点都不快(真实内核里反量化还要额外的指令,可能更慢)。W8A8 让算力翻倍;NVFP4 在 Blackwell Ultra 上是 FP8 的 3 倍算力。“哪种量化”取决于你要加速哪个阶段:decode 看字节,prefill 看计算精度。

5.3 临界批大小:不对称的量化

decode 只看权重时(ctx → 0),临界批大小 B∗=B^* = 读权重的时间 / 每个 token 的计算时间 =I∗×(每个权重的字节/2)= I^* \times (\text{每个权重的字节} / 2)。代码 03 演示 3:

芯片方案屋脊点B∗B^*
H100W16A16295295
H100W8A16295148
H100W4A1629576
H100W8A8(INT8 / FP8)591295
Blackwell UltraW16A16312312
Blackwell UltraW8A8(FP8)625312
Blackwell UltraW4A4(NVFP4)1875527
  • W8A8:字节减半、算力翻倍,B∗B^* 不变(第 1 讲误区 2)。
  • 仅权重量化是不对称的:B∗B^* 按比特数下降。W4A16 在 B≈76B \approx 76 之后就”吃饱”了 BF16 的算力,再往上它的优势只剩 KV 以外的那点字节。
  • NVFP4 是反方向的不对称:算力涨得比字节省得多,B∗B^* 升到约 527。(第 1 讲 §3.6 用单个 8192×8192 矩阵乘法、把激活读写也算进去,得到 747;差别来自激活字节。)

5.4 容量:量化的第一个好处

代码 03 演示 4:Llama-3.1-70B 放在一块芯片上,上下文 32K,预留 10%:

芯片权重 / KV 比特权重最多几个序列一步下界tok/s/芯片
H100W16 / KV16141 GB权重放不下––
H100W8 / KV870.6 GB放不下一个序列––
H100W4 / KV836.4 GB620.31 ms295
H100W4 / KV436.4 GB1120.61 ms534
Blackwell UltraW16 / KV16141 GB1030.80 ms325
Blackwell UltraW8 / KV1670.6 GB1731.50 ms540
Blackwell UltraW8 / KV870.6 GB3532.18 ms1,088
Blackwell UltraW4 / KV836.4 GB4131.99 ms1,281
Blackwell UltraW4 / KV436.4 GB7332.04 ms2,279
  • 在 H100 上,8 位权重放得下,剩下的 1.5 GB 却连一个 32K 序列的 KV 都放不下(8 位 KV 也要 5.4 GB)。4 位权重才让一块 H100 能服务 70B 模型。
  • 在 Blackwell Ultra 上,KV 从 16 位降到 8 位,序列数翻倍(17 → 35);权重再从 8 位降到 4 位只多 6 个——权重已经不是显存的大头。
  • 一步的时间几乎不变(都在 memory-bound,批越大读的越多),但每芯片的吞吐随批大小上升。量化的第一个好处常常是容量,而不是速度:它让你能沿着第 1 讲 §4.5 的前沿走得更远。

5.5 怎样选方案

场景瓶颈有效的方案没用的方案
小批量 decode(交互式聊天)读权重W8A16、W4A16(越少越好)只把计算换成 8 位
大批量、长上下文 decode读 KV、显存容量KV 量化(FP8 / INT8 / INT4)+ 权重量化只量化权重
prefill、离线批处理计算W8A8(FP8 / INT8)、NVFP4仅权重量化
模型放不下容量权重量化—

6. 稀疏 · Sparsity

代码:04_sparsity.py

6.1 剪掉一半:精度

代码 04 演示 1,所有线性层剪掉 50%(按幅度),和量化比较:

做法比特 / 权重权重相对误差KL 散度top-1 一致
非结构化 50%16 + 下标26.7%0.0915162.9%
2:4 结构化9.036.4%0.1573752.5%
INT8 每通道(对照)8.050.7%0.0000799.0%
INT4 每 128 个一组(对照)4.1311.7%0.0196484.0%
  • 非结构化稀疏剪掉最小的一半,相对误差 26.7%:被剪掉的数加起来仍占约 7% 的”能量”。2:4 结构化(每连续 4 个里只留 2 个)限制更多,哪怕 4 个都很大也必须剪 2 个,误差更大(36.4%)。
  • 在这个随机模型上,50% 稀疏的偏差比 INT4 量化大得多,比特数却是 INT4 的两倍多。随机高斯权重没有”可以安全剪掉”的冗余;训练过的模型有,配合 SparseGPT、Wanda 这类按激活加权的剪枝和少量微调,2:4 的精度损失可以小得多——但通常仍比 8 位量化大。
  • 稀疏和量化可以叠加:2:4 的非零值本身也可以是 FP8 / INT8。

6.2 “非零在哪”也要存

代码 04 演示 2:

稀疏度稠密 FP16CSR(值 + 16 位列下标)位图(值 + 每位置 1 位)2:4
50%1616.009.009.00
75%168.005.00–
90%163.202.60–
99%160.321.16–

50% 稀疏用 CSR 格式存,每个权重平均还是 16 位:省下的值全被”下标”吃掉了。非结构化稀疏要到 75%–90% 以上才在存储上划算。2:4 的下标只要 2 位(4 个位置里选 2 个),每个权重平均 (2×16+2×2)/4=9(2 \times 16 + 2 \times 2)/4 = 9 位。规整的结构让硬件好算,也让元数据便宜。

6.3 2:4 在 roofline 上

NVIDIA 从 Ampere 开始的稀疏 Tensor Core 要求被求和的维度满足”每 4 个最多 2 个非零”,按 2 位下标去取对应的激活,跳过一半的乘法:矩阵乘法的峰值算力翻倍。代码 04 演示 3(Llama-3.1-8B,H100):

方案比特 / 权重decode B=1B = 1decode B=64B = 64prefill 2048
稠密 BF16164.64 ms14.74 ms32.19 ms
2:4 稀疏 BF1692.68 ms12.78 ms16.65 ms
稠密 W8A1682.40 ms12.50 ms32.19 ms
稠密 W4A164.1251.32 ms11.41 ms32.19 ms
2:4 稀疏 + FP851.56 ms11.66 ms8.88 ms
  • decode(memory-bound):2:4 只把字节省到 9/16,快约 1.7 倍;同样省字节,W8A16 快约 2 倍、W4A16 快约 3.5 倍。在 decode 里,量化比 2:4 稀疏划算。
  • prefill(compute-bound):2:4 让矩阵乘法快一倍,整体快约 1.9 倍(注意力核心不受益)。
  • 叠上 FP8:prefill 快约 3.6 倍。前提是精度扛得住。另外,硬件对稀疏的支持并不总是”翻倍”:Blackwell Ultra 的 NVFP4 稀疏算力只比稠密高 1.33 倍(第 1 讲练习 9)。

6.4 激活稀疏:只帮得了小批量

另一种稀疏不在权重里,而在激活里:ReLU 把负数变成 0,MLP 里对应的神经元这一步什么也不输出,W_down 的对应行就不用读。代码 04 演示 4 在小模型上看 MLP 门控的输入 g=xWgateg = xW_\text{gate}:

  • 如果用 ReLU,50.0% 的神经元输出恰好为 0;用 SiLU(Llama 的做法),几乎没有精确的 0。
  • 但一个批里任何一个 token 用到的行都得读:
批大小 BBW_down 必须读的行(本模型,ReLU)假想:每个 token 只激活 10%
150.2%10.0%
493.7%34.4%
899.6%57.0%
16100.0%81.5%
64100.0%99.9%

B=8B = 8 时已经要读 99.6% 的行。激活稀疏只帮得了小批量——和第 1 讲的”每个样本一个矩阵”一样,它和批处理对着干。想利用它,要先把 SiLU 换回 ReLU 再微调(“ReLU 化”),还要一个便宜的预测器提前知道哪些神经元会是 0(Deja Vu 等工作)。


7. 混合专家:条件计算 · Mixture of experts

代码:05_moe_switch.py

7.1 动机:参数和计算脱钩

§6.4 的激活稀疏是”碰巧”的。混合专家(mixture of experts, MoE)是设计出来、训练出来的稀疏:把 MLP 换成 EE 个”专家”(每个是一个 MLP),一个小小的路由器为每个 token 选一个或几个专家。参数量涨 EE 倍,每个 token 的运算量几乎不变。

Switch Transformer 论文表 9(每个序列的前向 FLOPs):

模型参数FLOPs / 序列
T5-Base(稠密)0.2 B124 B
Switch-Base(128 专家)7 B124 B
Switch-XXL395 B6.3 T
Switch-C(2048 专家)1571 B890 B

Switch-Base 和 T5-Base 每个序列的运算量相同,参数多 35 倍,论文报告预训练快 7 倍(达到同样质量)。今天的 DeepSeek-V3 有 671B 参数,每个 token 只激活 37B(256 个路由专家选 8 个,外加 1 个共享专家)。

7.2 Switch 路由:只选一个

路由器是一个线性层加 softmax:p(x)=softmax(xWr)p(x) = \text{softmax}(x W_r)。之前的 MoE(Shazeer et al. 2017)认为每个 token 至少要选 2 个专家(top-2),路由器才能学到东西。Switch 的简化是只选 1 个(top-1):

y=pi(x)⋅Ei(x),i=arg⁡max⁡jpj(x).y = p_i(x) \cdot E_i(x), \qquad i = \arg\max_j p_j(x) .

乘上门控值 pi(x)p_i(x),路由器才能得到梯度。论文说 top-1 的质量不差,而且路由计算更少、每个专家的批更小、通信更少。

代码 05 演示 1 实现了一个 Switch 层:把选了同一个专家的 token 收集到一起,做一次 [C,d]×[d,f][C, d] \times [d, f] 的矩阵乘法,结果和逐 token 计算的参照完全一样(差 10−1610^{-16})。按专家分组之后,MoE 仍然是大矩阵乘法——这是它能高效运行的关键。

7.3 专家容量与容量因子

硬件(尤其是 TPU)要求静态的张量形状,所以每个专家预先分配固定大小的缓冲区:

专家容量 C=这批的 token 数专家数×容量因子.\text{专家容量 } C = \frac{\text{这批的 token 数}}{\text{专家数}} \times \text{容量因子} .

超出容量的 token 被丢弃:这一层对它输出 0,它只靠残差连接传到下一层。代码 05 演示 2(4096 个 token、8 个专家):

数据最忙专家的负载容量因子被丢弃空着的槽位
话题大小均匀1.031.01.1%1.1%
话题大小均匀1.031.250.0%20.0%
话题大小 ∝ 1/k2.661.2519.1%35.3%
话题大小 ∝ 1/k2.662.08.3%54.1%
话题大小 ∝ 1/k2.664.00.0%75.0%

(“最忙专家的负载” = 最忙专家分到的比例 × EE,1.00 表示完全均衡。)负载均衡时,容量因子 1.25 就不丢 token,只浪费 20% 的槽位;论文也发现 1.0–1.25 效果最好,丢弃通常 < 1%。负载不均时,加大容量因子只是把”丢弃”换成”浪费”。必须让路由本身更均衡。

7.4 负载均衡损失

Switch 在训练损失里加一项:

lossaux=α⋅E⋅∑i=1Efi⋅Pi,\text{loss}_\text{aux} = \alpha \cdot E \cdot \sum_{i=1}^{E} f_i \cdot P_i ,

fif_i 是分到专家 ii 的 token 比例(来自 argmax,不可导),PiP_i 是专家 ii 的平均路由概率(可导)。均匀时 ∑fiPi=1/E\sum f_i P_i = 1/E,乘 EE 后等于 1,是最小值;全部给一个专家时等于 EE。梯度只通过 PP 起作用:fjf_j 大的专家,分给它的概率被往下压(代码里手推了梯度:∂/∂ztj∝ptj(fj−∑ifipti)\partial / \partial z_{tj} \propto p_{tj}(f_j - \sum_i f_i p_{ti}))。论文取 α=10−2\alpha = 10^{-2}(在 10−510^{-5} 到 10−110^{-1} 之间扫过)。

代码 05 演示 3 在”话题大小 ∝ 1/k”的数据上训练路由器(任务项 + α × 均衡项):

α最忙专家的负载丢弃(CF 1.25)丢弃(CF 2)送到”自己话题”专家的比例
02.9022.7%11.3%86.9%
0.012.9022.6%11.2%86.9%
0.32.8021.5%10.0%86.7%
12.2714.5%3.3%81.5%
31.979.3%0.0%77.8%
  • α 越大越均衡,丢弃越少;代价是更多 token 被送离”自己话题”的专家——均衡和专业化之间有拉锯。偏好弱的 token(“去哪都行”)先被挪走。
  • 在这个玩具里 α 要到 1 左右才起作用,而论文用 0.01。原因是我们的任务项直接对路由器做交叉熵,信号比真实训练强得多;真实模型里路由器只通过门控值间接得到任务梯度,很弱的均衡项就够了。
  • DeepSeek-V3 换了一种做法(无辅助损失的负载均衡):给每个专家的路由分数加一个偏置,过忙就调低、过闲就调高;偏置只影响”选谁”,不影响门控值,避免了均衡项对任务梯度的干扰。

7.5 Switch 论文里的”精度”

Switch 论文和本讲的另一半主题有一个有趣的交集:选择性精度(selective precision)。MoE 的路由是硬决策,训练不稳定;全用 bfloat16 训练时 Switch-Base 发散了,全用 float32 稳定但慢。论文的办法是只在路由器内部转成 float32(路由器里有 softmax 的指数运算,对精度敏感),其余仍用 bfloat16:质量和 float32 一样,速度和 bfloat16 一样。这是”不同部分用不同精度”的又一个例子(§3.4)。

论文还把稀疏大模型蒸馏成小的稠密模型,保留了约 30% 的质量增益——对部署来说,“大而稀疏”的模型也可以只是训练时的老师。

7.6 MoE 在 roofline 上:批被稀释了

每个 token 只用 kk 个专家,可一个批里的 BB 个 token 各选各的。均匀路由时,被碰到的专家数的期望是 E (1−(1−k/E)B)E\,(1 - (1 - k/E)^B)。代码 05 演示 4:DeepSeek-V3 的一个 MoE 层(256 个路由专家,每个 4400 万参数,加 1 个共享专家,top-8),FP8 权重和计算,H100:

批大小 BB碰到的专家每个专家平均分到的 token读权重强度读权重时间计算时间
18.00.03396 MB2.0118 µs0.4 µs
857.40.252.57 GB2.5768 µs3.2 µs
32163.31.007.24 GB3.52.16 ms12.8 µs
128251.64.0011.12 GB9.13.32 ms51.3 µs
2048256.064.0011.32 GB143.43.38 ms820 µs
8192256.0256.0011.32 GB573.83.38 ms3.28 ms
  • B=1B = 1 时只读 9 个专家:这是”每 token 只激活 37B”的好处。
  • 批一大,碰到的专家迅速增多(B=128B = 128 时几乎全部),读权重的字节涨了 28 倍;而每个专家平均只分到 Bk/EBk/E 个 token——每个专家的有效批被稀释了 E/k=32E/k = 32 倍。
  • 所有专家都被碰到之后,要 compute-bound 需要
B>I∗⋅E2k⋅(每个权重的字节)≈9,452 个 token,B > I^* \cdot \frac{E}{2k} \cdot (\text{每个权重的字节}) \approx 9{,}452 \text{ 个 token},

而稠密层只要约 295。How to Scale Your Model 第 4 章对 TPU v5e、int8 权重给出 B>120E/k=3,840B > 120 E/k = 3{,}840,同一个道理。decode 时根本凑不出这么大的批,所以 MoE 的 decode 几乎总是 memory-bound,而且要读的权重是同等激活参数的稠密模型的很多倍。

DeepSeek 的应对是把专家分散到很多块 GPU 上(专家并行):每块卡只放几个专家,它们分到的 token 来自整个集群的批,用 all-to-all 通信把 token 送过去。这是用通信换”每个专家的有效批”——第 4 讲的内容。


8. 现状(2025–2026)· State of the field

  • FP8 成为默认:H100 / H200 / Blackwell 上,FP8(W8A8,常配 FP8 KV)是大模型推理的标准配置;DeepSeek-V3 连训练都用 FP8(块缩放 + 定期提升到 FP32 累加,P3 §4.5)。
  • 4 位进入主流:NVFP4 / MXFP4 在 Blackwell 上有原生支持,SGLang、TensorRT-LLM、vLLM 都支持 4 位权重 + 8 / 4 位激活;OpenAI 的 gpt-oss 直接以 MXFP4 权重发布。仅权重的 INT4(GPTQ、AWQ)在消费级 GPU 和本地推理里很普遍。
  • KV cache 量化:FP8 KV 已是常规选项;更激进的 2–4 位 KV(KIVI 等)用于超长上下文。
  • MoE 成为大模型的主流结构:DeepSeek-V3/R1、Qwen3 的 MoE 版本、gpt-oss、Llama 4 等都是 MoE;专家数越来越多、越来越细(256 个专家、top-8),这让 §7.6 的”批被稀释”问题和专家并行的通信问题更突出。
  • 结构化稀疏用得不多:2:4 稀疏在 LLM 推理里没有像量化那样普及——精度代价和工程复杂度相对收益偏高(§6)。

9. 小结 · Summary

  1. 量化省的东西取决于你在 roofline 的哪一边:小批量 decode 看权重字节,大批量长上下文 decode 看 KV 字节和容量,prefill 看计算精度。仅权重量化帮 decode 不帮 prefill;W8A8 两边都帮;KV 量化帮大批量。量化的第一个好处常常是容量。
  2. 8 位权重几乎免费,4 位要靠细粒度:INT8 每通道的 KL 只有 7×10−57 \times 10^{-5};INT4 要每 32–128 个数一个缩放因子,块缩放格式(MXFP4、NVFP4)遵循同样的规律。lm_head 是最敏感的单个矩阵;误差近似可加,所以可以按敏感度分配比特。
  3. 激活难量化,因为离群特征:大模型里少数特征维度系统性地很大。整数矩阵乘法的缩放因子不能沿被求和的维度变化,所以按 token、按通道的缩放都管不住它们。LLM.int8() 把离群维度拿出来用 16 位算;SmoothQuant 把难度挪给权重;块缩放格式在硬件上沿被求和的维度也分了块。
  4. 稀疏要算元数据和硬件:非结构化 50% 稀疏用 CSR 存不省字节;2:4 每个权重 9 位、算力翻倍,帮 prefill 多于帮 decode;激活稀疏和批处理对着干。
  5. MoE 让参数和计算脱钩:Switch 的 top-1 路由、专家容量、负载均衡损失;但 decode 时一个批会碰到大部分专家,每个专家的有效批被稀释 E/kE/k 倍,需要上万个 token 的批才能 compute-bound——这把问题推给了专家并行和通信。

10. 常见误区 · Common pitfalls

  1. “FP8 和 INT8 都是 8 位,精度一样。” E4M3 只有 3 位尾数,对权重往往不如 INT8 准;它的优势是动态范围(§3.2)。
  2. “W4A16 让一切都快 4 倍。” 只有小批量 decode 接近 4 倍;prefill 一点不快,大批量长上下文时 KV 是大头(§5)。
  3. “每个权重 4 位。” 还要加上缩放因子:INT4 g128 是 4.125 位,NVFP4 是 4.5 位,2:4 + FP8 是 5 位。
  4. “按 token 量化激活就能对付离群值。” 离群值沿特征维度(被求和的维度),按 token 的缩放会被它撑大(§4.5)。
  5. “LLM.int8() 是为了加速。” 它主要是为了省显存;小模型上反而更慢(§4.6)。
  6. “量化误差在矩阵乘法里会互相抵消,可以放心。” 误差近似可加,lm_head 这种直接决定输出的矩阵尤其敏感(§3.4;P3 §6.6)。
  7. “50% 稀疏 = 省一半字节。” CSR 格式的下标会把省下的全吃掉;2:4 也只省到 56%(§6.2)。
  8. “MoE 每个 token 只用 37B 参数,所以推理和 37B 的稠密模型一样便宜。” 一个批会碰到大部分专家,读的权重远多于 37B;而且全部 671B 都要放在显存里(§7.6)。
  9. “负载均衡损失越大越好。” 太大会把 token 送离合适的专家,伤害质量(§7.4)。
  10. “量化模型的困惑度只差 1%,就没问题。” 困惑度对某些能力(长推理、代码、数学)的损伤不敏感,部署前要跑下游任务。

11. 练习 · Exercises

带 💻 的题可以用本讲的模块验证。参考答案在每题后面。

练习 1(对称量化)。把向量 [0.1,−0.5,2.0,−1.2][0.1, -0.5, 2.0, -1.2] 用对称 INT8(absmax)量化。缩放因子、整数值、反量化后的值、最大误差各是多少?

参考答案

s=2.0/127≈0.01575s = 2.0 / 127 \approx 0.01575;q=round(x/s)=[6,−32,127,−76]q = \text{round}(x/s) = [6, -32, 127, -76];反量化 [0.0945,−0.5039,2.0,−1.1969][0.0945, -0.5039, 2.0, -1.1969];最大误差约 0.0055(第一个数,相对误差 5.5%)。小的数相对误差最大——这就是为什么一个离群的大数会伤害整组的精度。

练习 2(缩放因子放在哪)。解释为什么 yij=∑kxikwkjy_{ij} = \sum_k x_{ik} w_{kj} 的整数实现里,缩放因子可以是 sis_i(每 token)和 sjs_j(每输出通道),却不能是 sks_k。块缩放格式(MX)是怎么绕开这个限制的?

参考答案

∑k(sixqik)(sjwqkj)=sixsjw∑kqikqkj\sum_k (s^x_i q_{ik})(s^w_j q_{kj}) = s^x_i s^w_j \sum_k q_{ik} q_{kj}:sis_i、sjs_j 可以提到求和外面,整数累加完再乘一次。如果缩放因子随 kk 变,每一项的系数都不同,只能逐项先乘缩放因子,整数累加就失去了意义。MX / NVFP4 把 kk 方向切成 32 / 16 个一块,每块内缩放因子不变:硬件先在块内做低精度点积,再把每块的部分和乘上该块的缩放因子、用高精度累加起来。代价是每 16–32 个乘加多一次缩放(硬件里专门做了这一步)。

练习 3(权重字节)💻。Llama-3.1-70B 用 INT4、每 128 个一组(16 位缩放因子)存,全部参数一共多少字节?能放进一块 H100(80 GB)吗?剩多少给 KV?

参考答案

70.55×109×4.125/8≈70.55 \times 10^9 \times 4.125 / 8 \approx 36.4 GB(代码 03 演示 4)。放得下,按预留 10% 算还剩 72−36.4≈35.672 - 36.4 \approx 35.6 GB 给 KV:8 位 KV 时约 6 个 32K 的序列。

练习 4(LLM.int8() 的代价)。13B 模型(d=5120d = 5120),按论文最多 7 个离群维度,MLP 上投影 W[d,4d]W[d, 4d] 的 16 位部分占多少 FLOPs?为什么实测上 6.7B 以下的模型反而变慢?

参考答案

7/5120≈7 / 5120 \approx 0.14%(代码 02 演示 6)。变慢不是因为 FLOPs,而是实现开销:每次矩阵乘法要扫描激活找离群列、做列的收集(gather)、跑两个内核再相加。小模型的矩阵乘法本身很快,这些固定开销占比就大。

练习 5(专家容量)。一批 1024 个 token、16 个专家、容量因子 1.25。每个专家的容量是多少?如果某个专家被 150 个 token 选中,丢几个?

参考答案

C=1024/16×1.25=80C = 1024 / 16 \times 1.25 = 80。丢弃 150−80=150 - 80 = 70 个 token(它们只走残差连接)。

练习 6(负载均衡损失)。4 个专家,f=P=[0.4,0.2,0.2,0.2]f = P = [0.4, 0.2, 0.2, 0.2]。E∑fiPiE \sum f_i P_i 是多少?完全均衡时是多少?

参考答案

4×(0.16+3×0.04)=4 \times (0.16 + 3 \times 0.04) = 1.12;均衡时 4×4×(1/4)2=14 \times 4 \times (1/4)^2 = 1。

练习 7(MoE 的临界批)。一个 Mixtral 式的 MoE 层:8 个专家、top-2,BF16 权重和计算,H100(屋脊点 295)。所有专家都被碰到后,批要多大才 compute-bound?换成 FP8 权重和计算呢?

参考答案

B>I∗⋅E⋅b/(2k)B > I^* \cdot E \cdot b / (2k),bb 是每个权重的字节。BF16:295×8×2/4=295 \times 8 \times 2 / 4 = 1,180;FP8:591×8×1/4≈591 \times 8 \times 1 / 4 \approx 1,182——W8A8 字节和算力同比例变化,临界批不变。专家少、kk 大的 MoE(E/k=4E/k = 4)比 DeepSeek-V3(E/k=32E/k = 32)容易喂饱得多。

练习 8(被碰到的专家)。64 个专家、top-2、均匀路由,一个 16 个 token 的批平均碰到几个专家?

参考答案

64×(1−(1−2/64)16)≈64 \times (1 - (1 - 2/64)^{16}) \approx 25.5 个。16 个 token 一共 32 次选择,因为有重复,只碰到约 25.5 个不同的专家。

练习 9(2:4 + 低精度)。2:4 稀疏的非零值用 INT4 存,每个权重平均几比特?和稠密 INT4 g128(4.125 位)比呢?

参考答案

(2×4+2×2)/4=(2 \times 4 + 2 \times 2) / 4 = 3 位(不算缩放因子),比稠密 INT4 少约 27%。但精度上是两种损失的叠加;在 decode 里多省的这 27% 字节,要和 2:4 的精度代价一起权衡。

练习 10(方案比较)💻。Llama-3.1-70B 切到 8 块 H100(理想切分,不计通信),上下文 8192。用 quant_lab.step_time(..., n_chips=8) 比较 BF16、W8A8(FP8)、W4A16、W4A16 + KV FP8 在 B=8B = 8 和 B=128B = 128 时的一步下界。

参考答案
方案B=8B = 8B=128B = 128
BF165.99 ms18.01 ms
W8A8(FP8)3.39 ms15.41 ms
W4A162.14 ms14.16 ms
W4A16 + KV FP81.74 ms7.75 ms

小批量时 W4A16 最快(权重字节最少);大批量时 KV 是大头,只有加上 KV 量化才能明显变快。全部是 memory-bound。


12. 延伸阅读 · Further reading

本讲阅读材料

  • T. Dettmers, M. Lewis, Y. Belkada, L. Zettlemoyer, LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale, NeurIPS 2022(arXiv:2208.07339)。
  • W. Fedus, B. Zoph, N. Shazeer, Switch Transformers: Scaling to Trillion Parameter Models with Simple and Efficient Sparsity, JMLR 2022(arXiv:2101.03961)。

本讲补充材料

  • G. Xiao et al., SmoothQuant: Accurate and Efficient Post-Training Quantization for Large Language Models, ICML 2023(arXiv:2211.10438)。
  • E. Frantar et al., GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers, ICLR 2023(arXiv:2210.17323)。
  • J. Lin et al., AWQ: Activation-aware Weight Quantization for LLM Compression and Acceleration, MLSys 2024(arXiv:2306.00978)。
  • A. Mishra et al., Accelerating Sparse Deep Neural Networks, 2021(arXiv:2104.08378)。—— NVIDIA 2:4 结构化稀疏。
  • E. Frantar, D. Alistarh, SparseGPT, ICML 2023;M. Sun et al., Wanda, ICLR 2024。—— 大模型的一次性剪枝。
  • Z. Liu et al., KIVI: A Tuning-Free Asymmetric 2bit Quantization for KV Cache, ICML 2024。
  • Z. Liu et al., Deja Vu: Contextual Sparsity for Efficient LLMs at Inference Time, ICML 2023。
  • DeepSeek-AI, DeepSeek-V3 Technical Report, 2024 —— 无辅助损失的负载均衡、FP8 训练。
  • J. Austin et al., How to Scale Your Model 第 4 章 —— MoE 的临界批大小。

本仓库的相关讲义


13. 下一讲预告 · Next lecture

第 4 讲(Mark Horowitz)讲分布式推理,阅读材料是 How to Scale Your Model 的第 2、3、7 章和 Pope et al. 的 Efficiently Scaling Transformer Inference。到目前为止我们一直假设”一块芯片”(或者”8 块芯片理想切分、不计通信”)。可本讲已经看到:70B 的 BF16 模型放不进一块 H100,MoE 的专家要分散到很多块 GPU 上。第 4 讲要回答:怎样把矩阵切到很多块芯片上,每种切法要多少通信,延迟、吞吐和成本怎样取舍? 我们会用前置讲 P5 的虚拟集群,真的把第 2 讲的 Transformer 切到几块”虚拟 GPU”上做张量并行,并验证输出和单卡完全一致。

← 返回课程目录