博客算法工具链【大模型】DFlash:把串行 Draft 变成并行“填空”

【大模型】DFlash:把串行 Draft 变成并行“填空”

no_name2026-10-08
23
0

1. DFlash 想解决什么问题

大语言模型的 Decode 阶段具有严格的自回归依赖:只有生成当前 Token,才能计算下一个 Token。KV Cache 避免了历史上下文的重复计算,却没有消除 Token 之间的串行关系。

投机解码通过“Draft 先猜、Target 再验证”减少 Target Model 的串行 Forward 次数。但以 EAGLE/EAGLE-3 的默认路径为例,Drafter 自己通常仍要逐步生成 K 个候选 Token。

DFlash 的目标,是继续压缩这部分 Draft 串行开销:不再逐 Token 起草,而是把多个未来位置组织成一个 query block,在一次 Draft Forward 中并行提出 K 个候选。

2. 投机解码简介

典型的模型式投机解码由 Drafter 提出候选,Target Model 计算候选位置的 logits,并从前向后决定接受多少个候选。Drafter 猜错不会被直接写入最终结果,只会让本轮接受长度变短。

如果配置 num_speculative_tokens=K,一轮验证最多接受 K 个 draft token;当所有候选都通过时,还可以输出 Target 在后一个位置产生的 bonus token。因此一轮最多推进 K+1 个 Token。

3. DFlash 的核心变化:从“接龙”变成“并行填空”

自回归 Drafter 像接龙:第二个候选依赖第一个候选,第三个候选又依赖第二个候选。DFlash 则为未来位置放置专用的 mask token,让这些位置在一次 Draft Forward 中同时计算。

因此,DFlash 在 vLLM 中属于原生 parallel drafting 方法。它与“先串行生成 K 个 token、再并行验证”的主要区别,是 K 个 draft token 的 logits 来自同一次 query-block Forward。

4. 真正的输入布局:1 个 Anchor 加 K 个 Mask

大家很容易把 Context、Anchor 和 Mask 都当成普通 Token 输入。实际上,它们承担的是三种不同角色:
  • Context 表示已经被 Target Model 正式处理过的历史前缀对应的 Target hidden states。DFlash 会把这些特征提前投影成自己每一层 Attention 使用的 K/V,因此 Draft Forward 不需要重新把整段历史 Token 跑一遍;
  • Anchor 表示最新一个已经确定、但还没有进入这批 Context K/V 的 Token;
  • Mask 则代表接下来真正未知、等待 DFlash 猜测的 K 个未来位置。
  • Query Block:由 1 个已知的 Anchor Token 和 K 个未知位置的 Mask Token 组成。Anchor 负责告诉 Drafter“最新确定的 Token 是什么”,K 个 Mask 才是本轮需要预测的候选位置。
可以先记住下面的时间顺序:已确认的历史 Context → 已确认的最新 Anchor → K 个未知 Mask 位置。

4.1 先把三个角色放到时间轴上

假设 Target 已经确认了序列 x₁ x₂ … xₜ,并保存了这些位置的 hidden states。Target 随后又确定了下一个 Token xₜ₊₁。在下一轮 DFlash Draft 中:
  • x₁ … xₜ 对应 Context,以预计算 K/V 的形式提供给 Drafter;
  • xₜ₊₁ 是已经确定的 Anchor,不是 DFlash 猜出来的候选;
  • xₜ₊₂ … xₜ₊ₖ₊₁ 还是未知位置,因此用 K 个 Mask 占位。

4.2 为什么一定要有 Anchor

Context K/V 只覆盖到 xₜ,但最新确定的 Token 已经是 xₜ₊₁。如果直接放 K 个 Mask,Drafter 就看不到这个最新 Token,会产生一位信息滞后。Anchor 的作用,就是把 xₜ₊₁ 的 embedding 放进本轮 Query Block,作为“已知起点”连接历史 Context 与未来 Mask。

因此 Anchor 可以理解成一枚接力棒:它是上一轮 Target 已经确认的结果,本轮 DFlash 从它后面开始猜。

4.3 为什么K+1 个 Query 只输出 K 个候选

因为第一个 Query 是已知 Anchor,只用于提供条件和更新 Drafter 状态,不需要再次预测。真正需要读取 logits 并采样的是后面的 K 个 Mask 位置。

4.4 Mask 预测 Token方式

Mask 位置是不是像自回归一样依次拿到前一个预测 Token?答案:不是。

DFlash 在一次 Forward 中同时计算所有 Mask 位置,不会先得到 T1、再把 T1 的 Token ID 喂给下一步生成 T2。各 Mask 位置看到的是历史 Context、已知 Anchor、位置编码,以及 checkpoint 所定义的块内 Attention 信息。

这正是它能并行 Draft 的原因,也意味着后面的位置没有获得“前面候选已经确定”的自回归条件。DFlash 依靠专门训练的块级 Drafter 提高候选质量,最终再由 Target Verification 筛掉不一致的后缀。

常见误解

正确理解

Anchor 是第一个 Draft Token

不是。Anchor 已经由 Target 确定,本轮不从该位置采样。

Context 是原始历史 Token,再经过一次 DFlash

不是。Context 主要以 Target hidden states 投影后的 K/V 形式提供。

K+1 个 Query 会输出 K+1 个候选

不是。Anchor 不采样,只有 K 个 Mask 输出候选。

Mask 2 会拿到 Mask 1 最终预测的 Token ID

不会。K 个 Mask 在同一次 Forward 中并行计算。

下面用一个具体的 K=4 例子把完整过程串起来。为方便理解,示例按“词”展示;真实模型处理的是 tokenizer 切分后的 Token。
这个例子里,“很” 是已知的 Anchor,不属于四个 Draft 候选。DFlash 真正猜的是它后面的 “好,适合”。因此在配置 K=4 时,内部需要 5 个 Query 位置,但只返回 4 个 Draft Token。

5. 常见问题

5.1 Target Hidden States 如何变成 DFlash 的上下文

Target Model 已经对当前请求做过深层语义计算。DFlash 不再用一个小模型从原始 Token 开始完整理解上下文,而是接收 Target 的 hidden states,并将它们转换成自身 Attention 层使用的 K/V。

在 vLLM 实现中,这一步会对 context states 做归一化、K/V 投影和 RoPE,然后直接写入 DFlash 各层的 KV Cache。随后的一次 Draft Forward 主要处理 anchor 与 mask queries。

DFlash 可以使用 checkpoint 指定的多个 Target 辅助层 hidden states,拼接后再通过 FC 投影到 Drafter hidden size;但这不是所有 checkpoint 的强制要求。如果没有辅助层输入,也可以直接使用 Target 最后一层 hidden state。

5.2 Mask Token 是普通占位符吗

DFlash checkpoint 必须定义 mask_token_id。多数 checkpoint 直接使用词嵌入表中该 ID 对应的 embedding;也有 checkpoint 单独提供训练好的 mask_embedding.pt,运行时会用它替换普通词嵌入。

这意味着 mask 不是任意选一个 Token ID,也不能把其他模型的 mask embedding 随意复用。Mask embedding、Target 特征层、Drafter 权重以及 Attention 结构共同构成了 checkpoint 的训练契约。

5.3 DFlash 与 EAGLE-3 的区别

EAGLE-3 和 DFlash 都可以利用 Target Model 的隐藏特征,但它们处理多个候选位置的方式不同。

对比项

EAGLE-3

DFlash

类型

Speculative Decoding

Speculative Decoding

Draft

轻量 Draft Model

Block Diffusion Draft

Draft 方式

自回归

并行

一次 Draft

逐 Token

整个 Block

Attention

Causal

Non-Causal / Bidirectional

Target Hidden State

使用

使用

Target Verify

并行

并行

核心优化

降低 Draft 成本

消除 Draft 串行依赖

主要收益

更快 Draft

更高并行度

最简单的记忆方式是:EAGLE-3 默认是借助 Target 特征“逐步接龙”,DFlash 则借助 Target 特征“一次并行填一组空”。

5.4 为什么最终结果仍由 Target 保证

DFlash 只负责提出候选,候选仍需经过 Target Model 的验证。第一个不被接受的候选不会被直接写入结果,拒绝位置由 Target 的验证与采样结果接管。

5.5 DFlash性能为什么可能更好,也可能更差

DFlash 将 K 次小型自回归 Draft Forward 合并成一次 query-block Forward,能够减少模型权重重复读取和 Draft 串行等待,更容易利用 GPU 的并行计算能力。

但最终收益仍取决于多个因素:

  • 平均接受长度:一轮 Target Verification 实际推进多少 Token。
  • 候选K 的大小:候选 K 越大,一次提出的候选越多,但 Draft 与 Target 验证宽度也越大。
  • Draft 成本:包括 Context K/V 预计算、query-block Forward 和采样(Draft 模型完成 Forward 得到下一批Token 的概率分布后,根据这个分布选择/生成实际 Token 的过程)。
  • 并发度:高并发下,验证阶段的有效工作量会随 batch size 与 K 增长。

所以不能把论文或单个 checkpoint 的加速数字当作通用保证。正确做法是在目标硬件和真实流量下,同时测量 TPOT、吞吐量、平均接受长度和显存占用。

5.6 DFlash 与 KV Cache 的关系

DFlash 并不替代 KV Cache。Target Model 仍维护自己的 KV Cache;DFlash 也需要为 Draft Attention 维护 KV Cache。

DFlash 的特殊之处在于,它会先把 Target hidden states 投影成 Drafter 的 context K/V,写入 DFlash KV Cache,然后让 anchor/mask queries 在一次 Forward 中读取这些上下文状态。

6. 适用场景与工程限制

DFlash 更适合 Decode 占比高、传统 Drafter 串行开销明显、目标硬件能够高效执行 query-block Forward 的场景。对于长输出、代码生成、对话和 Agent 推理,它可能减少 Draft 延迟并提高单轮推进长度。

它也增加了新的约束:需要匹配的专用 checkpoint;需要额外 Draft 权重和 KV Cache;需要 Target 输出正确的 hidden states。若接受率低或系统已经处于高并发 compute-bound 状态,并行 Draft 并不一定转化为端到端加速。

7. 一句话理解 DFlash

DFlash 是一种面向投机解码的并行 Drafter:它把 Target hidden states 预计算成自身的 Context K/V,再使用 1 个 anchor/bonus query 和 K 个 mask queries,在一次 Draft Forward 中并行提出 K 个候选 Token,最后仍由 Target Model 批量验证。

8. 参考资料

  • vLLM:docs/features/speculative_decoding/README.md
  • vLLM:docs/features/speculative_decoding/dynamic_speculative_decoding.md
  • vLLM 实现:vllm/v1/spec_decode/dflash.py
  • vLLM 实现:vllm/v1/worker/gpu/spec_decode/dflash/speculator.py
  • vLLM 实现:vllm/model_executor/models/qwen3_dflash.py
  • vLLM DFlash2:vllm/model_executor/models/qwen3_dflash2.py
  • DFlash 论文(arXiv:2602.06036):https://arxiv.org/abs/2602.06036

算法工具链
社区征文杂谈
评论0
0/600