KaiSpace
tech

投机解码方法笔记

0. 先统一概念

投机解码(Speculative Decoding)的共同框架是:

  1. 用一个更便宜的过程先提出一段候选 token,记作长度为 K 的 draft block。
  2. Target model 对整段候选做一次并行验证。
  3. 从左到右接受通过验证的最长前缀;遇到第一个不通过的位置就停止接受,并按相应规则采样修正 token。

下文中:

  • Target:最终负责保证输出正确性的目标大模型。
  • Drafter:负责廉价提出候选的模块或方法。
  • AR:autoregressive,自回归;后一个 draft token 依赖前一个已经生成的 draft token。
  • K:一次尝试生成并验证的候选 token 数。

1. 总览

方法Draft 信息来源K 个候选如何产生是否显式依赖前一个 draft token主要计算形态
N-gramPrompt、已生成历史或外部语料检索并复制连续片段不是神经网络建模,而是整段匹配查表/检索
Standalone独立小语言模型逐 token 自回归生成K 次小模型前向
Self-speculativeTarget 自身的部分层或提前退出分支逐 token 自回归生成K 次不完整 Target 前向
Medusa / 并行 MTPTarget 的当前 hidden state多个未来位置 head 同时预测通常否一次并行多头预测
EAGLETarget feature + 已生成 token轻量 drafter 逐 token生成K 次轻量 AR 前向
DFlashTarget feature + anchor token + mask block一次并行预测整个 block一次较深的并行 drafter 前向
DSparkTarget 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”。常见结构至少有两类:

  1. 独立并行 head:类似 Medusa,各位置主要从同一个上下文特征出发。
  2. 串行 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→EE→FF→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。

最终可以压缩为七句话:

  1. N-gram:从历史中检索并复制候选片段。
  2. Standalone:独立小模型逐 token 自回归起草。
  3. Self-speculative:Target 通过跳层或提前退出,逐 token 给自己起草。
  4. Medusa / MTP:从当前表示预测多个未来 token;具体结构既可能并行,也可能带串行模块。
  5. EAGLE:使用 Target feature 的轻量自回归 drafter。
  6. DFlash:使用 Target feature,一次 block 前向并行预测全部未来位置。
  7. DSpark:并行预测主体加极轻串行依赖,并按置信度和硬件收益动态选择验证长度。

参考资料

Comments

No comments yet.