AI 开始抄自己的作业——而且抄得理直气壮
目录
AI 开始抄自己的作业——而且抄得理直气壮
想象一场期末考试。你翻开卷子,发现最后一道大题和上周的作业一模一样。老师的要求很严格:不许翻书,必须独立演算,每一步都要在答题卡上写出过程。你当然记得作业答案,但你不能直接把结论甩上去——你得把解题步骤重新写一遍,只是这次快得多:草稿纸就摊在手边,你誊上去的每一笔,都用心算当场验算过。最后交上去的卷子,和完全从头推导的卷子一模一样,一个字都不差,但你省下了将近一半的时间。
这不是考场作弊的故事。这是 UC San Diego 和 West Virginia University 的研究者在 2026 年 10 月做出来的一件事:他们让一个正在做强化学习训练的大语言模型,光明正大地"参考自己的作业"。方法叫 NgramRL,论文挂在 arXiv 2610.32881 上。它不偷看任何外部答案,不改变训练出来的模型,不改变学习过程的任何性质——但它把强化学习后训练里最烧钱的那段路,加速了接近 1.4 倍。
这件事值得停下来想一想。因为真正有意思的不是加速本身,而是它揭穿的一个事实:AI 在训练时,一直在疯狂地重复自己,却每次都装作第一次。这笔浪费,以前几乎没有人抬头看。
🧾 一张被忽视的账单:钱到底花在哪
要讲清楚这笔浪费,得先弄清楚钱是怎么花掉的。
现在提升大模型推理能力的主流方法,叫强化学习后训练,其中一个代表性算法是 GRPO。流程听起来很朴素:给模型一批题目,让它对每个题目写好几遍答案(这叫 rollout,采样),然后根据答案的对错算奖励,再用奖励信号去调整模型参数。做对了,下次往这个方向多走一点;做错了,往回收一点。
注意这里的花钱结构。很多人以为训练大模型贵,贵在"反向传播"——那一大堆梯度计算。但在强化学习后训练里,真正的开销大头根本不是梯度,而是 rollout:让模型一个 token 一个 token 地把答案写出来。论文给出的数字是,rollout 占掉训练时间的 70% 以上。
为什么 rollout 这么贵?因为大模型生成文本是自回归的,像老人写毛笔字——写完一个字,端详一下,再写下一个。每一个 token 都要完整跑一遍大模型,而一部 A100 每跑一遍,都要把几十亿个参数全部激活一遍。更要命的是,GRPO 不是让模型写一遍就完事。它要反复对同一批 prompt 采样,每个 prompt 写好几份 completions,写完一批再去更新参数,更新完再写下一批。论文的实验设置是:每一步 64 个 prompt,每个写 5 份答案,如此重复 15 个 epoch。等于同一个学生,被要求把同一道题反复做 5 遍、再做 15 轮。
为什么非得写这么多遍?这是由算法本身决定的。GRPO 的核心是比较:同一道题的 5 份答案,有的对有的错,它不看绝对分数,看这一组答案的相对表现——某份答案的奖励比组内平均高,就强化产生它的倾向;低于平均,就压制。样本太少,这个相对比较就是噪声;epoch 太少,策略学不动。所以"反复写"不是工程上偷懒,是算法的硬性要求。也正因为是硬性要求,只要生成速度提不上去,整个训练就提不上去。
这才是账单的真相:强化学习后训练本质上是把"生成"这个动作,在训练循环里一遍又一遍地重放。生成有多慢,训练就有多慢。想省钱,要么少生成,要么让每次生成变快——前者动了算法的根本,后者才是能走的路。
那么,能不能让生成快点?
⚡ 投机解码:让草稿先跑,考官殿后
让自回归生成变快的经典思路,叫投机解码(speculative decoding)。它解决的是一个很拧巴的局面:大模型准但慢,小模型快但不准。单独用谁都不满意。
投机解码的做法像一场接力。小模型——草稿模型——先上阵,一口气猜出后面 k 个 token,不用等,凭直觉连写。然后大模型登场,但它不是逐个检查,而是把这 k 个 token 一次性并行验证:一遍 forward,同时算出"如果是我,每个位置会怎么写"。能过的就整段接受,不能过的从出错的位置截断,由大模型自己补一个 token,然后草稿模型接着往下猜。
关键在于验证这一步的数学设计,它保证了一件事:最终吐出来的 token,在分布上和让大模型自己慢慢写完全一致。验证用的接受准则是:草稿提议的 token,以 min(1, π_θ(x|c)/q(x|c)) 的概率被接受——这里 π_θ 是当前策略的分布,q 是草稿的分布。换句话说,草稿模型在哪个位置越自信、越接近大模型,就越容易过;差距大的,大概率被打回,打回之后从一个"残差分布"里重新采样——残差分布就是"大模型分布里扣掉草稿已提议部分"剩下的那一份。这套校正机制让整个流程在数学上无损:输出分布不变,只是变快了。
举个具体的数感受下。假设草稿在某个位置提议 token x,自己的把握是 q=0.9,而大模型在这个位置的把握是 π=0.95,比值大于 1,接受概率就是 1——稳过。反过来,草稿很自信(q=0.9)而大模型根本不认(π=0.1),比值约 0.11,十次里九次被打回,而且打回后的重采样几乎必然来自大模型自己偏好的位置。草稿的错觉被系统地纠正,大模型的主权一寸不让。
生活里对应的情形是什么?是资深律师带实习生。让实习生先起草合同初稿,一口气写完,合伙人拿红笔并行审阅:没问题的一页页过,有问题的段落当场重写。最后交付给客户的东西,和合伙人一个字一个字亲自写的东西,法律上完全等价——但合伙人只花了审一遍的时间。
这个思路在推理加速里已经用了很久,效果一直不错。那为什么到了强化学习训练的场景,它突然就不好使了?
🧨 老办法为什么在训练场集体失灵
因为训练场景给投机解码出了两道 inference 场景里不存在的难题。
第一道难题来自"模型法"草稿路线。这类方法——EAGLE、Medusa 这一支——训练一个额外的小模型当草稿工。问题有两个。其一,这个小模型是要占 GPU 显存的,而训练时的显存本来就紧张,梯度、优化器状态、激活值已经把显存挤得满满当当,再加一个草稿模型,等于在春运的火车上再塞一个行李箱。其二,更要命的是:训练中的策略模型每一步都在变。你今天按它的脾气训练的草稿模型,三天后策略早就不是那个策略了,草稿的提议和当前策略对不上,拒绝率应声而涨。草稿过期,比没有草稿还糟——你白花了验证的算力。
第二道难题来自"无模型法"草稿路线。这类方法——prompt-lookup、REST 这一支——不训练小模型,而是直接从当前 prompt 或一个静态语料库里查:接下来的几个 token,是不是在原文里出现过?推理场景下这招很灵,因为任务常常是"复述型"的,摘要、翻译,答案本来就藏在输入里。但推理任务不一样。模型做数学题、写代码、查数据库的时候,rollout 是一条漫长的推理链,和 prompt 的重合度非常低。你在一道题目的题干里,找不到这道题的解答过程。查来查去,命中率惨不忍睹。
两条路线,一条死在显存和过期上,一条死在命中率上。把它们摆在一起看,其实指向同一个缺的东西:一个懂这道题、又不占地方、还随策略更新的草稿源。这个要求苛刻得近乎矛盾——懂题,意味着要有能力;不占地方,意味着别是个模型;随策略更新,意味着不能是静态语料。
把两道难题翻译成一句话:训练中的模型需要一个草稿源,这个草稿源既要不吃 GPU 显存,又要跟得上策略每天的变化,还要真的了解这道题的解法。前两条筛掉了模型法,后一条筛掉了无模型法。三条都满足的草稿源,上哪儿找?
NgramRL 的作者做了件聪明事:他们没有去找,而是去数了数。
🔍 没人抬头看的浪费:rollout 在重复自己
他们统计了一个简单到近乎尴尬的问题:同一个 prompt,在训练的不同 epoch 之间,它的 rollout 到底有多少重复?
数字摆出来之后,浪费的规模有点惊人。训练早期,同一个 prompt 的 rollout 之间就有约 50% 的部分是完全相同的;到中期,这个数字涨到 81%;到训练后期,同一个 prompt 的 rollout 竟然达到 100% 逐字相同——模型对这道题已经形成了肌肉记忆,每遍写出来的答案一个 token 都不带差的。再看连续两个 epoch 之间的 rollout 覆盖率:48% 到 96%。重复的片段长度呢?平均在 7 到 23 个 token 之间。
这不是边角料的重复,是结构性的重复。想想前面说的训练流程就明白了:GRPO 本质上就是反复对同一批题采样,而训练中途策略每次只被梯度更新推了一小步——模型的"脾气"一天之内变化不大,那么它对同一道题的作答方式,自然一天之内也变化不大。采样次数越多、策略变化越慢,重复就越彻底。到训练后期,模型对这批题已经收敛出稳定的作答套路,重复率顶到 100%,每一遍 rollout 都是上一遍的逐字复刻。
重复还有一个随训练演进的形状。早期只有 50%,是因为策略还在摸索,每次下笔都带着随机试探;中期 81%,说明套路开始成形但细节还在打磨;后期 100%,意味着探索已经基本收敛。重复率先升后稳的曲线,本身就是训练进度的镜子。
这一数,就数出了那个三条都满足的草稿源:rollout 自己。模型上一轮、上几轮写下的轨迹,就是当前策略最忠实、最对齐的草稿。它由策略自己产生,所以天然跟着策略变——策略怎么变,表里的内容就怎么跟着变,不存在过期问题;它躺在 CPU 内存里就行,不占 GPU 显存;它对这道题的了解,是"我上次就是这么做的"级别的了解,比任何外部语料都贴身。
还是那个期末考试的比喻:考题和上周作业一模一样。聪明人不会去翻别人的笔记(那是模型法,还得带个师弟占座位),也不会去翻课本(那是无模型法,题在课本里找不到答案)。他会把自己上周的作业摊在桌角,当草稿纸誊。
剩下的问题只有一个:怎么把这套"誊作业"的流程,工程化地塞进训练管线,并且不破坏任何训练性质。
📓 NgramRL:每道题配一本活页草稿本
NgramRL 的核心数据结构简单得近乎朴素:一个 n-gram 查找表。
所谓 n-gram,就是连续 n 个 token 组成的小片段。NgramRL 把模型写过的 rollout 切成一个个长度不超过 6 的片段(n≤6),存进一张哈希风格的查找表。查的时候,拿当前已经生成的几个 token 当钥匙,看表里有没有匹配的后续片段——有,就拿它当草稿,一次提议后面 k=16 个 token。走一遍流程:模型写答案写到一半,末尾正好是"所以最终答案是",查表发现这个 prompt 的历史 rollout 里,这个前缀后面跟着"42,因为……"整整一串,就把这一整串端去验证。一次 forward,可能白捡十几个 token。
但真正决定成败的,是这张表怎么组织。这里有三个关键的设计决策。
第一,每个 prompt 一张表,不做全局共享表。 直觉上,全局表的信息量更大,什么题都能查,似乎更划算。但数据说相反的话:消融实验里,全局 cache 只能做到 2.41 tokens/forward,而 per-prompt cache 能做到 3.05。哪怕把全局 cache 的容量加大 4 倍,也只有 2.65。原因很简单——重复是"按题目"聚集的。同一道题的 rollout 互相之间高度重复,不同题之间几乎不重复。把张三的作业和李四的作业混在一个抽屉里,除了添乱没有任何好处。这个看似不起眼的 keying 方式,其实是整套方法的地基。
第二,表存在 CPU 内存里,不占 GPU。 草稿数据放在主机内存,随取随用,GPU 显存里该训练什么还训练什么。训练时显存那口春运火车,一个行李箱都不用加。
第三,表是活的,持续用最近 W 步的 rollout 更新。 这就是它的自愈机制:表里的内容全部来自策略自己的输出,策略往哪走,表就往哪填。不需要训练任何额外模型,不需要维护任何外部对齐流程。策略变了,旧 rollout 会被慢慢挤出窗口,新 rollout 进来接班——表永远跟着当前策略走。
窗口 W 取多大?实验给出的答案很干脆:W=4 就能捕获 79% 到 91% 的收益,基本吃满;W 超过 16 反而下降——太久远的 rollout 已经"过期",和当前策略对不上,查出来也是错的,白浪费验证算力。这完全符合直觉:誊作业也只需要上周的作业,上学期的笔记参考价值就有限了。
👨🏫 考官坐镇:为什么抄作业不算作弊
到这里,敏锐的人会有一个正当的怀疑:让模型参考自己过去的输出,会不会污染训练?比如,模型会不会越来越只会复读自己,多样性塌掉,最后学出一个只会原地踏步的策略?
NgramRL 的回答靠的是数学,不是祈祷。它用的是标准投机采样的校正公式:草稿提议的 token,以 min(1, π_θ(x|c)/q(x|c)) 的概率被接受,被拒绝后从残差分布重新采样。翻译成普通话:草稿只负责"提议",每个提议都必须经过当前策略的检验,检验的强度由两者的概率比决定。草稿和策略在这个位置越一致,接受越痛快;草稿越离谱,越容易被毙;毙掉之后,从策略自己认可的分布里补一个。整条管线的输出分布,和不用草稿、让策略逐 token 慢慢采样,在数学上严格一致。
这一步的巧妙之处在于,它把"抄"和"学"干净地切开了。草稿提供的是速度,策略提供的是裁决。模型读到的、用来算奖励和梯度的每一个 token,分布上都来自当前策略自己——训练动态不变,奖励不变,梯度不变,最终学到的模型也不变。纯加速,零副作用。
为什么说"零副作用"在 RL 场景里格外要紧?因为强化学习对分布敏感得多。推理加速跑完就完了,输出分布对不对,影响的是这一次回答的质量;训练加速是要在加速后的数据上继续学习的,如果加速偷偷改了分布,等于改动了学习的原材料——奖励统计会变、梯度方向会变、收敛轨迹会变,最后学出的模型和原方案不是同一个模型。这种污染是隐性的,不做严格的对照实验根本发现不了。NgramRL 把无损验证放在整个设计的中心位置,等于先把"不许改答案"写进校规,再去谈怎么提速。
回到考场比喻:作业摊在桌角,但老师的要求一条没少——每一步都必须独立验算。你誊上去的每一笔,都被你用另一种方式重新推导过一遍。交上去的卷子,和从零推导的卷子一字不差。速度是你省下来的,正确性一分没有含糊。这就是为什么"抄"得理直气壮:它抄的是提示,不是答案。
顺带一提,这套校正机制还是投机解码家族的老配方,NgramRL 的贡献不在验证算法本身,而在于发现"该拿什么当草稿"——这是两回事,也都是必要的事。
🚰 真正的瓶颈不在算法,在流水线
故事讲到这儿,理论上该结束了:有了 per-prompt 的查找表,有了无损验证,rollout 加速应该水到渠成。但把系统真正跑起来,作者撞上了一个所有纸面方案都会撞上的墙。
墙在 CPU 和 GPU 之间。
cache 在 CPU 内存里,而模型在 GPU 上。这意味着每解码一步,都要走一趟"GPU 把当前上下文发给 CPU → CPU 查表 → 查到的草稿传回 GPU"的往返。听起来只是几次内存访问,慢不到哪去?实测发现,GPU 竟然有约 29% 的时间在闲着。不是查找慢,是同步和调度的开销——GPU 干完手头的活,就得停下来等 CPU 把草稿递过来,递过来再开工。像一家咖啡店,咖啡机(GPU)每做一杯就要停下来,等磨豆机(CPU)慢吞吞磨一份豆子,而磨豆其实只花几秒钟,时间和火力全耗在"等"和"递"上。
NgramRL 的应对,是这篇文章在系统工程上最漂亮的一笔:Adaptive Fan-out Prefetcher,自适应扇出预取器。
思路一句话就能说完,但值得慢慢品:别等 GPU 要草稿了才去查,趁 GPU 正在验证上一批草稿的时候,就把下一批可能的草稿提前备好。
具体怎么做?GPU 每验证一步,结果无非是"接受了前 j 个 token,第 j+1 个位置草稿猜的是 b"——记作一对 (j, b)。当前这一步的验证结果还没出来,CPU 不知道 j 和 b 会是多少,但可以用一个因果滑动窗口统计历史的 P(j):过去这种局面下,接受了 j 个 token 的概率是多少;以及每个分支的覆盖率 profile c_j(f)。这两个统计一个管"j 大概会是几",一个管"j 定了之后,该给这个分支预取几个候选"。因果窗口的意思是只用过去的数据,绝不用未来,保证统计本身不引入偏差。
在这套统计的基础上,给预取预算 N=16 个候选名额,贪心地分配给期望覆盖率最大的那些 (j, b) 组合——把名额花在最可能出现的局面上。贪心的逻辑很直白:名额有限,先保大头。高频分支多分几个,长尾分支少分或不分,让每字节的预取都押在概率最高的赌注上。
还有一个漂亮的观察:当全部草稿 token 都被接受时,下一个位置的续写 token 是什么?它是被缓存轨迹里的下一个 token,而这个 token 有 90% 的概率恰好就是 top-1 候选。所以全接受分支只需要预取 f_j=1 个候选——一个名额就锁死了最大概率的分支。把预算花在这,几乎从不落空。
效果:Spider 任务上,预取覆盖率做到 96.6%(Qwen3-8B)和 99.5%(DeepSeek-7B)。端到端加速 1.38 倍,而这套机制的加速上限是 1.40 倍——达到了理论上限的 98.6%。剩下偶尔扑空的时候,系统回退到同步查找,已经提交的 token 一个都不动。磨豆机在咖啡机萃取的间隙就把下一批豆子磨好了,咖啡机几乎再也没有停下来等过。
📊 数字说话
把镜头拉到全景,看一组完整的数字。
生成吞吐。 warm cache 进入稳定期后,NgramRL 达到每轮 forward 3.60 到 7.77 个 token——也就是说,大模型每激活一遍参数,平均交出四到八个 token 的成品。对比之下,比 SuffixDecoding 最高多出 1.99 倍,比 prompt-lookup 最高多出 7.0 倍。投机解码里,每轮 forward 能产出超过一个 token,就是赚;能稳定产出近四个,是质的飞跃。
消融确认地基。 per-prompt keying 的价值前面说过:全局 2.41,per-prompt 3.05,全局加 4 倍容量也就 2.65,而 SuffixDecoding 是 2.43。一句话,照抄旧答案这件事,必须按题分本。
重尾分布藏着金矿。 平均数会骗人,分布的形状不会。Spider 上,14.1% 的步骤接受了 12 个及以上的 token,10.1% 的步骤 16 个 token 全部被接受——这是整段答案的逐字重放,一个字都不需要重算。其他任务上这个比例分别是:DeepSeek 在 GSM8K 上 13.0%,Qwen 在 GSM8K 上 6.4%,CodeContests 上 3.7%。少数步骤吃下超长整段,多数步骤吃下短片段,合起来就是那 3.60 到 7.77 的平均。
两个耐人寻味的动态。 其一,Spider 上接受率在中期达到峰值,之后下降——策略的输出逐渐多样化,旧 cache 开始过期,符合预期,也说明这张表是活的而不是一潭死水。其二,CodeContests 上出现了反转:训练早期全局 cache 反而领先,因为不同 prompt 之间共享着代码模板,跨题的重复在这个阶段占了上风;但随着积累加深,per-prompt 反超。重复的结构不是恒定的,而 NgramRL 的结构恰好跟对了重复最浓的地方。
实验配置。 以上所有数字来自这样的设置:2 张 A100 80GB,模型是 Qwen3-8B 和 DeepSeek-LLM-7B,任务是 GSM8K、Spider 1.0、CodeContests 三个推理任务,每步 64 个 prompt × 5 份 completions × 15 个 epoch。规模不算惊人,但足够干净,足以支撑结论。
🧠 更深一层:浪费考古学
这篇文章我把它归入一个我自己很在意的系列——AI 的浪费考古。之前我们聊过别处的浪费,这次轮到了训练时的浪费。
训练时的浪费有一种特殊的隐蔽性。推理时的浪费是看得见的:多算的 token 就是多花的钱,账单不会说谎。训练时的浪费却藏在"学习"这个名义之下——模型反复对同一批题写几乎相同的答案,每一次都从第一个 token 开始,郑重其事地逐字重新生成。没有人觉得这有什么不对,因为"采样"本来就是训练的组成部分。但 NgramRL 的统计数字(50%、81%、100%)等于把这笔账翻了出来:采样当然必要,可对同一份轨迹的重复计算,大部分根本不是采样,是重放。重放是不需要重新算的。
更深一层的观察是:NgramRL 实际上把模型自己的历史轨迹,变成了一种外化的工作记忆。策略走过的路被记在 CPU 的查找表里,反过来给策略当拐杖。学习的产物第一次反哺了学习的过程本身——不是通过蒸馏、不是通过回放经验池那种旧办法,而是直接在生成层面对齐。这种"自我对齐的草稿源"会不会还有别的用法?缓存轨迹里藏着策略的置信结构,那么它除了当草稿,能不能当探针、当诊断工具?论文没有回答,但这是这个工作留给后来者最有味道的问题。
还有一层工程上的启示,值得单独记住:这套方法的收益大头,最后是靠 prefetcher 这个纯系统组件吃到的。算法上"可以加速"和系统上"真的加速"之间,隔着一条 CPU-GPU 的传送带。AI 系统论文做到今天,胜负手常常不在模型里,而在这些被 29% 的空转暴露出来的缝隙里。
AI 学会抄自己的作业,抄得理直气壮,因为它每一页都验算过。真正被翻出来的旧账是:我们让 AI 用最贵的方式,做了太久最重复的劳动。而答案,一直就写在它自己的作业本上。
📚 参考文献
- NgramRL: Accelerating RL Post-Training for Structured Generation via Rollout-Aware N-gram Speculative Decoding
#论文 #arXiv #NgramRL #强化学习 #投机解码 #小凯