Zer0e's Blog

去TM的AI其六:什么是投机解码 (Speculative Decoding)

字数统计: 3.7k阅读时长: 15 min
2026/08/16 Share

什么是投机解码 (Speculative Decoding)

投机解码(Speculative Decoding)是大语言模型推理加速领域的一项创新技术,它通过”先猜后验”的策略,在保持生成质量不变的前提下,将推理速度提升 2-3 倍。这项技术由 Google 等团队在 2023 年提出,迅速成为推理优化的重要方向。

为什么要提出投机解码?

传统自回归生成的瓶颈

大语言模型生成文本时采用的是自回归(Auto-regressive)方式:

1
2
3
4
5
第1步: 输入 "今天天气" → 模型计算 → 输出 "真"
第2步: 输入 "今天天气真" → 模型计算 → 输出 "好"
第3步: 输入 "今天天气真好" → 模型计算 → 输出 ","
第4步: 输入 "今天天气真好," → 模型计算 → 输出 "适合"
...

问题在哪里?

每个 token 的生成都需要:

  1. 将完整序列输入模型
  2. 执行一次完整的前向传播(Forward Pass)
  3. 从输出分布中采样一个 token
  4. 重复上述过程
flowchart TD
    A[输入序列] --> B["模型前向传播
计算成本高"] B --> C["采样 1 个 token"] C --> D[追加到序列] D --> B style B fill:#ffcdd2 style C fill:#ffe0b2 E[生成 N 个 token] --> F["需要 N 次前向传播
速度受限"] style F fill:#ff8a65,color:#fff

核心瓶颈:串行生成,每次只能产生 1 个 token

对于一个 1000 token 的生成任务,即使模型计算再快,也至少需要 1000 次串行推理步骤。这就是延迟瓶颈。

投机解码的核心洞察

关键发现:很多 token 的生成是”可预测”的

比如:

  • “人工”后面大概率是”智能”
  • “今天天气”后面很可能是”真好”
  • “hello”后面通常是”world”

投机解码的想法:用一个小的”草稿模型”快速猜测多个 token,然后用大模型一次性验证

flowchart TD
    A[传统方式] --> B1["大模型逐个生成
1000 次前向传播"] A --> B2["速度慢
延迟高"] C[投机解码] --> D1["小模型快速猜测 K 个"] C --> D2["大模型并行验证 K 个"] C --> D3["大幅减少前向传播次数"] B1 -.优化.-> D1 B2 -.优化.-> D3 style A fill:#ffebee style C fill:#e8f5e9 style D1 fill:#66bb6a,color:#fff style D2 fill:#42a5f5,color:#fff style D3 fill:#ffa726,color:#fff

投机解码的核心思想

投机解码采用Draft then Verify(先草稿后验证)的策略:

两模型架构

  1. 草稿模型(Draft Model)

    • 小模型(如 1B-3B 参数)
    • 快速生成多个候选 token
    • 可以是 distilled 模型或同一模型的早期层
  2. 目标模型(Target Model)

    • 大模型(如 7B-70B 参数)
    • 验证草稿的准确性
    • 保证生成质量

工作流程

flowchart TD
    subgraph Phase1["阶段1: 草稿生成"]
        A1[输入序列] --> B1[草稿模型]
        B1 --> C1["快速生成 K 个 token
x₁, x₂, ..., x_K"] end subgraph Phase2["阶段2: 并行验证"] A2[输入 + K 个草稿 token] --> B2[目标模型] B2 --> C2["一次性计算 K 个位置的分布"] C2 --> D2["验证每个草稿 token
是否匹配目标分布"] end subgraph Phase3["阶段3: 接受或拒绝"] A3[验证结果] --> B3{草稿是否正确?} B3 -->|接受| C3[保留正确的 token] B3 -->|拒绝| D3[从目标分布重新采样] C3 --> E3[继续下一轮] D3 --> E3 end Phase1 --> Phase2 Phase2 --> Phase3 style Phase1 fill:#fff3e0 style Phase2 fill:#e3f2fd style Phase3 fill:#e8f5e9 style C1 fill:#ffcc80 style C2 fill:#90caf9 style C3 fill:#a5d6a7 style D3 fill:#ef9a9a

投机解码的详细算法

算法步骤

假设置信度阈值为 $p$,草稿长度为 $K$:

步骤 1:草稿生成

草稿模型自回归生成 $K$ 个 token:

$$\hat{x}_1, \hat{x}_2, …, \hat{x}_K \sim P_{\text{draft}}(x | x_{1:t})$$

步骤 2:并行验证

目标模型一次性计算这 $K$ 个位置的概率分布:

$$P_{\text{target}}(x_1 | x_{1:t}),\ P_{\text{target}}(x_2 | x_{1:t}, \hat{x}_1),\ …,\ P_{\text{target}}(x_K | x_{1:t}, \hat{x}_{1:K-1})$$

步骤 3:投机接受

对于第 $i$ 个草稿 token $\hat{x}_{i}$,计算接受概率:

$$\alpha_i = \min\left(1,\ \frac{P_{\text{target}}(\hat{x}_i | x_{1:t+i-1})}{P_{\text{draft}}(\hat{x}_i | x_{1:t+i-1})}\right)$$

以概率 $\alpha_{i}$ 接受 $\hat{x}_{i}$,否则拒绝并从目标分布重新采样。

flowchart TD
    A[草稿 token x̂ᵢ] --> B["计算 P_target(x̂ᵢ)"]
    A --> C["计算 P_draft(x̂ᵢ)"]
    B --> D["α = min(1, P_target / P_draft)"]
    C --> D
    
    D --> E{随机数 r < α?}
    E -->|是| F["✅ 接受 x̂ᵢ"]
    E -->|否| G["❌ 拒绝
从 P_target 采样"] F --> H[继续验证下一个] G --> I[终止验证,从该位置继续] style D fill:#ffe0b2 style E fill:#ce93d8 style F fill:#a5d6a7 style G fill:#ef9a9a

一个具体示例

假设我们要生成”人工智能是未来的”:

传统方式:

1
2
3
4
5
6
输入: "人工"
→ 大模型推理 1 次 → "智能"
→ 大模型推理 1 次 → "是"
→ 大模型推理 1 次 → "未来"
→ 大模型推理 1 次 → "的"
总计: 4 次大模型推理

投机解码(K=3):

1
2
3
4
5
6
7
8
9
输入: "人工"
→ 小模型快速生成 3 个草稿: ["智能", "是", "未来"]
→ 大模型一次性验证 3 个位置
- "智能": P_target=0.85, P_draft=0.70, α=1.0 → ✅ 接受
- "是": P_target=0.92, P_draft=0.60, α=1.0 → ✅ 接受
- "未来": P_target=0.45, P_draft=0.80, α=0.56 → ❌ 拒绝,重新采样
→ 实际接受 2 个 token,第 3 个重新采样
总计: 1 次小模型推理 + 1 次大模型推理 = 生成 3 个 token
加速比: ~3x
flowchart LR
    subgraph Traditional["传统方式"]
        A1["大模型×4
4 次推理"] --> B1["输出 4 个 token"] end subgraph Speculative["投机解码"] A2["小模型×1"] --> B2["草稿 3 个 token"] B2 --> C2["大模型×1 验证"] C2 --> D2["接受 2 个 + 采样 1 个"] end style Traditional fill:#ffebee style Speculative fill:#e8f5e9 style A1 fill:#ffcdd2 style C2 fill:#66bb6a,color:#fff style D2 fill:#42a5f5,color:#fff

加速比分析

理论加速比

设:

  • $K$:草稿长度
  • $\alpha$:平均接受率(0 到 1 之间)
  • $T_{\text{draft}}$:草稿模型推理时间
  • $T_{\text{target}}$:目标模型推理时间

每轮生成的期望 token 数:

$$
E[\text{tokens per round}] = \sum_{i=1}^{K} \prod_{j=1}^{i} \alpha_j \approx \frac{1 - \alpha^{K+1}}{1 - \alpha}
$$

加速比:

$$
\text{Speedup} \approx \frac{K \cdot T_{\text{target}}}{T_{\text{draft}} + T_{\text{target}}} \cdot \alpha_{\text{avg}}
$$

实际性能数据

以 LLaMA-70B(目标)+ LLaMA-7B(草稿)为例:

场景 草稿长度 K 接受率 α 加速比
代码生成 4 0.85 3.2x
中文文本 3 0.75 2.5x
数学推导 2 0.60 1.8x
创意写作 3 0.65 2.1x
flowchart TD
    A[投机解码性能] --> B1["代码生成: 3.2x"]
    A --> B2["中文文本: 2.5x"]
    A --> B3["创意写作: 2.1x"]
    A --> B4["数学推导: 1.8x"]
    
    B1 --> C["草稿质量越高,加速比越大"]
    B2 --> C
    B3 --> C
    B4 --> C
    
    style A fill:#ff7043,color:#fff
    style B1 fill:#66bb6a,color:#fff
    style B2 fill:#42a5f5,color:#fff
    style B3 fill:#ab47bc,color:#fff
    style B4 fill:#ffa726
    style C fill:#26c6da

关键规律:

  • 草稿模型与目标模型越接近,接受率越高
  • 结构化强的任务(代码、翻译)接受率更高
  • 创意性强的任务接受率相对较低

投机解码的变体

1. Medusa(多头推测解码)

Medusa 在目标模型顶部添加多个”解码头”,每个头预测不同位置的 token:

flowchart TD
    A[目标模型 backbone] --> B1[Head 0: 预测 t+1]
    A --> B2[Head 1: 预测 t+2]
    A --> B3[Head 2: 预测 t+3]
    A --> B4[Head 3: 预测 t+4]
    
    B1 --> C1[无需草稿模型]
    B2 --> C1
    B3 --> C1
    B4 --> C1
    
    C1 --> D[一次性生成多个候选]
    D --> E[目标模型验证]
    
    style A fill:#ffa726
    style B1 fill:#66bb6a
    style B2 fill:#66bb6a
    style B3 fill:#66bb6a
    style B4 fill:#66bb6a
    style E fill:#42a5f5,color:#fff

优势:

  • 不需要额外的草稿模型
  • 所有头共享 backbone 表示
  • 训练成本低

2. EAGLE(Extra Acceleration through Generation with Language Enhancement)

EAGLE 通过以下方式进一步优化:

  • 使用目标模型的隐藏状态作为草稿模型的输入
  • 动态调整草稿长度
  • 更好的特征对齐

3. 自投机解码(Self-Speculative Decoding)

同一个模型既当草稿模型又当目标模型:

  • 使用模型的早期层作为草稿
  • 使用完整模型作为验证
  • 无需额外模型,但加速比相对较低
flowchart TD
    A[输入] --> B["模型层 1-10
草稿"] B --> C["生成 K 个 token"] C --> D["模型层 1-32
完整验证"] D --> E[接受或拒绝] style B fill:#ffcc80 style D fill:#90caf9 style E fill:#a5d6a7

代码实现(简化版)

下面是一个投机解码的简化实现,帮助理解核心逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
import torch
import torch.nn.functional as F

def speculative_decode(draft_model, target_model, input_ids, K=3, max_steps=100):
"""
投机解码简化实现

Args:
draft_model: 草稿模型(小模型)
target_model: 目标模型(大模型)
input_ids: 输入序列
K: 草稿长度
max_steps: 最大生成步数
"""
generated_tokens = []
current_ids = input_ids.clone()

for step in range(max_steps):
# 步骤 1: 草稿模型生成 K 个 token
draft_tokens = []
draft_probs = []

temp_ids = current_ids.clone()
for k in range(K):
with torch.no_grad():
draft_output = draft_model(temp_ids)
draft_logits = draft_output.logits[:, -1, :]
draft_prob = F.softmax(draft_logits, dim=-1)

# 采样
draft_token = torch.multinomial(draft_prob, 1)
draft_tokens.append(draft_token.item())
draft_probs.append(draft_prob)

temp_ids = torch.cat([temp_ids, draft_token], dim=1)

# 步骤 2: 目标模型并行验证
with torch.no_grad():
target_output = target_model(temp_ids)
target_logits = target_output.logits[:, -K-1:-1, :]
target_probs = F.softmax(target_logits, dim=-1)

# 步骤 3: 投机接受
accepted_tokens = []
for k in range(K):
draft_token = draft_tokens[k]
draft_prob = draft_probs[k][0, draft_token].item()
target_prob = target_probs[0, k, draft_token].item()

# 计算接受概率
alpha = min(1.0, target_prob / draft_prob)

if torch.rand(1).item() < alpha:
# 接受
accepted_tokens.append(draft_token)
else:
# 拒绝,从目标分布采样
new_token = torch.multinomial(target_probs[0, k], 1).item()
accepted_tokens.append(new_token)
break # 拒绝后停止验证

# 追加接受的 token
if accepted_tokens:
accepted_tensor = torch.tensor([accepted_tokens], device=current_ids.device)
current_ids = torch.cat([current_ids, accepted_tensor], dim=1)
generated_tokens.extend(accepted_tokens)

# 如果生成了结束符,提前终止
if generated_tokens and generated_tokens[-1] == target_model.config.eos_token_id:
break

return generated_tokens

投机解码 vs 其他推理加速技术

技术 加速原理 加速比 质量损失 适用场景
投机解码 小模型猜测+大模型验证 2-3x 无 通用场景
KV Cache 缓存历史 K/V 2-4x 无 长序列生成
Flash Attention IO 优化计算 2-4x 无 长序列计算
量化 降低精度 2-3x 微小 资源受限
连续批处理 动态 batching 10-20x 无 高吞吐服务
flowchart TD
    A[推理加速技术] --> B1["投机解码
算法级优化"] A --> B2["KV Cache
内存优化"] A --> B3["Flash Attention
计算优化"] A --> B4["量化
精度优化"] B1 --> C["质量无损
2-3x 加速"] B2 --> D["质量无损
2-4x 加速"] B3 --> D B4 --> E["微小损失
2-3x 加速"] style A fill:#ff7043,color:#fff style B1 fill:#66bb6a,color:#fff style C fill:#42a5f5,color:#fff

关键洞察:这些技术可以组合使用!

1
2
3
4
5
6
典型的高性能推理栈:
vLLM 框架
├── Continuous Batching(吞吐优化)
├── PagedAttention(内存管理)
├── Flash Attention(计算加速)
└── Speculative Decoding(延迟优化)← 本文主题

实际应用场景

1. 交互式对话

flowchart LR
    A[用户输入] --> B[投机解码生成回复]
    B --> C["响应时间减少 50%"]
    C --> D[提升用户体验]
    
    style A fill:#bbdefb
    style B fill:#66bb6a,color:#fff
    style D fill:#ffa726

效果:

  • GPT-4 级别模型的响应时间从 2 秒降到 0.7 秒
  • 实时对话更流畅

2. 代码补全

  • IDE 中的代码建议需要极低延迟
  • 代码具有强结构性,草稿接受率高(0.8-0.9)
  • 加速比可达 3x 以上

3. 批量翻译

  • 翻译任务模式固定,投机解码效果好
  • 结合 Continuous Batching 可实现高吞吐

4. 边缘设备部署

  • 小模型在端侧,大模型在云端
  • 端侧生成草稿,云端验证
  • 降低云端计算压力

投机解码的局限性

1. 需要额外的草稿模型

flowchart TD
    A[投机解码要求] --> B1["需要训练/选择草稿模型"]
    A --> B2["草稿模型质量影响加速比"]
    A --> B3["模型不匹配时效果差"]
    
    B1 --> C["增加系统复杂度"]
    B2 --> C
    B3 --> C
    
    style A fill:#ffcc80
    style C fill:#ef9a9a

2. 任务依赖性

  • 结构化任务(代码、翻译):接受率高,加速明显
  • 创意任务(故事、诗歌):接受率低,加速有限

3. 实现复杂度

  • 需要协调两个模型的推理
  • 需要处理接受/拒绝逻辑
  • 推理框架需要专门支持

4. 显存占用

  • 需要同时加载草稿模型和目标模型
  • 对于超大模型(如 175B),可能显存不足

解决方案:Medusa(不需要额外模型)或 Self-Speculative(同一模型不同层)

未来发展方向

timeline
    title 投机解码发展历程
    2023-01 : Google 提出 Speculative Decoding 理论框架
    2023-06 : Medusa 发布 多解码头方法
    2023-09 : EAGLE 提出 隐藏状态对齐优化
    2024-01 : Self-Speculative Decoding 单模型方案
    2024-06 : 主流推理框架支持 vLLM/TGI 集成
    2025-? : 自适应草稿长度 动态优化策略

研究热点

  1. 自适应草稿长度

    • 根据置信度动态调整 $K$
    • 高置信度时增加草稿长度
    • 低置信度时减少草稿长度
  2. 多草稿并行

    • 同时生成多组草稿
    • 选择接受率最高的草稿
  3. 与量化结合

    • 草稿模型用 INT4
    • 目标模型用 FP16
    • 进一步降低延迟
  4. 流式生成优化

    • Token-by-token 流式输出
    • 结合 speculative 实现超低延迟

总结

投机解码通过”先猜后验”的策略,在不损失生成质量的前提下,将大语言模型的推理速度提升 2-3 倍。

flowchart TD
    A[投机解码核心优势] --> B1["质量无损
数学保证"] A --> B2["延迟降低
2-3x 加速"] A --> B3["通用性强
适用于各种模型"] A --> B4["可组合
与其他加速技术协同"] B1 --> C[高效大模型推理] B2 --> C B3 --> C B4 --> C C --> D1[交互式应用] C --> D2[代码补全] C --> D3[实时翻译] C --> D4[批量生成] style A fill:#ff7043,color:#fff style C fill:#42a5f5,color:#fff style D1 fill:#66bb6a style D2 fill:#ab47bc style D3 fill:#26c6da style D4 fill:#ffa726

核心价值:

  • ✅ 数学保证:接受策略确保生成分布与目标模型完全一致
  • ✅ 即插即用:无需修改模型架构,适用于任何自回归模型
  • ✅ 生态成熟:vLLM、TGI 等主流框架已集成
  • ✅ 组合优化:可与 KV Cache、Flash Attention、量化等技术叠加

投机解码代表了推理优化的一个重要方向:通过算法创新,在保持质量的同时突破性能瓶颈。随着技术的不断演进,它将在大模型落地中发挥越来越重要的作用。

参考资料

  1. Chen, C., et al. (2023). “Accelerating Large Language Model Decoding with Speculative Sampling”
  2. Leviathan, Y., et al. (2023). “Fast Inference from Transformers via Speculative Decoding”
  3. Cai, T., et al. (2023). “Medusa: Simple LLM Inference Acceleration Framework with Multiple Decoding Heads”
  4. Li, Y., et al. (2024). “EAGLE: Speculative Sampling Requires Rethinking Feature Uncertainty”
  5. vLLM Documentation: Speculative Decoding Integration
CATALOG
  1. 1. 什么是投机解码 (Speculative Decoding)
    1. 1.1. 为什么要提出投机解码?
      1. 1.1.1. 传统自回归生成的瓶颈
      2. 1.1.2. 投机解码的核心洞察
    2. 1.2. 投机解码的核心思想
      1. 1.2.1. 两模型架构
      2. 1.2.2. 工作流程
    3. 1.3. 投机解码的详细算法
      1. 1.3.1. 算法步骤
      2. 1.3.2. 一个具体示例
    4. 1.4. 加速比分析
      1. 1.4.1. 理论加速比
      2. 1.4.2. 实际性能数据
    5. 1.5. 投机解码的变体
      1. 1.5.1. 1. Medusa(多头推测解码)
      2. 1.5.2. 2. EAGLE(Extra Acceleration through Generation with Language Enhancement)
      3. 1.5.3. 3. 自投机解码(Self-Speculative Decoding)
    6. 1.6. 代码实现(简化版)
    7. 1.7. 投机解码 vs 其他推理加速技术
    8. 1.8. 实际应用场景
      1. 1.8.1. 1. 交互式对话
      2. 1.8.2. 2. 代码补全
      3. 1.8.3. 3. 批量翻译
      4. 1.8.4. 4. 边缘设备部署
    9. 1.9. 投机解码的局限性
      1. 1.9.1. 1. 需要额外的草稿模型
      2. 1.9.2. 2. 任务依赖性
      3. 1.9.3. 3. 实现复杂度
      4. 1.9.4. 4. 显存占用
    10. 1.10. 未来发展方向
      1. 1.10.1. 研究热点
    11. 1.11. 总结
    12. 1.12. 参考资料