你用过的每一个大模型,都是从左往右一个 token 一个 token 写出来的。用久了很容易把这当成语言模型的天性——“语言本来就是一个字一个字说的”。
它不是。从左往右是一个建模选择,而且是一个可以被换掉的选择。 换掉它的模型 2026 年已经在卖 API 了:ByteDance 的 Seed Diffusion 论文报到 2146 token/秒,Google 的 Gemini Diffusion 报到 1479 token/秒——比同级自回归模型快一个数量级。
但这篇不想只讲”哇好快”。快是结果,我想弄清楚的是那些省下来的步数是从哪里借的——因为天下没有免费的并行。
前置阅读:这篇假设你已经知道”扩散”大致是怎么回事。如果没有,先读 扩散模型说人话:从擦掉噪点到造出新东西——那篇用一个能手算的一维例子把扩散从头讲一遍,读完再回来,这篇会顺很多。
名词速查
| 术语 | 一句话解释 |
|---|---|
| 自回归(AR) | 生成方式:每次只预测下一个 token,把已写出的部分当条件,循环 N 次写出 N 个 token |
| 掩码扩散 | 另一种生成方式:先把整句话全变成”待填空”,再分若干步把空填回来,每步可以同时填好几个位置 |
| 去噪步数 | 扩散模型填完整句话用了几次前向计算。步数越少越快,越多越准 |
| 双向注意力 | 每个位置能同时看到左边和右边(BERT 那种)。自回归只能看左边 |
| 块扩散 | 折中架构:句子切成小块(常见 32 token),块与块之间自回归,块内部用扩散并行填 |
| infilling | 挖空填词:给定前后文,补中间。自回归原生不擅长,扩散天然会 |
| KV cache | 注意力中间结果的缓存,让已生成 token 不必重算。详见站内 KV cache 那笔账 |
一句话主线
自回归用 N 次前向换来了一个数学上精确的概率分解;扩散语言模型把这个分解换掉,用”一次前向填多个空”换速度,代价是它在同一步里填的那几个空之间,看不见彼此。 这篇要把这句话手算清楚。
一、先看清自回归到底选了什么
自回归模型算的是这个东西:
这里有个容易被忽略的事实:这条式子是链式法则,它是恒等式,不是近似。 任何一个联合分布都可以这样拆,拆的时候没有丢任何信息。这是自回归最大的优点,也是它统治了整个领域的原因——你只需要学好”预测下一个词”这一件事,理论上就能表达任意复杂的语言分布。
代价写在式子里:乘号有 N 个,你就得跑 N 次前向。
而这 N 次前向贵在哪里,站内 Decode 速度上限公式 那篇已经用实测数据算过了:解码慢不是算力问题,是搬运问题。每生成一个 token,都要把整份模型权重从显存搬一遍进计算单元。
拿那篇的实测数字接着往下算(Qwen2.5-7B,4.677 GB 权重,llama-bench 实测 16.99 tok/s,反推带宽约 79.5 GB/s):
| 每次前向产出 | 每秒前向次数 | 速度上限 |
|---|---|---|
| 1 token(标准自回归) | 17.0 | 17.0 tok/s |
| 2 token | 17.0 | 34.0 tok/s |
| 4 token | 17.0 | 68.0 tok/s |
| 8 token | 17.0 | 135.9 tok/s |
带宽一点没变,速度翻了 8 倍。这张表就是整个”快速解码”研究领域的全部动机:既然瓶颈是”每 token 搬一次权重”,那么想快,唯一的出路是让一次搬运多产出几个 token。
于是分出两条路——这是我想让你从这篇带走的框架:
flowchart TD
A["根本约束:解码是内存带宽瓶颈<br/>每个 token 要搬一遍全部权重"] --> B["想快,只能让一次前向多产出 token"]
B --> C1["路线一:保留自回归分解<br/>猜 + 验证"]
B --> C2["路线二:换掉自回归分解<br/>并行填空"]
C1 --> D1["投机解码<br/>Medusa / EAGLE"]
C2 --> D2["扩散语言模型<br/>LLaDA / Mercury / Gemini Diffusion"]
D1 --> E1["输出分布严格不变<br/>靠验证步兜底"]
D2 --> E2["输出分布变了<br/>同步填的位置互相看不见"]
路线一是投机解码:用小模型或额外的头猜出好几个 token,再用大模型一次前向验证这几个猜测。关键在于它保留了链式法则——验证步保证最终输出分布和逐 token 生成严格一致,猜错了就丢掉重来。它是纯粹的工程加速,不改变模型说什么。
路线二就是这篇的主角:干脆不要那个分解了。
二、掩码扩散在做什么(人话版)
扩散模型在图像领域的做法是”加噪声再去噪”。文本是离散的 token,没法直接加高斯噪声,所以要换一个”加噪”的定义。
关键的一步来自 2021 年的 D3PM(arXiv:2107.03006,NeurIPS 2021)。这篇论文系统研究了离散数据该怎么”加噪”,其中一种选择叫吸收态(absorbing state):加噪就是按一定概率把 token 替换成 [MASK],而 [MASK] 一旦进去就不再变化。论文明确指出,这个选择”在扩散模型与自回归、掩码式生成模型之间建立了联系”。
这个联系值得停一下,因为它让整件事忽然变得不神秘了:
掩码扩散 = 掩码比例可变的完形填空。 BERT 训练时固定挖掉 15% 的词让模型填;掩码扩散把挖掉的比例从 0% 到 100% 随机取,然后让模型学会在任意掩码比例下填空。训练完成后,生成就是从”全是 [MASK]”出发,反复填空,直到没有 [MASK]。
没有微分方程,没有噪声调度的玄学——就是一个学会了在任意残缺程度下补全句子的 BERT。
2025 年的 LLaDA(arXiv:2502.09992)证明了这条路能撑到真正的大模型规模:8B 参数、从零开始按”预训练 + SFT”的标准范式训练,在通用任务、数学、代码上与作者自建的同规模自回归基线可比,论文称其在上下文学习能力上”与 LLaMA3 8B 这样的强模型有竞争力”。这篇的结论句说得很直接:大语言模型的核心能力并非内在地绑定于自回归。
采样循环长这样:
# 伪代码:掩码扩散的采样主干
# 依据 LLaDA (arXiv:2502.09992) 与块扩散 (arXiv:2503.09573) 论文描述的过程简化,
# 非某份源码的逐行对应;省略:温度/top-p 采样细节、EOS 与变长处理
sequence = [MASK] * length # 起点:整句都是空
for step in range(num_steps): # num_steps 就是"去噪步数"这个旋钮
logits = model(sequence) # 双向注意力,一次看全序列(注意:没有 KV cache 可用)
confidence, candidate = logits.softmax(-1).max(-1)
masked_positions = [i for i, t in enumerate(sequence) if t == MASK]
# 这一步要填多少个空:总空数 / 剩余步数。步数越少,每步填得越多
k = ceil(len(masked_positions) / (num_steps - step))
# 只填模型最有把握的 k 个位置——把不确定的留给后面几步(此时上下文更全)
for i in top_k_by(confidence, masked_positions, k):
sequence[i] = candidate[i] # ← 这 k 个位置是同时定下来的,彼此看不见
# 省略:部分实现允许"重新掩码"已填错的位置
return sequence
请盯住最后那行注释。整篇文章的账,都在那一行上。
三、那些省下的步数是从哪儿借的
借款一:同一步填的空,互相看不见
自回归的链式法则是恒等式,不丢信息。而当你在一步里同时填 k 个空时,你实际上是从 k 个边缘分布里各自独立采样,再把结果拼起来。而边缘分布的乘积,不等于联合分布。
这句话听起来抽象,手算一个最小例子就清楚了。假设有个句子,模型学到的分布只允许两种填法,各占一半:
- 「我要去北京,因为北京是我的家乡」——概率 0.5
- 「我要去上海,因为上海是我的家乡」——概率 0.5
两个空必须一致,这是语义要求。现在看模型在这两个位置上的边缘分布:
| 位置 | P(北京) | P(上海) |
|---|---|---|
| 第一个空 | 0.5 | 0.5 |
| 第二个空 | 0.5 | 0.5 |
如果这两个空在同一步被独立填上,你得到的联合分布是:
| 采样结果 | 独立采样概率 | 模型真实概率 |
|---|---|---|
| (北京,北京) | 0.25 | 0.5 |
| (北京,上海) | 0.25 | 0 |
| (上海,北京) | 0.25 | 0 |
| (上海,上海) | 0.25 | 0.5 |
一半的采样结果落在了模型自己认为概率为 0 的组合上,比如「我要去北京,因为上海是我的家乡」——每个词都合理,合起来自相矛盾。(这两张表的数字我用脚本验算过:边缘各 0.5,独立乘积各 0.25,落在零概率组合上的总质量恰好 0.5。)
这就是那笔借款的本息。去噪步数正是你为”抓住依赖关系”付的钱:极端情况下每步只填一个空,那么每个位置都能看到之前所有已定下的位置,模型就退化成了一个”任意顺序的自回归模型”——依赖关系全抓住了,但 N 步一步没省下,速度优势归零。(这个退化关系是我的理解,不同实现的训练目标权重有差异,严格等价性我没有逐式核对。)
反过来说,扩散语言模型的速度,本质上是在赌”这几个位置之间的依赖弱到可以忽略”。 什么时候这个赌注成立?代码缩进、样板结构、格式化输出、局部编辑——这些地方相邻 token 的依赖确实高度局部。什么时候不成立?需要跨越长距离保持一致的推理链。
借款二:KV cache 没了
第二笔账更工程。掩码扩散用的是双向注意力——每个位置都要看全序列(包括右边还没填的空)。而 KV cache 能生效的前提,恰恰是”已经算过的位置不会再变”,这是自回归的因果掩码给的保证。
块扩散论文把这个问题说得很明白:离散扩散”在生成过程中使用双向上下文,因此无法通过 KV cache 复用之前的计算”(arXiv:2503.09573,ICLR 2025 Oral,Cornell Tech / Kuleshov 组)。也就是说,扩散每一步都在重算整个序列——它省的是步数,不是每步的成本。
同一篇论文给出的解法,也是今天生产架构收敛到的样子:块扩散(BD3-LM)。把序列切成块,块与块之间保持自回归(于是 KV cache 回来了,也支持变长生成),块内部用扩散并行填。论文的原话是,通过调节块大小,可以在自回归与扩散之间插值,并明确这引入了”质量与采样效率之间的权衡”。
所以块大小就是那个旋钮:块 = 1 就是自回归,块 = 整句就是纯扩散。生产上常见取 32 左右——站内 五条主线 那篇提到过这个工程收敛,这里补上它背后的机制:32 不是调参调出来的幸运数字,它是”依赖强度随距离衰减”和”KV cache 想要因果性”两个约束谈判的结果。
四、账对不对?看两份独立证据
上面全是机制推演。机制推演最容易犯的错,是推得很顺、但现实不买账。所以要去看评测数字——而且要看能反驳我的数字。
我的机制推演给出一个可证伪的预测:扩散语言模型应该在局部依赖强的任务(代码、格式、编辑)上接近甚至超过自回归,在需要长距离一致性的任务(知识问答、多跳推理)上明显掉队。
Google 官方的 Gemini Diffusion 模型页给了完整对照表(对比同级自回归模型 Gemini 2.0 Flash-Lite,均为 pass@1、无多数投票):
| 基准 | Gemini Diffusion | 2.0 Flash-Lite | 差值 |
|---|---|---|---|
| HumanEval(代码) | 89.6% | 90.2% | −0.6 |
| MBPP(代码) | 76.0% | 75.8% | +0.2 |
| LiveCodeBench v6 | 30.9% | 28.5% | +2.4 |
| LBPP v2 | 56.8% | 56.0% | +0.8 |
| AIME 2025(数学) | 23.3% | 20.0% | +3.3 |
| BigCodeBench | 45.4% | 45.8% | −0.4 |
| SWE-Bench Verified | 22.9% | 28.5% | −5.6 |
| GPQA Diamond(科学推理) | 40.4% | 56.5% | −16.1 |
| Global MMLU Lite(多语知识) | 69.1% | 79.0% | −9.9 |
| BIG-Bench Extra Hard | 15.0% | 21.0% | −6.0 |
速度是 1479 token/秒(官方标注为”各项评测的平均采样速度,不含开销”,另有 0.84 秒开销)。
这张表和预测吻合得相当好:代码和数学基本打平或小胜,科学推理掉 16 分、多语知识掉 10 分。 Google 自己的措辞也承认这个形状——他们强调这种方式适合”编辑类工作,尤其是数学和代码”。
第二份证据来自 ByteDance 的 Seed Diffusion(arXiv:2508.02193)。它的价值在于论文里同时列了自家的自回归兄弟模型,属于难得的同门对照:
| 基准 | Seed Diffusion Preview | Seed-Coder-8B-Instruct(自回归) |
|---|---|---|
| Aider(tries=2) | 44.4 | 57.1 |
| MBXP(13 语言均值) | 72.6 | 75.3 |
| NaturalCodeBench | 42.2 | 49.6 |
速度 2146 token/秒(H20 GPU,跨八个开源代码基准)。即使在最有利的代码领域,它相对自家自回归兄弟仍然是净亏的——Aider 差 12.7 分不算小。论文自己也提醒跨模型速度对比不可靠(Mercury 用 H100 和私有数据集、Gemini 的数字来自混合任务且硬件未公开),并且承认报告速度”会受益于约束格式的系统提示”。这种自我设限的诚实值得记一笔。
至于 Inception Labs 的 Mercury,我去官网核实时发现:官方页面的速度对比图只标了”Tokens/sec”轴,没有给具体数字,硬件也未注明。官方给的可引用口径是定性的——“匹配速度优化型前沿模型的质量,首 token 延迟低于 300ms,吞吐高 5–7 倍,单任务成本最多低 70%“,定价 Mercury 2 为每百万 token 输入 $0.25 / 输出 $0.75。所以社区流传的那些精确 tok/s 数字都是二手的,我这里不采用。
五、被速度掩盖了的那件事:任意顺序生成
如果扩散语言模型只是”快但笨一点”,它大概只是个成本优化手段。但它还带来一个自回归结构上做不到的东西,我认为这比速度更值得关注。
自回归的因果掩码规定了信息只能从左往右流。这意味着”给定前后文补中间”(infilling)对它是别扭的——今天的代码补全能做到这件事,靠的是训练时的数据改造技巧(把后文搬到前面去),而不是模型的原生能力。
掩码扩散不需要任何技巧:它训练时就是在随机位置填空,“从哪儿开始生成”根本不是一个约束。 挖空的位置在中间、在两头、还是零散分布,对它都一样。
一个漂亮的证据:LLaDA 论文报告它在”反向诗句补全”任务上超过了 GPT-4o。这里指的是所谓 reversal curse——自回归模型学了「A 是 B」之后往往答不出「B 是什么」,因为它只在一个方向上见过这条信息。双向训练的扩散模型天然没有这个方向性偏见。
这解释了产品落点:Mercury 的产品线里专门有个 Mercury Edit——编辑,而不是从零续写,才是这个范式的原生主场。
六、灰度:这东西现在该怎么看
我不认为扩散会替代自回归,也不认为它只是噪音。按目前的证据,我的判断是:
| 场景 | 值不值得看扩散 | 理由 |
|---|---|---|
| 延迟敏感的交互(语音、实时副驾、端侧) | 值得 | 速度差一个数量级,质量损失在可接受范围 |
| 代码编辑、格式化输出、结构化填充 | 值得 | 局部依赖为主,且 infilling 是原生能力 |
| 知识问答、多跳推理、Agent 长链决策 | 暂时不值得 | GPQA 掉 16 分是机制性的,不是训练不足 |
| 长文创作 | 待观察 | 变长生成靠块扩散才刚补上 |
| 想省显存 | 不值得 | 它省的是步数,每步仍在重算全序列 |
一个怀疑读者到这里应该会问:那些差距是范式的极限,还是只是投入不够? 诚实的答案是我不知道,而且这是当前最关键的未知。支持”投入不够”的证据是这条线的绝对投入量还很小,且工程路线刚刚收敛(用自回归模型初始化 + 切换扩散目标继续训练,比从零训练省算力)。支持”机制性”的证据是第三节那笔账——独立采样丢失联合分布,是数学事实而非工程缺陷,只能靠增加步数来赎回,而增加步数就是放弃速度优势。
我倾向于后者更根本:扩散语言模型的速度上限和质量上限,被同一个旋钮反向锁在一起。 这不排除它在”依赖本就局部”的任务上长期胜出——那恰恰是它的正确定位,而不是它的过渡阶段。
带得走的东西
如果这篇只留一句,我希望是这句:看到任何”让 LLM 更快”的方案,先问它是在保留链式法则的前提下加速(投机解码那类,输出分布不变),还是在替换链式法则(扩散那类,输出分布变了)。 前者是纯工程账,后者是能力账——两者不能用同一把尺子量。
这把尺子还能量别的:多 token 预测、并行解码、Jacobi 解码,都能用”分解变没变”这一问归位。
诚实的提醒
这篇里所有的速度和评测数字都属于”核实过的来源”档——来自官方模型页和 arXiv 原文,我一个都没有亲手复现过。第一节的带宽推算来自站内旧文的实测数据,第三节的两张概率表我用脚本验算过。第二节末尾”多步扩散退化为任意顺序自回归”是我的理解,未逐式核对。
成本最低的亲手验证实验:LLaDA-8B-Instruct 是开源权重(HuggingFace 上 GSAI-ML 组织下),单卡可跑。拿它做一件事——固定 prompt,只把去噪步数从序列长度(每步填一个空)逐步降到长度的 1/8,记录 GSM8K 准确率和墙钟时间。 你会亲手画出那条”步数—质量”曲线,也就亲手摸到了第三节那笔借款的利率。如果曲线比预期平坦,那说明”依赖弱到可以忽略”的赌注比我估计的更成立——这个结果会直接反驳本文第六节的判断,值得写一篇。
参考来源
官方与工程实践
- Gemini Diffusion — Google DeepMind 模型页(速度与全部基准对照表来源)
- Inception Labs 官网(Mercury 定性口径与定价;速度图无具体数字)
- bd3lms — 块扩散官方实现
arXiv 论文
- arXiv:2107.03006 — Structured Denoising Diffusion Models in Discrete State-Spaces(D3PM,NeurIPS 2021):离散扩散的奠基,吸收态与掩码式生成的联系
- arXiv:2502.09992 — Large Language Diffusion Models(LLaDA):8B 从零训练,反向诗句补全超过 GPT-4o
- arXiv:2503.09573 — Block Diffusion(ICLR 2025 Oral):KV cache 缺失问题与块扩散插值方案
- arXiv:2508.02193 — Seed Diffusion(ByteDance Seed / 清华 AIR):2146 token/秒,含与自家自回归模型的同门对照
站内相关
- Decode 速度上限公式 — 第一节带宽推算的数据来源
- Medusa 深读:解码慢不是算力问题,是搬运问题 — 路线一(保留自回归)的完整拆解
- KV cache 那笔账 — 第三节借款二的背景
- Scaling 没有死,它换了坐标轴 — 扩散 LLM 在 2026 年格局中的位置(那篇是地图,这篇是机制)
- 采样不是玄学 — 伪代码里 softmax 采样部分的背景