#投机解码进阶专项强化题库(第二十批:Medusa、EAGLE、Lookahead Decoding 与接受率分析)

前面的题库在模块五和训练系统部分已经覆盖了 speculative decoding 的基础概念,但随着 Medusa、EAGLE、Lookahead Decoding 等方法在 2024-2025 年的提出和成熟,投机解码已经从"用小模型草拟、大模型验证"的基础范式,演进为一个包含多种技术路线的独立方向。

这一节专门补齐投机解码的进阶知识,把"知道 speculative decoding"推进到"能比较不同投机方法的优劣、分析接受率瓶颈、判断业务场景是否适合"。对于推理优化岗和算法岗,这一块是 2025-2026 年面试中高频且容易拉开差距的方向。

#一、高频问题速览

编号 问题 核心考点
327 Medusa 相比原始 speculative decoding 的核心改进是什么?为什么不需要 draft model? 多头并行预测、无 draft model、树形注意力
328 EAGLE 的 draft model 为什么可以比原模型小很多还能保持高接受率? 特征相关性、自回归头、训练数据构造
329 Lookahead Decoding 的 Jacobi 迭代视角是什么?与 beam search 有何不同? 并行验证、不动点迭代、无 draft model
330 Speculative decoding 的"接受率"瓶颈通常在哪?如何提升? Draft 质量、分布匹配、温度影响、任务类型
331 为什么 speculative decoding 在某些场景下收益不明显甚至负收益? Draft 开销、验证成本、短序列、高温度
332 Medusa 的树形注意力(Tree Attention)是如何工作的?为什么比普通 attention 更高效? 树结构候选、共享前缀计算、避免重复
333 EAGLE 的训练数据是如何构造的?为什么用"特征"而不是"token"来做 draft? 隐藏状态、特征自回归、压缩表示
334 投机解码中的"验证步"和"草拟步"的延迟如何权衡?什么比例最优? 每步草稿数、验证批大小、流水调度
335 在商用推理引擎(vLLM、TensorRT-LLM)中集成投机解码的难点是什么? KV cache 管理、page 对齐、动态 batch、回退
336 如果业务场景的延迟要求极高(如实时对话),是否还应该用投机解码? 延迟-吞吐权衡、TTFT 影响、端到端优化

#二、逐题详细解答

#327. Medusa 相比原始 speculative decoding 的核心改进是什么?为什么不需要 draft model?

#知识点

  • 原始 speculative decoding:draft model + target model
  • Medusa:多头并行预测
  • 无 draft model 架构
  • 树形注意力(Tree Attention)
  • 训练成本与推理收益

#详细解答

原始 speculative decoding 的局限

原始方法需要两个模型:一个小的 draft model(如 7B)快速生成候选 token,一个大的 target model(如 70B)验证这些候选。核心瓶颈在于:

  1. draft model 需要单独维护:需要额外显存放 draft model,额外计算资源运行 draft model;
  2. draft model 和 target model 的分布差异:draft model 生成的 token 分布与 target model 不同,导致接受率不高;
  3. draft model 的训练:draft model 通常是从 target model 蒸馏而来,需要额外的训练流程。

Medusa 的核心改进

Medusa 的核心洞察是:不需要单独的 draft model,直接在 target model 上增加多个"预测头",让每个头并行预测未来不同位置的 token。

具体做法:

  1. 在 target model 的顶层(最后一层隐藏状态之后),添加多个并行的 Medusa Head
  2. 每个 Medusa Head 是一个轻量的 MLP,负责预测未来某个位置的 token;
  3. 例如:Head-1 预测下一个 token(和原模型一样),Head-2 预测下下个 token,Head-3 预测下下下个 token...
  4. 这些 head 的预测组成一棵候选树,然后用树形注意力一次性验证整棵树。

为什么 Medusa 不需要 draft model?

因为 Medusa Head 直接共享了 target model 的前向计算:

  • 输入序列先过 target model 的主体(Transformer 层),得到隐藏状态;
  • 隐藏状态同时送给原始输出头和所有 Medusa Head;
  • 原始输出头给出第 1 个候选 token,Medusa Head-1 给出第 2 个候选 token,Medusa Head-2 给出第 3 个候选 token...
  • 这些候选构成一条"预测链",然后 target model 一次性验证整条链。

这样做的好处:

  1. 没有额外的 draft model:不需要额外显存和计算资源;
  2. 分布天然一致:Medusa Head 基于 target model 的隐藏状态预测,分布更接近 target model;
  3. 训练简单:只需在 target model 上训练几个轻量 head,不需要蒸馏整个 draft model。

Medusa 的局限

  • 需要训练 Medusa Head,不能零样本使用;
  • 多头预测会增加训练时的显存开销;
  • 对超长序列的预测链,后面的 head 准确率会下降;
  • 不同任务的最佳 head 数量不同,需要调参。

#328. EAGLE 的 draft model 为什么可以比原模型小很多还能保持高接受率?

#知识点

  • EAGLE:Extrapolation Augmented Generation
  • 特征自回归(feature-level autoregression)
  • 隐藏状态压缩
  • Draft model 的训练目标
  • 接受率与 draft 质量的关系

#详细解答

EAGLE 由 Li 等人于 2024 年提出,是投机解码领域的一个重要进展。它保留了"draft model + target model"的架构,但 draft model 的设计与原始方法完全不同。

原始 draft model 的问题

原始方法中,draft model 通常是一个小版的 Transformer(如 target 是 70B,draft 是 7B)。这个 draft model 在 token 级别做自回归预测——给定已生成的 token,预测下一个 token。

问题在于:即使 draft model 有 7B 参数,它在 token 级别的预测分布与 70B 的 target model 仍然有明显差异,导致很多 draft token 被 target model 拒绝。

EAGLE 的核心改进:特征自回归

EAGLE 的关键洞察是:draft model 不需要在 token 级别预测,而可以在 feature(隐藏状态)级别预测。

具体做法:

  1. 训练数据构造:从 target model 的推理日志中提取"特征轨迹"——对于每个输入序列,记录 target model 每一层的隐藏状态;
  2. Draft model 的输入:不是 raw token,而是 target model 的隐藏状态(features);
  3. Draft model 的目标:预测下一个位置的隐藏状态,而不是下一个 token;
  4. Token 生成:draft model 预测出下一位置的隐藏状态后,用 target model 的词表投影层把隐藏状态映射成 token;
  5. 验证:target model 验证这些 token,接受则继续,拒绝则回退。

为什么特征自回归比 token 自回归更好?

  1. 信息密度更高:隐藏状态是高维向量(如 4096 维),编码了丰富的上下文信息。基于隐藏状态预测,draft model 能利用更多信息;
  2. 分布更接近 target:因为 draft model 直接学习 target model 的隐藏状态转移模式,其生成的 token 分布与 target model 更一致;
  3. Draft model 可以更轻量:因为输入是信息密集的特征而不是离散的 token,一个小模型(如 0.5B 甚至更小)就能学到有效的预测模式;
  4. 接受率更高:实验表明 EAGLE 的接受率比原始 token-level draft model 高出 20-30%。

EAGLE 的训练

EAGLE 的 draft model 需要用 target model 的推理数据训练:

  1. 收集大量输入序列;
  2. 用 target model 做前向传播,提取每层的隐藏状态;
  3. 训练 draft model:输入当前位置的隐藏状态,预测下一位置的隐藏状态;
  4. 损失函数是预测隐藏状态与真实隐藏状态的 MSE。

EAGLE 的局限

  • 需要访问 target model 的隐藏状态,某些封闭 API 无法使用;
  • 需要额外的训练流程和数据收集;
  • 对 target model 的架构变化敏感(如果 target model 更新了,draft model 可能需要重新训练)。

#329. Lookahead Decoding 的 Jacobi 迭代视角是什么?与 beam search 有何不同?

#知识点

  • Lookahead Decoding 的基本原理
  • Jacobi 迭代法(不动点迭代)
  • 并行验证多条假设
  • 与 beam search 的区别
  • 与 draft-based 方法的区别

#详细解答

Lookahead Decoding 由 Fu 等人于 2023 年提出,是投机解码领域中一个不需要 draft model 也不需要训练的方法。

核心思想:Jacobi 迭代视角

标准自回归生成可以看作求解一个方程组:

token_t = f(token_1, token_2, ..., token_{t-1})

其中 f 是语言模型。这个方程组是"三角"的——每个 token 只依赖于前面的 token,所以可以用前向代入法(即自回归生成)求解。

Lookahead Decoding 把这个问题重新看作不动点迭代

  1. 先对后续 N 个位置的 token 做初始猜测(可以用简单的启发式,如重复最后一个 token,或从词表中随机选);
  2. 然后并行计算所有 N 个位置:用模型验证这些猜测,得到修正后的 token;
  3. 重复迭代,直到所有位置的 token 不再变化(收敛)。

这类似于数值分析中的 Jacobi 迭代法——解线性方程组时,先猜一个解,然后并行更新所有变量,直到收敛。

具体做法

  1. 维护一个"窗口",包含当前位置和未来 N-1 个位置的 token;
  2. 对这 N 个位置做并行前向传播(利用 causal mask 的灵活性);
  3. 模型同时输出 N 个位置的预测;
  4. 比较预测与当前猜测,如果一致则"接受",不一致则更新猜测;
  5. 迭代直到窗口内所有位置都收敛。

与 beam search 的区别

维度 Beam Search Lookahead Decoding
目标 找概率最高的序列 加速自回归生成
并行性 串行(每一步选 top-k) 并行(同时验证多个位置)
候选来源 当前步的 top-k token 对未来位置的启发式猜测
验证方式 无验证,直接选择 用模型验证猜测是否收敛
适用场景 追求质量的生成任务 追求速度的推理加速

与 draft-based 方法的区别

Lookahead Decoding 不需要 draft model,也不需要训练。它的"猜测"来自简单的启发式(如 n-gram 重复、频率统计),而不是学习的模型。这使得它:

  • 零样本可用:不需要额外训练;
  • 无额外显存:不需要 draft model;
  • 但接受率较低:因为猜测质量不如 trained draft model 或 Medusa Head。

Lookahead Decoding 的适用场景

  • 无法训练 draft model 的场景(如 API-only 模型);
  • 对延迟极度敏感但接受率要求不高的场景;
  • 与 draft-based 方法结合:先用 Lookahead 做快速预热,再用 draft model 做高质量预测。

#330. Speculative decoding 的"接受率"瓶颈通常在哪?如何提升?

#知识点

  • 接受率(acceptance rate)的定义
  • Draft 分布与 target 分布的匹配度
  • 温度对接受率的影响
  • 任务类型对接受率的影响
  • 提升接受率的方法

#详细解答

接受率的定义

在 speculative decoding 中,draft model 生成 N 个候选 token,target model 验证这些候选。如果第 i 个候选 token 被 target model 接受(即 target model 也认为这个 token 的概率足够高),则继续验证第 i+1 个;否则回退到第 i 个位置重新采样。

接受率 = 被接受的 draft token 数 / 总 draft token 数。

接受率直接决定了 speculative decoding 的加速比:

  • 接受率 100%:每步可以前进 N 个 token,理论加速比 Nx;
  • 接受率 50%:平均每步前进 1 + 0.5N 个 token;
  • 接受率 0%:每步只前进 1 个 token(无加速,甚至因 draft 开销而变慢)。

接受率的瓶颈

瓶颈一:draft 分布与 target 分布不匹配

这是最根本的瓶颈。如果 draft model(或 Medusa Head、Lookahead 猜测)生成的 token 分布与 target model 的真实分布差异大,接受率就会低。

数学上,接受概率与两个分布的"重叠度"有关。如果 draft 给某个 token 的概率很高,但 target 给它的概率很低,这个 token 几乎一定被拒绝。

瓶颈二:温度(temperature)的影响

  • 低温度(如 T=0.1):模型输出更确定,draft 和 target 更容易达成一致,接受率高;
  • 高温度(如 T=1.0):模型输出更随机,draft 和 target 更容易分歧,接受率低。

这就是为什么 speculative decoding 在 greedy decoding(T=0)场景下效果最好,在创意写作(高温度)场景下效果最差。

瓶颈三:任务类型

  • 代码生成:结构化强,draft 容易猜对,接受率高;
  • 事实问答:有确定答案,接受率中等;
  • 创意写作:开放性高,draft 很难猜对 target 的"创意",接受率低。

提升接受率的方法

方法 原理 代价
提升 draft model 质量 用更好的蒸馏方法训练 draft model 训练成本
用特征自回归(EAGLE) 在 feature 级别预测,分布更接近 target 需要训练、需要隐藏状态
用 Medusa Head 基于 target 自身隐藏状态预测 需要训练
降低温度 减少随机性,提高一致性 损失创造性
自适应草稿长度 接受率高时多草稿,接受率低时少草稿 需要动态调度
共享前缀验证 利用树的共享前缀减少重复计算 实现复杂度

#331. 为什么 speculative decoding 在某些场景下收益不明显甚至负收益?

#知识点

  • Draft 开销 vs 加速收益
  • 短序列场景
  • 高温度场景
  • 内存带宽瓶颈
  • 实现开销(调度、KV cache 管理)

#详细解答

Speculative decoding 不是"免费"的加速,它有自己的开销。在某些场景下,这些开销会抵消甚至超过加速收益。

场景一:序列太短

如果生成的序列很短(如 <20 token),speculative decoding 的收益有限:

  • 草稿-验证的流水线刚刚预热就结束了;
  • 初始的 draft 生成开销无法被后续的并行验证摊薄;
  • 短序列下即使不用投机解码也很快,加速比不明显。

场景二:温度太高

高温度(T > 0.7)下,模型的输出高度随机:

  • draft model 很难猜中 target model 的随机选择;
  • 接受率极低,大部分 draft token 被拒绝;
  • 每次被拒绝都需要回退和重新采样,反而增加了延迟。

场景三:Draft model 太弱

如果 draft model 比 target model 弱太多(如 target 70B,draft 100M):

  • draft 的分布与 target 差异巨大;
  • 接受率可能低于 20%;
  • draft 的计算开销 + 验证的计算开销 > 直接生成的计算开销。

场景四:内存带宽已饱和

Speculative decoding 的加速原理是"用 draft 的算力换 target 的步数"。但如果系统瓶颈不是算力而是内存带宽

  • draft model 也需要从 HBM 读取权重;
  • 两个模型争用带宽,可能导致 target model 的验证反而变慢;
  • 整体吞吐可能不升反降。

场景五:KV cache 管理开销

在实现 speculative decoding 时,KV cache 的管理变得复杂:

  • draft token 的 KV 需要先写入缓存;
  • 如果被拒绝,需要回滚 KV cache;
  • 回滚操作如果实现不好,会引入额外的同步和拷贝开销。

如何判断业务场景是否适合 speculative decoding?

  1. 测接受率:先在一个有代表性的数据集上测接受率。如果平均接受率 <30%,可能不适合;
  2. 测端到端延迟:不要只看理论加速比,要测真实的 TTFT(Time To First Token)和 TPOT(Time Per Output Token);
  3. 考虑温度设置:如果业务用高温度(创意生成),投机解码收益小;如果用低温度(代码/问答),收益大;
  4. 考虑序列长度:长序列(>100 token)收益更明显;
  5. 考虑硬件:如果 HBM 带宽是瓶颈,投机解码可能帮不上忙。

#332. Medusa 的树形注意力(Tree Attention)是如何工作的?为什么比普通 attention 更高效?

#知识点

  • 候选树的构建
  • 树形注意力的因果 mask
  • 共享前缀的计算复用
  • 与普通序列 attention 的对比
  • 验证效率

#详细解答

为什么需要树形注意力?

Medusa 的每个 head 可以预测多个候选 token,这些候选可以组合成一棵候选树。例如:

  • Head-1 预测下一个 token 可能是 A 或 B;
  • 如果下一个是 A,Head-2 预测下下个可能是 C 或 D;
  • 如果下一个是 B,Head-2 预测下下个可能是 E 或 F。

这样就形成了一棵候选树:

    [ROOT]
    /    \
   A      B
  / \    / \
 C   D  E   F

如果用最简单的方法验证这棵树,需要对每条路径分别做前向传播:

  • 路径 1: ROOT -> A -> C
  • 路径 2: ROOT -> A -> D
  • 路径 3: ROOT -> B -> E
  • 路径 4: ROOT -> B -> F

这会导致大量的重复计算——ROOT 和 A(或 B)被重复计算多次。

树形注意力的核心思想

树形注意力通过设计特殊的 causal mask,让模型一次性并行验证整棵候选树,同时复用共享前缀的计算。

具体做法

  1. 把候选树展平成一个序列,保持树的拓扑关系;
  2. 设计 attention mask,使得每个节点只能看到它的祖先节点(从根到该节点的路径);
  3. 这样,共享前缀的 attention 计算只需做一次,不同分支的后缀计算并行进行。

例子

对于树:

    [0:ROOT]
    /      \
  [1:A]   [2:B]
  /   \   /   \
[3:C][4:D][5:E][6:F]

展平序列:[ROOT, A, B, C, D, E, F]

Attention mask 设计为:

  • C 只能看到 ROOT 和 A(它的祖先)
  • D 只能看到 ROOT 和 A(它的祖先)
  • E 只能看到 ROOT 和 B(它的祖先)
  • F 只能看到 ROOT 和 B(它的祖先)

这样,ROOT 的 attention 计算只需做一次,A 和 B 的计算并行,C/D/E/F 的计算也并行。

效率提升

假设树的宽度为 W(每个节点有 W 个子节点),深度为 D:

  • 普通方法(逐路径验证):需要 W^D 次前向传播;
  • 树形注意力:只需 1 次前向传播,处理约 W*D 个 token。

面试中的关键表达

树形注意力的本质不是"改 attention 算法",而是通过一个精心设计的 causal mask,把树的拓扑结构编码进注意力计算中。这样共享前缀的计算被自动复用,不同分支的验证被自动并行,整个验证过程只需要一次前向传播。


#333. EAGLE 的训练数据是如何构造的?为什么用"特征"而不是"token"来做 draft?

#知识点

  • EAGLE 的训练流程
  • 隐藏状态作为训练目标
  • Feature-level vs Token-level prediction
  • 信息密度与压缩
  • 蒸馏与自回归

#详细解答

EAGLE 训练数据的构造

EAGLE 需要训练一个 draft model 来预测 target model 的隐藏状态。训练数据的构造分为三步:

第一步:收集输入序列

从目标任务的分布中采样大量输入序列(prompt)。这些序列应该覆盖目标任务的典型模式。例如,如果目标是代码生成,就收集大量代码相关的 prompt。

第二步:提取 target model 的隐藏状态轨迹

对每个输入序列,用 target model 做完整的前向传播(或者截断到某个长度),记录:

  • 每个 token 位置的隐藏状态(hidden state);
  • 通常只记录最后一层的隐藏状态(信息最丰富);
  • 也可能记录中间层的隐藏状态(用于多尺度 draft)。

这样得到的数据形式是:[(h_1, t_1), (h_2, t_2), ..., (h_n, t_n)],其中 h_i 是第 i 个位置的隐藏状态,t_i 是对应的 token。

第三步:构造 draft model 的训练样本

对于每个位置 i,构造一个训练样本:

  • 输入:h_i(当前位置的隐藏状态)
  • 目标:h_{i+1}(下一个位置的隐藏状态)

Draft model 学习的是:给定当前隐藏状态,预测下一个隐藏状态。

为什么用特征(隐藏状态)而不是 token?

原因一:信息密度

一个 token 只是一个离散符号(如词表中的 ID),信息密度很低。而一个隐藏状态是一个高维向量(如 4096 维浮点数),编码了模型对当前上下气的全部理解。

  • Token 级别的预测:从词表中选 1 个(如 50000 选 1),信息量为 log2(50000) ≈ 16 bit;
  • Feature 级别的预测:预测一个 4096 维向量,信息量为 4096 × 16 bit = 65536 bit。

Feature 级别的预测目标包含更多信息,draft model 可以学到更丰富的模式。

原因二:分布连续性

Token 是离散的,feature 是连续的。从 h_i 预测 h_{i+1} 是一个连续映射问题,比离散分类问题更容易学习(梯度更稳定)。

原因三:直接对齐 target model

EAGLE 的最终目标是让 draft model 生成的 token 被 target model 接受。如果 draft model 直接学习预测 target model 的隐藏状态,那么:

  • draft model 预测的 h_{i+1} 通过 target model 的 projection 层映射成的 token;
  • 与 target model 自己计算的 token 天然更接近;
  • 接受率自然更高。

原因四:压缩表示

隐藏状态可以看作是对输入序列的"压缩表示"。Draft model 学习的是这种压缩表示的转移规律,而不是表面的 token 转移规律。这类似于学习"语义级别的动力学"而不是"符号级别的动力学"。

EAGLE 的损失函数

L = MSE(draft_model(h_i), h_{i+1})

简单的 MSE 损失。因为隐藏状态是连续的,不需要复杂的分类损失。


#334. 投机解码中的"验证步"和"草拟步"的延迟如何权衡?什么比例最优?

#知识点

  • 草拟步(draft step)的开销
  • 验证步(verification step)的开销
  • 每步草稿数(K)的选择
  • 流水调度与批处理
  • 最优草稿长度的理论分析

#详细解答

Speculative decoding 的延迟由两部分组成:

草拟步(Draft Step)

  • Draft model(或 Medusa Head、Lookahead)生成 K 个候选 token;
  • 这一步通常很快(draft model 小,或 Medusa Head 是轻量 MLP);
  • 但 K 越大,draft 时间越长。

验证步(Verification Step)

  • Target model 验证这 K 个候选 token;
  • 这一步需要一次 target model 的前向传播;
  • 但验证可以通过树形注意力一次性完成,时间基本与 K 无关(在一定范围内)。

延迟权衡

总时间 ≈ draft_time(K) + verify_time(K)

  • draft_time(K) 随 K 线性增长(K 越大,draft 越久);
  • verify_time(K) 在一定范围内几乎不变(树形注意力可以并行验证多个 token)。

最优 K 的选择

理论上,最优 K 取决于接受率 α:

  • 如果接受率很高(如 α > 0.8),K 越大越好——因为大部分 draft token 被接受,每验证步前进的距离长;
  • 如果接受率很低(如 α < 0.3),K 应该小——因为大部分 draft token 被拒绝,大 K 只是浪费 draft 时间。

经验上,K 通常在 3-8 之间。Medusa 的实验表明,4-5 个 head 是 sweet spot。

流水调度优化

更高级的优化是流水线化

  1. 第 1 步:draft model 生成 K 个候选;
  2. 第 2 步:target model 验证第 1 步的候选(同时 draft model 开始生成下一批 K 个候选);
  3. 第 3 步:target model 验证第 2 步的候选(同时 draft model 生成下一批)...

这样 draft 和 verification 可以部分重叠,减少总延迟。

面试中的实用建议

不要追求"理论最优 K",而是:

  1. 在业务数据上测不同 K 的端到端延迟;
  2. 找到延迟最小的 K(通常通过二分搜索);
  3. 对不同的任务类型(代码/问答/写作)分别调 K,因为接受率不同。

#335. 在商用推理引擎(vLLM、TensorRT-LLM)中集成投机解码的难点是什么?

#知识点

  • vLLM 的 PagedAttention 与 KV cache 管理
  • TensorRT-LLM 的图优化与静态 shape
  • 动态草稿长度
  • KV cache 回滚
  • Batch 内不同请求的差异化处理

#详细解答

在商用推理引擎中集成投机解码,不是简单地把 draft model 和 target model 串起来。需要解决多个工程难点。

难点一:vLLM 的 PagedAttention KV cache 管理

vLLM 使用 PagedAttention 把 KV cache 分成固定大小的 page。投机解码需要:

  • draft token 的 KV 需要先写入 page;
  • 如果 draft token 被拒绝,需要"回滚"这些 page;
  • 但 page 是固定大小的,回滚可能发生在 page 中间,导致 page 碎片化。

vLLM 的解决方案:

  • 为 draft token 使用"临时 page";
  • 验证通过后再把临时 page"转正";
  • 验证失败则直接丢弃临时 page,无需回滚。

难点二:TensorRT-LLM 的静态 shape 优化

TensorRT-LLM 通过编译时静态 shape 优化获得高性能。但投机解码中:

  • 草稿长度 K 是动态的(接受率高时多草稿,低时少草稿);
  • 这导致输入 shape 不固定,无法充分发挥 TensorRT 的静态优化优势。

解决方案:

  • 把 K 固定为最大值(如 K=8),用 padding 填充;
  • 或者使用 TensorRT-LLM 的动态 shape 支持(性能略低于静态 shape)。

难点三:Batch 内请求的差异化

一个 batch 中可能有不同特性的请求:

  • 请求 A:代码生成(接受率高,适合大 K);
  • 请求 B:创意写作(接受率低,适合小 K)。

如果 batch 内所有请求用同一个 K,会导致:

  • 请求 B 拖累请求 A(必须等 B 的 draft 完成才能一起验证);
  • 或者请求 A 被拖累(batch 按最慢的请求调度)。

解决方案:

  • 按请求特性分组 batch(代码请求一组,写作请求一组);
  • 或者使用异步调度,不同请求独立做投机解码。

难点四:Draft model 的显存占用

商用部署中,显存是稀缺资源。Draft model 虽然小,但仍需要:

  • 权重显存;
  • KV cache 显存;
  • 激活显存。

如果 target model 已经占满了显存,可能没有空间放 draft model。解决方案:

  • CPU offload:draft model 放在 CPU,需要时拷贝到 GPU;
  • 权重量化:把 draft model 量化为 INT4/INT8;
  • Medusa 方案:不用 draft model,直接用 Medusa Head(额外开销很小)。

#336. 如果业务场景的延迟要求极高(如实时对话),是否还应该用投机解码?

#知识点

  • TTFT(Time To First Token)vs TPOT(Time Per Output Token)
  • 投机解码对 TTFT 的影响
  • 端到端延迟分析
  • 实时场景的权衡
  • 替代方案

#详细解答

这个问题没有固定答案,需要分析延迟的构成投机解码对延迟的影响

延迟的分解

在对话场景中,用户感知的延迟 = TTFT + 累计 TPOT:

  • TTFT(Time To First Token):从用户发送消息到看到第一个回复 token 的时间;
  • TPOT(Time Per Output Token):每生成一个 token 的时间。

投机解码对 TTFT 的影响

投机解码通常增加 TTFT

  • 在生成第一个 token 之前,需要先运行 draft model 生成候选;
  • 这个 draft 步骤增加了首 token 的延迟;
  • 如果用户非常在意"多久看到第一个字",增加 TTFT 可能是不可接受的。

投机解码对 TPOT 的影响

投机解码通常降低 TPOT

  • 如果接受率高,每验证步可以前进多个 token;
  • 用户看到后续 token 的速度变快;
  • 总生成时间缩短。

实时对话场景的权衡

场景 是否适合投机解码 原因
用户非常在意"秒回" 不太适合 TTFT 增加可能让用户感觉"卡顿"
用户在意"流畅输出" 适合 TPOT 降低,输出更流畅
短回复(<20 token) 不太适合 序列短,加速收益小,TTFT 增加占比大
长回复(>100 token) 适合 序列长,TPOT 收益累积,TTFT 增加占比小
代码生成场景 非常适合 接受率高,延迟敏感
创意写作场景 不太适合 接受率低,温度高

替代方案

如果投机解码不适合,可以考虑其他延迟优化方法:

  1. 模型量化:INT8/INT4 推理降低每次前向的延迟;
  2. GQA/MQA:减少 KV cache 读取,降低 decode 延迟;
  3. Continuous Batching:提高吞吐,减少排队延迟;
  4. Prefix Caching:复用 system prompt 的计算,降低 TTFT;
  5. 模型小型化:用小模型(如 7B)处理简单请求,大模型(如 70B)处理复杂请求。

面试中的高分表达

是否用投机解码,不能一概而论。关键要看业务场景对 TTFT 和 TPOT 的敏感度。如果业务是"用户说一句,模型回一句"的短对话,TTFT 更重要,投机解码可能得不偿失;如果业务是"用户要求写一篇长文",TPOT 更重要,投机解码的收益会被放大。最好的做法是在真实业务数据上做 A/B 测试,测端到端的用户体验指标(如用户满意度、任务完成率),而不是只看理论加速比。


#深度解析

1. 为什么不能按"词"切?

假设我们按空格分词,词表就是所有出现过的单词。会遇到三个致命问题:

问题一:词表爆炸

英语里 "run", "runs", "ran", "running" 是同一个词的不同形式。如果都进词表,一个词占了 4 个位置。英语还好,但像芬兰语、土耳其语这种黏着语,一个词可以有上百种形态变化。

问题二:OOV(未登录词)

人名 "Zhangsan"、新词 "元宇宙"、拼写错误 "teh"(应该是 "the")、代码变量名 user_id_2024——这些都不可能出现在训练词表里。按词切分的话,它们全部变成 <UNK>,模型完全不理解。

问题三:多语言灾难

中文没有空格。"我爱北京天安门" 如果按字切:"我" "爱" "北" "京" "天" "安" "门"——丢失了"北京""天安门"这样的词组信息。如果按词切(需要额外分词工具):"我" "爱" "北京" "天安门"——但需要先解决分词歧义("南京市长江大桥" 是 "南京/市/长江大桥" 还是 "南京市/长江/大桥"?)。


2. Tokenization 的本质是什么?

Tokenization = 把连续文本映射成离散符号序列

它不是语言学上的"分词",而是一个压缩问题

  • 目标:用最短的 token 序列表示原始文本
  • 约束:token 必须来自固定词表
  • 优化:高频组合尽量用一个 token,低频组合拆开

好的 tokenizer 应该满足:

  1. 紧凑性:平均每个词用尽量少的 token(英语 ~1.3 token/词,中文 ~1.5-2 token/字)
  2. 覆盖率:OOV 率尽量低
  3. 语义性:切分边界尽量在语义边界上

3. Token 数 vs 字符数:为什么这个比例很重要?

语言 文本 字符数 Token 数 比例
英文 "Hello world" 11 2-3 ~0.27
中文 "你好世界" 4 2-4 ~0.75
代码 def hello(): 12 4-6 ~0.42

为什么中文的 token/字符比更高?

因为大多数 tokenizer 在英语语料上训练,中文 token 较少。一个中文字经常被切成 1-2 个 token,甚至更多。这带来两个问题:

  1. 同样长度的上下文窗口,中文能容纳的"字"更少
  2. 中文推理成本更高(token 数多 → 计算量大)

这也是中文大模型常常需要专门训练 tokenizer 的原因。


4. 面试官常见深挖追问

  • "Tokenizer 对模型效果影响有多大?"
    • 答:非常大。Tokenizer 决定了:1)序列长度(影响 attention 计算量);2)词表大小(影响 embedding 层参数量,通常占模型总参数 10-20%);3)OOV 率;4)不同语言的切分公平性。有研究表明,换一个更好的 tokenizer 可以让同规模模型提升 2-5 个百分点。
  • "中英文混合文本怎么切分?"
    • 答:现代 tokenizer(如 SentencePiece)直接在字节/字符流上训练,不区分语言,所以中英文混用没问题。但问题是:如果训练语料中英语占 90%,tokenizer 会倾向于为英语生成更紧凑的 token,中文 token 就会更碎。解决方法:1)平衡多语言训练语料;2)专门为中文增加训练数据;3)用更大的词表。
  • "代码的 tokenizer 和文本的 tokenizer 有什么区别?"
    • 答:代码有大量重复模式(如 def , import , return ),而且空格/缩进有语义(Python)。好的代码 tokenizer 会把常见关键字和模式作为独立 token,比如 def, return, self., __init__。这能显著减少代码序列长度,提高代码生成效率。