清华团队再探 OPD:数据撑死,算法饿死

本文由清华大学联合中国科学院大学、东北大学、伊利诺伊大学厄巴纳-香槟分校、约翰霍普金斯大学等机构研究者合作完成。

把 On-Policy Distillation(在线策略蒸馏,OPD)的训练集从一万七千道题砍到一道,会发生什么?

直觉上,这道题很快就会被学完,曲线跑上几十步也该平了。实际情况并不是这样:它连着涨了几百步仍未停下。数学三个基准的平均分从 59.1 起步,300 步时升至 68.5;而用全部一万七千道题训练的对照组,同一时刻是 69.8——一条数据已经拿到了全量收益的大部分。

本篇是 Rethinking On-Policy Distillation 系列的第二篇。第一篇问的是 OPD 为什么有效,结论是师生思维模式需要匹配,而分数更高不等于有新东西可教;这一篇把问题推得更极端:为什么只给一条样本,OPD 仍能在几百步的训练里稳定提升、接近全量数据的效果?

清华团队给出的答案很干脆,八个字:数据撑死,算法饿死。

🎯 核心发现:一道题的 rollout 就摸到全量状态空间的 71.5%,16 道语义不同的题到 98.9%

🤔 意外之喜:吸收率的下滑曲线,喂 1 道题和喂 1.7 万道题几乎重合

💡 关键启示:卡住 OPD 的不是数据不够,是算法消化得太慢

➤ 论文链接

🔗 https://huggingface.co/papers/2609.04172

➤ 代码库

🔗 https://github.com/Thinking-Space/One-Shot-OPD

背景:OPD 的算法被反复研究,数据侧却少有人量过

近一年,OPD 已经成了前沿模型后训练的常规组件:Student 自己采样,Teacher 在 Student 走过的每个 token 位置上给出完整的目标分布,把 on-policy 的状态访问与 dense 的 token 级监督合在一起。

围绕它的机制研究不少,但几乎都站在算法一侧——训练集被当作给定条件。相比之下,数据这一侧的做法通常是做加法:多收集、多清洗、多合成。

清华团队问的是另外半个问题:把训练集减到一道题,模型还能学到什么?

怎么做的:把训练集从一万七千道题砍到一道

在 RLVR 上,one-shot RLVR 已经用「训练集只留一道题」的方式做过受控实验,那也是本篇的直接启发。之所以值得换个对象再测一次,是因为两种方法的信号落点不同:RLVR 的信号落在轨迹末尾,OPD 的信号则分散在每一个被访问的位置上。

实验保持其余配置不变,仅将数学训练集由 DAPO-Math-17K 缩减为一道题,随后同时观察训练曲线与内部指标,并把结论延伸到其他任务域、其他师生家族以及多 Teacher 设置。

主结果:一道题,稳涨几百步

在数学任务上,Student 的初始平均分为 59.1。训练到 300 步,单题设置达到 68.5,全量一万七千道题的对照组为 69.8——单题拿到了全量 OPD 增益的 87%,师生差距也收掉了 69%。继续跑到 1000 步,单题设置为 68.4,全量对照组为 72.1,恢复比例回落到 72%。不过 300 步之后,两条曲线大致保持着约 3 分的差距。换句话说,一道题能拿到全量 OPD 的大部分增益,但多训练并不能抹平它与全量数据之间的差距。

这个现象在其他任务域和师生家族上同样成立:换到 coding、instruction following 和 agentic tool use,单题训练分别缩小了 73%、66% 和 64% 的初始师生性能差距;换到 Qwen、Llama、OLMo 三组师生模型,One-Shot OPD 也都拿到了可观的提升。

同时,它对训练题本身的性质几乎不敏感。三道难度不同的数学题给出了相近的训练增益,其中最难的那道在 300 步里一次都没被 Student 解出,模型的验证表现照样提高;调整回答长度上限和采样温度,结论也不变。

两道问题由此留下来:只有一条训练输入,模型为什么还能拿到这么多增益?这些增益又为什么需要几百步才慢慢形成?

数据侧:一道题的 rollout 摸到七成状态空间

回答第一个问题,要先看 OPD 的学习单位。它不是题目,而是状态:一道 query 加上 Student 已经写出的半截回答,构成它下一步要面对的局面,每个局面都配有 Teacher 给出的目标分布。

于是,一道题并不只提供一份监督。默认设置下每次更新包含 64 条 rollout,即使题目只有一道,一次更新也会产生数万个带监督的 token 位置。题目数量记录的是输入有多少,却说明不了这些输入把模型带到了哪里。

为了量化状态覆盖的广度,研究者提出了「状态覆盖率」(state coverage)这一指标:把全量 OPD 访问过的状态汇成参考池、切成 200 个簇,一个设置访问到的簇数占这 200 个参考簇的比例,就是它的覆盖率。

结果,一道题就触及了全量状态空间的大半,而且大部分覆盖发生在最初几十步之内。 100 步时覆盖率 65.9%,300 步时 71.5%,之后的 200 步只再增加 5.6 个百分点。新状态仍在产生——重复采样同一道题能触及更广的状态空间——但新增速度明显放缓。

覆盖率与验证表现同时上升,只能说明二者相关。研究者又分别从 rollout 轨迹侧和 query 题目侧做了控制实验。

轨迹侧,让初始 Student 对同一道题生成 64 条固定轨迹,再分别保留 1 条、4 条、16 条或全部 64 条,重复填满同样大小的训练 batch:保留得越多,验证准确率越高,从 0.606 一路升到 0.666。

题目侧,从不同语义簇里各取一道代表性题目,凑出 1 道、4 道和 16 道题的训练集:状态覆盖率依次为 71.5%、79.8% 和 98.9%,在相同训练预算下,16 道题的结果已经和全量数据基本持平。

题目数量一致,多样性也可能天差地别。研究者比较了两组各含 16 道题的训练集:一组分别取自 16 个语义簇,另一组全部来自同一个语义簇。前者在覆盖率和验证表现上都更高;后者的覆盖率在训练前 100 步左右摸到约 76.8%,随后基本不再增长。

这些结果指向同一个解释:在 OPD 里,一道题的价值很大程度上取决于它能不能把 Student 带到此前没访问过的状态。单题输入虽少,反复 rollout 产生的状态却不少;再补上语义分散的题目,覆盖还能继续扩大。在本文的数学实验中,把整套 DAPO-Math-17K 都投进去训练,并没有比这 16 道语义多样化的题带来更多可见收益。相对于当下训练能利用的监督量,数据已经撑死。

算法侧:喂多少题,对吸收率几乎没有影响

覆盖率回答的是「有多少监督可以用」,回答不了「Student 用得多快」。为此论文定义了两个训练动态指标,一个描述吃到哪了,一个描述吃得多快:距离 d 是 Teacher 与 Student 在 Student 当前访问位置上、对实际生成 token 所给 log 概率之差的绝对值均值;吸收率 v 是一次更新消除了剩余距离的多大比例。

第一个观察:Student 并没有停下来,只是每步的进展越来越小。距离在整个 run 里持续下降,真正下滑的是吸收率——这也是一个 run 需要几百步、而不是几十步的原因。

第二个观察才是重点:这条下滑曲线几乎不随训练集大小改变。1 题、4 题、16 题、全量一万七千题四个 run,到 300 步各自消除了自身距离的 78% 到 84%。换句话说,加题不会让对齐更快,减题也不会让它更慢:对齐的节奏更多由 OPD 这个算法本身决定,而不是由题目数量决定。

另一种解释是:on-policy 训练一直在产生新状态,模型得不断适应变化的数据,run 才被拉长。研究者据此加了一组固定轨迹对照——始终复用初始 Student 生成的 64 条 rollout,不再从当前模型采样新状态。结果,即使训练状态完全固定,验证表现仍在约 200 步内持续提高。持续供给新状态并不是长训练曲线的唯一原因;Student 吸收已有状态上的监督,本身就需要多次更新。

学习率会不会是原因?研究者在四倍范围内试了 5×10⁻⁷、1×10⁻⁶ 和 2×10⁻⁶ 三个学习率:提高学习率能让模型用更少的步数走完相近的对齐过程,但把横轴按学习率重新缩放之后,三条距离曲线基本重合。在测试范围内,学习率改变的是时间尺度,不是吸收速度下降的整体形态。

两侧合起来:数据侧解释了单题为什么能拿到大笔增益——一道题反复 rollout 后已经能覆盖足够广的状态;算法侧解释了训练为什么仍要跑几百步——随着对齐推进,每次更新能吸收的剩余监督越来越少,而增加题目数量并不能明显改变这一点。它们共同构成了论文所说的「数据撑死,算法饿死」。

扩展到多 Teacher OPD:每个域十几道题打平全量

实际流程中更常见的是 Multi-teacher OPD(MOPD):多个领域放进同一次训练,由各自的 Teacher 提供监督,每道题路由到它所属领域的 Teacher。研究者做了同样的阶梯,结论依旧——在这三个域、每域一个 Teacher 的设置下,每个领域只要十几道语义互不相同的题,就能打平全量 MOPD。

Student 初始平均验证分数 43.5。300 步时,每域 1 道题为 50.1,每域 16 道语义不同的题为 52.9,全量 MOPD 为 52.8,三个 Teacher 的平均结果是 55.4。Teacher 数量继续增加后这一结论能延伸多远,本文没有测试。

极端实验:连「不成题的题」也能训

如果「状态才是学习单位」这个解释成立,那么可以推出一个有点激进的预测:输入里的任务内容未必必需。研究者设计了三组压力测试:空 template(用户轮为空,末尾只留一个

)、System Template(仅加一行领域指令)、以及 WildChat Prompt(192,824 条真实用户闲聊,保守判定下与数学相关的只有 0.17%)。

三种条件都跟真实数据的 baseline 咬得很紧,同时只消耗了后者三分之一到一半的 rollout token。 代码域上,500 步里真实数据消耗 277M rollout token,空模板只消耗 18M。

不过,也并非随便一段文本都能当输入。如果模板一开头就把

区块闭合,Student 会生成过短的元层面回复,形不成可用的训练轨迹。

所以更准确的说法是:任务内容并不是 OPD 输入产生有效监督的唯一来源,输入还决定了 Student 从哪里开始写、rollout 能不能走进 Teacher 有东西可教的状态。评价一条输入时,除了看它提出了什么任务,也要看它能引出什么样的状态。

最后,研究者把 OPD 和 RLVR 放在同一道题上做了受控对比:跑满 1000 步,OPD 的验证集涨幅是 RLVR 的两倍以上,同时缩小了自身与 Teacher 差距的 72%。不过两者的天花板指向不同方向——RLVR 不受任何 Teacher 分布约束,OPD 则以匹配某个分布为目标。这不是「OPD 比 RLVR 好」,而是两者各自被不同的东西卡住:一个卡在数据能提供多少结果差异,一个卡在自己吸收密集监督的速度。

写在最后:关于「喂多少」与「消化多少」

这些结果并不是说训练数据不重要。它们说明的是,在 OPD 里,题目数量本身未必是衡量有效监督量的合适尺度:一道题通过持续 rollout 就能产生大量不同状态,输入题目极少时,学生仍然拿得到足够丰富的训练信号。比题目数量更值得关注的,是这些题最终把模型带到了哪些状态,以及这些状态覆盖了多大的有效空间。

这也给数据筛选换了个角度:在 OPD 里评价一道训练题,该问的可能不是它有多难、多干净,而是它能不能把 Student 带到还没到过的地方、那些地方 Teacher 手里是否还有东西可教、以及在 Student 当前的吸收速度下这些监督几百步里是否消化得完。

把 Rethinking OPD 系列的两篇放在一起看,指向的是同一件事:OPD 里密集的奖励并不是免费的午餐,真正稀缺的往往不是供给。第一篇指出,Teacher 的分数更高并不意味着它更会教;这一篇则指出,数据喂得更多也并不意味着学生此刻就吃得下。

边界也要讲清楚:状态覆盖率是一个语义层面的代理指标,参考空间由一次全量 run 构建,因此它衡量的是一个设置触及了参考空间的多大比例,而不是它自身的覆盖范围;它对所有簇等权计数,既不区分访问频次,也不衡量每个簇里还剩多少师生差异。吸收率为什么随训练下降,目前也没有定论。这两点都写进了论文的 Limitations。

对正在做 OPD 的人来说,这条结论有个很便宜的用法:把题目集换成十几道语义上彼此不像的题,再跟自己的全量 run 摆在一起跑几百步。砍完还能打平,说明眼下卡住的确实不是题目数量;砍完掉了一截,说明这批题还没把状态覆盖住。两种结果都是净收获。

「贪多嚼不烂」这句老话,放在模型身上同样说得通。

更多细节请参阅原论文与开源仓库。