
先说一个你可能没想到的现象。
现在的大模型推理普遍用一种叫投机解码的技术来加速生成,简单说就是找一个小模型先打草稿,大模型再一次性检查这些草稿对不对。听起来挺合理,草稿写得越准,大模型就越省力。但问题是,草稿模型现在已经练得相当聪明了,聪明到什么程度呢?
论文作者测了一下,用Qwen3-4B配合几种主流草稿模型,发现相当多的草稿片段是百分之百被大模型原样接受的。也就是说,草稿模型这次写的东西,大模型一个字都没改。可即便如此,系统还是老老实实地停下来,让大模型跑一次完整的验证,然后才让草稿模型接着写下一段。
这就有点像你请了一个特别靠谱的助理帮你写邮件草稿,助理每写完一段就非要跑来找你签字确认,哪怕前面十段你都是原封不动直接通过的。你心里肯定会想,既然你写得这么准,能不能先多写几段,我攒一起看一遍就行了?
投机解码*:一种加速大模型生成文字的技术。核心思路是用一个小的、运行快的"草稿模型"先猜测接下来可能出现的几个词,再交给大的"目标模型"一次性检查这些猜测对不对,对的就直接采用,省去逐字生成的开销。
这篇论文要解决的,正是这个"该验证的时候才验证,不该验证的时候别打扰"的问题。
被验证卡住的效率
投机解码里有两个关键角色。一个是目标模型,就是你真正想用的那个大模型,生成质量全靠它;另一个是草稿模型,个头小、跑得快,专门负责"预测"接下来大模型大概率会说什么。草稿模型吐出一批候选词之后,目标模型跑一次前向计算,检查这批词哪些能留下来。
这个过程里有个硬性规定:无论草稿模型这次猜得多准,验证之后必须停下来等下一轮草稿。论文里把草稿模型一次吐出的词叫作一个"块",块的大小通常是固定的,比如7个词或16个词。即便这7个词全部命中,系统也不会让草稿模型趁热打铁继续往下写,而是老老实实先验证,再重新开始下一个块。
为什么这是个问题?
因为目标模型的验证是整个流程里最耗时间的环节。一次完整的前向计算,哪怕只验证7个词,花的时间和验证14个词差不了太多,瓶颈主要在于把整个大模型跑一遍这件事本身,而不是验证的词数多少。研究者发现,随着验证词数增加,单次验证的耗时增长得很慢。这意味着什么呢?
如果能把原本需要两次验证的内容,合并成一次验证完成,那省下来的几乎是整整一次目标模型前向计算的时间。
这就好比工厂质检,检验员检查一箱零件和检查两箱零件花的时间差不多,因为大头时间花在搬箱子、启动设备上,真正逐个看零件反而很快。那如果你每次都只搬一箱来检查,检验员一趟趟白跑,损失的就是那些没用上的搬运次数。
已有的解决思路为什么不够用
其实研究者早就注意到这个浪费,也提出过一些办法,统称为"自适应草稿长度"方法。这类方法的核心思路是,在生成过程中动态决定要写多少个草稿词才去验证,而不是死板地固定一个块大小。
但这里有个关键限制。这些方法基本都是针对自回归草稿模型设计的。
自回归草稿模型*:一种逐词生成的草稿模型,写完第一个词之后,才能根据这个词继续写第二个词,像接龙一样一个接一个来。
自回归模型因为是一个词一个词蹦出来的,所以理论上可以在任意位置喊停,不想写了就不写了,这个词的成本已经花出去了,停下来也不浪费。但现在业界还有一类更快的并行草稿模型,它们一次前向计算就把整块的好几个词全部吐出来,不是排队生成的。
并行草稿模型*:一种一次性生成整个草稿块的模型,所有词都在同一次前向计算里产出,而不是像自回归模型那样逐词排队生成。
对并行模型来说,自适应长度方法基本失效。因为无论你想不想提前停,这次前向计算的成本已经花完了,提前喊停也省不下计算量。而且更麻烦的是,如果想让草稿模型在这个块之后继续往下写,它需要知道前面这些词准确的"隐藏状态"。
隐藏状态*:神经网络内部某一层对输入信息的编码表示,可以理解成模型对一段文字的"理解摘要",后续计算都基于这个摘要展开。
但这些隐藏状态只有大模型做验证之后才会产生。还没验证的草稿词,大模型压根没碰过,自然没有这个信息。草稿模型如果硬要往下写,就只能拿自己生成的隐藏状态顶替,而它压根没在这种设定下训练过,写出来的东西质量会直线下降。
DLoop:让草稿模型自己判断要不要继续写
这篇论文提出的DLoop,核心想法其实很朴素:既然草稿模型自己能感知到"这次写得靠不靠谱",那就让它自己决定要不要多写一轮,攒够了再统一交给大模型验证。
DLoop由两个部分组成,一个是推理时用的置信度门控循环机制,另一个是训练阶段要做的循环感知训练。
先说置信度门控循环。草稿模型每写完一个块,会把这个块里每个词的对数概率加起来,算出一个总分。这个分数反映的是草稿模型对自己刚写内容的信心程度。如果分数超过一个预设的门槛,草稿模型就继续写下一个块;如果没超过,就停下来,让目标模型把攒下来的所有块一口气验证完。
对数概率*:模型给某个词打分的一种方式,分数越高(越接近0)代表模型越确信这个词是对的,分数是负数且绝对值越大代表越不确定。
为什么用求和而不是求平均或者别的方式?论文里解释得很直接,因为每个词的对数概率本身都是负数或者零,一旦某个位置特别不确定,这个负数就会很大,把整个和拉低,没法靠其他位置的高分去"平均"掩盖。这和验证规则是一致的:只要某个词被拒绝,它后面的所有词都会被连带舍弃。所以用求和这种"一票否决"式的计分方式,跟实际的验证逻辑对得上。
这里可以打个比方。你让一个实习生连续处理好几批文件,他每处理完一批就自己打个分,说"这批我有把握,第二批我也挺有把握,但第三批里有一处我拿不准"。只要有一个环节他自己都心虚,那这批文件乃至后面连带的所有工作都得重新审,不能因为前面两批特别靠谱就把后面的也一并放行。如果不这样设计,而是让他用平均分蒙混过关,那一旦真出错,后面所有没检查过的工作都白写了,返工成本更高。
循环感知训练:教会草稿模型在"虚空"中继续写作
光有判断逻辑还不够,草稿模型本身得适应这种新的工作方式。
原来的草稿模型只被训练来处理一种情况:拿着大模型给的真实隐藏状态,预测接下来一个块该写什么。可DLoop要求草稿模型在第一个块之后,继续用自己生成的隐藏状态作为"输入材料"去写第二个块、第三个块。这种输入它从没见过,直接拿去用,效果可想而知会打折扣。
EAGLE-3这个草稿模型此前已经有一个叫"训练时测试"的技巧,会让草稿模型在训练阶段就模拟"拿自己预测的隐藏状态继续写"这件事,但只局限在一个块的内部深度。DLoop把这个思路延伸了,让草稿模型在训练时体验更长的链条,跨越好几个块去模拟整个循环过程。
训练时测试*:一种训练技巧,让模型在训练阶段提前适应推理阶段会遇到的情况,比如这里让草稿模型练习"用自己生成的中间结果继续往下写",而不是只依赖外部提供的准确信息。
具体做法是,对每条训练数据,随机抽取要额外进行几轮草稿(论文里设定在1到3轮之间),然后真的跑这么多轮,第一轮照常用大模型给的隐藏状态,后面几轮就衔接上草稿模型自己算出来的隐藏状态。损失函数只看两头:第一轮的预测效果,和最后一轮(也就是最难、离真实信息最远那一轮)的预测效果,中间过程只负责传递隐藏状态,不直接参与计分。
为什么只盯着第一轮和最后一轮?
因为第一轮是原本的训练目标,必须保留,不然基础能力会退化。而最后一轮是整个链条里信息衰减最严重、难度最大的部分,把训练精力集中在这里,相当于专门攻克最薄弱的环节。中间那几轮只是陪练,负责把"自己生成隐藏状态"这件事的链条搭建起来,但不单独计分。
这就好比培训一个翻译新人,你不会让他在每一句话翻完之后都打分复盘,那样效率太低。更有效的办法是,让他连续翻译一整段文字,然后重点检查开头那句(看基本功有没有退步)和结尾那句(看长链条下误差会不会越滚越大),中间部分只是让他熟悉"连续工作不被打断"的状态。如果不这样做,而是每一句都精细打磨,新人反而学不会怎么在长篇任务里保持连贯和自我修正。
实测效果:速度提升5%到41%
论文在EAGLE-3、DFlash、Domino、DSpark这几种主流草稿模型上都做了测试,配合Qwen3-4B和Qwen3-8B作为目标模型,还测试了Qwen3.5-9B和Gemma-4-12B自带的多词预测模块。
EAGLE-3*:一种知名的自回归草稿模型系列,核心思路是让轻量级草稿模型借用大模型已经算好的隐藏状态,逐词预测后续内容。
DFlash、Domino、DSpark*:三种并行草稿模型,分别用不同方式实现一次前向计算吐出整个草稿块,去掉了自回归逐词生成的排队开销。
结果显示,在这些设置下,平均每次验证能接受的词数(叫作"接受长度")提升了12%到102%,整体的实际运行速度提升了5%到41%。这里有意思的是,提升幅度最大的是DSpark,恰好DSpark也是前面提到的"百分之百被整块接受"比例最高的草稿模型,说明它确实有最多的机会去多写几轮而不被打断。
论文还做了拆解实验,单独测试"只开循环不做训练"和"只做训练不开循环"这两种情况。结果是只开循环但不训练,对自回归模型(比如EAGLE-3)还有提升,因为它本来就习惯自己生成隐藏状态继续写;但对并行模型来说,速度提升反而有限甚至下降,因为额外多写的几轮时间,被接受长度的提升抵消得差不多了。只做训练不开循环,两个指标变化都很小。只有两者同时启用,效果才真正显著,而且提升幅度超过了两者单独效果的简单相加。
这说明循环感知训练不是在孤立地提升每一轮草稿的质量,而是在专门为"循环"这种新工作模式做适配准备。
和同类自适应长度方法的对比
论文还拿DLoop跟一堆已有的自适应草稿长度方法做了横向比较,包括BiLD、SVIP、AdaEDL、LLMCad、SpecDec++、DISCO、PACER、AdaEAGLE、DDD等等。这些方法大多基于草稿分布的某个统计量(比如概率、熵)来判断该不该提前停止生成,有的还要专门训练一个决策模块。
实验结果显示,在DSpark这种并行模型上,这些方法没有一个能超过原始草稿模型的速度,有的甚至更慢。原因前面也提到过,并行模型一次前向计算的成本是固定的,提前喊停省不下这个成本。而在EAGLE-3这种自回归模型上,这些方法确实有一定提升,但提升幅度最多也就0.14倍左右,远不如DLoop。DLoop在两种草稿模型上都拿到了最高的接受长度和速度提升。
速度提升到底从哪里来
论文专门做了一个细致的时间拆解,统计了解码过程中究竟花了多少时间在草稿上、多少时间在验证上,按照每次循环进行了几轮草稿来分类。
数据显示,DLoop让总解码时间降低了4.9%到24.3%,其中34%到71%的生成词数是在"多轮草稿共享一次验证"的情况下产生的。随着单次循环里草稿轮数的增加,单轮循环的耗时确实在增长,但接受词数的增长速度更快,结果就是平均每生成一个词所需的时间持续下降,从单轮草稿时的4.6到6.4毫秒,一路降到五轮以上草稿时的0.68到2.97毫秒。总的来看,验证次数减少了7.6%到34.1%。
由于每次验证都要跑一遍完整的大模型前向计算,这是整个流程里最贵的开销,验证次数的减少带来的收益,完全盖过了多写几轮草稿的额外成本。
这就好比快递员送货,原本每送一件包裹就要回站点打卡登记一次,现在改成一次能带5件包裹的时候就多拿几件,打卡次数少了,虽然每趟路上多花了点时间找包裹,但省下的来回跑站的时间远比这个多。如果坚持每次只送一件就回去打卡,表面上看每趟都很规范,实际上大量时间耗费在不必要的往返上。
和树形验证结合,效果继续叠加
除了基础的投机解码,业界还有一种叫"树形验证"的增强技术,大致是让草稿阶段生成多个候选分支,而不是单一路径,大模型一次验证整棵树,从中挑出最优路径。
树形验证*:一种扩展投机解码效率的方法,草稿阶段不只生成一条直线式的候选序列,而是生成一棵包含多个分支的候选树,大模型一次性验证整棵树,从而提高单次验证能覆盖的候选范围。
DLoop和树形验证并不冲突,反而可以叠加使用。论文里针对DFlash用了DDTree,针对Domino和DSpark用了DARTree。结果是,在树形验证的基础上再加DLoop,DFlash的平均速度又提升了12%到13%,Domino提升了7%到8%,DSpark提升了22%到25%。这说明树形验证解决的是"往哪个方向猜"的问题,DLoop解决的是"猜多久才去核对"的问题,两者本来就是不同维度的优化,叠加起来互不打架。
写在后面
读完这篇论文,印象最深的其实不是加速比数字本身,而是那个"全盘接受率"的统计图。
它提醒了我一件容易被忽略的事:很多工程系统里的低效,不是因为某个组件做得不够好,而是因为系统的调度节奏没跟上组件实际能力的提升。草稿模型越练越聪明,但验证节奏还停留在"保守估计它会犯错"的老规矩上,这种错配本身才是浪费的来源。
另一个值得琢磨的点是,论文反复强调DLoop不需要额外的参数、不改变草稿模型的结构,只是改变了"什么时候该停下来核对"这件事的决策方式。这种"不动筋骨只调节奏"的思路,其实在很多系统设计里都适用,有时候真正拖后腿的不是能力不够,而是流程设定得太死板。
论文结尾那句话让我多想了一下:验证前该进行几轮草稿,不必由固定的调度规则决定,完全可以留到运行时再判断。如果把这句话放到更大的范围去想,是不是很多我们习以为常的"固定流程",其实都值得重新问一句,这个节奏是真的必要,还是只是因为大家一直这么做?
Q&A
Q1:DLoop是什么?
A:DLoop是一种改进投机解码的方法,核心是让草稿模型根据自己的置信度决定要不要连续写多轮草稿,再统一交给大模型做一次验证,从而减少目标模型的前向计算次数。
Q2:DLoop相比普通投机解码能提升多少速度?
A:论文在EAGLE-3、DFlash、Domino、DSpark等多种草稿模型上测试,实际运行速度提升了5%到41%,同时保持生成结果和原来完全一致,不会损失准确性。
Q3:DLoop对并行草稿模型和自回归草稿模型都有效吗?
A:有效,而且这正是它和此前的自适应草稿长度方法最大的区别。此前的方法只对自回归草稿模型有提升,对一次性生成整块草稿的并行模型基本无效,DLoop通过循环感知训练解决了这个限制。







