投机解码方法笔记
0. 先统一概念
投机解码(Speculative Decoding)的共同框架是:
- 用一个更便宜的过程先提出一段候选 token,记作长度为
K的 draft block。 - Target model 对整段候选做一次并行验证。
- 从左到右接受通过验证的最长前缀;遇到第一个不通过的位置就停止接受,并按相应规则采样修正 token。
下文中:
Target:最终负责保证输出正确性的目标大模型。Drafter:负责廉价提出候选的模块或方法。AR:autoregressive,自回归;后一个 draft token 依赖前一个已经生成的 draft token。K:一次尝试生成并验证的候选 token 数。
1. 总览
| 方法 | Draft 信息来源 | K 个候选如何产生 | 是否显式依赖前一个 draft token | 主要计算形态 |
|---|---|---|---|---|
| N-gram | Prompt、已生成历史或外部语料 | 检索并复制连续片段 | 不是神经网络建模,而是整段匹配 | 查表/检索 |
| Standalone | 独立小语言模型 | 逐 token 自回归生成 | 是 | K 次小模型前向 |
| Self-speculative | Target 自身的部分层或提前退出分支 | 逐 token 自回归生成 | 是 | K 次不完整 Target 前向 |
| Medusa / 并行 MTP | Target 的当前 hidden state | 多个未来位置 head 同时预测 | 通常否 | 一次并行多头预测 |
| EAGLE | Target feature + 已生成 token | 轻量 drafter 逐 token生成 | 是 | K 次轻量 AR 前向 |
| DFlash | Target feature + anchor token + mask block | 一次并行预测整个 block | 否 | 一次较深的并行 drafter 前向 |
| DSpark | Target feature + 并行 backbone + 轻量串行模块 | 先并行出基础 logits,再串行修正 | 部分依赖 | 一次并行前向 + K 次极轻量修正 |
注意:MTP 是“预测多个未来 token”的大类,不是唯一固定结构。Medusa 式并行 head、DeepSeek 式串行 MTP 模块、DFlash 式 block-parallel drafter,都可以放在广义 MTP 视角下理解,但它们的依赖结构和推理成本并不相同。
2. N-gram:从历史文本中复制
核心思想
在已有上下文中寻找与当前末尾相同的 n-gram,然后把它后面的若干 token 当作候选。例如,历史中出现过:
... A B C → D E F ...
当前上下文又以 A B C 结尾,就可以直接提出 D E F。
特点
- 不需要训练 drafter,也几乎没有神经网络计算。
- 对代码、固定格式、重复文本、模板化内容尤其有效。
- 对开放式生成或历史中没有出现过的内容帮助有限。
- 它产生的是“检索到的一整段”,不是通过模型逐 token 推理得到的条件分布。
一句话:用重复性换速度。
3. Standalone:独立小模型逐 token 生成
核心思想
使用一个比 Target 更小的独立语言模型作为 drafter:
上下文 → 小模型生成 x₁ → 再生成 x₂ → … → 生成 xₖ
↓
Target 一次验证整个 block
特点
- Draft block 内保持完整的自回归依赖:
x₂能看到x₁,x₃能看到x₁,x₂。 - 通常需要
K次小模型前向,因此“小模型到底有多快”非常关键。 - 小模型和 Target 的分布越接近,候选接受率通常越高。
- 需要额外模型权重、显存以及可能独立的 KV cache。
一句话:让一个便宜的小模型先写草稿,大模型负责审稿。
4. Self-speculative:Target 跳层后自己生成
核心思想
不引入独立小模型,而是让 Target 自己走一条更便宜的路径,例如:
- 跳过若干 Transformer layer;
- 在中间层提前退出;
- 只执行选定层或轻量分支。
Draft 阶段一般仍然逐 token 自回归,只是每一步少做一部分 Target 计算;随后再由完整 Target 验证。
中间 hidden state 如何变成 token
中间层 hidden state 的宽度通常与最终层一致,所以可以经过:
中间 hidden state → final norm / exit adapter → LM head → vocabulary logits → token
这里的难点不是维度是否匹配,而是中间层特征未必已经落在最终 LM head 所期望的语义空间中。因此,有些方法需要专门训练 exit head 或 adapter;若直接复用最终 LM head,草稿质量可能下降。
特点
- 不需要保存一套完整的独立 drafter 权重。
- Draft 和 Target 天然共享 tokenizer、embedding 及部分表示。
- 每个 draft token 仍要运行一遍“削减版 Target”,当跳层后的网络仍较大时,速度收益可能不够。
一句话:Target 先用简化版的自己起草,再用完整版复核。
5. Medusa / MTP:多个未来 token head
Medusa 式并行预测
给 Target 的当前 hidden state 接多个预测 head:
当前 hidden state h
├─ Head 1:预测第 1 个未来 token
├─ Head 2:预测第 2 个未来 token
└─ Head K:预测第 K 个未来 token
这些 head 可以一次并行算完,因此 draft 开销很低。Medusa 还可从各 head 的 top-k 候选组成候选树,再让 Target 用 tree attention 验证。
它的主要限制是:预测第 k 个位置的 head 通常看不到前面实际选出来的 draft token,所以远期位置更容易预测不准。
MTP 不是都等于 Medusa
MTP(Multi-Token Prediction)只表示“同时或分阶段预测多个未来 token”。常见结构至少有两类:
- 独立并行 head:类似 Medusa,各位置主要从同一个上下文特征出发。
- 串行 MTP module:例如 DeepSeek 式结构,第
k个模块会结合前一未来 token 的表示,因此保留了一定的因果链,但不能简单视为所有位置完全同时预测。
一句话:Medusa 是并行多头 MTP 的代表,但 MTP 这个名称覆盖的范围更大。
6. EAGLE:使用 Target feature 的轻量 AR drafter
核心思想
EAGLE 不只给小模型输入离散 token,还把 Target 已经算出的高质量 feature 提供给一个轻量 drafter。Drafter 在 feature 空间中继续逐 token 自回归预测:
Target feature + 当前 token
↓
轻量 drafter → x₁ → x₂ → … → xₖ
↓
Target 一次验证
早期 EAGLE 主要预测下一步 feature,再通过 LM head 得到 token;后续版本也会利用 Target 多层 feature,直接加强 token 预测。
EAGLE可以视作
7. DFlash:一次并行生成整个 block
核心思想
DFlash 同样利用 Target feature,但不逐 token 跑 K 次 drafter,而是把一个 anchor token 和多个 mask 位置一起送入 block-level drafter:
Target feature + [anchor, MASK, MASK, …, MASK]
↓ 一次前向
[logits₁, logits₂, …, logitsₖ]
每个位置一次得到自己的 logits,从而直接提出整个 block。可以把它理解为:用比简单 MTP head 更强的并行网络,对多个未来位置进行联合表征,再分别输出 token。
它和 MTP 的差别
- 从广义上说,DFlash 仍属于多 token 预测。
- 与简单的多个线性 head 相比,DFlash 使用更深的 block-level backbone,让多个 mask 位置在预测前进行丰富计算。
- 但这些位置仍不知道前面位置最终采样出了什么 token,因此没有恢复真正的 token-level AR 条件链。
这也解释了为什么只做一次 block 计算可以显著改善并行 MTP,却不能完全等价于 AR drafter:它能学到“不同位置在给定上下文下应该是什么”,但难以处理一个位置存在多种合理选择、而后续必须跟随实际选择的情况。
一句话:以一次较强的并行计算,换掉 K 次轻量 AR 计算。
8. DSpark:并行 backbone + 轻量串行依赖 + 动态验证长度
核心思想
DSpark 先像 DFlash 一样,用并行 backbone 一次得到各位置的基础 logits:
U₁, U₂, …, Uₖ
然后加入一个成本极低的串行修正模块。例如一阶 Markov 版本可概括为:
最终 logitsₖ(v) = Uₖ(v) + B(xₖ₋₁, v)
其中:
Uₖ已经包含 Target 上下文和该未来位置的全局预测信息;B只根据前一个实际 draft token,对当前位置的词表分数做局部修正;B需要在 block 内每个位置调用一次,不是整个 block 只算一次,但它远小于 Transformer backbone。
还可以使用 RNN 类串行模块,把更长的 draft 前缀压进一个小状态中,而不只依赖前一个 token。
为什么这种修正可能有效
它并不是靠 B 单独重新预测 token。真正的主体仍是包含全局上下文的 Uₖ;B 只负责补充并行 backbone 缺失的局部因果依赖,例如搭配、语法衔接或“前一步选了哪个分支”。所以它可能用很少的串行计算获得明显收益。
但怀疑也是合理的:一阶 B 看不到完整 draft prefix,无法完全修复长距离或多步分支依赖;RNN 版本虽更强,也仍不等价于重新跑一次完整 AR Transformer。
动态验证长度
DSpark 还会根据各 draft 位置的置信度,动态选择值得交给 Target 验证的长度,避免把明显不可靠的长尾候选也送去验证。其目标不是单纯最大化接受 token 数,而是结合具体硬件上的 draft 成本、验证成本和吞吐收益来选长度。
一句话:重计算并行做,最必要的 token 依赖用极小串行模块补,再动态决定验证多长。
9. 用同一个例子对比
假设已确认上下文为 A B C D,现在希望提出 E F G:
- N-gram:在历史中搜索到
A B C D E F G,直接复制E F G。 - Standalone:小模型先生成
E,再基于E生成F,再基于E F生成G。 - Self-speculative:Target 的跳层版按同样的 AR 顺序生成
E F G。 - Medusa:从当前 hidden state 用三个 head 同时预测位置
E/F/G。 - EAGLE:把 Target feature 交给轻量 drafter,再按
E → F → G串行生成。 - DFlash:对三个未来槽位做一次 block 前向,并行输出
E/F/G的 logits。 - DSpark:先并行得到三个位置的基础 logits,再用
D→E、E→F、F→G的轻量依赖逐步修正。
10. 最重要的记忆框架
不要只按方法名背诵,可以用两条轴来判断:
轴一:Drafter 从哪里获得信息?
- 只用历史文本:N-gram。
- 使用独立小模型状态:Standalone。
- 使用 Target 自身部分计算:Self-speculative。
- 使用 Target feature:Medusa、EAGLE、DFlash、DSpark。
轴二:Block 内依赖如何建立?
- 完整 AR 串行:Standalone、Self-speculative、EAGLE。
- 完全或近似并行:Medusa、并行 MTP、DFlash。
- 并行主体 + 极轻串行修正:DSpark。
- 检索整段而非生成:N-gram。
最终可以压缩为七句话:
- N-gram:从历史中检索并复制候选片段。
- Standalone:独立小模型逐 token 自回归起草。
- Self-speculative:Target 通过跳层或提前退出,逐 token 给自己起草。
- Medusa / MTP:从当前表示预测多个未来 token;具体结构既可能并行,也可能带串行模块。
- EAGLE:使用 Target feature 的轻量自回归 drafter。
- DFlash:使用 Target feature,一次 block 前向并行预测全部未来位置。
- DSpark:并行预测主体加极轻串行依赖,并按置信度和硬件收益动态选择验证长度。
参考资料
- Leviathan et al., Fast Inference from Transformers via Speculative Decoding:https://arxiv.org/abs/2211.17192
- Draft & Verify(Self-speculative):https://arxiv.org/abs/2309.08168
- Medusa:https://arxiv.org/abs/2401.10774
- DeepSeek-V3(MTP):https://arxiv.org/abs/2412.19437
- EAGLE-3:https://arxiv.org/abs/2503.01840
- DFlash:https://arxiv.org/abs/2602.06036
- DSpark:https://arxiv.org/abs/2607.05147
Comments
No comments yet.