在这里插入图片描述

        前面三篇推理优化我们讲了:

  • 第 11 篇 三板斧(KV Cache / PagedAttention / Continuous Batching)—— 优化吞吐

  • 第 12 篇 量化压缩 —— 优化显存

  • 第 13 篇 Flash Attention —— 优化 prefill 延迟

        这一篇我们讲一个专门优化 decode 延迟的奇思妙想——

投机解码(Speculative Decoding)。

        它的神奇之处在于:用一种「先猜后验」的方式,让单个序列的生成速度提升 2-3 倍,而且数学上完全无损

        更神奇的是:它的核心思想极其简单——让小模型先草拟,大模型再验证——但这个简单想法背后是对 GPU 计算特性的深刻理解。

        如果你做过相关工作,下面这些问题应该不陌生:

  •  单用户场景下,70B 模型怎么跑得更快?

  •  为什么投机解码在 batch 大时反而没用?

  •  Medusa、EAGLE、Lookup Decoding 都是什么?

  •  投机解码不会改变输出吗?真的"无损"?

  •  vLLM 怎么开启投机解码?

        读完本文你将能:

  1.  理解投机解码的核心洞察——为什么验证比生成便宜

  2.  区分 4 种主流变种的差异、适用场景

  3.  用 vLLM 实际启用投机解码并实测

  4.  知道何时该用、何时不该用

        我们开始。


        一、为什么 Decode 是个独特的瓶颈

        1.1 Decode 阶段的特殊性

        回顾第 11 篇我们讲的 Prefill vs Decode:

阶段

计算特性

主要瓶颈

Prefill

一次处理 n 个 token,并行度高

算力(compute-bound)

Decode

一次处理 1 个 token,串行生成

显存带宽(memory-bound)

        Decode 阶段的核心痛苦:

每生成 1 个 token,都要把整个模型权重从 HBM 读到 SRAM 一遍。

        对 Llama-3-70B(INT8,70 GB 权重):

  • 单次 decode 必须读 70 GB

  • H100 显存带宽 3.35 TB/s

  • 理论最大速度:3350 / 70 ≈ 48 tokens/s(单序列)

        这是个硬上限——再怎么优化,单序列也快不过这个数。

        1.2 batch 救不了单用户

        第 11 篇我们讲过 Continuous Batching 能把吞吐量提到几千 tokens/s。但那是 batch 整体——单用户感知的速度仍然是 ~50 tokens/s。

单用户视角:48 tokens/s 在生成
batch 整体:4800 tokens/s(100 用户并发)

        对单用户来说,batch 不能加速我的请求。这就是为什么 ChatGPT 那种聊天场景,"打字速度"是个绕不开的体验痛点。

        1.3 GPU 的「隐藏算力」

        但其实 GPU 在 decode 阶段算力是闲置的

        H100 的算力 vs 带宽:

  • FP16 算力:989 TFLOPS

  •  带宽:3.35 TB/s

  •  算力/带宽比:295

        意思是:读 1 byte 数据,GPU 能算 295 次。但 decode 阶段每个 token 只算约 2 次(前向乘加)。算力利用率约 0.7%

GPU 99% 的算力在 decode 时被浪费了。

        如果能把这浪费的算力利用起来——让 GPU 一次性算 N 个 token 而不是 1 个——速度就能提升 N 倍。这正是投机解码的切入点。


        二、投机解码的核心原理

        2.1 一个反直觉的洞察

验证 N 个 token 和生成 1 个 token,对 GPU 来说时间几乎一样。

        为什么?因为 attention 在 prefill 模式下可以并行。

        具体说:

  •  生成 1 个 token:处理 1 个 query,往 KV Cache 找历史 → forward 一次

  •  验证 N 个 token:把 N 个 query 一起送入 → 同样 forward 一次

        两者计算量差异很大(N 倍),但都是 1 次 forward——而 forward 时间主要由「读完模型权重一遍」决定,与计算量关系不大(memory-bound)。

所以:在 decode 时,一次性验证 N 个 token 几乎是免费的

        2.2 投机解码的核心算法

        那怎么搞到 N 个候选 token 来验证?用一个小模型先草拟

完整流程:

1. Draft 模型生成 K 个候选 token (快)
2. Target 模型一次性 forward 验证 K 个 token
3. 比较两个模型的概率分布,决定接受哪些
4. 至少接受 1 个,最多 K 个
5. 重复

        关键问题:怎么"决定接受"?这里有数学保证——通过 rejection sampling,最终输出的概率分布与"Target 模型直接生成"完全一致

        2.3 接受策略:Rejection Sampling

        设 Draft 模型在某位置给出 token x 的概率是 q(x),Target 模型给出 x 的概率是 p(x)

情况 A:p(x) >= q(x)
  → 直接接受这个 token
  
情况 B:p(x) < q(x)
  → 以 p(x)/q(x) 的概率接受
  → 否则,从 normalized(max(0, p - q)) 重新采样一个 token

        这种策略让最终采样概率严格等于 p(x)——也就是 Target 模型自己直接采样的分布。

        关键认知投机解码数学上完全无损——和 Target 模型直接生成的结果分布完全一致(在贪心解码下甚至 token 序列也完全相同)。

        2.4 加速效果取决于什么

加速比 ≈ (1 + 接受长度) / (1 + Draft 模型相对耗时)

两个关键因素:

  •  接受长度 (Accept Length):Draft 模型预测准的连续 token 数

  •  Draft 模型耗时占比:Draft 模型相对 Target 模型的速度

举例(Target = Llama-3-70B,Draft = Llama-3-1B):

  •  Draft 耗时 ≈ Target 的 1/30

  •  平均接受长度 ≈ 4

  •  实际加速:约 2.5-3×

        2.5 一个完整的例子

Prompt: "今天天气真"

Draft 模型生成 4 个候选: "好啊,我想"
Target 模型一次性 forward 这 4 个 token:
  位置 0 (好): Target 概率高 → 接受
  位置 1 (啊): Target 概率高 → 接受  
  位置 2 (,): Target 概率高 → 接受
  位置 3 (我): Target 觉得应该是"出"  → 拒绝,重新采样得到"出"

最终输出: "好啊,出"

下一轮 Draft 从"出"继续生成 4 个候选 ...

一次 Target forward 输出了 4 个 token——而平时只能输出 1 个。4× 加速


        三、投机解码的四大变种

        3.1 经典投机解码(2022, Google + DeepMind 同时提出)

        最朴素的形式:用一个独立的小模型做 Draft。

        典型组合

Target

Draft

加速

Llama-3-70B

Llama-3-1B / 8B

2-3×

GPT-4

GPT-3.5

Claude Opus

Claude Haiku

2.5×

        条件

  •  Draft 和 Target 必须 tokenizer 完全一致(不一致就没法验证)

  •  Draft 越准越好(但不一定是越大越好——平衡点)

  •  Draft 越快越好

        优点

  •  概念简单

  •  不需要改 Target 模型

  •  通用

        缺点

  •  需要部署 2 个模型

  •  显存占用增加

  •  通常 Draft 比 Target 小 7-10 倍才能有收益

        3.2 Medusa(2024):自己「分裂」出多个头

        不想部署两个模型?那就让 Target 模型自己同时预测多个未来 token。

        Medusa 的做法

在 Target 模型最后一层 加 K 个额外的 head:
  head_0: 预测下一个 token (原本就有)
  head_1: 预测下下个 token
  head_2: 预测下下下个 token
  ...
  
每个 head 是个轻量分类器,只需训练几小时。

        工作流程

  1.  一次 forward,每个 head 给出多个候选

  2.  形成"候选 token 树"

  3.  Target 模型再 forward 一次验证整个树

  4.  接受最长的有效路径

        优点

  •  只用一个模型

  •  训练成本低(额外的 head 几小时训完)

  •  加速 2-3×

        缺点

  •  需要轻微改造模型

  •  接受长度通常比双模型方案短

        3.3 EAGLE 系列(2024-2025):当下 SOTA

        EAGLE 把投机解码推到了新高度。核心思想:

用 Target 模型的 hidden state 而不是最终 logits 做 draft。

        原因:hidden state 信息量远高于 logits(logits 是 hidden state 投影到 vocab 后的结果),用它做 draft 更准。

        EAGLE 的结构

Target 模型
  ├─ 最后一层 hidden state ──→ EAGLE 小模型 (1-2 层 Transformer)
  └─ logits 输出                  ↓
                              生成候选 token 树
                                  ↓
                          Target 验证

        EAGLE-2 (2024 中) 引入了动态树结构——根据置信度自适应调整候选树形状。

        EAGLE-3 (2025) 进一步把多层 hidden state 一起利用,加速达到 3-5×

        实测(Llama-3-70B):

方法

加速

无投机

经典(Llama-1B Draft)

2.3×

Medusa

2.5×

EAGLE-2

3.5×

EAGLE-3 4.5×

        EAGLE 系列已经成为 vLLM、SGLang 等推理框架的默认推荐

        3.4 N-gram / Lookup Decoding(不需要模型!)

        最极端的方案——根本不用模型来做 draft

        核心洞察:很多场景下,输出和输入有大量重复

  •  RAG:模型经常引用 prompt 中的原文

  •  Code 助手:modify 代码时大量复用原代码

  •  多轮对话:经常重复之前提到的实体

  •  翻译:人名地名直接保留

        Lookup Decoding 做法

1. 从 prompt + 已生成 token 中提取所有 n-gram
2. 当当前生成位置匹配某个 n-gram 的前缀
3. 直接用 n-gram 的后续作为候选
4. Target 验证

        优点

  •  零额外模型

  •  零额外显存

  •  实现极简

        缺点

  •  只对"重复输入"场景有效

  •  通用对话场景效果差

        适用场景

  •  RAG → 2-3× 加速

  •  Code 完成 → 3-5× 加速(代码重复多)

  •  长文档总结 → 2× 加速

  •  闲聊 → 几乎无收益

        3.5 四种方法对比

方法

需要额外模型

加速比

适用场景

实现复杂度

经典

是(小模型)

2-3×

通用

Medusa

否(加 head)

2-3×

通用

EAGLE-3

否(轻量小网络)

3-5×

通用

Lookup

2-5×(视场景)

RAG / Code

极低

        四、工程实战:怎么启用投机解码

        4.1 vLLM 启用 EAGLE 投机解码

# vLLM 0.6+ 支持 EAGLE
vllm serve meta-llama/Llama-3-70B-Instruct \
    --speculative-model "yuhuili/EAGLE-LLaMA3-Instruct-70B" \
    --num-speculative-tokens 5 \
    --use-v2-block-manager \
    --speculative-draft-tensor-parallel-size 1 \
    --tensor-parallel-size 4

        关键参数

  • --speculative-model:Draft 模型(EAGLE 权重)

  • --num-speculative-tokens:每次猜测的 token 数(典型 4-8)

  • --use-v2-block-manager:必须开

  • --speculative-draft-tensor-parallel-size:Draft 的并行配置

        4.2 vLLM 启用经典投机解码

        如果你的 Target 是 70B,用 8B 做 Draft:

vllm serve meta-llama/Llama-3-70B-Instruct \
    --speculative-model meta-llama/Llama-3-8B-Instruct \
    --num-speculative-tokens 5 \
    --tensor-parallel-size 4

        ⚠️ Tokenizer 必须完全一致——同家族模型才行(Llama → Llama,不能 Llama → Qwen)。

        4.3 vLLM 启用 Lookup(n-gram)投机

vllm serve meta-llama/Llama-3-70B-Instruct \
    --speculative-model "[ngram]" \
    --num-speculative-tokens 5 \
    --ngram-prompt-lookup-max 4

        特别适合 RAG 场景——零成本启用。

        4.4 实测性能数据

        我们用 Llama-3-70B INT8 在 H100 × 2 上做对比(单序列、prompt 1K、生成 500):

配置

TPOT (ms)

tokens/s

相对

无投机

22

45

Lookup (n-gram)

12

83

1.8×

经典 (8B Draft)

9

110

2.5×

Medusa

8

125

2.8×

EAGLE-3 5 200 4.4×

        输出完全一致(贪心解码下逐 token 比对,无差异)。

        4.5 选 Draft 模型的经验

        如果用经典投机解码,Draft 模型选择规则:

Target 规模

推荐 Draft

加速

7B-14B

0.5B-1B

32B

1B-3B

2.5×

70B

7B-8B

2.5-3×

405B / 671B

13B-32B

        规则:Draft 约为 Target 的 1/10-1/30 大小。太小不准(接受率低),太大太慢(draft 自己耗时多)。


        五、避坑 + 决策建议

        5.1 投机解码的「死敌」:大 batch

        投机解码最大的反直觉事实

batch 越大,投机解码的收益越小。

        原因

  • 投机解码利用的是"闲置算力"

  • 大 batch 已经把算力打满

  • 没有"免费午餐"可吃了

        实测:

Batch Size

投机解码加速

1

4

16

1.3×

64

1.05×

256

~1×

        结论

  •  单用户场景(batch=1):投机解码 = 必开

  •  高并发场景(batch > 32):投机解码意义不大

  •  中等并发:根据 SLA 决定

        5.2 适用场景判断

场景

投机解码推荐度

单用户对话

(ChatGPT 风格)

⭐⭐⭐⭐⭐ 必开

Code 助手

(IDE 集成)

⭐⭐⭐⭐⭐ 必开

RAG

(重复内容多)

⭐⭐⭐⭐ 推荐 Lookup

Agent

 (单次复杂任务)

⭐⭐⭐⭐ 推荐

大流量 API

 (batch 大)

⭐⭐ 边际收益

批量任务

(离线处理)

⭐ 不必开

        5.3 5 大常见坑

        坑 1:Tokenizer 不匹配

        症状:启用后直接 crash 或输出乱码。

        对策:Target 和 Draft 必须完全相同 tokenizer 和 vocab。同家族模型一般 OK,跨家族不行。

        坑 2:Draft 太大

        症状:开了投机解码反而变慢。

        对策:Draft 模型本身耗时不能超过 Target 的 1/3,否则得不偿失。

        坑 3:num_speculative_tokens 设置不当

        症状:加速不明显。

        对策

  •  设小(如 2):接受率高但收益少

  •  设大(如 16):接受率下降快

  •  经验值:4-8

        坑 4:长尾恶化

        症状:99% 延迟反而比平均延迟差。

        原因:投机解码对某些"难预测"内容(数字、代码、专有名词)效果差。

        对策

  •  监控 p99 延迟

  •  必要时为不同请求路径选不同策略

        坑 5:在采样温度高时失效

        症状:温度 = 1.0 时加速比明显下降。

        原因:高温度让 Target 分布平坦,rejection sampling 拒绝率高。

        对策

  •  温度低的场景(< 0.5):投机解码效果最好

  •  温度高的场景:考虑改用 Lookup 等不依赖 Draft 分布的方法

        5.4 进阶:投机解码 + 其他优化的组合

        投机解码可以和大部分推理优化叠加

组合

协同效果

投机解码 + Flash Attention

✓ 完美兼容

投机解码 + PagedAttention

✓ vLLM 默认支持

投机解码 + INT4 量化

✓ Target/Draft 都可量化

投机解码 + Continuous Batching

△ batch 大时效益降

        实际生产部署的"豪华套餐":

Llama-3-70B AWQ INT4 + Flash Attention v3 + PagedAttention 
  + EAGLE-3 投机解码 + FP8 KV Cache

        单 H100 上的吞吐:从无优化的 30 tokens/s 提升到 3500+ tokens/s——综合 100×+ 加速


        六、扩展话题与下一篇预告

        6.1 投机解码的未来方向

        研究热点

  1.  自适应 N:根据当前 token 难度动态调整候选数量

  2.  多 Draft 集成:多个小模型同时草拟,取共识

  3.  Draft 在线学习:Draft 模型根据 Target 反馈持续微调

  4.  Tree-based decoding 进化:更复杂的候选树结构

        6.2 大公司怎么用

        OpenAI、Anthropic 用没用投机解码?

        虽然没公开承认,但 ChatGPT / Claude 的延迟特性强烈暗示用了:

  •  单用户感知速度远超理论极限(70B 单卡 48 tokens/s vs Claude 实测 100+)

  •  速度对内容类型敏感(代码块更快)

        OpenAI o1 系列的"thinking"阶段特别适合投机解码——因为它会生成大量"草稿思考",重复模式很多。

        6.3 投机解码 vs 多 Token 预测(MTP)

        DeepSeek V3 引入了 MTP(Multi-Token Prediction)——这是训练时就让模型一次预测多个 token。和投机解码思想相似但是训练阶段的优化。

        两者可以叠加

  •  MTP 让模型本身就能多 token 预测

  •  投机解码进一步加速 decode

        DeepSeek V3 的 R1 模型据说就用了这种组合。

        6.4 不适用场景

        虽然投机解码很强,但这些场景不要用:

  •  极致追求吞吐的批处理(用 batch 更划算)

  •  极小模型(< 7B)单用户 —— 已经够快,没必要

  •  嵌入式 / 端侧场景 —— 显存吃紧不容许多模型


七、结语:从「省时间」到「省 GPU」

        读完本文你应该明白:

  •  Decode 阶段 GPU 99% 算力闲置——这是投机解码的物理基础

  •  验证 N 个 token 几乎等于生成 1 个 token 的时间——这是核心洞察

  •  数学上无损——rejection sampling 保证输出分布与原模型一致

  •  EAGLE-3 是当下 SOTA——3-5× 加速,已在主流推理框架可用

  •  Lookup Decoding 在 RAG / Code 场景免费 2-3×

  •  大 batch 下意义不大——这是投机解码最重要的边界条件

参考文献:

14.投机解码:让大模型推理快 2-3 倍的「免费午餐」

Logo

免费领 150 小时云算力,进群参与显卡、AI PC 幸运抽奖

更多推荐