【免费下载链接】dflash-mlx
Lossless DFlash speculative decoding for MLX on Apple Silicon
dflash-mlx 是一个跑在 Apple Silicon(MLX)上的无损推测解码运行时:它用约 1B 参数的小模型通过块扩散(block diffusion)一次性起草 16 个 token,再让目标大模型单次前向验证,只提交被验证通过的 token。本文将剖析这套"块扩散草稿 + 单次前向验证"机制为何能做到无损,以及它 2x~4x 加速背后的原理。
什么是"无损"推测解码?
先说结论:dflash-mlx 输出的每一个 token,都是目标模型在验证时刻的贪心 argmax——没有任何一个"没被大模型检查过的 token"会被提交给输出。官方文档对此的定义非常直接(见 README.md):
Lossless: every emitted token is the target model's greedy argmax at verification time.No unverified token is ever emitted.
打个比方:小模型像是"快速初稿写手",一口气写 16 个词;大模型是"终审编辑",只读一遍整段初稿。编辑从第一个词开始逐词核对,核对一致就保留,一旦不一致立刻截断;被截断之后,编辑亲手补写一个自己认为最正确的词,接上下一个循环。
因为"终审"不可绕过,所以输出质量完全等于目标模型自己的贪心输出——这就是"无损"的含义:加速不牺牲质量。
两个主角:1B 草稿模型 + 目标大模型
| 角色 | 规模 | 职责 | 实现位置 |
|---|---|---|---|
| 草稿模型(DFlash draft) | 约 1B 参数 | 一次前向并行生成 16 个候选 token | dflash_mlx/model.py |
| 目标模型(Target) | 4B~35B | 单次前向验证整块候选 | dflash_mlx/engine/spec_epoch.py |
两者必须成对注册(如 Qwen3.5-9B 配 Qwen3.5-9B-DFlash),没有匹配草稿的模型会被直接拒绝加载,这从入口上杜绝了"退化成普通慢速生成却还声称加速"的可能。
块扩散草稿:16 个 token 一次成型
传统自回归草稿模型要串行跑 16 步才能出 16 个 token,这本身就慢。DFlash 草稿模型改用块扩散方式:
- 构造一个块:第 1 位放目标模型的"暂存 token"(staged token),后面 15 位全部是mask 占位符;
- 草稿模型对整块做一次前向,所有 mask 位置同时产出各自的预测;
- 关键设计:草稿模型的注意力不仅看块内 token,还通过交叉注意力直接读取目标模型多层 hidden state(见 dflash_mlx/draft_backend.py 中的
target_hidden输入)。
第二步的"同时产出"是块扩散(block diffusion)的核心——像扩散模型去噪一样,一个前向把整块填完,而不是逐位自回归。这让草稿成本几乎恒定,无论块多长。
单次前向验证:一个循环的完整流程
整个推测解码循环在 dflash_mlx/engine/spec_epoch.py 中驱动,docs/architecture.md 将其总结为 9 步:
① 目标模型分块 prefill,初始化 KV 缓存与循环状态 ② 目标模型对最后一个 token 取 argmax,得到"暂存首 token" ③ 草稿模型收到 [暂存token, mask, mask, ..., mask] 组成的块 ④ 草稿模型一次前向,输出 15 个候选尾 token ⑤ 目标模型对 [暂存token + 15 个候选] 做一次前向(单次!) ⑥ 对比草稿候选与目标后验分布,计算接受长度 ⑦ 提交 1 + 接受长度 个 token ⑧ 被拒绝位置后目标模型预测的 token,成为下一循环的暂存首 token ⑨ KV 缓存按长度裁剪;GDN 循环状态用 tape-replay 回滚注意第⑤步:目标模型只跑一次前向就验证了全部 16 个候选——这就是标题里"单次前向验证"的含义,也是 dflash-mlx 相对传统 draft-then-verify 逐位验证的最大成本优势。
为什么它一定是无损的?三层保证
保证一:每个 token 都经过目标模型前向
没有任何 token 能跳过目标模型的验证直接到达输出。草稿模型说什么都行,目标模型一次前向后,每个位置的 logits 都会算出自己的贪心 argmax(后验 token 序列)。
保证二:前缀贪心匹配,拒绝即截断
接受逻辑实现得极其简洁,核心就几行(见 dflash_mlx/engine/acceptance.py):
matches = mx.equal(drafted_tokens, posterior_tokens).astype(mx.int32) return mx.sum(mx.cumprod(matches, axis=0))cumprod的语义是:只要某个位置草稿 token ≠ 目标模型贪心 token,该位置及之后所有位置全部作废。也就是说,系统永远不会"跳过错误位置接受后面的 token"——接受的一定是从头开始的正确前缀。
而被拒绝的第一个位置怎么办?它不是丢弃目标模型的答案,而是采用目标模型自己的贪心 token作为下一轮的暂存首 token。所以即使草稿全错,每轮也至少前进 1 个 token,且这 1 个 token 是纯目标模型产物。
保证三:缓存回滚,状态永远一致
验证会污染目标模型的缓存(KV 和循环状态都被 16 个候选 token 更新过)。若只接受 5 个,必须把状态精确回滚到"只走过 5 步"的样子,否则下一个循环的验证就失真了:
- 注意力 KV 缓存:直接裁剪掉多余长度即可;
- GatedDeltaNet 循环状态:不能简单裁剪(循环状态无法倒带)。dflash-mlx 实现了tape-replay rollback(dflash_mlx/recurrent_rollback_cache.py):验证期间记录一条"创新磁带"(每步的 k、g、qkv 增量),回滚时用一个 Metal 内核只重放被接受的步数,精确重建循环状态,且成本远低于整段重算。
回滚由 dflash_mlx/engine/target_qwen_gdn.py 中的restore_after_acceptance统一调度,保证每个循环开始时缓存状态与"已提交 token"严格对齐——这是长期生成下输出仍与纯自回归一致的隐藏前提。
性能表现:无损也能 2x~4x 快
无损不等于慢。Apple M5 Max 上(MLX 0.31.1,3 次重复取中位数)的部分基准数据:
| 模型 | 生成长度 | 基线 | DFlash | 加速 | 接受率 |
|---|---|---|---|---|---|
| Qwen3.5-4B | 1024 tok | 53.80 tok/s | 182.87 tok/s | 3.40x | 86.4% |
| Qwen3.5-9B | 1024 tok | 30.95 tok/s | 135.34 tok/s | 4.37x | 89.6% |
| Qwen3.5-27B-4bit | 2048 tok | 33.10 tok/s | 70.21 tok/s | 2.12x | 89.6% |
| Qwen3.6-35B-A3B-4bit | 1024 tok | 138.26 tok/s | 300.33 tok/s | 2.20x | 91.0% |
接受率普遍在 85%~91%,意味着平均每轮约 14 个 token 被草稿"白送"。完整数据见 benchmark/results/ 中的逐次 JSON 记录。
💡 小模型(4B~9B)加速比更高,因为草稿验证的相对开销更小;27B 以上模型虽然加速比收敛到 1.3x~3x,但绝对吞吐仍大幅提升。
快速上手体验无损加速
pip install dflash-mlx # 一次性生成,草稿模型自动解析 dflash generate --model Qwen/Qwen3.5-9B --prompt "..." # OpenAI 兼容服务 dflash serve --model mlx-community/Qwen3.6-27B-4bit \ --draft z-lab/Qwen3.6-27B-DFlash --port 8000 # 本地基准对比 baseline vs DFlash dflash benchmark --model Qwen/Qwen3.5-9B --max-tokens 1024 --repeat 3服务器模式下curl http://127.0.0.1:8000/metrics可以实时查看tokens_per_cycle、接受率等指标,直观验证无损推测解码的运转情况。
总结:无损的三个关键词
| 机制 | 作用 | 对应实现 |
|---|---|---|
| 块扩散草稿 | 1 次前向出 16 个候选,草稿成本恒定 | dflash_mlx/model.py |
| 单次前向验证 | 目标模型一次前向验证整块 | dflash_mlx/engine/spec_epoch.py |
| 贪心接受 + 回滚 | 只接受正确前缀,状态精确回滚 | dflash_mlx/engine/acceptance.py |
dflash-mlx 的"无损"不是营销话术,而是由验证不可绕过 + 前缀截断 + 状态回滚三重机制共同保证的工程事实:小模型负责快,大模型负责对,输出永远是大模型自己的贪心结果。对于需要在 Mac 上获得显著生成加速又不愿牺牲任何质量的用户,这正是推测解码应有的样子。
【免费下载链接】dflash-mlx
Lossless DFlash speculative decoding for MLX on Apple Silicon
相关推荐
为什么说是无损加速?NVIDIA-Nemotron-3.5-Bolt-E4B-4GB-DFlash 的 DFlash 验证机制原理解析
为什么说是无损加速?NVIDIA Nemotron 3.5 Bolt E4B 4GB DFlash 的 DFlash 验证机制原理解析 NVIDIA Nemot
为什么 DSpark 推测解码是无损加速?NVIDIA-Nemotron-3.5-Lightning-30B-A3B-DSpark-bf16 的验证机制原理详解
为什么 DSpark 推测解码是无损加速?NVIDIA Nemotron 3.5 Lightning 30B A3B DSpark bf16 的验证机制原理详解
Kimi-K3-DSpark-Block5 是什么?一文读懂 DSpark 推测解码草稿模型的核心原理
Kimi K3 DSpark Block5 是什么?一文读懂 DSpark 推测解码草稿模型的核心原理 Kimi K3 DSpark Block5 是面向 Ki
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考