笔记日期: 2026-07-22 笔记作者: Zhongzhu Zhou 论文标题: AdaFlash: Adaptive Speculative Decoding via On-Policy Distilled Diffusion Drafters 论文作者: Yu-Yang Qian, Hao-Cong Wu, Chen Chen, Jiacheng Sun, Zhenhua Dong, Peng Zhao, Zhi-Hua Zhou arXiv: 2607.19223 发布状态: 预印本(南京大学 / 华为诺亚方舟实验室合作),2026-07-21
一句话总结
投机解码(speculative decoding)通过让一个便宜的”草稿”模型一次性提议多个 token,再让昂贵的”目标”模型用一次并行前向传播来验证,从而加速大模型推理。最近的一个趋势是用扩散语言模型(diffusion drafter)取代传统的自回归草稿模型——DFlash 就是这类”一步式”扩散草稿模型的代表工作,也是本文的直接前身。AdaFlash 的贡献起点是一个经验性诊断:正是让扩散草稿模型能一次性看到整个草稿区块、一次前向传播就生成完毕的双向注意力机制,同时也让它的草稿质量变得极不稳定——既表现为跨任务领域的不稳定(聊天 vs. 代码 vs. 数学),也表现为单条草稿序列内部的不稳定(前面的 token 和后面的 token 质量差异很大)。一个离线训练好之后就冻结、验证长度固定不变的草稿模型,对这两种漂移都无法做出反应。AdaFlash 提出两个互补的、持续在线运行的机制来弥补这个缺口:(1)在线策略蒸馏(on-policy distillation,OPD)循环,利用目标模型自己产生的实时反馈持续重训练草稿模型,使用反向 KL 目标,并针对扩散草稿模型”概率质量分布过于分散”的倾向设计了一个逐条目散度裁剪的技巧;(2)自适应长度头(adaptive length head),一个很小的辅助网络,预测当前草稿中有多少 token 大概率会被接受,并据此截断发给目标模型验证的长度,这样目标模型就不必去检查那些几乎注定会被拒绝的 token。论文在 8 个基准测试集、3 个目标模型(既有稠密模型也有 MoE 模型)、从并发度 1 到 128 的各种设置下做了端到端评测,AdaFlash 持续优于 EAGLE-3、原版 DFlash,以及一个已有的在线蒸馏基线方法(OSD),而且优势在此前扩散草稿方法会退化到不如普通自回归解码的高并发场景下最为明显。
核心要点
- 核心诊断是”方差”,而不是”准确率”。 DFlash 的平均草稿质量本来就不错——论文指出的问题是,这个平均值掩盖了巨大的波动:在 Qwen3-8B 上,平均接受长度 从 3.40(ShareGPT 聊天场景)到 7.09(MathQA 数学场景)不等,波动幅度达到 2.1 倍,而自回归草稿模型 EAGLE-3 只有 1.2 倍波动(3.89 到 4.60)。
- 两种截然不同的失效模式,对应两种截然不同的解法。 领域级方差(草稿模型质量因任务领域而异)用持续在线重训练草稿模型来解决(OPD);token 级方差(草稿模型质量因区块内位置而异,越往后越差)则不去修草稿模型本身,而是用动态收缩每个请求的验证长度来解决(自适应长度头)。
- 蒸馏目标用的是反向 KL,而不是常见的正向 KL / 交叉熵,因为用双向注意力训练出来的扩散草稿模型倾向于把概率质量铺得很广(高熵);反向 KL 是”模式寻求”(mode-seeking)型的,会把草稿模型的概率质量集中到目标模型认为高概率的 token 上——这恰好是投机解码的接受/拒绝规则所奖励的行为。
- 朴素反向 KL 存在一个设计缺陷,用逐条目散度裁剪来修补:词表中总有少数几个条目,草稿模型分配了不可忽略的概率,而目标模型几乎不分配概率,这些条目会让 KL 求和爆炸,进而主导(并破坏)梯度;在求和之前先给每一项设一个阈值 做裁剪,就能去掉这些离群值,同时保留其余条目里有信息量的梯度信号。
- 长度头几乎是”零成本”训练出来的。 它的监督目标——实际被接受的草稿 token 比例——本来就是投机解码验证步骤已经要计算的副产品;不需要额外的 rollout,也不需要辅助的价值网络,而且它的梯度和草稿模型的梯度是分离(detach)的,两个学习问题互不干扰。
- 系统工程的重要性不亚于算法本身。 异步训练/推理分离(这样重训练永远不会阻塞 token 生成)、只热重载草稿模型和长度头参数的轻量级机制,以及带 EMA 准入控制的可变长度感知调度器——这些都是让”边服务实时流量边持续训练模型”从研究玩具变成可部署系统的必要条件。
- 代表性数字: 在 Qwen3-8B、并发度为 1 时,AdaFlash 相对标准自回归解码达到 4.06× 平均加速,而原版 DFlash 是 3.53×,OSD 在线蒸馏基线是 3.95×,EAGLE-3 是 2.34×。在并发度 128 时——此时 DFlash(0.76×)和 OSD(0.83×)都已经低于标准自回归解码——AdaFlash 依然能保持 1.15× 的加速,论文报告在最高并发场景下相比次优方法吞吐量最高提升约 66%。
- 这不是没有代价的免费午餐:论文坦率地指出,在其中一个目标模型(采用 Gated DeltaNet 架构的 Qwen3.5-9B)上,高并发下的收益会缩小,原因是服务引擎(SGLang)目前还没有针对该架构实现高效的变长验证——这提醒我们,自适应算法的效果终究受限于底层调度器的能力上限。
前置知识:读懂这篇论文需要先了解什么
自回归解码,以及它为什么慢
一个标准的(自回归,“AR”)大语言模型逐个 token 地生成文本:要生成 token ,模型必须已经生成了 ,把整个序列重新喂回网络,再从得到的词表概率分布中采样。这个过程本质上是顺序的——你不可能在第 4 个 token 还不存在的时候就开始计算第 5 个——对于一个有数十亿参数的模型来说,每一次这样的单 token 前向传播都很昂贵。如果生成一条完整回复需要 500 个 token,你就要为一个巨大网络付出 500 次完整前向传播的代价,尽管现代 GPU 在单 token 解码时通常是”内存带宽受限”的(GPU 大部分时间花在搬运权重上,而不是做浮点运算),这意味着大量昂贵的计算能力其实是闲置的。
投机解码:基本思路
投机解码(Leviathan et al., 2023;Chen et al., 2023)正是利用了这部分闲置算力,引入了第二个体积更小、速度更快的”草稿”模型 。流程不再是用大的”目标”模型 一个 token 一个 token 地生成,而是每一轮这样运行:
- 草稿模型 快速提议一段长度为 的候选 token 序列(因为 很小,这一步很便宜)。
- 目标模型 对整段候选序列做一次前向传播——因为 transformer 的前向传播本来就能并行地给整段序列打分(这也是训练能加速的原因),只要 GPU 有余量算力,一次检查 个 token 的代价只比检查 1 个 token 稍高一点。
- 每个候选 token 会以概率 被接受——也就是说,如果目标模型对这个 token 的偏好不低于草稿模型,就肯定接受;否则按概率接受。
- 从第一个被拒绝的 token 开始截断,之前的所有 token 都保留下来。在拒绝发生的那个位置,会从残差分布 (归一化后)重新采样出一个修正 token——这正是让整个过程完全无损的数学技巧:最终输出的分布可以严格证明与单独用目标模型逐 token 生成得到的分布完全相同。
每一轮期望产出的输出 token 数为
其中 是接受率——任意一个草稿 token 平均能存活下来的概率。相对标准 AR 解码的整体加速比为
其中 是候选(草稿)长度, 是草稿模型单轮推理时间与目标模型推理时间之比。两个量决定了一切:草稿有多便宜()、以及草稿猜对的频率有多高(,决定了 )。这篇论文的一切内容,归根结底都是在真实、不断变化的部署环境下,努力提升这两个量当中的一个或两个。
用扩散语言模型做草稿模型
标准 AR 草稿模型仍然要一个个地生成 个候选 token——通过小型草稿网络做 次顺序前向传播。扩散语言模型(dLLM)提供了另一种生成机制:不再根据前面所有内容预测下一个 token,dLLM 被训练成接收一个部分位置被”遮盖”的序列,然后同时预测所有被遮盖的位置,使用双向注意力(每个被遮盖的位置既能关注前面的内容,也能关注后面的内容,包括其他同样正在被遮盖的位置)。经典的扩散式文本生成会反复运行这个遮盖-预测步骤,逐步”去噪”越来越多的位置——这仍然需要多次前向传播,部分抵消了并行化带来的好处。
本文直接依赖的前身工作 DFlash(Chen et al., 2026)把这个过程”短路”了:给定前缀 ,它初始化 个被遮盖的位置,通过双向网络的单次前向传播就生成整个候选区块 :
其中 表示草稿区块的遮盖状态。因为整个区块来自一次前向传播而不是 次顺序传播,时间比变成了 ——单 token 起草开销大约缩小了 倍,这让 DFlash 可以负担得起更深、表达能力更强的草稿网络而不增加额外延迟,实测中也确实同时带来了更便宜的起草步骤和比同等规模 AR 草稿模型更高的接受率。
为什么双向注意力是一把双刃剑
这是 AdaFlash 贡献的核心,值得在论文自己的符号体系接管之前仔细讲清楚。AR 草稿模型对位置 的预测永远只依赖位置 ——这个因果结构在训练时和推理时、对每个领域、每个位置都是一致的,所以它的行为往往相当均匀。而扩散草稿模型对某个被遮盖位置的预测,依赖的是整个周围上下文,包括其他值还未知的被遮盖位置。这带来了远为丰富的上下文建模能力——也让它能一次前向传播就完成——但也意味着预测任意一个位置的实际难度,很大程度上取决于它周围具体是什么:“领域内”的程度如何、这个位置在区块中有多深、其他被遮盖位置的联合不确定性有多少会渗透进这一个位置的预测中。一个在数学数据上表现不错的草稿模型,在聊天数据上可能校准得很差;一个能精准命中区块前几个 token 的草稿模型,到最后几个 token 可能就崩了。这就是”双刃剑”:让一次前向传播并行起草成为可能的机制,正是让它质量不稳定的同一个机制。
在线策略蒸馏:实时地从自己的错误中学习
普通(“离线”)知识蒸馏训练一个小的学生模型去模仿一个大的教师模型的输出,用的是训练开始前一次性收集好的固定训练集。在部署场景下的问题是分布不匹配(也叫暴露偏差 exposure bias):学生模型在测试时是在它自己生成的序列上被评估的,这些序列可能和离线训练分布完全不一样——学生模型在训练时从未见过自己实际会犯的生成错误。在线策略蒸馏(on-policy distillation,Agarwal et al., 2024)的做法是:让学生模型自己生成一条轨迹,然后沿着这条真实轨迹去查询教师模型的软输出分布作为监督信号。这天然适合投机解码:草稿模型本来就会产生候选序列,目标模型在验证时本来就会计算这些相同位置上自己的分布——所以在线策略蒸馏所需的”监督信号”,其实是流水线本来就要执行的一个步骤的副产品。此前的工作(OSD,Liu et al., 2024)用标准的正向 KL / 交叉熵损失应用了这个思路;AdaFlash 在这里的贡献是把目标函数本身(改用反向 KL,而非正向 KL)适配到扩散草稿模型的具体失效模式上,并加上一个正向 KL 蒸馏从来不需要的稳定化技巧(逐条目裁剪)。
正向 KL vs. 反向 KL:为什么散度的选择很重要
给定一个共享词表上的目标分布 和草稿模型分布 ,把 拉向 最常用的两种散度是:
正向 KL 就是普通交叉熵训练所对应的目标:它会严厉惩罚 在 高概率的地方分配低概率,所以学生模型被推着去覆盖教师的每一个模式——哪怕某些模式单独看频率很低,只要在训练数据里出现过。这在你想要广泛覆盖的场景(比如标准监督微调)中是正确的选择。反向 KL 则反过来,严厉惩罚 在 没有概率的地方分配概率,所以学生模型被推着把概率质量集中到教师模型中它觉得最容易表示的那(几)个模式上,代价是不去尝试覆盖教师分布的全部散布范围。对投机解码来说,这种不对称性非常重要:一个草稿 token 只需要落在目标模型的高概率区域内就能大概率被接受——草稿模型去”覆盖”某个目标模型很少会生成的、晦涩的低概率延续,没有任何好处。而扩散草稿模型恰恰因为双向注意力使它倾向于产生高熵(分布很散、不聚焦)的输出分布,特别容易把概率质量浪费在目标模型根本不在乎的区域——这正是反向 KL 适合纠正的失效模式。
架构总览
下图展示了 AdaFlash 的两个可学习组件——扩散草稿模型 和自适应长度头——如何围绕冻结的目标模型 组织在一轮投机解码中,以及在线训练循环如何在不阻塞生成的前提下反馈给草稿模型。
flowchart TB
subgraph ServingLoop["在线服务中的第 t 轮"]
A["前缀 x(当前上下文)"] --> B["扩散草稿模型 q_w:\n单次前向传播,\nk 个遮盖位置"]
B --> C["候选区块\nx_1 ... x_k"]
C --> D["自适应长度头:\n预测接受率 a_hat_t"]
D --> E["截断为 k_hat_t = clamp(floor(a_hat_t * k), 1, k)"]
E --> F["目标模型 p_v:\n对 k_hat_t 个 token\n做一次并行前向传播"]
F --> G["接受/拒绝规则:\n每个 token 用 min(1, p_v/q_w)"]
G --> H["接受的前缀 + 1 个重采样的 token\n=> 本轮输出 token"]
end
subgraph TrainLoop["异步训练 worker"]
F -.->|"目标分布\n(回放缓冲区)"| I["OPD 损失:\n混合反向-KL + 硬标签交叉熵\n+ 逐条目裁剪"]
G -.->|"真实接受率 a*_t"| J["长度头损失:\nMSE(a_hat_t, a*_t)"]
I --> K["更新后的草稿模型权重"]
J --> L["更新后的长度头权重"]
end
K -.->|"热重载\n(仅草稿模型 + 长度头)"| B
L -.->|"热重载"| D
图 1(架构):AdaFlash 的服务循环与训练循环。 关键的结构性要点是,目标模型 完全不被训练循环触碰——只有草稿模型和长度头会被热重载——所以昂贵的、冻结的目标模型的权重和 KV 缓存管理完全不受持续在线学习的干扰。
数据流 / 流水线图:完整一轮的每一步
sequenceDiagram
participant Client as 客户端
participant Drafter as 扩散草稿模型 q_w
participant Head as 长度头
participant Target as 目标模型 p_v
participant Trainer as 异步训练器
Client->>Drafter: 前缀 x_<t
Drafter->>Drafter: 一次前向传播,k 个遮盖位置
Drafter->>Head: 隐藏状态 h_t(共享输入)
Head->>Head: 线性层 + SiLU + 均值池化 + sigmoid -> a_hat_t
Head-->>Target: k_hat_t = clamp(floor(a_hat_t k), 1, k)
Drafter->>Target: 候选 token x_1..x_(k_hat_t)
Target->>Target: 单次并行前向传播
Target-->>Client: 接受的前缀 + 1 个重采样 token
Target-->>Trainer: p_v(.|x_<t+i),i=1..k_hat_t(回放缓冲区)
Target-->>Trainer: 真实接受比例 a*_t
Trainer->>Trainer: 计算 OPD 损失(反向KL + 硬标签CE,已裁剪)
Trainer->>Trainer: 计算长度头 MSE 损失(已 detach)
Trainer-->>Drafter: 热重载更新后的权重(异步,不在关键路径上)
Trainer-->>Head: 热重载更新后的权重(异步,不在关键路径上)
图 2(流水线): 每一条指向 Trainer 框的箭头,都是流水线在做验证时已经顺带产生的监督信号——这里不需要额外的 rollout、额外的目标模型调用,也不需要辅助奖励模型。
两种方差来源,可视化呈现

面板 (a) 是领域级的故事:聊天风格提示词(ShareGPT)的接受率分布集中在较低值、峰型尖锐,而数学类提示词(MATH-500)集中在高得多的值——这不是重叠的分布,而是本质上不同的区间。面板 (b) 用接受长度 重述了同一个事实:MT-Bench/ShareGPT 聊天场景大约 –,而 GSM8K/MATH-500 数学场景大约 –——差不多是 2 倍的差距,而且都来自同一个、冻结不变的草稿模型。面板 (c) 是单条序列内部的故事:两个仅相差一个 token 的提示词(“2 的 32 次方是多少?” vs. “2 的 8 次方是多少?“)分别产出了 4 个和 8 个被接受的 token——逐位置接受概率随着草稿区块的推进稳步衰减,而衰减速度本身仅从提示词是无法预测的。这正是构建两个独立解决方案而不是一个的经验依据:领域级的差距需要草稿模型本身移动(重训练),而 token 级的衰减需要系统针对每个请求做出反应(动态截断),因为再怎么重训练也无法消除本质上源于”在区块中所处深度”的衰减。
方法第一部分:面向扩散草稿模型的在线策略蒸馏(OPD)
算法,逐步拆解
核心思想是一个持续运行的”起草 → 反馈 → 适配”循环,利用目标模型的验证步骤本来就会计算出蒸馏损失所需监督信号这一事实。
算法1:在线策略蒸馏循环(每个训练轮次 )
1. 给定前缀 x_<t,从当前的扩散草稿模型 q_w 采样候选区块
{x_1, ..., x_k} ~ q_w(. | x_<t, z)
(这与服务流水线本来就要执行的起草步骤相同)。
2. 把候选区块发给目标模型 p_v 做验证
(这也是服务流水线本来就在做的事情)。
3. 记录每个位置 i = 1..k 上,目标模型的完整输出分布
p_v( . | x_<t+i )——这是免费得到的,因为验证步骤本来就会
计算它,不需要额外调用目标模型。
4. 计算相对于目标模型自己 top-1 token 的硬标签交叉熵
x*_i = argmax_x p_v(x | x_<t+i):
l_hard = -(1/k) * sum_i log q_w(x*_i | x_<t, z)
5. 计算每个位置 i 上的反向 KL 散度,但在对词表求和之前,
先对每个词表条目的贡献按阈值 delta 做裁剪(见下面算法2)。
6. 组合:l_OPD = alpha * l_hard + (1 - alpha) * l_rkl_clipped
7. 只把 l_OPD 反向传播给草稿模型 q_w;累积进回放缓冲区,
直到收集满 128 条在线策略样本。
8. 缓冲区填满后,用 AdamW 训练 2 个 epoch(有效批大小为 2,
最大序列长度 2048),得到更新后的 q_w。
9. 只把更新后的草稿模型参数热重载进在线服务引擎;
目标模型 p_v 完全不被触碰。
10. 清空缓冲区,用更新后的草稿模型回到第 1 步。
这个循环的关键设计特性在于:候选区块、目标模型的分布、真实的 top-1 token——这些输入全都是服务流水线在正常执行投机解码时本来就会计算出来的东西。在线策略蒸馏是在一个本来就要发生的推理步骤之上叠加了一个训练步骤,而不需要一个单独的离线数据收集阶段。
反向 KL + 硬标签混合损失,推导过程
记 为草稿模型在位置 上的分布, 为目标模型在同一位置上的分布,两个原始损失项为
组合两者的直觉在于:单独的 能把草稿模型的分布形状朝目标模型模式寻求式地拉近,但在训练早期,当草稿模型的分布几乎完全没有和目标模型的支撑集重叠时,它的梯度可能信号很弱或很嘈杂(当 接近零而 不可忽略时,对数比可能表现很差)。 是相对于单个硬标签的普通交叉熵——梯度信号方差低得多,但只告诉草稿模型最可能的那一个 token,而不告诉它目标分布在这个 token 周围的整体形状。用 混合两者,
让硬标签项有机会快速、低方差地把草稿模型锚定到正确的 top-1 token 上,而(裁剪后的)反向 KL 项继续雕琢草稿模型分布在这个锚点周围的形状。论文自己的消融实验(下面复现的表 3(a))扫描了 ,发现从 (纯反向 KL)到 呈单调改善,然后在 (纯硬标签)时略有回退——证实了两个极端都不是最优的,即使在硬标签监督占主导地位时,反向 KL 带来的模式寻求正则化仍然有用。
逐条目散度裁剪,推导过程
把反向 KL 求和在整个词表 上展开,可以把失效模式讲清楚:
因为扩散草稿模型倾向于产生高熵(分布很散)的分布,词表中通常总会有少数条目 , 不可忽略但 极小——对这些条目而言, 很大,即便乘上一个不大的 ,也能让这一项主导整个求和,产生一个实际上只是在响应某一个离群 token、而不是整体形状差异的梯度(论文用它自己的图 3——一张 GSM8K 回复的逐 token 散度热力图——直观展示了这一点,图中少数几个位置的散度比其余位置高出好几个数量级)。修复方法是在求和之前,先给每一个 项设置一个阈值 做上限裁剪:
算法2:逐条目裁剪的反向 KL
输入:每个位置 i=1..k 上草稿模型的分布 q_w(.|x_<t,z),
每个位置上目标模型的分布 p_v(.|x_<t+i),
裁剪阈值 delta > 0
对每个位置 i = 1 .. k:
对词表 V 中的每个条目 y:
term(i, y) = q_w(y | x_<t, z)_i * log( q_w(y|x_<t,z)_i / p_v(y|x_<t+i) )
clipped_term(i, y) = min( term(i, y), delta )
position_loss(i) = sum_y clipped_term(i, y)
l_rkl_clipped = (1/k) * sum_i position_loss(i)
返回 l_rkl_clipped
写成闭式:
为什么用这种设计而不是更显然的替代方案? 更常见的解决离群梯度的方法是普通的梯度裁剪(对整个反向传播后的梯度做范数上限)——但那样会不分青红皂白地丢弃信息,一旦范数超预算,所有位置和所有词表条目都会被一起裁剪,包括那些有信息量的条目。逐条目裁剪则直接针对问题诊断本身:它识别出具体是哪些(位置,词表条目)配对造成了不稳定,只裁剪那些配对,而其他表现良好的条目的梯度信号完全不受影响。论文自己的超参数扫描(表 3(b))精确展示了这个权衡:不裁剪的基线略差;适度裁剪()能得到最好的接受长度和加速比;但过度激进的裁剪()开始压制有用的梯度,因为超过某个程度,阈值会连普通的、非离群的散度项也一起裁掉,论文报告这会拖慢收敛,同时降低接受长度和每秒 token 数。这是一个教科书式的设计边界:这个机制之所以有效,是因为它足够精准;但同一个机制过于激进的版本,就和直接删除信号没什么区别了。
方法第二部分:自适应长度头
动机与设计
即使草稿模型完全处于分布内、质量完美,区块中越靠后的位置本质上也越难预测正确——越靠后的位置依赖越多周围(同样不确定的)被遮盖上下文,所以接受概率会随着区块推进自然衰减(见上文图3(c))。如果系统总是不加区分地验证完整的固定候选长度 ,就会浪费目标模型的算力去检查那些本来就不太可能被接受的 token——更糟的是,这种浪费在高并发下会被放大,因为每一个被浪费的目标模型 token 算力,都是从其他本可以真正推进进度的请求那里偷走的 GPU 时间。
自适应长度头是一个小型辅助网络,接在扩散草稿模型本来就已经计算好的隐藏状态上,唯一的任务是预测当前草稿中有多少值得发给目标模型验证。
算法3:自适应长度头,前向传播
输入:隐藏状态 h_t,形状为 R^{k x H}(与草稿模型使用的输入相同),
缩放因子 gamma(可调,随负载调整),区块大小 k
1. 对每个位置 i = 1..k:
h'_i = SiLU( Linear(h_t[i]) ) # 逐位置投影,H' < H
2. h_bar_t = (1/k) * sum_i h'_i # 均值池化 -> 一个全局向量
3. a_hat_t = sigmoid( Linear(h_bar_t) ) # 预测的标量接受率
4. a_hat_t = a_hat_t * gamma # 可选的、随负载调整的缩放
5. k_hat_t = clamp( floor(a_hat_t * k), 1, k )
返回 k_hat_t # 本轮的验证长度
只有草稿的前 个 token 会真正发给目标模型做验证——其余部分完全不会被检查,所以目标模型对这个请求的前向传播会相应变短(关键的是,它的代价会随每个请求的实际长度变化,而不是固定在最坏情况的 )。
长度头的在线更新,推导过程
目标模型验证完实际发送的 个 token 之后,真实的接受比例 (这些 token 中实际被接受的比例)立即可用——同样,这是流水线本来就要执行的一个步骤的免费副产品。长度头用普通的均方误差训练:
一个重要但容易被忽略的设计选择: 的梯度被显式地与草稿模型分离(detach)——它只更新长度头自己的参数,永远不会流回 。为什么这一点很重要?因为草稿模型同时在被上面那个(性质截然不同的)OPD 目标更新。如果长度头的 MSE 梯度也被允许流入草稿模型,草稿模型的参数就会同时受到两个方向不相关的目标拉扯(既要匹配目标模型的 token 分布,又要让自己的接受率更容易被预测),这很容易破坏训练稳定性,或者让草稿模型偏向”可预测”而不是”准确”。分离操作让两个学习问题完全解耦——长度头学会追踪草稿模型当前的实际质量,但无法反过来影响那个质量本身。
为什么不干脆用一个离线调好的固定候选长度? 论文自己的消融实验(表 2,下面讨论)直接回答了这个问题:如果把验证长度固定为经验平均值(11 个 token,与 AdaFlash 自己的平均值 11.271 相符),并发度 128 时的加速比会崩溃到 1.00×——在高负载下完全抹平了投机解码的收益,因为在高并发场景下,那些本来只会接受 3–4 个 token 的请求,仍然被迫为验证全部 11 个 token 付出代价。一个选来在平均意义上”合理”的固定长度,从构造上讲,对任何偏离平均值的请求都是错的。
为什么用逐请求的置信度而不是逐位置的置信度(与 DSpark 的对比)? 一项同期工作 DSpark 用类似的思路解决了类似的问题,它估计逐位置的条件接受概率,并配合一个硬件感知调度器,但这需要事后校准来修正跨位置的误差累积(位置 5 的概率估计隐含地假设位置 1–4 都被接受了,误差会沿着这条链累积)。AdaFlash 的设计刻意选择直接从一个池化后的表示预测整个区块的整体接受率,完全绕开了那条逐位置概率校准链的需要——这是一个更简单的估计目标,代价是无法确定截断前缀中究竟哪个位置最薄弱(这是一个值得指出的边界条件,下面的批判性分析部分会讨论)。论文指出这两种方法是互补而非互斥的:DSpark 的硬件感知调度原则上可以叠加在 AdaFlash 的长度预测之上。
基线 / 已有工作对比
在看 AdaFlash 自己的数字之前,值得先梳理清楚论文对比表中每个基线具体代表什么,因为”投机解码方法”这个说法其实覆盖了相当宽的设计空间:
flowchart LR
AR["标准 AR 解码\n(无投机,基线 1x)"]
EAGLE3["EAGLE-3\nAR 特征级草稿模型,\n树状结构验证,\n离线训练,静态"]
DFlash["DFlash\n扩散草稿模型,\n一步式区块生成,\n离线训练,静态,固定 k"]
OSD["OSD\nDFlash 草稿模型 +\n在线正向-KL蒸馏,\n仍是固定 k"]
AdaFlash["AdaFlash(本文)\nDFlash 草稿模型 +\n在线反向-KL OPD\n+ 自适应长度头"]
AR --> EAGLE3
AR --> DFlash
DFlash --> OSD
OSD --> AdaFlash
DFlash --> AdaFlash
style AdaFlash fill:#f9e0e0,stroke:#c0392b
图 4(基线谱系): 对比集合形成了一条清晰的阶梯:EAGLE-3 代表成熟的、纯离线的 AR 草稿模型学派;DFlash 是本文方法所修改的离线扩散草稿模型基线;OSD 加入了某种在线适配,但用的是”错的”散度,也没有长度自适应能力;AdaFlash 则是 DFlash 加上本文两个贡献的组合。这个阶梯结构恰好支撑了论文之后的消融实验方法论(表 2)——每往阶梯上走一步,就单独隔离出一个新增机制的贡献。
实验:数字究竟说明了什么
主要结果(复现表 1)
以 Qwen3-8B 为目标模型,在六个基准测试集(MathQA、GSM8K、OpenCodeInstruct、CodeAlpaca、ShareGPT、Blend)上取平均:
| 方法 | 并发=1 加速比 | 并发=1 τ | 并发=32 加速比 | 并发=64 加速比 | 并发=128 加速比 |
|---|---|---|---|---|---|
| EAGLE-3 | 2.34× | 4.40 | 0.68× | 0.43× | 0.33× |
| DFlash | 3.53× | 5.86 | 1.54× | 1.01× | 0.76× |
| OSD | 3.95× | 7.05 | 1.70× | 1.12× | 0.83× |
| AdaFlash | 4.06× | 7.28 | 1.74× | 1.33× | 1.15× |
三点值得注意。第一,在并发度为 1 时,除 EAGLE-3 外的所有方法都稳稳超过 1×——真正有意思的区间是高并发。第二,除 AdaFlash 之外,所有其他方法的加速比在并发度 128 时都跌破了 1.0×(即比普通 AR 解码还慢),只有 AdaFlash 保持在 1× 以上。第三,OSD 和 AdaFlash 在并发度 1 时的差距(3.95× vs. 4.06×,两者都用了在线蒸馏)比较小,但在并发度 128 时急剧拉大(0.83× vs. 1.15×)——这正是论文的核心经验性论断:单靠在线蒸馏(OSD)能修复领域级方差,但对高并发下主导性能的”验证浪费”问题束手无策,只有自适应长度头才能解决这个问题。
消融实验(复现表 2)
| 散度裁剪 | 混合 OPD 损失 | 长度头 | 在线更新 | 并发=1 τ | 并发=1 加速比 | 并发=128 τ | 并发=128 加速比 |
|---|---|---|---|---|---|---|---|
| – | – | ✓ | ✓ | 7.389 | 4.17× | 6.802 | 1.18× |
| ✓ | – | ✓ | ✓ | 7.590 | 4.31× | 7.212 | 1.25× |
| ✓ | ✓ | ✓ | ✓ | 7.751 | 4.54× | 7.270 | 1.27× |
| ✓ | ✓ | – | – | – | – | 6.594 | 1.00× |
| ✓ | ✓ | ✓ | – | – | – | 7.652 | 1.21× |
| ✓ | ✓ | ✓ | ✓ | – | – | 7.270 | 1.27× |
从上半部分往下读:在纯反向 KL 基础上加入散度裁剪,在各个并发度下都能同时提升接受长度和加速比;再叠加硬标签混合项能进一步提升——每个 OPD 子组件都在设计论证所预测的方向上独立地贡献了收益。再看下半部分:完全去掉长度头(改用固定平均长度 11)会让并发度 128 的加速比一路跌到 1.00×——彻底丧失了投机解码在高负载下的收益,几乎精确对应了前面主表中观察到的现象。重新加回长度头但参数固定不更新,能恢复大部分收益(1.21×),而允许它持续在线更新则能补齐剩下的差距(1.27×)——证实了草稿模型和长度头之间的持续协同适配,而不仅仅是”有一个长度头”,才是在草稿模型自身分布因 OPD 而不断变化时维持收益的关键。
复现的效果分析图

面板 (a) 是一个很有用的健全性检查,确认在线学习确实在收敛而不是在振荡:平均每秒 token 数在 200 轮内大致单调上升(带有预期的噪声),最终稳定在 EAGLE-3 和 DFlash 参考线之上。面板 (b) 是支持领域级方差论断最清晰的单一证据:DFlash 在 GSM8K 和 CodeAlpaca 上的接受率密度几乎不重叠(两个分离的凸起),而 AdaFlash 在同样两个数据集上的密度整体右移并显著重叠——直观确认了在线策略蒸馏确实在缩小领域差距,而不只是在提升平均值。面板 (c) 展示了 token 级的故事:DFlash 的逐位置接受概率从位置 1 的约 0.89 衰减到位置 15 的约 0.13,而 AdaFlash 衰减得更平缓(0.92 到 0.26)——草稿模型在每个位置上都变好了,但靠后位置改善得更多,这正是平均接受长度从 7.09 增长到 9.83 的原因。
跨领域泛化、NPU 可移植性与鲁棒性检查
论文还运行了几项值得简要一提的补充实验:(i)一个在混合”PerfectBlend”数据集(与每个单独测试领域都不同)上离线训练、之后不再做进一步在线更新的 AdaFlash 草稿模型,在 MathQA/GSM8K/OpenCodeInstruct/CodeAlpaca 上依然优于离线 DFlash,虽然落后于领域内在线适配的版本——说明 OPD 的一部分收益即使没有实时逐领域适配也能迁移,但完整的收益确实需要它;(ii)当服务后端从 GPU 换成通过 SGLang-NPU 接入的华为昇腾 910C NPU 时,同样的相对排序(AdaFlash > DFlash)依然成立,说明该方法并不绑定于某一种硬件栈;(iii)在随机采样(,而非贪心 )下,DFlash 和 AdaFlash 的接受长度都会下降(符合预期,因为更发散的目标分布使得 token 精确一致的概率天然更低),但 AdaFlash 在并发度 1 时依然保持了健康的 3.56× 平均加速比;(iv)在一个较小的目标模型(Qwen3-1.7B)上完全从零训练一个 AdaFlash 草稿模型(没有预先存在的 DFlash checkpoint),在相同起草预算下依然达到 1.84× 加速比,优于 EAGLE-3 的 1.34×,说明这个框架并不局限于对已有扩散草稿模型做微调。
系统工程:让在线适配真正可部署
一个在线上服务过程中持续自我重训练的算法,只有在不拖慢它本想加速的那个服务循环时才有用。AdaFlash 的系统工程部分(构建在 SGLang 之上)解决了两个具体需求。
异步训练/推理分离。 系统运行两个逻辑上分离、通过共享回放缓冲区连接的 worker:一个推理 worker 负责服务实时流量,把在线策略轨迹(提示词 + 草稿模型响应 + 目标模型分布)记录进缓冲区;一个训练 worker 异步地消费缓冲区,计算 OPD 损失和长度头损失,更新权重——完全运行在独立的 GPU 资源上,所以训练算力永远不会与 token 生成竞争或阻塞它。一旦新权重就绪,只有草稿模型和长度头的参数会被热重载进推理服务器的 GPU 内存;(体积大得多的)目标模型的权重和 KV 缓存状态完全不被触碰,所以重载本身在普通的调度步骤之间就能完成,几乎不增加额外延迟。
面向可变验证长度的自适应请求调度。 标准的服务引擎(包括 SGLang)都是围绕固定形状的批次构建的:一个验证批次中的每个请求共享同一个候选长度 。一旦自适应长度头让 因请求而异,这个假设就不成立了,所以 AdaFlash 修改了引擎,把请求打包进一个总长度等于 (而不是 )的验证批次——把长度头带来的逐请求节省直接转化为更小、更紧凑的批次,而不是把一切又补齐到最坏情况。因为固定算力/内存预算下能容纳的请求数量现在每轮都在变化(很多短请求组成的批次和少数长请求组成的批次看起来完全不同),调度器维护一个近期观测到的请求数量的指数移动平均,,并用 来决定下一轮准入多少新请求;如果实际内存使用仍然超出预算,多余的请求会被退回等待队列,而不是导致内存溢出。这是一个相当标准的准入控制模式(EMA 平滑的负载估计、优雅的背压机制),但它是长度头的必要配套——没有它,可变长度验证批次要么会导致 GPU 利用不足(保守的固定准入),要么会有 OOM 风险(naive 的完全准入)。
一个完整的数值算例:为一个玩具位置计算 OPD 损失
为了让抽象公式变得具体,考虑单个草稿位置 ,用一个只有四个 token 的玩具词表 。假设草稿模型和目标模型在这个位置上的分布分别是:
| Token | (草稿模型) | (目标模型) |
|---|---|---|
| A | 0.55 | 0.80 |
| B | 0.30 | 0.15 |
| C | 0.10 | 0.04 |
| D | 0.05 | 0.01 |
第 1 步 —— 硬标签项。 目标模型的 top-1 token 是 (概率 0.80),所以
第 2 步 —— 每个条目的原始反向 KL 项,:
- :
- :
- :
- :
求和:。注意这里没有哪个条目是极端离群值(所有项相互之间大约在 4 倍以内),所以在论文默认配置的裁剪阈值 下,如果这个玩具例子的各项具有代表性,这四项每一项实际上都会被裁剪到最多 0.01——这正是关键所在:在一个超过 10 万 token 的真实词表里,大部分概率质量都落在像这个玩具例子里的少数几个 token 上,但少量罕见 token 可能产生大好几个数量级的项(比如 对上 ,仅这一个罕见 token 就给出约 的项)——裁剪恰好压制了这些离群值,同时几乎不触碰上面这种本来就低于 的正常项。
第 3 步 —— 用 (论文默认值)组合:
这一个数字就是这一个位置反向传播给草稿模型的损失;实际训练中它会在区块内全部 个位置上取平均,再执行一次优化器更新。
边界条件决策图
下图把”AdaFlash 的机制在什么情况下最有帮助,什么情况下论文自己的结果提示需要谨慎”整理成一个简单的决策结构:
flowchart TD
Start["部署一个扩散草稿模型\n投机解码系统?"]
Start --> Q1{"解码模式?"}
Q1 -->|"贪心 (T=0)"| Q2{"预期并发度?"}
Q1 -->|"采样 (T>0)"| Caution1["收益仍为正,\n但验证得不够充分\n(只测了 4/8 个基准, 2/4 个并发度)"]
Q2 -->|"低(C=1)"| Good1["包括原版 DFlash 在内的所有方法\n都已经优于 AR 解码;\nAdaFlash 的优势比较有限"]
Q2 -->|"高(C>=64)"| Good2["这是 AdaFlash 最强的区间:\n固定长度方法跌破 1x,\n只有 AdaFlash 保持在 1x 以上"]
Good2 --> Q3{"目标模型架构?"}
Q3 -->|"标准稠密/MoE transformer"| BestCase["完整收益已被验证\n(Qwen3-8B, Qwen3-Coder-30B-A3B)"]
Q3 -->|"Gated DeltaNet\n(如 Qwen3.5-9B)"| Caution2["并发>=32 时收益缩小:\n服务引擎的可变长度调度\n对这类架构尚不成熟"]
图 6(决策边界): 论文的证据最强支持的是这个决策空间中”贪心解码 + 高并发 + 标准 transformer 架构”这个角落;每个偏离这个角落的分支,在论文自己的数字里依然是正向收益,但支撑证据都更单薄一些。
符号参考表
| 符号 | 含义 |
|---|---|
| 目标(大)模型的分布 | |
| 草稿(小型扩散)模型的分布 | |
| 草稿区块大小(候选长度),实验中固定为 16 | |
| 第 轮自适应截断后的验证长度 | |
| 接受率(期望的逐 token 接受概率) | |
| 每轮投机解码的平均接受长度 | |
| 草稿模型推理时间与目标模型推理时间之比 | |
| 整体的挂钟时间加速因子 | |
| OPD 损失中硬标签 CE 和反向 KL 的混合系数 | |
| 逐条目散度裁剪阈值 | |
| 长度头对第 轮预测的接受率 | |
| 第 轮真实实现的接受率 | |
| 施加在 上的随负载调整缩放因子 | |
| 请求数调度器的 EMA 平滑系数 |
常见问题
AdaFlash 需要重训练目标模型吗? 不需要——目标模型 从未被修改;只有(小得多的)草稿模型和长度头会被在线更新。
在线训练会给某个单独的请求增加延迟吗? 不会直接增加——训练在单独的 GPU 资源池上异步运行;服务路径上唯一与延迟相关的步骤是定期热重载更新后的草稿模型/长度头权重,论文描述这一步能在调度步骤之间完成。
AdaFlash 是不是专门为 DFlash 设计的,能否用于其他扩散草稿模型? 论文直接建立在 DFlash 的一步式区块生成机制之上,在线策略蒸馏/长度头的设计都假设了这个机制(一次前向传播产生一个固定大小的候选区块);要适配到多步扩散草稿模型,可能需要重新思考”长度头读取的隐藏状态”这个概念本身该怎么定义,因为那样会有多次去噪传播而不是一次。
如果回放缓冲区一直填不满(流量很低)会怎样? 论文没有明确讨论这一点;按照设计,训练只会在累积满 128 条在线策略样本后才触发,所以极低流量的部署场景适配速度会非常慢,系统的行为会接近静态的 DFlash/OSD 基线,直到积累够足够的流量。
论文承认的局限性与边界条件
- 贪心解码是默认且验证最充分的场景。 论文里几乎所有代表性数字(表 1、图 2、各项消融实验)都是在贪心解码()下测量的。采样模式()的结果(表 9)只报告了 4 个基准测试集、2 个并发度,是实验网格中小得多的一部分切片,而且接受长度全面明显更低(比如平均 –6.43,而贪心下约为 )——论文坦率承认采样会缩小收益,但没有像贪心结果那样充分刻画它在高并发下的变化趋势。
- Gated DeltaNet 架构暴露了一个真实的工程差距。 在 Qwen3.5-9B 上,AdaFlash 在 ShareGPT 领域的加速比在并发度 时会缩小甚至略微回退,论文明确将其归因于 SGLang 目前还没有针对该特定架构实现完全高效的可变长度验证——这提醒我们,自适应长度头的收益终究受限于底层服务引擎对特定模型架构实际能利用可变批次形状的程度。
- 区块大小 在所有实验中都是固定的。 论文没有探讨这个诊断(领域/token 方差)或修复方案(OPD + 长度头)与更大或更小的草稿区块之间的相互作用;过大的区块可能让 token 级衰减更加明显,而小得多的区块可能从一开始就减轻 token 级方差问题的严重程度,从而改变长度头相对于 OPD 应得的贡献比重。
- 长度头预测的是整个区块的单一标量接受率,而不是逐位置的风险。 如上文所述,这绕开了 DSpark 需要的逐位置概率校准,但也意味着长度头无法表达”第 1–3 个 token 是安全的,但第 4 个 token 具体来说有风险,而第 5–10 个又没问题”——它只能表达一个单一的截断点。如果区块内的接受概率曾经出现非单调的情况,长度头的模型结构上就无法捕捉这种情况。
批判性分析:不足与可改进之处
这篇论文特有的不足。 第一,论文核心的效率论断(表 1 中 4.06×/1.15× 的代表性数字、“最高约 66% 更高吞吐量”的说法)几乎完全是在贪心解码下验证的;而真实生产环境中的大模型服务在面向用户的聊天和生成流量上绝大多数使用温度采样,采样模式的证据(表 9)只覆盖了论文 8 个基准测试集中的 4 个、4 个并发度中的 2 个,也没有在采样条件下重跑一遍等价的消融实验(表 2),去检验当目标分布更发散时,OPD 与长度头各自的相对贡献是否会发生变化。第二,论文从未隔离出 AdaFlash 相对于 OSD 的优势,究竟有多少来自反向-KL/硬标签混合目标本身,又有多少仅仅来自”有没有长度头”——OSD 自己的数字本来就不含长度自适应能力,所以表 1 中 OSD 对 AdaFlash 的直接比较,混淆了”更好的蒸馏目标”和”有没有长度头”这两个因素,只有内部消融实验(表 2)部分地厘清了这一点,但那个消融实验只在 GSM8K 上、用一个目标模型跑过,没有在支撑整篇论文动机的领域多样性上重复验证。第三,逐条目裁剪阈值 和混合系数 各自独立扫描(表 3(a)(b)),但从未联合扫描过——很可能一旦 偏离其扫描时使用的默认值,最优的 也会随之改变,论文没有报告所谓”最优”设置究竟是联合最优,还是两个独立调好的一维扫描碰巧组合出来的结果。
论文低估或省略的局限性。 Gated DeltaNet 在 Qwen3.5-9B 上的工程差距被披露了,但论文没有量化如果 SGLang 的可变长度调度针对该架构成熟起来,能恢复多少额外的加速比——它被呈现为一个前瞻性的警示,而不是经过实验界定的东西,这使人难以判断这个差距究竟只是一个次要的舍入误差,还是实际上抹掉了该模型系列上一大部分声称的收益。另外,论文报告了在线训练的超参数(回放缓冲区大小 128、每个缓冲区 2 个 epoch、草稿模型用 AdamW 学习率 、长度头用 ),但从未报告在大规模部署下训练 worker 本身的挂钟时间或 GPU 内存成本占总服务成本的比例——所有报告的加速比似乎都已经默认训练用的 GPU 是”免费的”(一个独立的资源池),这在部署上是一个合理的假设,但应该作为一个成本核算边界明确写出来,而不是隐含在背后。最后,token 级方差的诊断(图1(c))只用了恰好两个相差一个 token 的示例提示词来说明——生动,但属于个例;论文从未报告一个分布层面的统计量(比如按领域统计的逐位置接受概率方差),让读者判断那一个戏剧性的例子究竟有多大代表性。
具体、可执行的改进建议。 (1)在全部 8 个基准测试集(而不只是 4 个)上,在采样()条件下重跑完整的消融实验(表 2)和完整的主对比实验(表 1),因为采样才是聊天类产品真实世界中占主导的服务场景,而论文自己的数据已经暗示收益在那里会缩小。(2)增加一组受控实验,固定长度头不变,只在 OSD 的正向 KL 和 AdaFlash 的反向-KL-加裁剪之间切换蒸馏目标,从而干净地把”更好的蒸馏目标”和”有没有长度头”这两个因素分离开——做这个实验所需的原始素材其实已经存在于论文自己的消融表中,只是没有以这个确切组合跑过。(3)报告一个 的二维联合扫描,哪怕只是一个粗网格,检验各自报告的”最优”设置联合起来是否依然最优。(4)对 Gated DeltaNet / SGLang 调度差距做实验性量化(例如为这一个架构手写一个可变长度 kernel,哪怕还没到生产可用程度),让读者能够界定 AdaFlash 高并发优势中有多少与架构无关、多少依赖于调度器。(5)报告在部署所用的回放缓冲区/epoch 设置下,每单位服务 GPU 小时对应的训练 worker GPU 小时数,让实践者能够为论文隐含假设的那个”免费”训练资源池做预算。
可复现性说明
- 使用的模型: Qwen3-8B(稠密)、Qwen3-Coder-30B-A3B(混合专家)、Qwen3.5-9B(Gated DeltaNet 架构)作为目标模型;基础草稿模型架构为 DFlash 风格的扩散草稿模型。
- 数据集: MathQA、GSM8K(数学);OpenCodeInstruct、CodeAlpaca(代码);ShareGPT(对话);Blend(混合领域);MATH-500、AIME25(长序列推理,32K 上下文,开启思考模式)。
- 关键超参数: 草稿区块大小 ;OPD 混合系数 ;反向 KL 温度 ;裁剪阈值 ;草稿模型优化器为 AdamW,学习率 ;长度头优化器学习率 ,MSE 损失;回放缓冲区累积满 128 条在线策略样本后触发训练,每个缓冲区训练 2 个 epoch,批大小为 1,梯度累积步数为 2(有效批大小为 2),最大序列长度 2048;长度缩放因子 (默认值,论文自己的扫描中表现最好)。
- 服务技术栈: 基于 SGLang 构建,使用持续批处理;混合精度(bfloat16);Flash Attention;也在华为昇腾 910C NPU(通过 SGLang-NPU)上做过验证。
- 解码模式: 除非明确说明是采样()的某项消融实验,其余均为贪心解码()。
- 代码可获得性: 本文提供的正文中没有包含公开代码链接;读者可以查阅作者所在机构主页(南京大学 LAMDA 组、华为基础模型部门)获取可能发布的实现,也可以参考密切相关的 DFlash 和 OSD 基线各自的代码仓库,了解本文所依赖的共享服务引擎修改。
结论
AdaFlash 的贡献最好理解为”先诊断、后对症下药”的组合,而不是单一的新技巧:这个诊断(双向注意力让扩散草稿模型既强大又不稳定,且体现为两种截然不同、可分离的方式)本身就有独立于具体修复方案的价值,而两个修复方案清晰地对应着两种失效模式——用在线反向 KL 蒸馏应对草稿模型自身质量随领域漂移的问题,用一个轻量级、在线更新的长度头应对再怎么重训练草稿模型也无法完全消除的、残余的 token 级衰减。论文的经验性论证在生产服务最看重的地方最为有力:高并发场景下,对比集合中的每一个已有方法(包括论文自己的在线蒸馏前身 OSD)都会退化到不如普通自回归解码,只有 AdaFlash 能保持净正的加速比。论文对 Gated DeltaNet/SGLang 工程差距的坦诚披露,以及在采样条件下收益适度但真实的缩小,都是不错的迹象,说明其经验性论断没有被夸大——但这两个警示,再加上纠缠在一起的 OSD 对 AdaFlash 对比,恰恰是一个细心的读者(或者一篇后续论文)在把 4.06×/1.15×/“最高约66%” 这些代表性数字当作普适结论之前,应该首先仔细审视的地方。