技术札记

语言模型一定要逐字生成吗?我对 dLLM 的理解与工程判断

从掩码去噪到并行解码,理解扩散语言模型改变了什么,以及为什么更少的生成轮次不一定换来更低延迟。再把问题落到代码、Agent 和本地推理。

HOUHUIYANG.COM

扫码继续阅读

正在生成…

语言模型一定要逐字生成吗?我对 dLLM 的理解与工程判断

houhuiyang.com/zh/notes/diffusion-language-models-parallel-generation-and-engineering

之前写从零训练 Transformer和拆解 MicroGPT时,我一直沿着一条熟悉的路径理解语言模型:给定前缀,预测下一个 token,再把结果接回输入。

这条路径很自然。聊天界面也是一个字接一个字往外冒,久而久之,很容易把“语言模型”与“从左到右生成”当成同一件事。

扩散语言模型让我重新拆开这两个概念。我感兴趣的问题是:如果答案不必按顺序写出来,而可以从一组未知位置逐步形成,生成系统会发生什么变化?

这里的 dLLM 指 diffusion large language model。本文是我基于公开论文与实现的技术梳理,不是本地实测报告。配图、示例和延迟算式用于解释机制;涉及代码、Agent 和 Mac 的部分,是我准备如何评估这条路线的工程判断。

自回归逐步追加,与掩码扩散多位置逐步确定的对比

先分清网络结构与生成方式

Transformer 解决的是信息如何在位置之间交互;自回归描述的是如何按条件概率组织序列生成。这是两个层次。

因此,“扩散模型会不会取代 Transformer”并不是一个准确的问题。LLaDA 本身就使用 Transformer,变化主要在训练目标、注意力的可见范围和采样过程。LLaDA 官方说明也专门区分了这两个概念。

我习惯把一个生成系统拆成四层:

层次要回答的问题
网络用什么结构处理上下文?
训练目标哪些预测会得到奖励,哪些错误会产生损失?
采样先确定哪些位置,何时接受候选,何时停止?
服务怎样缓存、调度、流式输出,再接入工具?

只看界面上的输出速度,很难判断是哪一层带来了改进。逐字显示也不等于底层一定逐 token 计算,整段出现同样不能证明使用了扩散模型。

从接着写,到补全未知位置

普通自回归解码可以概括为:

前缀 → 预测一个 token → 追加到前缀 → 再预测

注意,这里说的是生成阶段。训练时已知完整样本,多个位置的预测可以并行计算;推测解码也可以让自回归模型一次验证多个候选,减少串行调用。因此,“自回归完全不能并行”并不准确。推测解码论文给出了这种加速路径。

掩码扩散提供了另一种组织方式:先留出未知位置,再利用当前可见内容推断缺失部分。下面用词块演示,方括号不代表真实 tokenizer 的切分:

初始       [MASK] [MASK] [MASK] [MASK]
第 1 轮    [MASK] [模型] [MASK] [答案]
第 2 轮    [这个] [模型] [MASK] [答案]
第 3 轮    [这个] [模型] [生成] [答案]

这个例子强调的是确定位置的顺序可以不同于文本顺序,并不意味着每次都能只用三轮完成,也不意味着每轮的所有候选都会被接受。

以 LLaDA 为例,训练通过不同程度的随机掩码构造输入,学习恢复缺失 token;生成则从掩码状态出发,逐步确定输出。这是离散的 masked diffusion。它与图像扩散都使用逐步去噪的思想,但不能把文本 token 直接理解成带高斯噪声的像素。LLaDA 论文

还要区分候选与已接受结果。某轮预测出来的候选可以被丢弃、留待下一轮重算;已经接受的位置能否再次改写,要看采样器是否允许重新掩码。有的过程只逐步揭开未知位置。“支持迭代”不等于“所有已生成内容都可以随时回改”。

我怎样看 LLaDA 与 Mercury

LLaDA 是理解这条路线的一个公开入口:它展示了从头训练的 8B 掩码扩散模型,也提供了可阅读的推理实现。对我而言,价值在于可以把论文中的采样过程落实到代码,而不是仅凭聊天演示建立印象。官方仓库

Mercury 则代表商业化方向。Inception 在最初的发布中强调扩散生成的速度潜力,并将代码生成作为重要场景。厂商披露的性能需要连同硬件、任务与比较基线一起看,不能直接换算成我自己电脑上的收益。Mercury 首发介绍

我把它们放在一起,是为了区分研究证据与产品交付,并不把 LLaDA 的具体采样方式套到 Mercury 的内部实现上。这也不是截至今天的型号排行榜。

为什么并行生成不一定更快

我最关心的不是“一次能预测几个 token”,而是“一份可用答案总共需要多少时间”。

先用一个刻意简化的模型建立直觉。设输出长度为 N,扩散轮次为 S:

普通自回归延迟 ≈ 提示词预填充 + N × 平均单步解码耗时
扩散生成延迟   ≈ 初始化 + S × 平均去噪轮耗时 + 调度与输出开销

这不是复杂度证明,也没有涵盖推测解码、批处理等优化。它只是提醒我:S 小于 N,并不能推出扩散更快,因为一轮去噪和一步增量解码的成本不同。

假设某个虚构任务需要 256 个输出 token,自回归平均每步 4 ms,解码部分约 1,024 ms。如果扩散只需要 32 轮,但每轮花 40 ms,就是 1,280 ms;若降到 16 轮,则是 640 ms。这里所有数值都是教学假设,不是任何模型或设备的测量结果。

数字变小之后,还要继续问:16 轮的答案是否与 32 轮同样可用?如果错误率增加、必须重试,省下的时间可能又被花掉。

轮数、每轮耗时与输出质量共同决定可用答案的延迟

缓存复用有条件

普通因果解码里,追加新 token 不会改变旧前缀的表示,所以各层旧位置的 key/value 可以复用。双向去噪中,其他位置改变后,一个位置的上下文也可能改变,照搬这种缓存就可能使用过期表示。

这并不意味着 dLLM 永远不能使用缓存。Fast-dLLM研究了近似 KV Cache 和置信度驱动的并行解码;Block Diffusion则在块间保留自回归关系,在块内使用扩散,为缓存与可变长度生成提供另一种设计。

对工程实现,我会追问:哪些位置被缓存,什么时候刷新,误差如何评估?“用了 KV Cache”只是起点。

同时预测,不代表联合正确

两个位置分别看起来很有把握,放在一起却可能不一致。代码里的变量名与引用、JSON 的字段与取值,都存在相互约束。Fast-dLLM 讨论了并行解码时 token 依赖被破坏的问题,所以并行接受策略也是质量控制的一部分。论文

我不会直接把“多位置可见”理解成“结构天然正确”。模型能看见约束,和生成结果满足约束,中间还隔着训练、采样与验证。

对代码与 Agent,我更在意提交边界

对代码生成,我想先试函数体补全和局部修复:固定输入、签名与上下文,在有限范围内生成,再交给编译器和测试。这个实验比一上来比较整仓库生成更容易定位问题。

对 JSON、SQL 和我自己的 HHY DSL,我会分别接上 schema 校验、解析器或类型检查。扩散过程提供了另一种构造候选的方式,合法性仍然需要独立验证。

Agent 还多出一个问题:生成中的内容什么时候可以执行?

候选参数 → 模型确定结果 → 解析与校验 → 权限检查 → 工具执行

我会把中间候选当成草稿。一个金额、文件路径或 SQL 条件即使暂时出现在界面上,也不能成为触发工具的依据。采样器“接受 token”只是内部状态,业务系统还需要自己的提交边界。

模型候选经过校验与权限检查后,才进入工具执行

这条原则对自回归 Agent 同样成立。dLLM 只是让我更清楚地看见:可显示的文本、稳定的输出和允许执行的动作,是三种不同状态。

如果放到本地,我会怎样验证

我之前的 MLX 实验让我形成了一个习惯:先验证完整链路,再讨论速度。下面是我为 dLLM 设计的评测方案,还没有在本文中执行。

我会准备三类固定任务:带测试的函数补全、带 schema 的 JSON 生成、带解析与语义检查的 DSL 转换。固定提示词集合和输出预算,记录模型版本、精度、设备、上下文长度、去噪轮次、块大小与缓存配置,并区分冷启动和预热后的请求。

指标我想看清什么
首次稳定输出时间什么时候能交付不会再改变的内容?
完整答案延迟,含 p50 / p95用户通常等多久,尾部请求又等多久?
任务通过率代码能否通过测试,结构是否合规?
成功任务总耗时把校验、重试与修复算进去后还有多少收益?
峰值内存、持续负载表现能否在本机稳定运行,而非只完成一次演示?

我会同时报告成功数和失败数,避免只统计成功样本造成速度假象。输出 token/s 可以保留,但不能替代这些指标。

比较也分两步:先在同一个扩散模型上调整采样参数,找出速度与质量的关系;再与适合本机的自回归模型比较任务结果。第二步同时混入了训练数据和模型能力的差异,不能全部归因于生成架构。

在 Mac 上,能装下权重只是第一关。算子支持、量化、内存带宽、缓存布局与采样器实现都需要验证。Ollama 或 MLX 的名字也不能替代具体模型的兼容性检查。

同样,把 GPT 或 Qwen 的因果掩码去掉,并不会自动得到可用的扩散模型。即便选择从已有权重出发,仍然需要匹配新的训练目标、采样方式与评测流程。

我目前的判断

我愿意持续跟进 dLLM,因为它把一个长期习以为常的选择重新打开了:语言生成是否必须沿着单一前缀逐步推进?

但在产品里,我最终要交付的是通过检查的代码、可解析的结构和可靠完成的任务。能并行预测更多位置很有价值;只有当这种能力转化成可重复的质量与延迟收益时,才值得改变现有系统。

下一步,我更想做一个范围很小的对照实验:相同的一组函数补全任务,记录通过率、完整延迟、内存和重试成本。先找到它确实有优势的边界,再决定把它放进哪一段工作流。

参考资料

返回技术札记