对应 Maps to:CMU 15-779(Fall 2025,Zhihao Jia),Lecture 17 · Fri 10/31 · LLMs: Serving (Speculative Decoding)(slides 标题页写的是 Lecture 16,课表上是第 17 次课)。 必读论文:Y. Leviathan, M. Kalman, Y. Matias, Fast Inference from Transformers via Speculative Decoding(ICML 2023);X. Miao et al., SpecInfer: Accelerating Large Language Model Serving with Tree-based Speculative Inference and Verification(ASPLOS 2024)。 依据说明:本讲以原课 slides
16-LLM-serving-part2.pdf为主线:
- 自回归解码;LLM 又慢又贵(GPT-3 175B 至少 10 块 A100-40GB、256 个 token 约 20 秒、每个请求 3 GB KV);
- 增量解码的问题(LLaMA-2-70B 在 4 块 A100 上:算力利用率 2%、显存带宽 76%);
- GPT-3 家族的大小模型权衡表;推测解码与验证;
- SpecInfer:工作流程、学习型推测器、集体 boost-tuning、令牌树合并、令牌树验证、按序列解码 vs 树注意力、树形并行解码、随机解码与朴素采样、推测采样、分布式服务、实验结果、三个开放问题;
- Medusa、EAGLE / EAGLE-2;不用模型的推测:Prompt Lookup、SuffixDecoding、Lookahead Decoding。
论文数字只做转述。SpecInfer 的数字以 ASPLOS 版本为准(1.5–2.8 倍 / 2.6–3.5 倍);slides 上的 1.3–2.4 倍来自 arXiv 第二版。随机验证的证明在 SpecInfer 的 arXiv 第二版附录 A 里(ASPLOS 版本写的是”见 [28]”)。
specdec.py(玩具语言模型、推测采样的精确分布、多草稿验证、令牌树、树注意力)和全部实验都是本讲义自己设计的;目标模型和草稿模型是第 5 讲的 LikesWorld 小 Transformer,不是真实的 LLM。 配套代码 Code:code/lecture17/(5 个脚本 + 一个共享模块;需要 numpy 和 torch;最长的约 35 秒) 预计阅读时间:3 小时(含运行代码)
0. 本讲导航 · How to read this lecture
0.1 学习目标
学完这一讲,你应该能够:
- 用 roofline 解释:为什么解码一步时,让大模型多处理几个 token 几乎不增加时间;以及什么时候这不再成立(大批、短上下文)。
- 写出推测采样的接受规则和剩余分布,证明输出严格服从大模型的分布;说出三种常见的错误写法错在哪里。
- 用 α、γ、c 估算每步产生的 token 数、墙钟加速比和运算量的增加,并解释公式的假设什么时候不成立。
- 解释 SpecInfer 的令牌树:合并式与扩展式构造、深度优先线性化、拓扑感知的因果掩码;说明树注意力为什么是精确的。
- 比较朴素采样和多步推测采样(MSS),并说清楚 MSS 无偏的前提。
- 说出 Medusa、EAGLE、Prompt Lookup、SuffixDecoding、Lookahead Decoding 各自的草稿来源,以及它们适合什么样的负载。
0.2 先修与本讲范围
| 默认你已经会 | 本讲只给直觉,后面再展开 |
|---|---|
| 第 16 讲:预填充 / 解码、KV 缓存、roofline 时间模型 | 推测解码和连续批处理、分页 KV 的结合(§6 简述) |
| 第 5 讲:注意力、因果掩码、RoPE;LikesWorld 小 Transformer | 草稿模型的蒸馏、对齐训练(§5、延伸阅读) |
| 概率:离散分布、总变差距离(会在 §2 复习) | Medusa / EAGLE 的头部结构细节 |
| 第 7 讲:kernel 启动开销、mega-kernel(§2.5 会用到) |
0.3 本讲地图
§1 为什么"验证"几乎不要钱 ← 代码 01 演示 1
§2 推测解码:小模型猜、大模型验 ← 代码 01 演示 2–5、代码 02
§3 在真的模型上:哪些 token 好猜 ← 代码 03
§4 SpecInfer:猜一棵树 ← 代码 04
§5 后来的方法:Medusa、EAGLE、不用模型的推测 ← 代码 05
§6 把它们放在一起
§7 论文精读:Leviathan et al.;SpecInfer
§8 小结 §9 常见误区 §10 练习 §11 延伸阅读 §12 下一讲预告
0.4 关于 specdec.py
- 玩具模型:词表 5 个 token,目标模型 p 的下一个 token 分布只看最近 2 个 token(一张确定的随机表),草稿模型 q = softmax(log p + 噪声)。所有概率已知,所以可以精确枚举推测解码的每一个分支,得到输出的精确分布,再和目标模型比较。
- 小 Transformer:第 5 讲的 LikesWorld 模型(8.5 万参数,答案准确率 100%)当目标;另训练两个草稿:1 层 d=32(1.2 万参数)和 2 层 d=32(2.2 万参数)。CPU 上的墙钟时间没有意义,所以只统计”大模型的串行调用次数”。
- 时间模型:沿用第 16 讲的 roofline(A100-40GB),新增
verify_time:一次迭代里每个请求处理 k 个 token。
1. 为什么”验证”几乎不要钱 · Why verification is (almost) free
1.1 回顾:增量解码的问题
第 16 讲说过,自回归解码一次只生成一个 token;生成每个 token 都要把全部权重从显存读一遍。slides 给的实测:LLaMA-2-70B 在 4 块 A100 上解码,算力利用率 2%,显存带宽利用率 76%。两个问题:
- 一个请求内部的并行度很低(一次一个 token),GPU 的算力大部分闲着;
- 每个 token 都要读一遍全部参数,速度被显存带宽卡住。
第 16 讲的办法是批处理:把很多请求的 token 凑在一起,读一次权重摊到更多 token 上。它提高的是吞吐;对单个请求来说,一步仍然只出一个 token,延迟没变。
1.2 一次处理 k 个 token 要多久(代码 01 演示 1)
假设一个请求在一次迭代里不是处理 1 个新 token,而是 k 个(它们的内容都已知,只是要算出每个位置的输出)。按 roofline 模型,OPT-13B / A100-40GB、上下文 512:
| 批大小 | k = 1 | k = 4 | k = 8 | k = 16 | k = 32 |
|---|---|---|---|---|---|
| 1 | 18.49 ms | 18.49 | 18.49 | 18.50 | 18.51 |
| 8 | 20.38 | 20.39 | 20.41 | 20.44 | 37.63 |
| 32 | 26.85 | 26.90 | 37.63 | 73.76 | 146.02 |
批 1 时,k 从 1 到 32 时间几乎不变:读权重的时间一点没少,多出来的计算只是让本来闲着的算力动起来。这就是推测解码的物理基础:如果有人能事先”猜”出接下来的几个 token,大模型可以用几乎相同的时间一次验证它们。
但这不是无条件的:批越大、算术强度越高,越早碰到算力的上限(批 32 时 k = 8 就从 26.9 跳到 37.6 ms)。推测解码本质上是用算力换延迟。
1.3 大模型和小模型的权衡
slides 用 GPT-3 家族的数字说明大小模型的权衡(GPT-3 论文里的数据):
| 参数 | 175B | 13B | 2.7B | 760M | 125M |
|---|---|---|---|---|---|
| TriviaQA | 71.2 | 57.5 | 42.3 | 26.5 | 6.96 |
| 延迟(256 token) | 20 s | 7.6 s | 2.7 s | 1.1 s | 0.3 s |
| A100 数量 | 10 | 1 | 1 | 1 | 1 |
大模型准确但又慢又贵,小模型便宜但不准。推测解码想要两者兼得:用小模型的速度,得到大模型的输出。
2. 推测解码:小模型猜、大模型验 · Speculative decoding
2.1 直觉:处理器的推测执行
处理器里的分支预测:遇到条件跳转,不等条件算出来,先按预测的方向往下执行;猜对了就赚到了时间,猜错了就把结果丢掉重来。推测解码把同样的思路搬到语言模型上:
- 推测:一个便宜的小模型(草稿模型,draft model;SpecInfer 叫 SSM,small speculative model)自回归地猜 γ 个 token;
- 验证:大模型(目标模型)用一次前向同时算出这 γ+1 个位置上”它自己会怎么生成”;
- 接受:从第一个开始,一致的就接受,遇到第一个不一致的就用大模型自己的结果替换它,后面的草稿全部丢掉。
slides 的例子:提示词 “[Accelerating LLM requires machine]“,小模型猜 “learning systems design”,大模型验证后接受 “learning systems”,把 “design” 换成自己的 “optimization”——一次调用大模型生成了 3 个 token。
每次调用大模型至少产生 1 个 token(最坏情况等于普通解码),最多产生 γ+1 个(草稿全对时,最后一个位置的输出白送一个”奖励 token”)。
2.2 贪心解码时的验证
贪心解码(每一步取概率最大的 token)时,验证很简单:草稿 token 等于大模型在这个位置的 argmax 就接受。输出和大模型自己贪心生成的结果逐字相同,因为被接受的 token 就是大模型自己会选的 token。代码 03 演示 2 在 200 个提示词上验证了这一点:两个草稿模型、四种 γ,全部 200/200 逐字相同。
2.3 随机采样时的验证:推测采样(代码 02 演示 1–3)
随机采样(temperature > 0)时,大模型每次的输出本来就是随机的,“草稿对不对”不再有意义。我们要的是:输出的分布和大模型自己采样完全相同。
推测采样(speculative sampling):设大模型在这个位置的分布是 p,草稿模型是 q。
- 从 q 采一个 x;
- 如果 q(x) ≤ p(x),接受;
- 如果 q(x) > p(x),以 p(x)/q(x) 的概率接受;
- 被拒绝时,从剩余分布 p’(x) = norm(max(0, p(x) − q(x))) 重新采一个。
为什么输出服从 p?对任意 token x:
- 草稿采到 x 并接受的概率:q(x) · min(1, p(x)/q(x)) = min(p(x), q(x));
- 拒绝的总概率是 1 − Σ_y min(p(y), q(y)),而 Σ_y max(0, p(y) − q(y)) 也正好等于它(因为 p、q 都归一),所以拒绝后再采到 x 的概率是 max(0, p(x) − q(x));
- 两部分相加:min(p, q) + max(0, p − q) = p(x)。
直觉:q 偏低的 token(q < p)只要草稿采到就一定接受,但还差 p − q;q 偏高的 token 被草稿采得太频繁,按 p/q 的比例丢掉一部分。丢掉的总概率正好拿去补前一类的缺口——剩余分布只在 p > q 的地方有质量。
接受率:一个位置上接受的概率是 β = Σ min(p, q) = 1 − TV(p, q)(论文定理 3.5,TV 是总变差距离)。也就是两个分布重叠部分的面积。代码 02 演示 1 的例子:p 和 q 的 α = 0.851,输出与 p 的最大差 1.4e-17。
精确检验(代码 02 演示 2):在玩具模型上精确枚举推测解码的所有分支,得到 token 流前 4 个 token 的分布(625 种序列),和目标模型逐个采样比较:γ = 1..4 时 TV 距离都是 1e-16(浮点舍入)。这不是”差不多”,而是同一个分布。
三种常见的错误写法(γ = 3):
| 错误 | TV 距离 |
|---|---|
| 拒绝后直接从 p 重新采样(忘了减掉 q) | 0.206 |
| 不验证,草稿说什么就是什么 | 0.483 |
| 随机采样时还用贪心规则(草稿 = argmax p 才接受,否则从 p 采样) | 0.514 |
第一种最隐蔽:单看一个位置只是”有点偏”,小规模测试不容易发现。只靠蒙特卡洛也只能说”看不出差别”:代码 02 演示 3 随机跑 2 万次,推测解码的经验分布和精确分布的 TV 是 0.023,和”同一个模型换个种子”的差距(0.028)同量级。精确枚举和蒙特卡洛配合,是检验采样代码的好办法。
2.4 一次猜 γ 个:算法 1
论文的算法 1(SpeculativeDecodingStep):
- 草稿模型自回归地采样 γ 个 token x₁ … x_γ,记下每一步的分布 q₁ … q_γ;
- 大模型一次前向算出 γ+1 个分布 p₁ … p_{γ+1}(输入是前缀 + 全部草稿,因果掩码保证第 i 个位置只看前 i−1 个草稿);
- 从第一个草稿开始做推测采样:接受就继续,遇到第一个拒绝的位置 n+1,从 norm(max(0, p_{n+1} − q_{n+1})) 采一个 token 替换它,结束;
- 如果 γ 个全部接受,再从 p_{γ+1} 采一个奖励 token。
返回 n 个草稿 + 1 个大模型的 token。
2.5 能快多少(代码 01 演示 2–5)
每步产生的 token 数:假设每个位置的接受率独立同分布、均值为 α,一步产生的 token 数是一个”带上限的几何分布”,期望是
E = (1 − α^{γ+1}) / (1 − α) (论文公式 1)
E 有上限 1/(1−α):α = 0.8 时每步最多 5 个 token,γ 再长也没用——第 i 个草稿要被接受,前面 i−1 个都要先被接受,概率 α^i 衰减得很快。
墙钟加速比:设 c = 草稿一步的时间 / 大模型一步的时间。一步要花 γ 次草稿 + 1 次大模型 = (γc + 1)T,产生 E 个 token:
加速比 = (1 − α^{γ+1}) / ((1 − α)(γc + 1)) (定理 3.8)
只要 α > c,取 γ = 1 就至少有 (1+α)/(1+c) 倍(推论 3.9)。
运算量:被拒绝的草稿白算了,总运算量变成原来的 (1 − α)(γĉ + γ + 1)/(1 − α^{γ+1}) 倍(定理 3.11,ĉ 是两个模型每个 token 的运算量之比)。推测解码不省运算,它省的是读权重的次数,也就是大模型串行运行的次数。
代码 01 复现了论文表 1 的 6 行(c = ĉ = 0),全部一致,例如 α = 0.8、γ = 5:运算量 1.63 倍,速度 3.69 倍。
最优 γ(论文图 3):α 越高、c 越小,最优 γ 越大。α = 0.8 时,c 从 0.01 升到 0.1,最优 γ 从 14 掉到 6,加速比从 4.2 倍掉到 2.5 倍。
c 在 GPU 上是多少(代码 01 演示 4):OPT-125M 的参数只有 OPT-13B 的 1%,但按我们的时间模型 c = 0.09——小模型一步只要读 0.25 GB 权重(0.16 ms),剩下 1.5 ms 是固定开销(kernel 启动、采样等)。固定开销压到 0.2 ms(CUDA Graph、mega-kernel,第 7 讲)时 c 降到 0.02。论文在 TPU 上的实验里 c 总小于 0.05。这也解释了后来的 Medusa、EAGLE 为什么不再用独立的小模型(§5)。
批大小和上下文长度(代码 01 演示 5,α = 0.8,每格取最优 γ):
| 上下文 | 批 1 | 批 16 | 批 64 | 批 128 | 批 256 |
|---|---|---|---|---|---|
| 64 | 2.55× | 2.56× | 1.65× | 1.04× | 1.00×(不推测) |
| 512 | 2.56× | 2.64× | 2.33× | 2.07× | 1.78× |
| 2048 | 2.58× | 2.81× | 3.01× | 3.07× | 3.06× |
- 上下文短时,批一大,验证 γ+1 个 token 就变成计算受限,收益消失。这和 SpecInfer 的观察一致:“批越大,相对现有系统的提升越小”(它的实验 prompt 短、最多生成 128 个 token)。
- 上下文长时反过来:每个请求每一步都要读自己的整段 KV,这部分不能被批摊薄;而验证 γ+1 个 token 只读一遍 KV,推测解码同时摊薄了读权重和读 KV,大批时反而更划算。
- 所以准确的说法不是”推测解码只适合小批”,而是”只适合访存受限的时候”——批大小、上下文长度都会改变算术强度。(这里只看时间模型的趋势,大批 × 长上下文的 KV 远超一块卡的显存。)
2.6 公式的假设,以及 α 取决于什么(代码 02 演示 4–5)
独立同分布的假设:玩具模型上,各上下文的 β 从 0.53 到 0.93 不等,但相邻位置的 β 几乎互不相关,实测每步 token 数和公式差不到 0.01。真实语言里不是这样(§3.3)。
α 取决于两个分布有多像:草稿越像目标,α 越高。论文表 3 的经验:比目标小两个数量级左右的草稿模型,α 在 0.5–0.9 之间;连一元 / 二元语法这种”零成本”草稿都有不可忽略的 α(英译德 0.2,c ≈ 0 时加速 1.25 倍)。
温度的影响取决于分布的形状:
| 分布形状 | T=0.25 | T=0.5 | T=1.0 | T=1.5 | 贪心 |
|---|---|---|---|---|---|
| 玩具(词表 5,无明显的峰),噪声 0.8 | 0.633 | 0.683 | 0.767 | 0.828 | 0.600 |
| 像 LLM(词表 1000,有峰 + 长尾),噪声 0.6 | 0.953 | 0.936 | 0.883 | 0.859 | 0.957 |
| 像 LLM,且草稿更平(τ = 1.5) | 0.894 | 0.817 | 0.526 | 0.645 | 0.923 |
- 没有明显的峰时,升温让两个分布都趋于均匀,重叠变大,α 随温度上升;
- 有一个很有把握的 token 加长尾时(真实 LLM 的典型形状),两个模型的 argmax 大多相同,分歧主要在尾巴上——升温让尾巴变重,α 下降,贪心最高;
- 小模型通常比大模型”更没把握”(分布更平),这时 α 掉得更厉害;
- 论文表 3 里所有模型都是 argmax 的 α 高于 T = 1(T5-XXL 配 T5-small:英译德 0.75 vs 0.62),对应的是后两种情况。我们的第一版玩具得到了相反的趋势——用玩具验证结论时,要注意它像不像真实情况。
3. 在真的模型上:哪些 token 好猜 · Speculation on a real (tiny) model
论文的出发点之一是:“困难的语言建模任务里,常常包含能被更高效的模型很好地近似的简单子任务”。LikesWorld 正好有这种结构:
<bos> dan likes jam . fay likes nuts . … q fay nuts . q dan jam . <eos>
└─ 事实:人名、物品随机 ─┘ └─ 问答:答案要回头查 ─┘
3.1 按 token 种类的接受率(代码 03 演示 1)
| 被预测的 token | 占比 | 草稿 A:β(T=1) / argmax 相同 | 草稿 B:β(T=1) / argmax 相同 |
|---|---|---|---|
| 套话 likes、. | 40% | 1.000 / 1.000 | 1.000 / 1.000 |
| 事实里的人名 | 16% | 0.930 / 0.535 | 0.980 / 0.579 |
| 事实里的物品 | 16% | 0.965 / 0.134 | 0.978 / 0.242 |
| 被问的人名 | 8% | 0.844 / 0.263 | 0.962 / 0.456 |
| 答案 | 8% | 0.125 / 0.140 | 0.380 / 0.444 |
(草稿 A:1 层,答案准确率 14%;草稿 B:2 层,44%;目标模型 100%。)
三个发现:
- 套话(由语法决定的 token)连 1 层的小模型都必中;
- 本来就随机的 token(事实里的人名、物品):目标模型自己也只能给出接近均匀的分布,草稿也给出接近均匀的分布,采样时 β 有 0.93–0.98;但贪心时要求两个”几乎一样高”的概率里挑出同一个最大值,argmax 相同的只有 13%–58%。接受率衡量的是两个分布像不像,不是草稿猜得准不准;
- 真正拉开草稿差距的是需要推理的 token(答案要”回头查”):草稿 A 不会查(β 0.13),草稿 B 学会了一部分(0.38)。
3.2 贪心:逐字相同,调用次数减少(代码 03 演示 2)
200 个提示词(只给事实,让模型自己生成问答直到 ),目标模型逐个生成共 1956 个 token:
| 草稿 | γ | 逐字相同 | 目标调用 | token / 调用 | 实测 α | 公式 E |
|---|---|---|---|---|---|---|
| A | 4 | 200/200 | 865 | 2.26 | 0.598 | 2.30 |
| A | 6 | 200/200 | 855 | 2.29 | 0.600 | 2.43 |
| B | 4 | 200/200 | 696 | 2.81 | 0.731 | 2.94 |
| B | 6 | 200/200 | 601 | 3.25 | 0.751 | 3.47 |
草稿 A 的 γ 从 4 加到 6 几乎没用:每隔 4 个 token 就有一个它猜不中的答案。
3.3 公式为什么高估(代码 03 演示 2)
实测的 token / 调用比公式 E(用实测平均 α 代入)低,γ 越大差得越多。原因:难猜的位置(被问的人名、答案)周期性地出现(每 4 个 token 一次),几乎每一步走到那里就停了。独立同分布的公式把这些”必经的难点”平均掉,高估了一口气接受很长一串的概率。这也是 EAGLE-2 等工作要”根据上下文动态决定猜多少”的原因(§5.2)。
3.4 采样:同分布,而且 α 更高(代码 03 演示 3)
200 个提示词 × 3 次,T = 1,提示词 = 事实 + “q 第一个被问的人”:
| 方式 | 第一个答案正确 | 问题数分布的 TV | 目标调用 |
|---|---|---|---|
| 逐个采样(种子 1) | 99.8% | 0(参照) | 4170 |
| 逐个采样(种子 2) | 100% | 0.023 | 4180 |
| 推测采样(草稿 B,γ = 4) | 100% | 0.012 | 1583 |
推测采样的统计量落在样本噪声之内。同一批提示词上,采样的 α(0.73)高于贪心(0.65)——因为这个世界里很多 token 是”在几个人名里随便选一个”(§3.1)。这和 §2.6”像 LLM 的分布贪心 α 更高”并不矛盾:结论取决于分歧发生在 argmax 上还是在长尾上。
4. SpecInfer:猜一棵树 · Tree-based speculative inference
4.1 为什么要树
一条草稿链的问题:第 i 个 token 要被接受,前 i−1 个都要猜对,概率指数衰减;而且小模型和大模型的容量差距(通常小 100–1000 倍)限制了单条序列的对齐程度。
SpecInfer 的观察(论文表 1):小模型的 top-1 和大模型不一致时,大模型选的 token 通常在小模型的前几名里。LLaMA-7B 配 LLaMA-68M:用 top-5 代替 top-1,验证成功率在贪心时从 70% 提到 89%(CIP 数据集),随机解码时从 57% 提到 97%。
于是:同一个位置多猜几个候选,组织成一棵令牌树,大模型一次验证整棵树。slides 的说法:不把大模型当”增量解码器”,而当”并行的令牌树验证器”。
4.2 构造令牌树(代码 04 演示 1)
令牌树(定义 3.1):每个节点是一个 token,从根到某个节点的路径就是一条候选序列。根是最后一个已验证的 token。
两种构造方法:
- 合并式(merge-based,定义 3.2):多个小模型各猜一条序列,合并成一棵树,共享前缀只存一次。slides 的例子:
SSM 0: [machine] learning algorithm SSM 1: [machine] learning system design SSM 2: [machine] translation model → machine ─┬─ learning ─┬─ algorithm │ └─ system ── design └─ translation ── model - 扩展式(expansion-based):一个小模型在每一层取前 k_i 个 token,按预设的扩展配置 ⟨k₁, k₂, …⟩ 展开。论文图 3 的 ⟨2,2,1⟩ 得到 4 条序列;实验用的是 ⟨1,1,3,1,1,1,1,1⟩(只在第 3 层分叉)。
集体 boost-tuning(合并式的小模型怎么来):用 LLM 在通用语料上生成输出,先把一个小模型微调到最好,标出它和 LLM 输出一致的样本;把这些样本过滤掉,用剩下的去微调下一个小模型……像 AdaBoost 一样,让一池小模型合起来覆盖尽可能多的 LLM 输出(slides 的图:LLaMA-7B + LLaMA-160M、Alpaca、A10 上,1 → 2 → 4 个小模型,每步验证的 token 数一致提高)。小模型用数据并行放在不同的 GPU 上,多用几个不增加推测的延迟和单卡的显存。
4.3 树注意力:一次前向验证整棵树(代码 04 演示 1–2)
树注意力(定义 4.1):对树上每个节点,它的输出定义为”把根到它的路径当成普通序列做注意力”的结果。难点是怎样一次算出所有节点:
- 按序列解码(现有系统的做法):把树拆成若干条序列,每条一个 kernel、一份 KV 缓存。问题:共享前缀的部分重复计算(slides 例子里 “learning” 算两遍,7 个 token 而不是 6 个);序列越多 KV 越多、kernel 启动越多。
- SpecInfer 的树形并行解码:
- 深度优先把树排成一行放进 KV 缓存(祖先总在前面);
- 拓扑感知的因果掩码:每个 token 只能看已验证的前缀 + 自己在树里的祖先;
- 所有 token 在一个 kernel 里算完。
slides 例子的掩码(✓ = 行可以看列;已验证的前缀所有行都能看,没画):
machine learning algorithm system design translation model
machine ✓
learning ✓ ✓
algorithm ✓ ✓ ✓
system ✓ ✓ ✓
design ✓ ✓ ✓ ✓
translation ✓ ✓
model ✓ ✓ ✓
“translation” 排在 “learning”、“system” 后面,却看不到它们。实现上还有一处要改:位置编码按深度给,同一深度的节点位置编号相同。
代码 04 在小 Transformer 上实现了这两处改动(tree_forward),和”每条路径单独前向”比较:30 个提示词上 logits 的最大差 1.9e-5(float32 的舍入误差);⟨2,2,1⟩ 的树比按序列单独算少 1.2 倍的计算。论文图 11:小批时和按序列解码相当,大批时最多快 1.8 倍。
4.4 贪心验证与树的形状(代码 04 演示 3)
VerifyGreedy(算法 2):从根往下走,大模型在当前节点的 argmax 是某个孩子就接受并走过去,否则把这个 argmax 当作大模型自己的 token,结束。
100 个提示词,深度 4:
| 扩展配置 | 草稿 A:token / 调用 | 草稿 B:token / 调用 | 每次验证的节点 |
|---|---|---|---|
| ⟨1,1,1,1⟩(链) | 2.26 | 2.83 | 5 |
| ⟨3,1,1,1⟩ | 2.47 | 3.19 | 13 |
| ⟨1,3,1,1⟩ | 2.55 | 3.20 | 11 |
| ⟨5,1,1,1⟩ | 2.71 | 3.30 | 21 |
| ⟨3,3,1,1⟩ | 2.75 | 3.47 | 31 |
- 树让每次调用多确定 22%–23% 的 token(论文表 2:贪心时调用次数少 1.2–1.5 倍),所有配置的输出都和大模型自己贪心逐字相同;
- 代价不成比例:验证的节点从 5 个涨到 31 个。小批时这些计算是”免费”的,大批时就不是了——论文图 10:批大小 ≥ 4 时最优树宽只有 2–3;
- 加宽哪一层更划算取决于难猜的 token 出现在哪里(这里 ⟨3,1,1,1⟩ 和 ⟨1,3,1,1⟩ 差不多)。slides 列出的开放问题之一就是静态 vs 动态的树:静态的树可以离线优化,动态的树能针对具体的提示词。
4.5 随机验证:朴素采样 vs 多步推测采样(代码 04 演示 4–5)
朴素采样(naive sampling):直接从大模型采一个 token,看它在不在树里。无偏,但浪费了草稿的信息。
slides 的例子:4 个 token;LLM 是均匀分布;SSM 1 一定给 t1,SSM 2 是均匀分布、这一次给了 t2。
- 朴素采样:LLM 采到 t1 或 t2 的概率 = 50%;
- 更好的做法:先以 min(1, 0.25/1.0) = 25% 接受 t1;拒绝后剩余分布是 (0, ⅓, ⅓, ⅓);再以 min(1, ⅓ ÷ ¼) = 1 接受 t2 → 100%。
- slides 的关键句:朴素采样忽略了 P(·|SSM) 和 P(·|LLM) 之间的相关性。
这就是 SpecInfer 的多步推测采样(multi-step speculative sampling,MSS,算法 2 的 VerifyStochastic):在一个节点上依次对每个孩子做一次推测采样,拒绝就把剩余分布 norm(max(0, r − q_j)) 传给下一个孩子;所有孩子都被拒绝,就从最后的剩余分布采样。论文证明:
- 定理 4.2:MSS 的输出分布和 LLM 逐个采样相同;
- 定理 4.3:MSS 的拒绝概率不超过朴素采样。
代码 04 演示 4 用精确枚举验证(玩具模型,词表 5):
| k 个草稿 | 朴素采样 | MSS(独立采样) | MSS(不放回采样) | 取前 k 个 + 同一公式 |
|---|---|---|---|---|
| 1 | 0.639(无偏) | 0.921(无偏) | 0.921(无偏) | 0.996(TV 0.205) |
| 2 | 0.756 | 0.952 | 0.928 | 0.996(TV 0.205) |
| 4 | 0.846 | 0.957 | 0.995 | 1.000(TV 0.205) |
(数字是”接受了某个草稿”的概率。)
一个容易忽略的前提:MSS 的证明(SpecInfer arXiv 第二版附录 A)里,第 j 轮接受 x 的概率写成 min(q_j(x), …)——也就是”第 j 个草稿是从 q_j 独立采样出来的”乘以接受概率。如果树的孩子是”草稿模型概率最大的 k 个”(确定地取),再套同一个公式,输出分布就偏了(最后一列,k = 1 时就偏)。看起来接受率接近 100%,其实只是让输出更像草稿的前几名。修正方法:孩子按不放回采样产生,第 j 轮用”去掉已采过的 token 再归一化”的 q_j(第三列,无偏)。所以:贪心验证可以放心用”前 k 个”建树;随机验证时树的孩子要靠采样产生。(注意 slides 的例子里,“这一次给了 t2”的条件分布并不均匀——无偏性是对草稿的随机性取平均之后才成立的,取平均后 MSS 81.25%、朴素采样 43.75%。)
在小 Transformer 上(代码 04 演示 5,树的孩子从草稿 B 采样,T = 1):
| 扩展配置 | 朴素采样 | MSS | MSS / 朴素 |
|---|---|---|---|
| ⟨1,1,1,1⟩ | 2.02 | 3.43 | 1.70× |
| ⟨3,1,1,1⟩ | 2.30 | 3.45 | 1.50× |
| ⟨5,2,1,1⟩ | 2.65 | 3.58 | 1.35× |
论文表 3(LLaMA-7B + LLaMA-68M,树宽 5、深 8):朴素 1.73–1.87,MSS 2.21–2.38,提高 1.26–1.28 倍。我们的比值更高,因为”在几个人名里随便选一个”的位置上,朴素采样要 LLM 和树恰好选中同一个人;比值随树变宽而下降,因为树宽了以后朴素采样”碰巧落在树里”的机会变大。
4.6 系统设计与结果
- 运行时(slides 的”分布式 LLM 服务”图):请求管理器在 CPU 上做迭代级调度(沿用 Orca 的连续批处理)、令牌树合并和验证;小模型用数据并行分散在各 GPU 上;LLM 用 Megatron 式的张量并行(节点内)+ 流水线并行(节点间)。请求管理器和 GPU 之间只传 token,不传向量,开销可以忽略。
- 开销:小模型比 LLM 小 100–1000 倍,每个小模型增加的显存不到 1%;树的 KV 和注意力分数相对长序列的 KV 缓存可以忽略;验证多算的部分利用的是增量解码时本来闲着的算力。
- 两个应用场景:分布式推理(减少解码步数,从而减少每步的 GPU 间通信次数);卸载式推理(参数放在 CPU 内存,每一步都要从 CPU 搬权重到 GPU,减少步数就是减少搬运——这个场景下收益最大)。
- 结果(ASPLOS 版本):
- 分布式推理比现有系统(vLLM、HuggingFace TGI、FasterTransformer)快 1.5–2.8 倍(单节点多卡 1.5–2.5 倍,两节点 2.4–2.8 倍);
- 卸载式推理比 FlexGen 快 2.6–3.5 倍(单块 24 GB A10 跑 OPT-13B / 30B);
- 树比单条序列的推测再快 1.2–1.5 倍;
- 批越大收益越小(§2.5)。
- slides 上写的是 1.3–2.4 倍、GPU 显存访问减少 2.5–4.4 倍,和 arXiv 第二版的摘要一致(更早的实验设置)。
4.7 slides 的三个开放问题
- 平衡的树 vs 不平衡的树:平衡的树推测开销低(每层宽度一样,好批处理);不平衡的树验证成功率高(把宽度花在不确定的地方)。
- 静态的树 vs 动态的树:静态的树允许离线优化;动态的树能针对具体的提示词(EAGLE-2 走的就是这条路)。
- 怎样验证随机解码:朴素采样、多步推测采样、贪心采样,或者它们的混合。
5. 后来的方法 · Beyond SpecInfer
slides 把方法分成两类:基于模型的推测(用一个单独的草稿模型或额外的头预测)和不用模型的推测(用已经生成过的 token 预测后面的 token)。
5.1 Medusa:多个解码头
不要单独的小模型,而是在大模型最后一层的隐藏状态上加几个可训练的头,第 i 个头直接预测”后面第 i+1 个 token”。每个头取前几名,组合成一棵候选树,用树注意力一次验证。
- Medusa-1:冻结主干只训练头,无损;论文报告超过 2.2 倍加速;
- Medusa-2:头和主干一起微调,2.3–2.8 倍,但需要保住主干能力的特殊训练方法;
- 论文还提出了”典型接受”(typical acceptance),放宽验证规则来提高接受率——这时输出就不再严格等于原模型的分布了。
好处:草稿几乎没有额外开销(几个小矩阵乘法),也不用部署第二个模型(§2.5 的 c 的问题)。代价:各个头互相独立地预测,第 3 个头不知道第 2 个头猜了什么。
5.2 EAGLE 与 EAGLE-2:在特征层面自回归,动态的树
- EAGLE:草稿模型复用大模型的词嵌入和输出头(LM head),中间只加一层可训练的 Transformer 层,在**特征(倒数第二层的隐藏状态)**上做自回归预测,再用大模型的输出头得到 token。比 Medusa 的独立头更准,又比独立的小模型便宜。
- EAGLE-2:发现 EAGLE 的草稿模型校准得很好——它的置信度近似等于接受率,而接受率依赖上下文(不只依赖位置)。于是用动态的草稿树:
- 扩展阶段:每一步选累积概率最高的 K 个节点继续展开;
- 重排阶段:按累积概率(路径上概率的乘积)选前 N 个节点送去验证。
- 论文(arXiv 版本)报告 3.05–4.26 倍,比 EAGLE-1 快 20%–40%,无损(EMNLP 版本的摘要写的是最多 5 倍、EAGLE 的 1.3 倍)。
这正好回答了 §3.3 的问题:既然难点的位置随上下文变化,就该按草稿模型自己的置信度决定在哪里多猜。
5.3 不用模型的推测(代码 05)
草稿模型要训练、部署、占显存。不用模型的方法直接查表,c ≈ 0,但收益完全取决于负载里有没有重复。
- Prompt Lookup Decoding:取最后几个 token,在提示词 + 已生成的内容里找它上一次出现的位置,把那之后的几个 token 当草稿。适合输出大段照抄输入的负载(代码编辑、摘要、RAG)。
- SuffixDecoding:用后缀树缓存提示词和以前请求的输出(slides 的”请求树”和”全局树”),匹配当前上下文的后缀,按出现频率给候选打分、在预算内展开成一棵推测树,并根据接受的可能性自适应地决定猜多少。论文面向 Agent 类负载(多智能体流水线、自我改进循环会反复生成相似的序列),在 SWE-Bench、Text-to-SQL 等基准上最多 5.3 倍,比 EAGLE-2/3 快 2.8 倍、比不用模型的 Token Recycling 快 1.9 倍。
- Lookahead Decoding:基于 Jacobi 迭代——窗口里放一串猜测,一次前向得到每个位置在”前面都按猜测”时的预测,反复迭代直到不动点。论文观察到单纯的 Jacobi 解码几乎不能减少步数:生成的 token 常被放在错误的位置,放对的又常被后面的迭代换掉。改进:保留 Jacobi 迭代的轨迹,从中收集 n-gram 放进池子,用”以最后一个 token 开头的 n-gram”当候选;lookahead 分支(继续 Jacobi)和 verification 分支(验证候选)在同一次前向里完成。论文报告 MT-bench 上最多 1.8 倍,代码补全任务上多卡强扩展到 4 倍。
代码 05 演示 1(每次调用大模型确定几个 token;“改写代码""Agent”两种负载的大模型是确定的规则,只用来数调用次数):
| 负载 | Prompt Lookup | 后缀索引 | 两者合并 |
|---|---|---|---|
| LikesWorld(小 Transformer) | 1.81 | 1.99 | 1.86 |
| 改写代码(输出是改了变量名的输入) | 4.46 | 2.09 | 4.46 |
| Agent 工具调用(JSON 模板 + 抄提示词的槽位) | 1.19 | 1.97 | 2.17 |
- 改写代码:Prompt Lookup 一次确定好几个 token,只在改名的地方断开;
- Agent:模板部分在以前的输出里反复出现(后缀索引),槽位里的词从提示词抄(Prompt Lookup),两者合并最好——这正是 SuffixDecoding 同时维护两棵树的原因;
- LikesWorld:也有 1.8–2.0 倍,但只来自结构性的 token;同一负载上草稿模型 B(γ = 6)是 3.25。
代码 05 演示 2:后缀索引猜一棵树,节点预算从 1 加到 48,每次调用确定的 token 从 1.46 涨到 2.68,但被接受的节点比例从 47% 掉到 7%——把验证预算花在最可能被接受的地方,是 SuffixDecoding 和 EAGLE-2 共同的思路。
代码 05 演示 3:单纯的 Jacobi 迭代在小 Transformer 上几乎不快——用固定 token 初始化时恰好 1.00(猜测退化成同一个 token 重复几遍,整个窗口永远落后一个位置),随机初始化 1.06。
6. 把它们放在一起 · Putting it together
推测解码的设计空间可以用三个问题来描述:
| 草稿从哪里来 | 草稿的形状 | 怎样验证 | |
|---|---|---|---|
| Leviathan et al. / Chen et al. | 独立的小模型 | 一条链 | 推测采样 |
| SpecInfer | 一个或多个小模型(boost-tuning) | 静态的树(扩展式 / 合并式) | 树注意力 + 贪心 / MSS |
| Medusa | 大模型上的多个头 | 头的候选组合成树 | 树注意力;可选”典型接受”(有损) |
| EAGLE-2 | 复用大模型的嵌入和输出头 + 一层 | 按置信度动态展开的树 | 树注意力 + 推测采样 |
| Prompt Lookup / SuffixDecoding | 提示词 / 以前的输出(查表) | 链 / 按频率打分的树 | 贪心或推测采样 |
| Lookahead | Jacobi 迭代的轨迹(n-gram 池) | 多个 n-gram | 同一次前向里验证 |
几条共同的原则:
- 只在访存受限时划算:验证的计算要”免费”,就要有闲着的算力(§1.2、§2.5)。
- 无损来自验证规则,与草稿无关:草稿再差也只会慢,不会错;但验证规则写错了(§2.3),或者违反了它的前提(§4.5),就会悄悄改变输出。
- 草稿的开销不能忽略:c 里有固定开销;越往后的方法,草稿越”贴着”大模型(头、复用隐藏状态、查表)。
- 猜多少、猜在哪里:独立同分布的公式只是起点;真实的接受率随上下文剧烈变化,动态的形状(EAGLE-2、SuffixDecoding)把验证预算花在刀刃上。
和第 16 讲的关系:推测解码和连续批处理、分页 KV 是正交的,但会相互影响——被拒绝的草稿的 KV 要回滚(分页 KV 只需要释放最后几块);批越大推测的收益越小,所以服务系统通常根据负载动态打开 / 关闭推测或调整 γ。
7. 论文精读 · Paper reading
7.1 Fast Inference from Transformers via Speculative Decoding(Leviathan, Kalman, Matias, ICML 2023)
1. 问题:大型自回归模型(大 Transformer)解码 K 个 token 要串行运行 K 次。能不能在不改模型结构、不重新训练、不改变输出分布的前提下更快地采样?
2. 为什么重要又困难:
- 大模型比小模型强得多,但一步解码慢得多,而且步与步之间严格串行;
- 已有的加速方法(蒸馏、稀疏、量化、改结构、自适应计算 / 提前退出)要么对所有输入一视同仁,要么要改结构、重新训练,而且通常改变输出;
- 随机采样时”猜对了”没有明确的定义,推测执行在随机的情形下怎样保证分布不变,此前没有答案。
3. 以前的工作为什么不行:
- Blockwise Parallel Decoding(Stern et al., 2018):并行解码多个 token,但只支持贪心,要额外训练定制的模型,目标是保持下游任务质量而不是输出完全相同;
- Shallow Aggressive Decoding(Sun et al., 2021):只支持”把输入抄到输出”的场景(如语法纠错),也只支持贪心;
- Wisdom of Committees 等自适应计算方法用启发式决定何时停止,失去了”输出与大模型相同”的保证。
4. 新意:
- 两个观察:困难的任务里有能被小模型近似的简单子任务;大模型推理常常受限于显存带宽和通信,有富余的算力;
- 推测采样:把推测执行推广到随机的情形——任何草稿分布 q 下都能精确得到目标分布 p;
- 推测解码(算法 1):小模型猜 γ 个,大模型并行验证,至少产出 1 个、最多 γ+1 个 token;
- 完整的分析:接受率 β = 1 − D_LK(p, q)(D_LK 就是总变差距离);期望 token 数、墙钟加速比、运算量的公式;如何选 γ;
- 指出任何草稿都可以:现成的小 Transformer、n-gram(c ≈ 0)、非自回归模型、甚至随机猜(保证有一点点提升)。
5. 结果:
- T5-XXL(11B)上相对 T5X 实现 2–3 倍:英译德 3.4 倍(贪心)/ 2.6 倍(T = 1),CNN/DM 摘要 3.1 / 2.3 倍;最好的草稿是 T5-small(77M),而不是更大的 T5-large——α 更高但 c 也更高;
- 实测的 α:比目标小两个数量级的草稿 0.5–0.9;二元语法 0.2;分布越尖 α 越高(贪心高于 T = 1);
- 独立工作 Chen et al. (2023) 在 Chinchilla 70B 上得到 2–2.5 倍。
本讲的验证:玩具模型上精确枚举所有分支,γ = 1..4 的输出分布与目标模型 TV 1e-16;复现表 1 全部 6 行;三种错误写法偏 0.21–0.51;在小 Transformer 上贪心逐字相同、采样同分布。另外两个和论文不同的发现:真实语言里难点周期出现,公式会高估;“贪心 α 更高”取决于分布形状。
局限:
- 用增加的运算换延迟,算力不富余时(大批、短上下文)无效;
- 独立同分布的分析是近似,γ 固定(论文自己指出动态 γ 还能再快,并估计理想情况下最多再快约 60%);
- 需要一个分布兼容(同一个分词器)的草稿模型;
- 论文只在 T5 上测了墙钟时间,只测了批 1;
- 和束搜索的结合留给了以后(附录有讨论)。
7.2 SpecInfer: Accelerating Large Language Model Serving with Tree-based Speculative Inference and Verification(Miao et al., ASPLOS 2024)
1. 问题:生成式 LLM 的服务延迟高、计算要求高。增量解码的并行度受限于一次一个 token;KV 缓存又限制了能同时服务的请求数。已有的推测解码只用一个小模型猜一条序列,能被接受的部分有限。
2. 为什么重要又困难:
- 一个小模型比 LLM 小 100–1000 倍,单条序列和 LLM 的对齐受限于容量差距,成功率随长度指数衰减;
- 搜索空间巨大:OPT 词表 50,272,预测接下来 4 个 token 就有约 6 × 10^18 种组合;
- 随机解码时要证明输出分布不变,而多个候选的验证比单条序列复杂;
- 验证整棵树时,不同候选序列的 KV 缓存互相冲突(同一个位置在不同分支上的 token 不同)。
3. 以前的工作为什么不行:
- 基于序列的推测推理(Leviathan、Chen 等)只考虑一个小模型的一条序列;
- 现有的 LLM 服务系统只能按序列解码:把树拆成多条序列,每条一份 KV、一个 kernel,前缀重复计算、kernel 启动多;
- 已有的随机验证方法是为单条序列设计的。
4. 新意:
- 令牌树:合并式(多个 boost-tuning 的小模型)和扩展式(一个小模型取前 k 个,扩展配置 ⟨k₁, …⟩)两种构造;
- 树形并行解码:深度优先线性化 + 拓扑感知的因果掩码,一个 kernel 算完整棵树,共用一份 KV 缓存;
- 多步推测采样(MSS):随机解码下逐个孩子做推测采样,证明分布不变(定理 4.2),且拒绝概率不超过朴素采样(定理 4.3);
- 系统:基于 FlexFlow;请求管理器做迭代级调度和树的合并 / 验证;小模型数据并行,LLM 张量 + 流水线并行;支持分布式和卸载式推理。
5. 结果:
- 分布式推理比 vLLM、TGI、FasterTransformer 快 1.5–2.8 倍,卸载式推理比 FlexGen 快 2.6–3.5 倍,输出与增量解码完全相同;
- 树比单条序列再快 1.2–1.5 倍;树形并行解码比按序列解码最多快 1.8 倍(大批时);
- MSS 比朴素采样每步多验证 1.26–1.28 倍的 token;
- 批越大收益越小;批 ≥ 4 时最优树宽 2–3。
本讲的验证:树注意力在小 Transformer 上与逐路径前向一致(1.9e-5);贪心时树比链多确定 22%–23% 的 token;MSS 在独立采样的草稿下精确无偏、接受率高于朴素采样(玩具模型精确枚举),在小 Transformer 上 1.35–1.70 倍。额外的发现:MSS 的无偏性要求孩子是采样得到的,“取前 k 个”再套同一公式会有偏(TV 0.205)。
局限:
- 静态的扩展配置要按数据集调(论文用 ⟨1,1,3,1,1,1,1,1⟩),动态构造留作未来工作;
- 扩展式的树在随机解码时怎样保证孩子”按采样得到”,论文正文没有展开(§4.5);
- 收益随批大小下降,主要面向低延迟场景;
- 合并式需要训练、维护一池小模型。
7.3 两篇论文之间的联系
- 从链到树:Leviathan et al. 把推测执行推广到随机采样,给出了单条链的完整分析;SpecInfer 把链推广成树,把”一个位置一个候选”变成”一个位置多个候选”,相应地把推测采样推广成 MSS。两者的无损性都来自同一个原理:每一轮都是一次完整的推测采样,拒绝后把剩余分布传下去。
- 从算法到系统:Leviathan et al. 主要是算法和分析(实验是 TPU 上的批 1);SpecInfer 是系统论文,解决了树的 KV 冲突、kernel 融合、分布式运行时、和连续批处理的结合。
- 同一个瓶颈判断:两篇都基于”解码受限于访存,算力有富余”;也都观察到收益依赖富余算力(Leviathan 的 c 和”足够的计算资源”假设;SpecInfer 的批大小实验)。
- 后续的分叉:Leviathan 指出”任何草稿都可以”(包括 n-gram、抄上下文)——这引出了 Prompt Lookup、SuffixDecoding;SpecInfer 的树注意力成了 Medusa、EAGLE、SuffixDecoding 共同使用的验证机制;SpecInfer 的开放问题”静态 vs 动态的树”由 EAGLE-2 等工作回答。
8. 小结 · Summary
- 解码受限于访存:批 1 时一次处理 1 个和 32 个 token 的时间几乎一样。推测解码用富余的算力一次验证多个猜测的 token,用算力换延迟。
- 推测采样:从 q 采样,以 min(1, p/q) 接受,拒绝后从 norm(max(0, p − q)) 采样,输出严格服从 p;接受率 β = Σmin(p, q) = 1 − TV(p, q)。
- 能快多少:E = (1 − α^{γ+1})/(1 − α),加速比 E/(γc + 1);c 在 GPU 上被固定开销抬高;收益只在访存受限时存在(大批短上下文时消失,长上下文时反而更大)。
- 真实的接受率随上下文剧烈变化:套话必中、需要推理的 token 难猜;难点周期出现时独立同分布的公式会高估;贪心和采样的 α 谁高取决于分布的形状。
- SpecInfer:令牌树 + 深度优先线性化 + 拓扑感知掩码,一次前向验证整棵树(精确);MSS 在随机解码下无偏且优于朴素采样,前提是树的孩子由采样产生。
- 后续方法:Medusa(多个头)、EAGLE / EAGLE-2(复用大模型的特征、动态的树)、Prompt Lookup / SuffixDecoding(查表,依赖负载的重复)、Lookahead(Jacobi 轨迹的 n-gram)。
9. 常见误区 · Common misconceptions
- “推测解码会改变模型的输出,是一种近似。” 正确实现的推测解码(贪心验证或推测采样)是无损的:输出分布和大模型自己生成完全相同(代码 02 精确枚举 TV 1e-16)。有损的是某些变体,例如 Medusa 的”典型接受”。
- “推测解码减少了计算量。” 正好相反,被拒绝的草稿和多余的验证都是白算的(表 1 的运算量总是 ≥ 1 倍)。它减少的是大模型的串行步数、也就是读权重的次数。
- “草稿模型越大越好。” α 上升,但 c 也上升。论文里最好的是 T5-small(77M)而不是 T5-large(800M)。
- “草稿模型的参数是大模型的 1%,所以 c ≈ 0.01。” 小模型的延迟被固定开销主导(kernel 启动、采样),代码 01 里 OPT-125M 的 c 是 0.09。
- “γ 越大越快。” E 的上限是 1/(1−α),而草稿的开销随 γ 线性增长;存在最优的 γ(α = 0.8、c = 0.05 时是 8)。
- “推测解码只适合批大小为 1。” 准确地说是只适合访存受限的时候:短上下文时大批确实没收益,长上下文时 KV 的读取占主导,大批反而更划算(代码 01 演示 5)。
- “随机采样时,验证就是看草稿 token 是不是大模型的 argmax。” 这是贪心的规则,用在随机采样上会把输出推向 argmax(TV 0.51)。随机采样必须用推测采样。
- “多个候选的话,取草稿的前 k 个,再逐个用 min(1, p/q) 验证就行。” MSS 的无偏性要求候选是采样出来的;确定地取前 k 个再套同一公式有偏(代码 04 TV 0.205)。
- “接受率高说明草稿猜得准。” 接受率衡量的是两个分布像不像。对本来就随机的 token,草稿只要也给出接近均匀的分布,采样时的接受率就很高(代码 03:0.93–0.98),尽管它的 argmax 常常和大模型不一样。
10. 练习 · Exercises(含参考答案)
练习 1 α = 0.75,γ = 4。(1)每步平均产生多少 token?(2)c = 0.05 时墙钟加速比是多少?(3)ĉ = 0.02 时总运算量变成多少倍?
答案:
- E = (1 − 0.75⁵)/(1 − 0.75) = (1 − 0.2373)/0.25 = 3.05;
- 加速比 = 3.05 / (4 × 0.05 + 1) = 3.05 / 1.2 = 2.54 倍;
- 运算量 = (4 × 0.02 + 4 + 1)/3.05 = 5.08/3.05 = 1.67 倍。
练习 2 证明一个位置上的推测采样输出服从 p。提示:分别算”草稿采到 x 并接受”和”拒绝后重新采到 x”的概率。
答案:
- 草稿采到 x 并接受:q(x) · min(1, p(x)/q(x)) = min(p(x), q(x));
- 拒绝的总概率:1 − Σ_y min(p(y), q(y))。因为 Σp = Σq = 1,Σ_y max(0, p(y) − q(y)) = Σ_y (p(y) − min(p(y), q(y))) = 1 − Σ_y min(p(y), q(y)),所以剩余分布 norm(max(0, p − q)) 的归一化常数正好等于拒绝概率;
- 拒绝后采到 x:(1 − Σmin) · max(0, p(x) − q(x)) / (1 − Σmin) = max(0, p(x) − q(x));
- 合计 min(p, q) + max(0, p − q) = p(x)。
练习 3 p = (0.6, 0.4),q = (0.5, 0.5)。分别求正确的推测采样、以及”随机采样时用贪心规则”(草稿等于 argmax p 才接受,否则从 p 采样)的输出分布。
答案:
- 正确:min(p, q) = (0.5, 0.4),接受率 α = 0.9;剩余分布 norm(max(0, p − q)) = (1, 0);输出 = (0.5 + 0.1, 0.4) = (0.6, 0.4) = p。
- 贪心规则:草稿以 0.5 的概率给出 token 0(= argmax p),直接输出 0;另外 0.5 的概率从 p 采样,得到 (0.3, 0.2)。合计 (0.8, 0.2) ≠ p——输出被推向 argmax。
练习 4 代码 01 演示 1 里,OPT-13B、批 32、上下文 512 时普通解码的算术强度约 21.4 FLOPs / 字节,A100 的拐点约 120。粗略估计每个请求最多验证几个 token 还保持访存受限?和表里的数字对照。
答案:
- 每个请求处理 k 个 token 时,计算量约变成 k 倍,读的字节基本不变,算术强度约 21.4k;
- 21.4k < 120 → k < 5.6;
- 表里 k = 4 时 26.90 ms(还是访存受限),k = 8 时跳到 37.63 ms(计算受限),一致。
- 所以批 32 时 γ 最多取 4 左右,再大验证就不再”免费”。
练习 5 三个小模型分别猜出 [a b c]、[a b d e]、[f g](根是已验证的 token r)。(1)合并后的令牌树有几个猜测节点?(2)写出深度优先的顺序和每个节点的位置偏移(深度)。(3)节点 e 在注意力里能看到哪些节点?
答案:
- 节点:a、b、c、d、e、f、g,共 7 个(a、b 共享);按序列单独算是 3 + 4 + 2 = 9 个;
- 深度优先:r(0) a(1) b(2) c(3) d(3) e(4) f(1) g(2);
- e 能看到:已验证的前缀(含 r)、a、b、d、e 自己;看不到 c、f、g(尽管 c 排在它前面)。
练习 6 LLM 的分布是 4 个 token 上的均匀分布。SSM 1 的分布是 (0.5, 0.5, 0, 0),这一次采到 t2;SSM 2 是均匀分布,这一次采到 t3。分别用朴素采样和 MSS 算出”接受某个草稿”的概率。
答案:
- 朴素采样:LLM 采到 t2 或 t3 的概率 0.5;
- MSS:先以 min(1, 0.25/0.5) = 0.5 接受 t2;拒绝后剩余分布 norm(max(0, p − q₁)) = (0, 0, 0.5, 0.5);再以 min(1, 0.5/0.25) = 1 接受 t3。合计 0.5 + 0.5 × 1 = 1.0。
练习 7 为什么”确定地取草稿概率最大的 1 个 token,再以 min(1, p/q) 接受、拒绝后从 norm(max(0, p − q)) 采样”是有偏的?举一个两 token 的反例,并说出一种无偏的改法。
答案:
- 反例:p = (0.5, 0.5),q = (0.6, 0.4)。草稿总是 token 0,以 0.5/0.6 = 0.833 的概率接受;拒绝后剩余分布 = (0, 1)。输出 = (0.833, 0.167) ≠ (0.5, 0.5)。
- 原因:推测采样的证明里,“草稿是 x 的概率”必须等于 q(x);确定地取 argmax 时这个概率是 1,接受的质量变成 min(1, p/q) 而不是 min(p, q)。
- 改法:让草稿真的从 q 采样(贪心验证时才可以取 argmax);或者多个候选时不放回地采样,并在第 j 轮用去掉已采 token、重新归一化的 q_j。
练习 8 (开放)你要为三种服务场景选推测解码的方案:(a)在线聊天机器人,批小,延迟敏感;(b)代码助手的”改写这个函数”功能;(c)高吞吐的离线批处理(批几百、输出短)。分别给出建议和理由。
参考思路:
- (a) 访存受限、算力富余,适合基于模型的推测;部署上优先考虑 EAGLE / Medusa 这类”贴着大模型”的草稿(c 小、不用单独部署),用动态的树按置信度展开;
- (b) 输出大段照抄输入,Prompt Lookup / SuffixDecoding 这类查表方法 c ≈ 0、零训练,收益可能比模型草稿还大(代码 05:4.46);
- (c) 批大、输出短、上下文短,验证很快变成计算受限,推测的收益很小甚至为负,可以关掉;如果上下文很长(KV 读取占主导),可以重新评估(代码 01 演示 5)。
- 共同点:都要实测 α 和 c,并且随负载动态调整 γ 或开关推测。
11. 延伸阅读 · Further reading
- C. Chen et al., Accelerating Large Language Model Decoding with Speculative Sampling(DeepMind, 2023)——与 Leviathan et al. 独立提出的推测采样,Chinchilla 70B 上 2–2.5 倍。
- T. Cai et al., Medusa: Simple LLM Inference Acceleration Framework with Multiple Decoding Heads(ICML 2024)。
- Y. Li et al., EAGLE: Speculative Sampling Requires Rethinking Feature Uncertainty(ICML 2024);EAGLE-2: Faster Inference of Language Models with Dynamic Draft Trees(EMNLP 2024)。
- Y. Fu et al., Break the Sequential Dependency of LLM Inference Using Lookahead Decoding(ICML 2024)。
- G. Oliaro et al., SuffixDecoding: Extreme Speculative Decoding for Emerging AI Applications(NeurIPS 2025)——同一研究组(Zhihao Jia)的后续工作。
- Z. Sun et al., SpecTr: Fast Speculative Decoding via Optimal Transport(NeurIPS 2023)——多个草稿时用最优传输的观点设计验证规则。
- R. Sadhukhan et al., MagicDec: Breaking the Latency-Throughput Tradeoff for Long Context Generation with Speculative Decoding(ICLR 2025)——长上下文、大批量下的推测解码(对应代码 01 演示 5 的现象)。
12. 下一讲预告 · Next lecture
第 16、17 讲都在讲怎样服务一个训练好的 LLM。第 18 讲《参数高效微调》回到训练,但不是从头训练,而是把一个预训练好的大模型改成适合某个任务的模型:
- 全量微调为什么这么贵(GPT-3 175B 全量微调要 80 块 A100-40GB);
- 提示工程、提示调优 / 前缀调优、适配器、LoRA 各自改了模型的哪一部分;
- QLoRA 怎样把基座模型量化到 4 比特(NF4、分块量化、双重量化)还不掉精度;
- 为什么 LoRA 省了优化器状态却省不了激活,旁路微调(side tuning)怎样连激活也省掉。
我们会从零实现 LoRA、NF4 量化和旁路网络,在第 5 讲的小 Transformer 上教它一个新任务(反过来问”谁喜欢茶?”)。