1. 自回归解码的瓶颈

1.1 逐 token 生成的低效

LLM 自回归生成:每次前向传播只产生 1 个 token。对于 $N$ 个 token 的输出,需要 $N$ 次串行前向传播。

每次前向传播的计算量:

$$\text{FLOPS}{\text{per step}} \approx 2 \times \text{Params}{\text{active}}$$

但实际 GPU 利用率很低——单 token 推理是内存带宽受限的:

$$\text{Compute intensity} = \frac{\text{FLOPS}}{\text{Bytes loaded}} = \frac{2N}{N \times \text{dtype_size}} = \frac{2}{\text{dtype_size}}$$

FP16 下计算强度仅为 1 FLOP/byte,远低于 GPU 的算力/带宽比(A100: 312 TFLOPS / 2TB/s = 156 FLOP/byte)。GPU 大部分时间在等待内存加载权重

1.2 理想情况

如果能一次前向传播生成 $k$ 个 token,就能将权重复用 $k$ 次,计算强度提升 $k$ 倍。这就是推测解码的核心动机。

2. 推测解码原理

2.1 基本思想

推测解码(Speculative Decoding)使用两个模型:

  • Target Model:大而精确的目标模型(如 LLaMA-2 70B)
  • Draft Model:小而快速的草稿模型(如 LLaMA-2 7B 或 1B)

流程:

1. Draft Model 自回归快速生成 k 个 token: [t1, t2, ..., tk]
2. Target Model 一次性对 [prompt + t1, ..., tk] 做前向传播
3. Target Model 的输出可以验证 t1..tk 的正确性
4. 接受匹配的 token,拒绝不匹配的,从第一个拒绝点重新生成

2.2 为什么能加速

关键洞察:Target Model 一次处理 $k+1$ 个 token 的成本远小于 $k+1$ 次单 token 推理

方式前向传播次数每次处理 token 数总权重加载次数
标准自回归$k$1$k$
推测解码1$k+1$1

即使 Target Model 要处理更多 token,但权重只加载一次,内存带宽利用大幅提升。

2.3 接受/拒绝机制

Draft Model 生成 token 时也输出了概率分布 $q(x)$,Target Model 输出概率分布 $p(x)$。接受规则:

$$\text{accept}(x) \text{ with probability } \min\left(1, \frac{p(x)}{q(x)}\right)$$

如果拒绝,从残差分布中重新采样:

$$p_{\text{residual}}(x) = \frac{\max(0, p(x) - q(x))}{\sum_{x’} \max(0, p(x’) - q(x’))}$$

重要性质:这种接受/拒绝机制保证推测解码的输出分布与纯 Target Model 完全一致——零质量损失。

import torch
import torch.nn.functional as F

def speculative_sampling(draft_logits, target_logits, draft_token):
    """推测解码的接受/拒绝采样"""
    draft_prob = F.softmax(draft_logits, dim=-1)
    target_prob = F.softmax(target_logits, dim=-1)
    
    # 接受概率
    accept_prob = torch.minimum(
        torch.ones_like(draft_prob),
        target_prob / (draft_prob + 1e-10)
    )
    
    # 随机决定是否接受
    rand = torch.rand_like(draft_prob)
    accepted = rand < accept_prob
    
    if accepted[draft_token]:
        return draft_token  # 接受 Draft Model 的 token
    else:
        # 从残差分布重新采样
        residual = torch.clamp(target_prob - draft_prob, min=0)
        residual = residual / residual.sum()
        return torch.multinomial(residual, 1).squeeze()

3. 加速效果分析

3.1 接受率

设 Draft Model 的每个 token 被接受的概率为 $\alpha$(接受率),则 $k$ 个推测 token 中平均接受的数量为:

$$E[\text{accepted}] = \frac{1 - \alpha^{k+1}}{1 - \alpha} - 1$$

当 $\alpha = 0.8$, $k = 4$ 时:平均接受 2.95 个 token,每次 Target Model 前向传播产出 3.95 个 token。

3.2 加速比

设 Draft Model 速度是 Target Model 的 $1/\beta$ 倍($\beta \ll 1$ 表示 Draft Model 快得多),则理论加速比:

$$\text{Speedup} \approx \frac{E[\text{accepted}] + 1}{1 + \beta \cdot k}$$

接受率 $\alpha$推测长度 $k$$\beta$ (Draft/Target 速度比)理论加速比
0.540.11.6×
0.740.12.3×
0.840.12.8×
0.940.13.2×
0.880.13.5×
0.840.22.0×

关键结论:接受率是决定性因素。接受率 >0.7 时加速效果显著。

3.3 影响接受率的因素

因素影响原因
Draft 模型与 Target 模型相似度分布更接近
任务类型贪婪生成 > 创意生成贪婪更可预测
上下文复杂度简单上下文 > 复杂上下文简单上下文两个模型更易一致
推测长度短 > 长越长累积偏差越大
温度低温 > 高温低温接近贪婪,更可预测

4. Medusa:多头并行推测

4.1 问题

传统推测解码需要两个模型,增加系统复杂度和内存占用。Draft Model 的选择也需要工程考量。

4.2 Medusa 方案

Medusa 在 Target Model 之上增加 $k$ 个预测头,每个头预测未来第 $i$ 个 token:

Target Model 隐状态
    ├── LM Head → t0 (当前 token)
    ├── Medusa Head 1 → t1 (下一个 token)
    ├── Medusa Head 2 → t2
    └── Medusa Head 3 → t3

一次前向传播直接得到多个候选未来 token,无需独立 Draft Model。

class MedusaHead(nn.Module):
    """Medusa 预测头"""
    def __init__(self, d_model, vocab_size):
        super().__init__()
        self.linear = nn.Linear(d_model, d_model)
        self.lm_head = nn.Linear(d_model, vocab_size)
        
    def forward(self, hidden_state):
        x = F.silu(self.linear(hidden_state))
        return self.lm_head(x)

class MedusaModel(nn.Module):
    def __init__(self, base_model, num_heads=4):
        super().__init__()
        self.base_model = base_model
        self.medusa_heads = nn.ModuleList([
            MedusaHead(base_model.config.hidden_size, base_model.config.vocab_size)
            for _ in range(num_heads)
        ])
        
    def forward(self, input_ids):
        # 基座模型前向传播
        outputs = self.base_model(input_ids, output_hidden_states=True)
        hidden = outputs.hidden_states[-1]
        
        # 基座预测当前 token
        base_logits = self.base_model.lm_head(hidden)
        
        # Medusa 头预测未来 token
        medusa_logits = [head(hidden) for head in self.medusa_heads]
        return base_logits, medusa_logits

4.3 Medusa 的优势

特性传统推测解码Medusa
额外模型需要 Draft Model无需
内存开销额外 Draft Model 参数仅几个额外线性层
推测方式串行自回归并行一次出多个
训练需求Draft Model 预训练Medusa 头需微调
加速比2-3×2-3× (相当)

5. EAGLE

5.1 改进

EAGLE 在 Medusa 基础上进一步改进:

  1. 使用隐状态而非 token:Draft 头基于 Target Model 的隐状态预测,信息更丰富
  2. 自回归推测:Medusa 头的输出反馈给自身,支持更长的推测链
  3. 树状推测:生成多个候选路径,Target Model 一次验证整棵树

5.2 树状推测

                    t0
                   / | \
              t1a  t1b  t1c
              / \   |    |
          t2a t2b t2c  t2d

Target Model 一次性验证所有路径上的 token,选择最长的接受路径。

5.3 性能对比

方法推测长度加速比额外参数
标准推测解码4-82-3×Draft Model (~1B)
Medusa42-3×~200M
EAGLE8+3-4×~200M

6. vLLM 中的推测解码实现

6.1 使用方式

from vllm import LLM, SamplingParams

# 配置推测解码
llm = LLM(
    model="meta-llama/Llama-2-70b-hf",
    speculative_model="meta-llama/Llama-2-7b-hf",  # Draft Model
    num_speculative_tokens=5,  # 推测长度
    use_v2_block_manager=True,
)

sampling_params = SamplingParams(
    temperature=0.0,  # 贪婪解码接受率最高
    max_tokens=1000,
)

outputs = llm.generate(["请解释量子计算的基本原理"], sampling_params)

6.2 vLLM 推测解码架构

┌─────────────────────────────────────────┐
│           vLLM 推理引擎                  │
│                                         │
│  ┌──────────┐    ┌──────────────────┐  │
│  │ Draft     │───→│ Target Model     │  │
│  │ Model     │    │ (单次前向验证)    │  │
│  │ (快速生成) │    │                  │  │
│  └──────────┘    └──────────────────┘  │
│       │                   │             │
│       ▼                   ▼             │
│   [t1,t2,t3,t4,t5]  ← 验证 & 接受/拒绝  │
│                                │         │
│                                ▼         │
│                    [accepted tokens]     │
│                                │         │
│                                ▼         │
│                    更新 KV Cache         │
└─────────────────────────────────────────┘

6.3 性能数据

vLLM 官方基准测试(LLaMA-2 70B + 7B Draft):

场景标准解码推测解码加速比
代码生成 (temp=0)28 tok/s85 tok/s3.0×
文本摘要 (temp=0)28 tok/s72 tok/s2.6×
创意写作 (temp=0.7)28 tok/s45 tok/s1.6×
对话 (temp=0.7)28 tok/s52 tok/s1.9×

代码/摘要等可预测性高的任务加速最明显,创意生成因接受率低而加速有限。

7. 推测解码的适用场景

7.1 高收益场景

场景原因加速预期
代码生成语法规则强,Draft 可预测2.5-3.5×
文本摘要结构化输出2-3×
问答系统答案可预测性较高2-2.5×
翻译逐句对应2-3×
贪婪解码接受率最高2.5-3.5×

7.2 低收益场景

场景原因加速预期
创意写作高温度,不可预测1.2-1.6×
多样化采样高温度采样1.1-1.4×
极短输出开销占比大<1.2×
Draft Model 太大推测成本高<1.5×

8. 总结

推测解码是当前最实用的 LLM 推理加速技术之一,它通过"快速推测 + 批量验证"的范式,在不损失输出质量的前提下实现 2-3× 加速。核心限制是接受率——Draft Model 与 Target Model 的分布一致性决定加速上限。Medusa 和 EAGLE 通过消除独立 Draft Model、引入树状推测,进一步简化了部署并提升了效率。对于代码生成、摘要等可预测性高的任务,推测解码几乎是"免费的加速"。在 vLLM 等推理框架中,推测解码已开箱即用,是生产部署的标准加速选项。

加入讨论

这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。

碳基与硅基的智慧碰撞,认知差异创造无限可能。