第 17 讲 · LLM 服务(二):推测解码 · Speculative Decoding

Advanced Topics in Machine Learning Systems (LLM Edition) · Lecture 17。个人学习讲义与配套实验,按主题整理并持续修订。

课程源码 ZIP ↓

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

本篇目录

对应 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 学习目标

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

  1. 用 roofline 解释:为什么解码一步时,让大模型多处理几个 token 几乎不增加时间;以及什么时候这不再成立(大批、短上下文)。
  2. 写出推测采样的接受规则和剩余分布,证明输出严格服从大模型的分布;说出三种常见的错误写法错在哪里。
  3. 用 α、γ、c 估算每步产生的 token 数、墙钟加速比和运算量的增加,并解释公式的假设什么时候不成立。
  4. 解释 SpecInfer 的令牌树:合并式与扩展式构造、深度优先线性化、拓扑感知的因果掩码;说明树注意力为什么是精确的。
  5. 比较朴素采样和多步推测采样(MSS),并说清楚 MSS 无偏的前提。
  6. 说出 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 = 1k = 4k = 8k = 16k = 32
118.49 ms18.4918.4918.5018.51
820.3820.3920.4120.4437.63
3226.8526.9037.6373.76146.02

批 1 时,k 从 1 到 32 时间几乎不变:读权重的时间一点没少,多出来的计算只是让本来闲着的算力动起来。这就是推测解码的物理基础:如果有人能事先”猜”出接下来的几个 token,大模型可以用几乎相同的时间一次验证它们。

但这不是无条件的:批越大、算术强度越高,越早碰到算力的上限(批 32 时 k = 8 就从 26.9 跳到 37.6 ms)。推测解码本质上是用算力换延迟。

1.3 大模型和小模型的权衡

slides 用 GPT-3 家族的数字说明大小模型的权衡(GPT-3 论文里的数据):

参数175B13B2.7B760M125M
TriviaQA71.257.542.326.56.96
延迟(256 token)20 s7.6 s2.7 s1.1 s0.3 s
A100 数量101111

大模型准确但又慢又贵,小模型便宜但不准。推测解码想要两者兼得:用小模型的速度,得到大模型的输出。


2. 推测解码:小模型猜、大模型验 · Speculative decoding

2.1 直觉:处理器的推测执行

处理器里的分支预测:遇到条件跳转,不等条件算出来,先按预测的方向往下执行;猜对了就赚到了时间,猜错了就把结果丢掉重来。推测解码把同样的思路搬到语言模型上:

  1. 推测:一个便宜的小模型(草稿模型,draft model;SpecInfer 叫 SSM,small speculative model)自回归地猜 γ 个 token;
  2. 验证:大模型(目标模型)用一次前向同时算出这 γ+1 个位置上”它自己会怎么生成”;
  3. 接受:从第一个开始,一致的就接受,遇到第一个不一致的就用大模型自己的结果替换它,后面的草稿全部丢掉。

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。

  1. 从 q 采一个 x;
  2. 如果 q(x) ≤ p(x),接受;
  3. 如果 q(x) > p(x),以 p(x)/q(x) 的概率接受;
  4. 被拒绝时,从剩余分布 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):

  1. 草稿模型自回归地采样 γ 个 token x₁ … x_γ,记下每一步的分布 q₁ … q_γ;
  2. 大模型一次前向算出 γ+1 个分布 p₁ … p_{γ+1}(输入是前缀 + 全部草稿,因果掩码保证第 i 个位置只看前 i−1 个草稿);
  3. 从第一个草稿开始做推测采样:接受就继续,遇到第一个拒绝的位置 n+1,从 norm(max(0, p_{n+1} − q_{n+1})) 采一个 token 替换它,结束;
  4. 如果 γ 个全部接受,再从 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
642.55×2.56×1.65×1.04×1.00×(不推测)
5122.56×2.64×2.33×2.07×1.78×
20482.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.25T=0.5T=1.0T=1.5贪心
玩具(词表 5,无明显的峰),噪声 0.80.6330.6830.7670.8280.600
像 LLM(词表 1000,有峰 + 长尾),噪声 0.60.9530.9360.8830.8590.957
像 LLM,且草稿更平(τ = 1.5)0.8940.8170.5260.6450.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.0001.000 / 1.000
事实里的人名16%0.930 / 0.5350.980 / 0.579
事实里的物品16%0.965 / 0.1340.978 / 0.242
被问的人名8%0.844 / 0.2630.962 / 0.456
答案8%0.125 / 0.1400.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
A4200/2008652.260.5982.30
A6200/2008552.290.6002.43
B4200/2006962.810.7312.94
B6200/2006013.250.7513.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.0234180
推测采样(草稿 B,γ = 4)100%0.0121583

推测采样的统计量落在样本噪声之内。同一批提示词上,采样的 α(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 的树形并行解码:
    1. 深度优先把树排成一行放进 KV 缓存(祖先总在前面);
    2. 拓扑感知的因果掩码:每个 token 只能看已验证的前缀 + 自己在树里的祖先;
    3. 所有 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.262.835
⟨3,1,1,1⟩2.473.1913
⟨1,3,1,1⟩2.553.2011
⟨5,1,1,1⟩2.713.3021
⟨3,3,1,1⟩2.753.4731
  • 树让每次调用多确定 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 个 + 同一公式
10.639(无偏)0.921(无偏)0.921(无偏)0.996(TV 0.205)
20.7560.9520.9280.996(TV 0.205)
40.8460.9570.9951.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):

扩展配置朴素采样MSSMSS / 朴素
⟨1,1,1,1⟩2.023.431.70×
⟨3,1,1,1⟩2.303.451.50×
⟨5,2,1,1⟩2.653.581.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 的三个开放问题

  1. 平衡的树 vs 不平衡的树:平衡的树推测开销低(每层宽度一样,好批处理);不平衡的树验证成功率高(把宽度花在不确定的地方)。
  2. 静态的树 vs 动态的树:静态的树允许离线优化;动态的树能针对具体的提示词(EAGLE-2 走的就是这条路)。
  3. 怎样验证随机解码:朴素采样、多步推测采样、贪心采样,或者它们的混合。

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.811.991.86
改写代码(输出是改了变量名的输入)4.462.094.46
Agent 工具调用(JSON 模板 + 抄提示词的槽位)1.191.972.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提示词 / 以前的输出(查表)链 / 按频率打分的树贪心或推测采样
LookaheadJacobi 迭代的轨迹(n-gram 池)多个 n-gram同一次前向里验证

几条共同的原则:

  1. 只在访存受限时划算:验证的计算要”免费”,就要有闲着的算力(§1.2、§2.5)。
  2. 无损来自验证规则,与草稿无关:草稿再差也只会慢,不会错;但验证规则写错了(§2.3),或者违反了它的前提(§4.5),就会悄悄改变输出。
  3. 草稿的开销不能忽略:c 里有固定开销;越往后的方法,草稿越”贴着”大模型(头、复用隐藏状态、查表)。
  4. 猜多少、猜在哪里:独立同分布的公式只是起点;真实的接受率随上下文剧烈变化,动态的形状(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 时一次处理 1 个和 32 个 token 的时间几乎一样。推测解码用富余的算力一次验证多个猜测的 token,用算力换延迟。
  2. 推测采样:从 q 采样,以 min(1, p/q) 接受,拒绝后从 norm(max(0, p − q)) 采样,输出严格服从 p;接受率 β = Σmin(p, q) = 1 − TV(p, q)。
  3. 能快多少:E = (1 − α^{γ+1})/(1 − α),加速比 E/(γc + 1);c 在 GPU 上被固定开销抬高;收益只在访存受限时存在(大批短上下文时消失,长上下文时反而更大)。
  4. 真实的接受率随上下文剧烈变化:套话必中、需要推理的 token 难猜;难点周期出现时独立同分布的公式会高估;贪心和采样的 α 谁高取决于分布的形状。
  5. SpecInfer:令牌树 + 深度优先线性化 + 拓扑感知掩码,一次前向验证整棵树(精确);MSS 在随机解码下无偏且优于朴素采样,前提是树的孩子由采样产生。
  6. 后续方法: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 上教它一个新任务(反过来问”谁喜欢茶?”)。

← 返回课程目录