解密Prompt系列71. 从DSpark聊聊大模型 Decoding 提速的技术演化

📅 2026/7/30 20:31:20 👁️ 阅读次数 📝 编程学习
解密Prompt系列71. 从DSpark聊聊大模型 Decoding 提速的技术演化

LLM 生成 Token 是串行的:产生第 \(N\) 个 Token,必须把前 \(N-1\) 个 Token 统统塞回模型里,重新算一遍矩阵乘法。由于现代 GPU 拥有海量的并行计算单元(ALU),一次只算一个 Token 根本填不满 GPU 的吞吐能力(Memory-bound 严重)。这就像开着一辆 80 吨的大卡车,每次只运送一粒花生米。

那能不能让模型一次“吐出”多个 Token?

核心思路很简单:先打草稿(Drafting),再由大模型并行批量验证(Verification)。但问题随之而来:如何生成得更快?同时生成的更好(采纳率不下降)?最重要的是——如何保证输出质量完全不缩水(Lossless)?

从 DeepMind 提出的“双模型协作”,到 DeepSeek 招牌的 MTP,再到最新的 DFlash 和 DSpark,今天我们就来扒一扒这套“提速大法”的技术演化史。

gpt_1785045490914_0

01. 元老派:多模型投机解码 (Speculative Decoding)

DeepMind:Fast Inference from Transformers via Speculative Decoding

既然大模型计算贵、速度慢,那能不能找个“小弟”给它打下手?

这就是最经典的 Speculative Decoding 思想:引入一个参数量极小(通常为主模型的 \(1/10\) 甚至更小)的草稿模型(Draft Model) 和一个目标大模型(Target Model)

image

1.1运行机制

  1. 草稿生成:小模型一路狂飙,以自回归方式快速串行生成 \(\gamma\) 个 Token(比如 4 个)。
  2. 并行验证:把这 4 个草稿 Token 连同上文一次性打包成 Batch,塞给目标大模型做一次前向传播。大模型只用1次推理就能并行计算出这 4 个位置的真实概率分布 \(p(x)\)
  3. 拒绝采样(Reject Sampling)
    假设草稿模型的输出概率为 \(q(x)\),目标模型为 \(p(x)\)
  • 如果 \(p(x) \ge q(x)\):说明大模型觉得小模型“猜得太保守了”,直接无条件接受
  • 如果 \(p(x) < q(x)\):小模型“飘了”,大模型以概率 \(\frac{p(x)}{q(x)}\) 接受它。一旦某个 Token 被拒绝,后续草稿全部作废。大模型基于最后接受的token推理1个新token,再进一下一轮。

数学上的美感:这种拒绝采样机制从概率论上证明了:投机解码输出的概率分布,与纯粹用大模型一步步生成的分布完全一致! 真正做到了无损加速。

1.2 瓶颈在哪?

单次推测的平均延迟可以表示为:

\[T_{\text{avg}} = \frac{\gamma \cdot T_{\text{draft}} + T_{\text{verify}}}{\mathbb{E}[\text{Accepted Tokens}]} \]

要提速,必须满足 “草稿做得快”“接受率高”。但这往往是个悖论:

  • 找个与主模型同源的小尺寸模型(如用 Gemma-1B 帮 Gemma-27B 打草稿),在 vLLM 中虽然能拿到 2~3 倍加速;
  • 但异构/小模型对语义偏好、复杂逻辑的理解和小动作并不一致,导致接受率在复杂任务上断崖式下跌。

2. 自给自足派:Medusa, MTP & EAGLE

既然外挂一个小模型有“心灵不相通”的跨模型代沟,那干脆让大模型长出“多个头”,自己给自己打草稿!

2.1 Medusa:美杜莎的多头并行(暴力外推,但易翻车)

Medusa: Simple and Effective Predictive Decoding for Large Language Models

image

Medusa 的做法非常野蛮:主干冻结,顶层接挂。主要被llama系列、Mistral系列模型采纳。

它在 LLM 最后一层 Hidden State( \(h_t\))后,并排挂了K个独立的轻量级预测头(Medusa Heads,通常是带残差的 1~2 层 MLP):

  • Head 1:拿着 \(h_t\),强行预测 \(t+2\) 位置的 Token;
  • Head 2:拿着 \(h_t\),强行预测 \(t+3\) 位置的 Token;
  • ...以此类推。

这属于典型的“无中生有”。在 \(t\) 时刻,连 \(t+1\) 是什么都不知道,就凭 \(h_t\) 强行预测 \(t+3\)?由于打破了因果链(Causal Dependency),位置越靠后,头的采纳率越呈指数级衰减。它就像让一个没看过下一页小说的人直接猜下下页的剧情,翻车率极高。

并且轻量级预测头是在冻结基座的情况下,先让基座模型向外推理K步,再用独立的预测头去并行预测K步,并尝试和基座预测效果对齐,用交叉熵作为损失函数,本质上预测头是在蒸馏基座模型的串行推理能力。

2.2 MTP:DeepSeek 的预训练串行级联(优雅的极致)

DeepSeek-V3 Technical Report

截图_选择区域_20260726132837

DeepSeek-V3 抛弃了 Medusa 的“各猜各的”,提出了 Multi-Token Prediction (MTP)。它的核心思想是:打破“并行”幻觉,用串行级联(Cascading)维持因果严谨性,且原生融入预训练!

以 MTP Module 1(预测 \(t+2\))为例,它的输入不是空穴来风,而是将:

  1. 上一步预测出的 Token \(x_{t+1}\) 经过 共享 Embedding 得到的语义向量;
  2. 主干模型在 \(t\) 时刻输出的隐藏状态 \(h_t\)

进行拼接,送入一个轻量级 Transformer 模块(MTP Layer)计算后,再复用主干的 LM Head 做出预测!

为什么 MTP 效果吊打传统多头?

  1. 保留因果链(Causal Lineage):前一个头的预测 Token 通过过词向量送给下一个头,确保了自回归的数学严谨性。
  2. 原生预训练(Native Pre-training):MTP 标头不是后训“补丁”,而是在预训练阶段就以 0.3 的 Loss 权重和主干模型联合训练!主干模型在训练时就学会了“在 Hidden State 里多存一点未来信息”。并且在消融实验中因为外推多步预测,所以进一步提升了模型整体效果。
  3. 极极致的参数复用:MTP 复用了巨无霸的 Embedding 矩阵和 LM Head,引入的额外参数极少。
  4. 更深度的外推思考:每一个预测头都拥有自己的一层轻量 Transformer Layer,对比Medusa的MLP浅层映射,每个输出头能有更多的思考空间。

DeepSeek V3用的是1个MTP,也就是同时预测两个token,第二token的接受率在85%+,所以带来1.8倍左右的平均推理tps。

深度思考??

为什么MTP使用预测Token过词向量,而不是直接传1个token的 Hidden State?

我想可能有2个原因:

  • 对齐训练与推理:训练时前一步是 Ground Truth Token,推理时是预测 Token。传 Token Embedding 能保证训练/推理的输入表征空间分布高度一致。而主干和head的输出隐藏层其实是没有对齐的。
  • 预训练信息注入:deepseek是在预训练就引入了MTP头,所以主干模型天然学到了我要“多保留一些信息用于后几步的预测”,因此最终deepseek主干模型的hidden state中本身就会保留更丰富的外推token信息。

2.3 EAGLE:后训练领域的串行打草稿王者

  • https://github.com/SafeAILab/EAGLE

EAGLE 与 MTP 的思路不谋而合,但它的目标是“拯救那些没有在预训练阶段加入 MTP 的开源模型”。

先说共同点,EAGLE和MTP的相同在于

  • 都通过使用前一个推理token过向量作为特征输入,保留了串行推理的因果性
  • 架构都包含和主模型共享LM head和Embedding层

那差异在哪里呢?

个人感觉差异主要来自训练差异,因为不像MTP在预训练阶段融入,EAGLE还是冻结主干微调,因此

  • 草稿头结构更轻:EAGLE-1仅使用一层FC+解码层,没有transformer
  • 草稿特征更复杂:和MTP的选择相反,EAGLE没有使用主干隐藏层,而是使用草稿上一个token的隐藏层。同样部分因为Eagle是冻结主干模型,所以主干隐藏层输出的信息有限。

如果模型在预训练阶段就引入多头预测的话,整体上限会更高,这里就不多介绍EAGLE了。

03. 革命派:基于扩散与块并行的极致暴击 (DFlash & DSpark)

串行多头(如 MTP)虽然采纳率高,但因为“头与头之间依然是串行计算”,限制了生成草稿的速度上限。能不能既要“并行齐射”的速度,又能拿到“串行因果”的高接受率?

答案交给了 Block Diffusion 与 Semi-Autoregressive。

DFlash:Block Diffusion 的全图通透

  • DFlash: Block Diffusion for Flash Speculative Decoding

image

DFlash 的理念极其硬核:放弃因果掩码(Causal Mask),引入 Block 级别的双向注意力(Bidirectional Attention)

传统自回归 Mask:              DFlash 块内 Block Mask:
1 0 0 0                      1 1 1 1  <── 块内所有 Token
1 1 0 0                      1 1 1 1      互相可见!
1 1 1 0                      1 1 1 1      (双向 Attention)
1 1 1 1                      1 1 1 1
  1. 主干Hidden全面融入:DFlash并不只在草稿模型第一层注入主模型特征,而是在草稿 Transformer 的每一层都嵌入主干模型的顶层 Hidden State,让主干语义彻底贯穿草稿网络。
  2. 块内双向注意力:在打草稿的 8 个或 16 个 Token 区域内,Token 之间使用双向 Attention。这让草稿网络在预测第 \(k\) 个 Token 时,能同时兼顾前后文语义,大幅拉升了并行预测的逻辑连贯性。
  3. 性能表现:Block Size 设置为 8 时,DFlash 依然能保持约 70% 的高接受率,推理速度比 EAGLE-3/MTP 快了 2 倍以上!

所以如果简单来说,最重要的就是相比Medusa的多头独立输出,DFlash通过双向注意力使得输出的多个token之间有更好的一致性。

DSpark:马尔可夫偏置 + 置信度调度的集大成者

  • DSpark: Confidence-Scheduled Speculative Decoding with Semi-Autoregressive Generation

image

最近爆火的 DSpark 站在了前人的肩膀上,把工程和算法的协同推到了极致。

DSpark 意识到:并行推测虽然快,但 Block 越大,缺少邻近 Token 约束的因果性,后方预测就越容易发散。 怎么解决?DSpark 引入了两大杀招:

杀招一:马尔可夫串行头,引入局部因果约束

DSpark 预测第 \(k\) 个未来位置的最终 Logits 分布时,采用了如下公式,每个token的概率由并行预测的置信度结合T-1 Token 马尔科夫转移概率共同决定。

\[P_k = \text{Softmax}\Big( U_k + B_k(x_{k-1}) \Big) \]

  • \(U_k\)(并行骨干 Logits):通过类似 DFlash 的并行结构一次性齐射算出来的 Logits,代表全局语义上下文
  • \(B_k(x_{k-1})\)(马尔可夫偏置):这是一个极其轻量的转移矩阵查表操作。\(x_{k-1}\) 是前一个位置刚选出的离散 Token,矩阵 \(B_k\) 瞬间查找出它对当前位置 \(k\) 的转移偏置。当然这个V*V(词表大小)的巨无霸矩阵,也可以通过低秩压缩只存储低秩变量。

生动比喻:并行骨干算出来第二字可能是“皮”或“肉”(各 50%)。如果前一个字确定是“苹”,查表矩阵 \(B_k(\text{“苹”})\) 立即给“果”加 100 分,给“皮”加 10 分。零延时的查表操作,秒级纠正了并行生成的无序性!

杀招二:置信度剪枝,动态草稿长度

虽然并行推理很快,也能同时推理很大的block size。但是更长的草稿,并不一定带来更快的推理Through Put。因为接受率较低,会导致后面在校验阶段被大量丢弃,在用户高并发请求的场景下,会平白浪费显卡资源。

DSpark 在顶层挂挂了一个置信度头,预测当前 Token 的条件存活概率 \(c_k \in (0, 1)\),和前缀联合概率,也就是基于概率链式法则,整个草稿前缀全被接受的概率:

\[a_{r,j} = \prod_{i \le j} c_{r,i} \]

同时在使用以上概率时,DSpark还引入以下两个机制

  • 顺序温度缩放 (STS):原始神经网络预测的置信度容易过度自信。DSpark 采用从左到右的温度系数逐位校准,修正置信度分布。使得预期接受token的期望值,和真实接受token数之间的误差越小越好。

杀招三:硬件感知的调度器

前面的剪枝是用户单条请求下的理想情况,但是现实往往有多个用户请求并发,因此还需要考虑GPU处理速度和带宽问题,因为最终的目标其实是最大化吞吐速度。所以期望验证通过率更高,也不一定就能获得最快的推理速度。

所以DSPark还有一层异步的调度器,调度器会把多个草稿生成的前缀按照置信度进行倒排,然后从队列头,根据历史估算的GPU吞吐曲(SPS)线进行预估

  • 先只验证第1个词(置信度最高)。算算当前每秒能产出多少词(吞吐量 = 验证速度 × 期望接受数)。记录为“当前最优值”。
  • 试探把第2个词也加进来(批处理变大了)。因为批处理大了,验证速度可能变慢一点点,但多验证了一个词,期望产出的总词数变多了。算一下新的总吞吐量。
  • 如果新的吞吐量 > “当前最优值”,说明加这个词是赚的,保留它。
  • 继续试探第3个、第4个……
  • 如果试探到第5个词时,发现把第5个词加进来后,因为验证速度变慢太多,导致总吞吐量反而下降了,调度器立刻停止,不再往后看了(即使后面第6个词置信度很高也不看了)。

并且在调度器实际和GPU配合时,调取器是根据上一个batch的预估结果,用于下一个batch的判断,因此无需让GPU等待CPU判断到底要验证几个词,实现吞吐最大化。

未来的 LLM 推理架构会走向何方?
随着 DeepSeek MTP 和 DSpark 的成功,预训练与推理调优的界限正在被彻底抹平。将“多步预测能力”作为模型本身的基础设施进行原生训练,已然成为了大模型演进的必然趋势!