投机解码是一种让大语言模型在不损失质量的前提下更快生成文本的技术。一个小型草稿模型快速猜测接下来的几个 token,然后大模型在一次运行中验证所有这些猜测。

在这篇博客中,我们将学习投机解码——它是什么、为什么没有它时大语言模型的生成速度很慢、小型草稿模型和大型目标模型如何协同工作以更快地生成 token、保证零质量损失的拒绝采样数学原理、展示 2 到 3 倍加速的真实数据、它在生产环境中的应用场景,以及需要注意的权衡取舍。

我们将涵盖以下内容:

  • 投机解码解决什么问题?
  • 整体概览
  • 为什么大语言模型的生成速度很慢?
  • 投机解码背后的核心思想
  • 逐步详解
  • 验证步骤
  • 真实数据与加速效果
  • 应用场景
  • 权衡取舍
  • 快速总结

我是 Amit Shekhar,Outcome School 的创始人。我教导和指导过许多开发者,他们的努力为他们赢得了高薪技术工作,帮助许多科技公司解决了他们的独特问题,并创建了众多被顶级公司使用的开源库。我热衷于通过开源、博客和视频分享知识。

我在 Outcome School 教授 AI 和机器学习。

让我们开始吧。

投机解码解决什么问题?

当我们使用内部采用大语言模型的 ChatGPT、Claude 或 Gemini 时,会注意到模型一次只写一个词。它不会一次性写出整段文字,而是先写一个 token,再写下一个 token,接着再写下一个。

这很慢。

模型越大,速度就越慢。每生成一个 token,整个模型都要执行一次完整的前向传播。为了产生一个 token,数十亿参数要从 GPU 内存中加载。然后下一个 token 又重复同样的过程。一次又一次,循环往复。

投机解码是一种让这一过程更快的技术,而且无需更改模型,也不会损失任何质量。

整体概览

在深入细节之前,我们先来了解整体概览。

投机解码使用两个模型。一个是小模型,速度快但不太聪明;一个是大模型,速度慢但非常聪明。小模型快速起草接下来的几个 token,然后大模型一次性验证所有这些 token。

简单来说:

投机解码 = 小模型起草 token + 大模型并行验证。

如果草稿质量好,我们只需付出一次大模型步骤的代价就能获得多个 token。如果草稿质量差,我们就丢弃错误的 token,并从草稿正确的位置继续。

输出与我们单独使用大模型时的结果完全相同。没有任何质量损失。

为什么大语言模型的生成速度很慢?

要理解投机解码,我们必须先理解为什么正常的 LLM 生成速度慢。

LLM 以自回归的方式生成文本。这意味着模型先产生一个 token,然后将该 token 作为输入,再产生下一个。

假设我们想让模型写下"I love teaching AI and Machine Learning."。模型的执行过程如下:

Step 1: Input "I"               -> Output "love"
Step 2: Input "I love"          -> Output "teaching"
Step 3: Input "I love teaching" -> Output "AI"
... and so on

每一步都要完整运行一次大模型。如果模型有 700 亿个参数,那么每生成一个 token,这 700 亿个参数都要从 GPU 内存中读取一遍。

现在,有趣的部分来了。GPU 并不慢在数学计算,而是慢在内存搬运。生成单个 token 的大部分时间都花在将模型权重加载到计算单元上,而不是进行实际的数学运算。

这意味着 GPU 有大量空闲的计算能力,在等待内存时被闲置。

你看出问题了吗?我们为每一个 token 都支付了完整的内存成本,却几乎没有利用 GPU 的算力。

这正是投机解码要填补的空白。

要学习 LLM 内部机制、KV Cache 和投机解码,请查看 Outcome School 的 AI 和机器学习课程。

投机解码背后的核心思想

投机解码背后的想法非常简单。

假设我们有两个模型:

  • 一个小模型,称为草稿模型。它速度很快。比如说,10 亿个参数。
  • 一个大模型,称为目标模型。它是聪明的那一个。比如说,700 亿个参数。

计划是:

  • 草稿模型快速地连续写出接下来的 4 到 5 个 token。
  • 目标模型将所有这些草稿 token 一起接收,并在单次前向传播中进行检查。
  • 对于每个草稿 token,目标模型会判断:"是的,我也会这样写"或"不,这是错的。"
  • 我们保留目标模型接受的所有 token,丢弃其余的,然后从这里继续。

高层次的流程如下:

                 [ draft model ]
                       |
             writes 4 tokens sequentially
                       v
           +--------------------------+
           |  draft: t1  t2  t3  t4   |
           +--------------------------+
                       |
                       v
                [ target model ]
                       |
            single forward pass on all 4
                       |
                       v
       +----------------------------------+
       | accept t1, accept t2, reject t3, |
       |     fix t3, drop t4              |
       +----------------------------------+
                       |
                       v
              continue from accepted prefix

这里的关键在于第二步。目标模型在单次前向传播中同时检查 4 到 5 个 token。它利用了原本闲置的 GPU 算力。加载模型权重的内存成本只支付一次,却能换回多个 token。

让我们用一个现实世界的类比来说明。

假设有一位非常细心但速度很慢的资深工程师,还有一位速度快但会犯一些错误的初级工程师。在没有投机解码(Speculative Decoding)的情况下,资深工程师每一行代码都要亲自编写。而在使用投机解码时,初级工程师快速起草 5 行代码,资深工程师一次性审阅这 5 行,批准正确的部分,修正第一个错误之处,然后从那里继续。大多数时候,初级工程师至少前几行是对的。因此,资深工程师可以批量审阅,而不必从头编写每一行。

这就是全部的核心思想。

正在计划转型进入 AI 领域吗?

无论你今天在哪个技术领域工作,只要付出努力,你都可以转型到以下这些角色:

  • AI 工程师
  • 前沿部署工程师
  • AI Agent 工程师
  • Harness 工程师
  • 语音 AI 工程师
  • AI 架构师
  • LLM 工程师
  • LLM 推理工程师
  • 生成式 AI 工程师
  • MLOps 工程师
  • 机器学习工程师
  • 深度学习工程师

Outcome School 的 AI 与机器学习课程 将带你实现这一目标。

逐步详解

现在,让我们完整走一遍投机解码的一轮流程,看看它在实践中是如何运作的。假设目前的提示词是 "I love teaching AI",草稿长度为 4。

第 1 步: 草稿模型接收 "I love teaching AI",并逐个生成 4 个 token,如下所示:

Draft tokens: "and", "Machine", "Learning", "today"

草稿模型很小,所以这一步非常快。

第 2 步: 目标模型将提示词和 4 个草稿 token 一起输入。在一次前向传播中,目标模型为每个位置生成概率分布,如下所示:

Position 1 (after "AI"):                          target predicts "and"      -> matches draft
Position 2 (after "AI and"):                      target predicts "Machine"  -> matches draft
Position 3 (after "AI and Machine"):              target predicts "Learning" -> matches draft
Position 4 (after "AI and Machine Learning"):     target predicts "everyday" -> does NOT match "today"

第 3 步: 我们接受这些 token,直到出现第一个被拒绝的 token。

  • "and" 被接受
  • "Machine" 被接受
  • "Learning" 被接受
  • "today" 被拒绝。目标模型想要的是 "everyday"。

第 4 步: 我们用目标模型的选择替换被拒绝的 token。因此我们接受 "and Machine Learning everyday"。

第 5 步: 我们以提示词 "I love teaching AI and Machine Learning everyday" 开始下一轮。

在直观地查看验证过程之前,先看看在一次并行前向传播中同时输入目标模型的所有内容,如下所示:

All 8 inputs go into the target model in parallel (one forward pass):

  Position:     1       2        3            4       5       6          7           8
  Input:       "I"    "love"  "teaching"    "AI"    "and"  "Machine"  "Learning"  "today"
              \____________ prompt ____________/    \___________ 4 draft tokens ____________/
                |       |        |           |       |        |          |           |
                v       v        v           v       v        v          v           v
              +---------------------------------------------------------------------+
              |              Target model (ONE parallel forward pass)               |
              +---------------------------------------------------------------------+
                |       |        |           |       |        |          |           |
                v       v        v           v       v        v          v           v
  Predicts     next    next     next        next    next     next       next        next
  next token:  token   token    token       token   token    token      token       token

全部 8 个位置同时处理。在每个位置,目标模型预测下一个(NEXT)token 应该是什么。因此,我们在一次并行前向传播中一次性得到 8 个下一 token 预测。我们将这些预测与 4 个草稿 token 进行比较,以决定接受哪些——4 个 token 同时验证,而不是逐个验证。

下面是同一轮流程的直观图示:

+----------+--------+----------+------------+------------+
| Position |   1    |    2     |     3      |     4      |
+----------+--------+----------+------------+------------+
| Draft    | "and"  |"Machine" | "Learning" |  "today"   |
| Target   | "and"  |"Machine" | "Learning" | "everyday" |
| Decision |  [OK]  |  [OK]    |    [OK]    |   [FIX]    |
+----------+--------+----------+------------+------------+

What happens:
  - Positions 1, 2, 3: accepted (draft matches target)
  - Position 4: rejected, replaced with target's "everyday"

Tokens added this round: "and" + "Machine" + "Learning" + "everyday"  =  4 tokens
Target model forward passes used: 1

在一轮中,我们得到了 4 个 token,而不是仅仅 1 个。目标模型只运行了一次。这就是加速的来源。

如果全部 4 个草稿 token 都被接受,我们就能在一轮中得到 5 个 token,即 4 个草稿 token 加 1 个奖励 token。奖励 token 是免费获得的,因为目标模型的单次前向传播会在每个位置产生一个概率分布,包括最后一个草稿 token 之后的位置。所以我们无需额外成本就能多得到一个 token。

它运行得非常完美。

给你的一点提示

无论你在哪个技术领域工作,都要熟悉这些主题:

  • LLM
  • RAG
  • MCP
  • Agent
  • 微调
  • 量化

我们把它们全部整合在一个视频中:

AI 工程详解:LLM、RAG、MCP、Agent、微调与量化

不必中断阅读——先收藏起来,等有空时再看。未来的你会感谢现在的你。

现在,让我们回到正题。

验证步骤

这时一个自然的问题出现了——目标模型如何决定接受还是拒绝一个草稿 token?

简单版本是:如果草稿 token 与目标模型本来会选的 token 相同,就接受它。

但事情并非这么简单。LLM 是概率性的,它们并不总是选择概率最高的那个 token,而是从概率分布中采样。所以我们不能只检查“草稿 token 是否与目标模型的首选一致”,而需要一个更聪明的规则。

于是,拒绝采样就登场了。规则如下:

对于每个草稿 token,我们有两个概率:

  • q(x) = 草稿模型选择该 token 的概率
  • p(x) = 目标模型选择该 token 的概率

接受规则如下:

If p(x) >= q(x):
    Always accept
Else:
    Accept with probability p(x) / q(x)

让我们用具体数字来看一下。

示例 1:草稿 token "Machine"——目标模型强烈认同。

  • q("Machine") = 0.6(草稿模型以 60% 的概率选择 "Machine")
  • p("Machine") = 0.8(目标模型以 80% 的概率选择 "Machine")

由于

p >= q

(0.8 >= 0.6),我们总是接受 "Machine"。目标模型的认同度甚至比草稿模型更高,所以没有理由拒绝。

示例 2:草稿 token "today"——目标模型不认同。

  • q("today") = 0.5(草稿模型以 50% 的概率选择 "today")
  • p("today") = 0.1(目标模型只有 10% 的概率选择 "today")

由于

p < q

(0.1 < 0.5),我们不总是接受,而是以如下概率接受:

p / q = 0.1 / 0.5 = 0.2

所以我们有 20% 的时间接受 "today",80% 的时间拒绝它。我们通过掷一枚有偏硬币来决定。

直觉很简单。当目标模型认同草稿时(

p >= q

),草稿没问题——接受它。当目标模型不认同时(

p < q

),草稿有风险——我们只按目标模型对它仍然持有的信任程度成比例地偶尔接受它。

如果 token 被拒绝,我们不会就此放弃。我们会从一个修正后的分布中重新采样一个 token,该分布填补了两个模型之间的差距。这个差距的计算方式为

max(0, p(x) - q(x))

然后进行归一化。

让我们用数字来看一下。假设在被拒绝的位置上,有三种可能的 token:"everyday"、"today"、"tomorrow"。草稿模型和目标模型的概率如下:

              q(x)    p(x)
"everyday"    0.2     0.5
"today"       0.5     0.1
"tomorrow"    0.3     0.4

草稿模型选择了 "today",但它被接受规则拒绝了。现在我们为每个 token 计算修正后的分布

max(0, p - q)

然后进行归一化,如下所示:

"everyday": max(0, 0.5 - 0.2) = 0.3
"today":    max(0, 0.1 - 0.5) = 0
"tomorrow": max(0, 0.4 - 0.3) = 0.1

Sum = 0.4

corrected("everyday") = 0.3 / 0.4 = 0.75
corrected("today")    = 0
corrected("tomorrow") = 0.1 / 0.4 = 0.25

因此,替换的 token 将从这个修正后的分布中采样:"everyday" 的概率为 75%,"tomorrow" 的概率为 25%。概率质量向目标模型比草稿模型更想要的 token 偏移。"today" 得不到任何概率,因为目标模型不想要它——是草稿模型过度偏爱了它。

这里的数学设计非常精巧,使得投机解码的最终输出与单独逐个 token 运行目标模型时的概率分布完全相同。这不是近似值,而是数学上精确正确的值。

注意: 为了让这篇博客保持简短易懂,我们有意省略了完整的数学证明。该证明表明,最终得到任意 token 的概率

x

恰好等于

p(x)

,无论算法走哪条路径。如果你对细节感兴趣,可以在原始的投机解码论文中找到完整的证明。

这是投机解码最重要的特性。我们获得了速度上的收益,而在质量上毫无损失。这正是投机解码的精妙之处。

不过,并非所有技术都保持这个分布不变。有些技术会故意将其轻微偏移。我们有一篇详细介绍 LLM 水印是如何工作的? 的博客,解释了这样一个微小的偏移如何在生成的文本中隐藏一个信号。

真实数据与加速效果

让我们用真实数字来直观展示我们到底节省了多少时间。

假设:

  • 目标模型每个 token 需要 50 毫秒
  • 草稿模型每个 token 需要 5 毫秒
  • 我们使用 4 个 token 的草稿长度

不使用投机解码(基线):

每个 token 都需要一次完整的目标模型前向传播。

1 token = 1 target pass = 50 ms

因此,生成任意 N 个 token 需要

N x 50 ms

.

使用投机解码,每一轮的成本为:

4 draft tokens x 5 ms = 20 ms  (drafting)
1 target verification = 50 ms  (verification of all 4 in parallel)
Total per round        = 70 ms

每轮的成本固定为 70 毫秒。变化的是我们在这一轮中产出多少个 token,这取决于有多少草稿 token 被接受。

让我们看看这三种情况。

最佳情况——全部 4 个草稿 token 都被接受。 我们得到 4 个被接受的 token,外加目标模型赠送的 1 个免费 token,即 70 毫秒内产出 5 个 token。

Without spec dec: 5 tokens x 50 ms = 250 ms
With spec dec:                      = 70 ms
Speedup: 250 / 70 = 3.57x

平均情况 - 4 个草稿 token 中有 3 个被接受。 前 3 个草稿 token 被接受,第 4 个被拒绝并由目标模型的选择替换。所以我们得到 3 个被接受 + 1 个被修正 = 前进 4 个 token(这与上面演示中的情况相同)。

Without spec dec: 4 tokens x 50 ms = 200 ms
With spec dec:                      = 70 ms
Speedup: 200 / 70 = 2.86x

最坏情况 - 0 个草稿 token 被接受。 即使在这种情况下,我们仍然前进 1 个 token。当第一个草稿 token 就被拒绝时,我们会从修正后的分布中

max(0, p - q)

在该位置采样一个替代 token,因此进度是有保证的,绝不会为零。但我们浪费了起草的时间。

Without spec dec: 1 token x 50 ms = 50 ms
With spec dec:                     = 70 ms
Speedup: 50 / 70 = 0.71x  (slower!)

所以投机解码是一场赌博。当草稿模型与目标模型一致时,我们大获全胜。当它们不一致时,我们付出小小的代价。只要平均接受率足够高,这场赌博就是划算的,而对于相似的模型家族来说,接受率通常都很高。

现在,让我们看看对完整回复的用户可见影响。

假设用户提出一个问题,目标模型需要生成一个 200 token 的回答。举个例子,采用上面的平均情况,即 Agent 每轮前进 4 个 token(3 个草稿 token 被接受,加上拒绝位置上的 1 个修正 token)。在实际工作负载中,这个数字可能在 1 到 5 之间 - 我们使用 4 只是作为说明性示例。计算结果如下:

Without Speculative Decoding:
  200 tokens x 50 ms = 10,000 ms = 10.0 seconds

With Speculative Decoding:
  Cost per round: 4 draft tokens x 5 ms + 1 target pass x 50 ms = 70 ms
  Tokens per round (average): 4
  Rounds needed: 200 tokens / 4 tokens per round = 50 rounds
  Total time: 50 rounds x 70 ms = 3,500 ms = 3.5 seconds

Time saved: 6.5 seconds (a 2.86x speedup)

下面是在时间线上展示的相同对比:

Time (seconds):  0    1    2    3    4    5    6    7    8    9    10

Without:         |==================================================|  10.0s
                 (200 target passes, one per token)

With:            |==================|                                   3.5s
                 (50 rounds, 4 tokens each)

                                     <-------- 6.5s saved -------->

用户等待同样的答案只需 3.5 秒,而不是 10 秒。这就是用户可见的收益。

在实践中,大多数工作负载的加速比在 2 倍到 3 倍之间。具体数字取决于:

  • 小型草稿模型与大型目标模型的匹配程度
  • 草稿的长度
  • 输入风格与草稿模型所见内容的相似程度

草稿模型的行为越接近目标模型,接受率就越高,加速比也就越大。

保持更新: 订阅我们的 newsletter,将我们最新的 AI 和机器学习博客直接发送到您的收件箱。

应用场景

如今,投机解码已被用于许多生产级 LLM 系统中。

一些实际的例子:

  • vLLM、SGLang 和 TensorRT-LLM 开箱即用地支持投机解码,用于服务大型模型。
  • Llama 模型通常将一个小型 Llama 与一个大型 Llama 配对,以加速推理。
  • DeepSeek-V3 使用一种名为 Multi-Token Prediction (MTP) 的辅助目标来训练模型,它增加了一个轻量级的额外模块(一个 transformer 层加一个输出头),用于学习预测未来位置的下一个 token。在推理时,该模块充当投机解码的内置起草器,因此不需要单独的草稿模型。DeepSeek 报告称由此获得了约 1.8 倍的生成加速。

这个想法之所以有如此多的变体,是因为它是在不重新训练大模型的情况下,为 LLM 服务获得 2 倍到 3 倍加速的最廉价方法之一。

如果我们想深入学习 vLLM、LLM 推理工程以及模型部署与服务(端到端),我们有一个完整的课程——请查看 Outcome School 的 AI 与机器学习课程。

权衡取舍

推测解码让生成更快,但它也带来了一些权衡:

  • 我们需要第二个模型。草稿模型会占用额外的 GPU 显存。对于超大规模部署来说,这一点很重要。
  • 两个模型必须使用相同的 分词器。如果它们的词表不同,草稿 token 就无法与目标模型期望的输入对齐。
  • 接受率取决于草稿模型与目标模型的一致程度。如果草稿模型与目标模型差异太大,大多数草稿 token 会被拒绝,我们就会浪费时间。
  • 对于非常短的输出,运行两个模型的开销可能会抵消加速效果。
  • 草稿长度必须经过调优。太短则加速效果不明显;太长则会浪费被拒绝的 token。
  • 加速效果在低批次大小时最为明显,因为此时解码受内存带宽限制,GPU 的算力处于闲置状态。在非常高的批次大小时,Continuous Batching 已经让 GPU 保持忙碌,推测解码所依赖的空闲算力就不复存在了。收益会缩小,在某些情况下甚至会反转。

对于大多数大模型服务场景,这种权衡是值得的。用户能看到更快的响应,GPU 的使用效率也更高。

后来的方法干脆去掉了第二个模型,让大模型自己来起草。我们有一篇关于 Medusa 的详细博客,解释了额外的解码头是如何进行起草的。

EAGLE 更进一步,它基于模型平滑的内部特征而不是嘈杂的输出 token 来起草。我们有一篇关于 EAGLE 的详细博客,端到端地介绍了这一点。

N-gram Speculation 则反其道而行之,完全不需要模型来起草。它查找提示词中最后几个 token,把它们之后出现的内容复制为草稿,然后让大模型进行验证——当答案重复输入内容时,这种方法效果非常好。

快速总结

让我们回顾一下所学到的内容:

  • 问题所在: LLM 一次只生成一个 token。每个 token 都迫使完整的大模型运行一次。GPU 的算力在内存加载时处于闲置状态。这很慢。
  • 核心思想: 使用一个小而快的草稿模型写出几个 token,然后用大而慢的目标模型在一次并行处理中验证所有这些 token。
  • 接受规则: 每个草稿 token 根据一种叫做拒绝采样的数学规则被接受或拒绝。这个数学规则保证了最终输出与大模型单独生成的结果完全一致。
  • 收益: 2 到 3 倍的加速,且没有质量损失。
  • 代价: 草稿模型需要额外的 GPU 显存,而且加速效果取决于两个模型的一致程度。
  • 简单来说: 投机解码 = 小模型起草 token + 大模型并行验证。

投机解码是那种罕见的技术之一,它在不牺牲质量的前提下带来速度提升。它不改变模型,不改变输出,只是更聪明地利用 GPU。

这就是为什么如今几乎每个现代 LLM 服务系统都支持它。

常见问题

投机解码会降低输出质量吗?

不会。拒绝采样规则的设计确保最终输出与逐 token 运行目标模型时的概率分布完全相同。这不是近似。我们获得了速度优势,而质量上毫无损失。

投机解码会让生成变慢吗?

在某些情况下会。如果没有草稿 token 被接受,一轮的开销会超过单次目标模型前向传播,例如 70 ms 而不是 50 ms。在非常大的批处理规模下,收益也可能缩小甚至反转,而且由于两个模型的开销,非常短的输出可能无法受益。

草稿模型和目标模型需要相同的分词器吗?

是的。两个模型必须共享相同的分词器。如果它们的 token 词表不同,草稿 token 将无法与目标模型期望的输入对齐。

我们总是需要一个单独的草稿模型吗?

不需要。DeepSeek-V3 使用多 token 预测,其中一个轻量的额外模块充当内置的起草者。Medusa 让大模型使用额外的解码头为自己起草。N-gram 投机则完全不需要用于起草的模型,因为它直接从提示词中复制草稿。

目前就到这里。

谢谢

Amit Shekhar Outcome School 创始人

你可以通过以下方式与我联系:

关注 Outcome School: