跳转到正文

15.7 在线策略蒸馏 OPD

上一节讨论了 RLVR 如何用规则验证器替代 RM,在数学、代码等有客观答案的领域给出精确的奖励信号。本节看另一条路线:不让小模型从零探索,而是让强模型在它自己生成的轨迹上逐 token 给指导。这条路线叫在线策略蒸馏(On-Policy Distillation, OPD)。

核心流程只有三步:student 先自己对 prompt 生成回答(即"策略");teacher 不重写答案,而是看 student 写出的每一步,判断每个 token 合不合理;这个判断作为密集的 token 级信号回传给 student,指导它调整。这里的关键术语对应:student 模型就是 RL 中的策略(policy),每个被选出的 token 是动作(action),teacher 对该 token 的 log-prob 就是反馈信号

OPD 和前面方法的核心区别在于两个维度。一是谁生成训练轨迹:SFT / SeqKD 用 teacher 写的答案,GRPO 用 student 自己的探索,OPD 也用 student 自己的轨迹。二是反馈有多密:GRPO / RLVR 通常是结果级奖励——答案对了给 1 分、错了给 0 分,一个 2000 token 的解题过程只有最后那个 0 或 1;OPD 则几乎每个 token 都有信号,因为 teacher 对每个 token 都能给出 log-prob。

方法谁生成训练轨迹反馈来自哪里反馈粒度主要问题
SFT / SeqKD人类或 teacher标准答案 tokentoken-levelstudent 不练自己的错误
PPO / GRPOstudentRM 或规则验证器多为 sequence-level奖励稀疏,采样昂贵
DPO离线偏好数据chosen / rejected 对sequence-pair-level不能在线探索
OPDstudentteacher log-probtoken-level需要好 teacher 和好初始化

一句话概括:OPD 同时拿到 RL 的 on-policy 分布和蒸馏的密集监督。

OPD 的核心思想

Teacher 在 OPD 中的角色

所有蒸馏方法里都有 teacher,区别在于 teacher 在训练中扮演什么角色。

teacher 的角色训练轨迹来自谁student 学到什么局限
SFT / SeqKD答案作者:生成完整答案teacher老师怎么写只在 teacher 上下文里学,不练自己的错误
Off-policy 蒸馏专家示范者:展示完整解题轨迹teacher老师怎么想轨迹仍是 teacher 走到的,student 自己卡住的地方没有信号
OPD在线评价器:看 student 前缀,逐 token 判断是否合理student在自己走错的路上怎么纠偏需要好的 teacher 和好的初始化

从上到下,teacher 从数据生产者变成奖励提供者,student 从模仿 teacher 轨迹变成在自己的轨迹上被纠偏。OPD 的动机正是消除分布偏移:student 推理时看到的是自己生成的上下文,训练时直接在这些区域给信号,而不是只在 teacher 走过的路上铺数据。

OPD 到底在优化什么

OPD 的训练循环很短:

  1. 给定 prompt ,student 自己采样回答
  2. 对 student 生成的每个前缀 ,teacher 计算当前 token 的概率。
  3. 用 teacher 对 student 自己 token 的评价更新 student。

Google DeepMind 的 GKD 是这条线的代表性起点:它不再只依赖固定 teacher 输出,而是让 student 在自生成序列上接受 teacher 反馈,并允许 forward KL、reverse KL、JSD 等不同散度。[1]

如果采用最常见的 reverse KL 形式,目标可以写成:

这就是在 student 自己访问到的状态上最小化:

展开后,每个 token 都有一个很自然的训练信号:

teacher 比 student 更认可这个 token, 就高;teacher 觉得这个 token 不像好策略, 就低。Thinking Machines Lab 的实现里,几乎就是把 RL 训练脚本里的 KL regularizer model 换成 teacher:采 student rollout,算 student log-prob,再让 teacher 对同一条轨迹算 log-prob,最后把负 reverse KL 当作 per-token advantage。[2]

所以 OPD 和 RL 的对应关系非常直接:

RL 概念OPD 中的对应物
状态 prompt + 已生成前缀
动作 下一个 token
策略student
奖励teacher 给 student token 的相对认可度
采样分布student 自己的分布
优势估计常用

这里要注意一个细节:teacher 不是环境真理,它只是一个强策略。OPD 不是让模型"发现超过 teacher 的新策略",而是把 teacher 在 student 自己状态上的行为压进 student。它更像有过程监督的模仿学习,而不是纯探索型 RL。

术语辨析:distillation, off-policy, on-policy

先把 distillationoff-policy / offlineon-policy 三个概念分清。它们经常被混在一起,但指的是不同层面的事情。

蒸馏(distillation) 描述 teacher-student 关系:一个强模型 把能力迁移给一个较小或较便宜的模型 。最朴素的蒸馏是让 teacher 生成答案,student 对这些答案做交叉熵训练:

这里 student 看到的是 teacher 选出来的 token 。如果你能拿到 teacher 的完整概率分布,还可以做"软蒸馏":不只告诉 student 正确 token 是什么,还告诉它其他 token 有多合理。

软蒸馏的信息量更大。比如 teacher 可能认为 "therefore" 很好,"so" 也可以,"banana" 完全不行。硬标签只告诉你 teacher 最后选了哪个词,软标签告诉你 teacher 对整个动作空间的判断。

policy(策略) 在 LLM 里就是"给定上下文,输出下一个 token 分布"的模型:

这里的状态 是 prompt 加已生成前缀,动作 是下一个 token。谁生成轨迹,谁就是行为策略(behavior policy)。这件事决定了训练数据的分布。

Off-policy 指的是:训练数据不是当前 student 自己生成的,而是来自另一个策略 。这个 可以是 teacher、旧 checkpoint、用户日志、历史模型,也可以是一批固定数据集。普通 teacher 蒸馏就是典型 off-policy:

数据来自 teacher ,更新的是 student 。好处是便宜、可复用、稳定;坏处是 student 没有在自己的错误上下文里训练。

Offline 比 off-policy 更严格:不只是数据来自别的策略,而且训练时不再新采样,只使用一份固定数据集。SFT、DPO、离线偏好训练通常都是 offline。可以这样记:

概念数据从哪里来训练时还采新数据吗例子
offline固定历史数据集不采SFT、DPO、离线 SeqKD
off-policy不是当前 student 的策略可以采,也可以不采teacher 轨迹、旧模型 replay
on-policy当前 student 自己生成要采PPO、GRPO、OPD rollout

所以,offline 在 LLM 后训练里通常也是 off-policy,但 off-policy 不一定 offline。如果你每一轮都让 teacher 重新生成数据,再训练 student,它不是 offline,但仍然是 off-policy,因为行为策略还是 teacher,不是 student。

On-policy 指的是:用当前 student 自己生成的数据来更新当前 student。形式上是:

它的优势是训练分布和推理分布一致。student 推理时会走到什么上下文,训练时就真的让它走过去,然后在那里给反馈。代价也很明显:每轮更新前都要重新 rollout,旧数据很快过期,样本效率低。

把这几个概念合起来看,OPD 的位置就很清楚了:

  • 它是 distillation,因为反馈来自 teacher。
  • 它是 on-policy,因为轨迹来自 student 自己。
  • 它通常不是纯 offline,因为训练中要不断重新采 student rollout。
  • 它和普通 off-policy 蒸馏的区别,不在于有没有 teacher,而在于 teacher 是在谁的轨迹上给信号。

为什么普通蒸馏不够

知识蒸馏(Knowledge Distillation, KD)的老问题是:大模型好用但贵,小模型便宜但弱。做法也很直接:让 teacher 生成数据,student 在这些数据上做监督学习。LLM 时代的 KD 综述通常把它分成几类:白盒蒸馏看 teacher logits,黑盒蒸馏只看 teacher 输出;也可以按能力分成推理、对齐、领域知识、工具使用等技能蒸馏。[3][4]

这条路非常有用。DeepSeek-R1 的蒸馏模型就是典型例子:先让强推理模型生成高质量轨迹,再把这些轨迹 SFT 到小模型上。对小模型来说,这往往比直接做 RL 更稳。

但它有一个根本缺口:训练时 student 看到的是 teacher 的状态分布,推理时 student 走的是自己的状态分布。

设 prompt 是 ,teacher 轨迹是:

普通蒸馏训练的是:

这里的上下文 来自 teacher。可一旦 student 在第 3 步写错了,后面的上下文就变成了 。这个状态 teacher 可能从来不会走到,SFT 数据里也没有"从这里怎么救回来"的示范。错误会沿着自回归生成不断放大,这就是 exposure bias,也可以理解成模仿学习里的分布偏移。DAgger 早就指出过:要缓解这种问题,必须把 learner 自己访问到的状态也纳入训练。[5]

OPD 就是把这个思想搬到 LLM 蒸馏里。

Online OPD vs Offline OPD

既然 OPD 是 on-policy,Lightning OPD 为什么又叫 offline on-policy distillation?两者并不矛盾。关键在于区分"轨迹是谁生成的"和"teacher 什么时候打分"。

标准 OPD 是 online 的。 每一轮训练都用当前 student 生成新回答,teacher 现场计算每个 token 的 log-prob,然后更新 student。下一轮 student 参数变了,重新生成、打分、更新。

text
当前 student 生成回答
→ teacher 现场打分
→ 更新 student
→ 新 student 再生成回答
→ teacher 再现场打分
→ ...

这条路线最忠实于 on-policy 定义,但成本高:teacher 往往比 student 大很多,标准 OPD 需要一个 live teacher server 在训练期间持续运行。Lightning OPD 论文把这个称为标准 OPD 的基础设施瓶颈:teacher 要在每个梯度步上给新 rollout 计算 log-prob。[6]

Lightning OPD 是 offline 近似。 它先做一个 SFT student,然后用这个 SFT student 生成一批固定回答;teacher 只对这批回答打分一次,把每个 token 的 log-prob 存下来。真正训练 OPD 时,不再启动 teacher server,而是直接从缓存里读取 teacher 分数,同时在线计算当前 student 自己的 log-prob。

text
预处理阶段:
SFT student 生成回答
→ teacher 打分一次
→ 存下 token 和 teacher log-prob

训练阶段:
读取固定回答和 teacher log-prob
→ 计算当前 student log-prob
→ 更新 student

Lightning OPD 仍然属于 on-policy,因为这些固定回答不是 teacher 写的,而是 student 系列模型生成的。teacher 仍然是在 student 轨迹上给反馈,保留了 OPD 最关键的东西:teacher 批改的是 student 会写出来的内容

但 Lightning OPD 不再是严格的 online OPD,因为训练过程中 student 参数变了,rollout 却不刷新。它成立依赖一个经验观察:OPD / RL 后训练里的 student 通常不会离 SFT 初始化太远,所以用 SFT student 的 rollout 可以近似后续 student 的 rollout。论文还强调一个条件:teacher consistency——生成 SFT 数据的 teacher 和 OPD 打分的 teacher 最好是同一个,否则缓存轨迹和后续打分会产生系统性偏差。[6:1]

可以这样选:

方案优点代价适合什么时候
标准 online OPD最贴近定义,训练分布最新要 live teacher,成本高研究机制、追求最稳、资源充足
Lightning / offline OPD便宜,容易复现,不需要 teacher serverrollout 固定,依赖 teacher consistency快速验证、资源有限、student 漂移较小
普通 offline KD最简单,直接 SFT只学 teacher 轨迹冷启动、先把 student 拉到合理区域

所以更准确的说法是:标准 OPD 是 online on-policy distillation;Lightning OPD 是把标准 OPD 离线化的一种工程近似;普通 teacher SFT 是 off-policy / offline distillation。

为什么是 reverse KL

蒸馏里最容易混淆的是 KL 方向。

经典 KD 常用 forward KL:

它要求 student 覆盖 teacher 的概率质量。对分类任务这很自然:teacher 说猫 0.7、狗 0.2、狐狸 0.1,student 也应该学这个软标签。但对长文本生成来说,覆盖所有 teacher 可能说的话会让 student 概率分布变平滑:许多低概率、边缘的 token 也被抬高,生成会发散。

MiniLLM 的核心判断是:生成式 LLM 蒸馏更适合 reverse KL:

reverse KL 是 mode-seeking:它鼓励 student 集中到 teacher 高概率的少数模式上,而不是覆盖 teacher 的所有可能模式。MiniLLM 还把这个目标落成 on-policy 优化,从而减少长文本生成里的 exposure bias。[7]

但 reverse KL 有一个现实限制:它只能在 student 当前会采到的区域里修正策略。 如果某个关键 token 在 student 初始化里概率几乎为 0,student 根本采不到它,teacher 再懂也没机会给它高分。这就是为什么很多实用 recipe 都不是"直接 OPD",而是:

  1. 先用 off-policy SFT / SeqKD 做冷启动,把 student 拉到 teacher 支持集附近。
  2. 再用 OPD 在 student 自己轨迹上做精修。

Thinking Machines Lab 的复现实验也是这样:先做 off-policy reasoning distillation,再用 OPD 做后训练提升。2026 年的 OPD 机制分析把这件事讲得更彻底:OPD 成功不只取决于 teacher 分数,还取决于 teacher 和 student 在 student 当前状态附近能不能形成可优化的局部重叠。[8]

还有一条工程化路线是调散度本身。DistiLLM 使用 skew KL 和 adaptive off-policy 策略,目标是在 teacher 信号和 student 可学习性之间找更平滑的折中。[9]

为什么强 teacher 也会翻车

直觉上,做 OPD 就是"找一个更强的 teacher,把它的能力压进 student"。但 OPD 真正迁移的不是排行榜分数,而是 teacher 在 student 自己前缀上的局部偏好场

具体来说,teacher 只有在 student 真实走到的状态 上,能把 student 已经认真考虑的候选 token 重新排序,这个信号才会变成有用梯度。如果 teacher 很强,但它的高概率 token 和 student 的高概率 token 基本不重叠,reverse KL 看到的是两个互相错开的支持集:student 采不到 teacher 想要的关键 token,teacher 也只能对 student 已经写出的奇怪 token 给低分。训练变成每一步都被批评,但没有可吸收的方向。

这就是 "thinking-pattern consistency" 的具体含义:teacher 和 student 是否在用相近的中间语言思考同一道题。一个数学 teacher 可能习惯先写完整推导,一个 student 可能习惯先猜公式;一个 reasoning teacher 可能会显式拆分子问题,而一个 base student 只会接着 prompt 补一句短答案。它们最终都可能给出正确答案,但中间路径不一样,逐 token 蒸馏时就会互相冲突。

2026 年的机制分析论文把 OPD 从"能力迁移"重新定义为"局部可教性"问题。[8:1] teacher 分数高,只说明它自己会做题;teacher 可教,才说明它能在 student 当前的位置给出能被吸收的方向。在 SFT 里这两件事经常被混在一起,因为 SFT 直接把 student 拉到 teacher 轨迹上;但在 OPD 里,student 先走自己的路,teacher 只能在 student 走到的地方说话。于是核心问题从"teacher 有多强"变成了"teacher 的偏好能不能在 student 当前分布上形成梯度"。

高分不等于新知识。 如果 teacher 只是同一条训练 pipeline 里更大的 sibling,它可能只是把同一种数据和 recipe 吃得更充分,对 student 来说局部偏好并不提供新的方向,只是在已有分布上更自信。经过额外 RL post-training、数据扩展或任务专门训练的 teacher,哪怕参数不夸张,也可能带来 student 训练中没见过的决策边界。

OPD 不是把 teacher 的答案压缩进 student,而是把 teacher 的"局部取舍方式"压缩进 student。 一个只是"更大但同质"的 teacher,给 student 的可能是更强的确认偏差;经历过新的 RL、看过新的失败样本、形成了新的推理习惯的 teacher,才更可能提供新的可迁移结构。

这也解释了为什么 OPD 比普通蒸馏更"挑老师"。off-policy SFT 可以强行把 student 拉去看 teacher 轨迹;OPD 则是在 student 自己的地形上做局部导航——如果当前位置附近没有通向 teacher 模式的梯度,再大的 teacher 也只是站在远处报答案。

多教师 OPD:工业界如何把专项模型合回一个模型

先看一个具体的后训练任务。一个团队希望同时提升数学、代码和指令遵循能力。数学适合使用可验证答案的 RL,代码需要可执行环境,指令遵循又依赖另一套奖励。把三种任务塞进同一段 Mix-RL 训练,会让数据比例、奖励尺度和超参数彼此牵制;串行训练则会让后面的领域损害先前学到的能力。

一种可执行的做法是先训练一个覆盖所有目标能力的 SFT 检查点,再从这个检查点分出三条独立的 RL 分支。每条分支只需要把一项专项能力训好,因此三位 teacher 可以并行开发。某个领域调整奖励或重启训练时,其他分支不受影响。MiniCPM5-1B 的公开配方和 MiMo 团队的 MOPD 都采用了这种“共享 SFT、分领域 RL、最后整合”的结构。[10][11]

专项 teacher 训好后,student 回到共享 SFT 检查点开始训练。它在多领域 prompt 上生成自己的回答,数学轨迹送给数学 teacher,代码轨迹送给代码 teacher。被选中的 teacher 对整条 student 轨迹做 prefill,student 再根据逐 token reverse KL 更新。Teacher 只在训练时提供信号,产品上线时仍然只部署一个 student。所谓多教师 OPD,就是用这种方式在策略空间里完成能力整合

这里的共享起点还有第二层作用:它让 teacher 和 student 保持相近的生成分布。在 MOPD 的受控实验中,同源数学 teacher 与 student 的初始逐 token KL 约为 0.04。换成数学能力更强、但分布不同的 Qwen3-235B-A22B 后,初始 KL 上升到约 0.19,策略梯度版的归一化分数从 0.9373 降到 0.6003,top- 版在约第 18 步崩溃。Teacher 选择因此要同时考察任务能力和局部可教性,初始师生 KL 是一个直接的诊断量。

路由正确以后,还有一个更隐蔽的问题。Open-MOPD 的受控实验中,数学、代码和指令遵循 prompt 分别占 39.8%、39.8% 和 20.3%;数学与代码回答平均约有 10,500 个 token,指令遵循回答平均只有 409 个 token。换成真正参与梯度计算的回答 token 后,三个领域的份额变成 49.7%、49.3% 和 0.99%。指令遵循占了约五分之一的 prompt,却只得到约百分之一的 token 级训练预算。[12]

这就是多教师 OPD 中的能力失衡。每条样本都路由给了正确的 teacher,多个领域仍然在争用同一个 student 的参数和有限更新次数。长回答天然产生更多 token,于是数学和代码会主导梯度;短回答对应的能力很早就停止增长。

Teacher 冲突并没有解释这个现象。实验中,teacher 分歧的均值只有 0.126 nat,超过 1 nat 的 token 占 0.62%。屏蔽高分歧 token 或改用 teacher 共识信号,总分下降了 0.52 到 0.83 分。主要瓶颈出在每个领域实际获得的优化预算。

真正的优化预算失衡来自三个环节:

环节失衡怎样产生整合进训练流程的处理
回答长度长回答贡献更多梯度 token,短任务获得的更新很少按领域重加权,使每个领域先获得相同的 token 预算
收敛速度各领域的师生差距缩小速度不同,固定权重会继续训练已经接近 teacher 的领域根据各领域平均绝对 token 奖励估计剩余差距,把更多预算给尚未收敛的领域
多步更新一批 rollout 被拆成多次 PPO 更新后,student 已经变化,采样时缓存的 student-dependent 奖励逐渐过时每次内部更新前重新计算当前 student 对应的 token 奖励

第二项处理需要再解释一步。若某个领域当前的 teacher 与 student 已经很接近,它的平均绝对 token 奖励会变小;另一个领域仍有较大差距时,奖励幅度会更大。用这个量动态调整领域权重,训练预算就会跟随尚未迁移完的能力,而非静态数据比例。

第三项处理针对 OPD 奖励里的 student 项。teacher 的 log-prob 可以缓存,因为 teacher 保持冻结;student 每完成一次参数更新,当前 log-prob 就会改变。继续使用 rollout 时计算的旧奖励,会让后续 PPO 更新依据过时的师生差异。每次内部更新前都要刷新 student-dependent reward,使密集反馈重新对应当前策略。

把这些环节放到一起,多教师 OPD 的完整因果链就清楚了:共享 SFT 为各领域提供可比的起点,独立 RL 分支负责产生专项能力,领域路由决定每条轨迹由谁教,逐 token 损失决定每一步怎样学,预算平衡决定各种能力最终能学到多少。

两组实验分别验证了这条链的不同部分。在 Qwen3-30B-A3B 上,MOPD 的平均归一化分数为 0.9373,高于 Mix-RL 的 0.8818,三个领域分别恢复了约 91%–95% 的师生差距;同一流程也用于整合 MiMo-V2-Flash 的数学、代码、指令遵循、软件工程和工具使用能力。在另一组开源实验中,动态预算平衡把共享 student 与分领域 RouteOPD 之间的能力整合差距从 3.50 分缩小到 0.31 分,并把领域 RL 能力的恢复比例从 35.6% 提高到 83.4%。[11:1][12:1]

Overlap token 才是主战场

论文里最有启发的实验是把 token 集合拆开:只在 student 和 teacher 都认为高概率的 overlap tokens 上优化,效果几乎不掉;只看 non-overlap tokens,几乎帮不上忙。这解释了为什么 top- OPD 往往已经够用,也解释了为什么失败 run 的 loss 看起来还在动,能力却没有涨。

语言模型的下一个 token 分布非常尖——大多数概率质量集中在少数候选上。如果这些候选 token 师生共享,teacher 的 log-prob 就像一个细粒度排序器,告诉 student "你已经在正确候选集里了,把权重挪到更好的选项上"。如果候选集不共享,teacher 的反馈主要是在否定 student 采到的 token,却没有把 student 带进新的高概率区域。

密集监督不等于每个 token 都同样有用。 真正有用的是那些 student 已经能想到、teacher 又能进一步排序的候选。overlap token 像一座桥:一头接 student 当前能力,另一头接 teacher 的更好偏好。non-overlap token 更像远处的灯塔——能说明方向错了,但不告诉 student 下一步怎么走。

论文还观察到一个反直觉现象:失败 teacher 的全局 reward 也能区分正确/错误 rollout,但这个信息没有形成局部可利用的优化几何。全局会打分,不等于局部能教学。 这解释了为什么有些 OPD run 看起来 reward 没坏、teacher 也不弱,最后却学不动。

长链条里的免费午餐会变质

OPD 的诱人之处是每个 token 都有 reward,但长思维链会暴露一个弱点:越往后,student 前缀越可能偏离 teacher 熟悉的分布,teacher 在这些陌生前缀上给出的 log-prob 越像噪声。论文在长响应里观察到一种从后向前扩散的熵坍塌:suffix 先变得高熵和不稳定,然后这种不稳定逐步传回更早位置。

这说明 OPD 的 scaling bottleneck 不只是 teacher forward 太贵,而是 密集监督的可靠性随轨迹深度下降。在短数学题或格式化回答里,teacher 批改 student 前缀通常可靠;在 15K token 的长推理、工具调用、多轮 agent 轨迹里,teacher 很可能已经不在自己训练时熟悉的状态分布上。更稳的做法是分段蒸馏、混入序列级验证器、限制每段 horizon,或只在高置信 overlap 区域施加 token-level loss。

动手:OPD 打分最小实现

前面把 OPD 的机制和坑理清了。现在用代码实现 OPD 最核心的一步:student 生成回答,teacher 在 student 的轨迹上逐 token 打分。

先理解 logits、probability 和 logps

模型预测下一个 token 时,先输出 logits,再转成概率和 log-prob:

候选 token45\n
logits2.01.00.0
softmax 后的概率0.670.240.09
log probability-0.41-1.41-2.41

logps 是 log probabilities 的缩写,即“一组 token 的对数概率”。数值越接近 0,模型越认可该 token;teacher logp 减 student logp 为正,表示 teacher 更认可它。

full_ids 是什么

tokenizer 把文字切成 token,并给每个 token 一个数字编号。full_ids 保存“题目 + 模型回答”的全部编号。

文字片段2+2答案4
token ID18429276
来源题目题目题目模型回答模型回答

因此,full_ids = [[18, 42, 9, 27, 6]]。前三个 token 是题目,所以 prompt_len = 3。表中的 ID 只是演示数字。

python
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer

student_name = "Qwen/Qwen2.5-0.5B-Instruct"
teacher_name = "Qwen/Qwen2.5-1.5B-Instruct"

tokenizer = AutoTokenizer.from_pretrained(student_name)
student = AutoModelForCausalLM.from_pretrained(
    student_name, torch_dtype=torch.bfloat16, device_map="auto"
)
teacher = AutoModelForCausalLM.from_pretrained(
    teacher_name, torch_dtype=torch.bfloat16, device_map="auto"
)
student.eval()
teacher.eval()

prompt = "Solve: if x + 3 = 7, what is x? Show your work."
inputs = tokenizer(prompt, return_tensors="pt").to(student.device)
prompt_len = inputs["input_ids"].shape[1]

# Step 1: student 生成回答
with torch.no_grad():
    output_ids = student.generate(
        **inputs,
        max_new_tokens=64,
        do_sample=True,
        temperature=0.7,
    )

# full_ids 包含“prompt + student 生成的回答”的全部 token ID
full_ids = output_ids

# Step 2: 计算 student 和 teacher 的逐 token log-prob
def next_token_logps(model, input_ids):
    """计算模型对给定序列中每个 token 的 log probability。

    logps[:, i] 是位置 i 的 logits 预测的 token i+1 的 log-prob。
    """
    logits = model(input_ids).logits

    # log_softmax 一步完成 softmax 和取对数
    logps = torch.log_softmax(logits[:, :-1], dim=-1)

    # gather 只取实际出现的下一个 token;生成 4 时,只取 4 的 log-prob
    next_ids = input_ids[:, 1:]

    # 输出形状:[batch_size, sequence_length - 1]
    return logps.gather(-1, next_ids.unsqueeze(-1)).squeeze(-1)

with torch.no_grad():
    student_logps = next_token_logps(student, full_ids)
    teacher_logps = next_token_logps(teacher, full_ids.to(teacher.device)).to(student_logps.device)

# Step 3: 计算逐 token reward(teacher 认可度 - student 自信度)
gen_mask = torch.zeros_like(student_logps, dtype=torch.bool)
gen_mask[:, prompt_len - 1 :] = True  # 只看生成部分

# 正值:teacher 更认可;负值:teacher 更不认可
token_rewards = teacher_logps - student_logps
generated_ids = full_ids[:, 1:][gen_mask]
generated_rewards = token_rewards[gen_mask]

for tok_id, reward in zip(generated_ids[:32], generated_rewards[:32]):
    token = tokenizer.decode([tok_id.item()])
    print(f"{token!r:12s} reward={reward.item():+.3f}")

训练循环的伪代码:

python
for prompts in dataloader:
    # Step 1: student rollout
    trajectories = student.rollout(prompts)
    student_logps = student.logprobs(trajectories)
    teacher_logps = teacher.logprobs(trajectories)

    # Step 2: 计算 per-token advantage
    advantages = teacher_logps - student_logps
    advantages = normalize_and_mask(advantages, trajectories.response_mask)

    # Step 3: 策略梯度更新
    loss = policy_gradient_loss(
        new_logps=student.logprobs(trajectories),
        old_logps=student_logps.detach(),
        advantages=advantages.detach(),
    )
    loss.backward()
    optimizer.step()

从单 teacher 扩展到多 teacher 打分

现在把同一段逻辑扩展到多教师 OPD。假设一个 batch 里有数学、代码和指令遵循三类轨迹,每条轨迹都带有已知的领域标签。打分时不需要让三位 teacher 同时评价每条轨迹;领域标签直接选择对应的 teacher。MiMo MOPD 和 Open-MOPD 都采用这种按 prompt 领域路由的方式。

下面的 samples 来自 student 的 batch rollout。为了把路由过程写清楚,代码逐条处理轨迹;实际训练会先按 domain 分组,再把同一领域的轨迹批量送给对应的 teacher server。

python
# 这里假设三个 Transformers teacher 已经加载;生产环境通常使用独立推理服务
teachers = {
    "math": math_teacher,
    "code": code_teacher,
    "if": instruction_teacher,
}

# full_ids 是“prompt + student 回答”的全部 token ID,形状为 [1, sequence_length]
samples = [
    {"domain": "math", "full_ids": math_ids, "prompt_len": math_prompt_len},
    {"domain": "code", "full_ids": code_ids, "prompt_len": code_prompt_len},
    {"domain": "if", "full_ids": if_ids, "prompt_len": if_prompt_len},
]


@torch.no_grad()
def score_with_routed_teachers(samples, student, teachers):
    scored = []

    for sample in samples:
        domain = sample["domain"]
        teacher = teachers[domain]  # hard routing:每条轨迹只选一位 teacher
        full_ids = sample["full_ids"]
        prompt_len = sample["prompt_len"]

        student_ids = full_ids.to(student.device)
        teacher_ids = full_ids.to(teacher.device)
        student_logps = next_token_logps(student, student_ids)
        teacher_logps = next_token_logps(teacher, teacher_ids).to(student_logps.device)

        # next_token_logps 的第 prompt_len-1 位开始预测回答 token
        response_slice = slice(prompt_len - 1, None)
        rewards = (teacher_logps - student_logps)[0, response_slice]

        scored.append(
            {
                "domain": domain,
                "rewards": rewards,
                "num_tokens": rewards.numel(),
            }
        )

    return scored


scored = score_with_routed_teachers(samples, student, teachers)

为什么切片从 prompt_len - 1 开始

full_ids 是“题目 + 模型回答”的 token 编号。继续使用“2+2 答案是 4。”:

logps 位置0123
预测的 token答案4
token 来源题目题目回答回答
reward丢弃丢弃保留保留

logps[i] 预测下一个 token,所以回答中的 4 虽然位于完整序列的位置 3,分数却在 logps[2]。题目有 3 个 token,因此回答从 prompt_len - 1 = 2 开始。

题目由数据集提供,模型只生成回答,所以训练时丢弃题目 reward,只保留回答 reward:

text
all_rewards       = [[-0.20, +0.30, +0.70, -0.10]]
all_rewards[0, 2:] = [               +0.70, -0.10]

[0, 2:] 的意思是:取第 0 条样本,再取位置 2 之后的值。

到这里已经完成了朴素 M-OPD 的多教师打分。teachers[domain] 负责路由,rewards 保存被选中 teacher 给出的逐 token 信号。三位 teacher 的输出不会在同一条样本上求平均,每位 teacher 只负责自己的领域。

逐 token 信号的两种实现

上面的 rewards 对应 MOPD 的策略梯度实现。对 student 实际采样的 token ,论文使用

其中 是当前领域的 teacher, 表示停止梯度。实际训练会把这个逐 token 优势同时截断到 ,避免某些 token 的师生 log-prob 差距过大。这种形式只需要 teacher 返回 student 实际选中 token 的 log-prob,容易接入现有 PPO 或 GRPO 训练框架。

若希望在每个位置使用更多 teacher 分布信息,MOPD 还提供 top- 实现。记 为 teacher 在位置 的 top- token 集合,对该位置使用

这里的 是截断校正项。如果只在 top- 集合上直接计算截断的 reverse KL,损失的最小点不再对应 ;加上这两项后,每个保留 token 的最优点恢复为师生概率相等。Top- 还把 teacher 服务需要传输的数据从全词表分布缩小到稀疏 logits。论文使用 时,同源 teacher 下的策略梯度版和 top- 版分别得到 0.9373 和 0.9093,说明两者在该设置下表现接近。

Teacher prefill 可以作为独立服务运行。Student 完成一条 rollout 后异步发送 prefill 请求,teacher 计算时间与 student 继续采样其他序列的时间重叠。这样,多教师带来的主要成本是额外计算资源,不必等比增加每个训练步的串行延迟。

逐 token 损失解决了“每一步怎样学”,训练循环还要处理“每个领域学多少”。对每个领域,我们再计算两个量:回答 token 占整个 batch 的比例 token_share,以及逐 token 奖励绝对值的均值 mean_abs_reward。前者反映序列长度造成的预算差异,后者近似该领域尚未消除的师生差距。

python
def compute_domain_weights(scored):
    total_tokens = sum(item["num_tokens"] for item in scored)
    stats = {}

    for domain in {item["domain"] for item in scored}:
        domain_rewards = torch.cat(
            [item["rewards"] for item in scored if item["domain"] == domain]
        )
        stats[domain] = {
            "token_share": domain_rewards.numel() / total_tokens,
            "mean_abs_reward": domain_rewards.abs().mean(),
        }

    target_share = 1.0 / len(stats)
    mean_gap = torch.stack(
        [stat["mean_abs_reward"] for stat in stats.values()]
    ).mean().clamp_min(1e-6)

    raw_weights = {}
    for domain, stat in stats.items():
        share_weight = target_share / stat["token_share"]
        gap_factor = torch.clamp(
            stat["mean_abs_reward"] / mean_gap,
            min=0.05,
            max=20.0,
        )
        raw_weights[domain] = share_weight * gap_factor

    # 保持加权前后 batch 的平均 reward 尺度接近
    normalizer = sum(
        stats[domain]["token_share"] * weight
        for domain, weight in raw_weights.items()
    )
    return {
        domain: weight / normalizer
        for domain, weight in raw_weights.items()
    }, stats


domain_weights, domain_stats = compute_domain_weights(scored)

for item in scored:
    item["weighted_rewards"] = (
        item["rewards"] * domain_weights[item["domain"]]
    )

这段代码把 Open-MOPD 的前两项修复落成了具体变量:share_weight 补偿长短回答的 token 份额差异,gap_factor 让预算跟随仍未收敛的领域。weighted_rewards 随后进入归一化和策略梯度更新。

还剩第三项 reward refresh。一次 rollout 被用于多次 PPO 内部更新时,teacher log-prob 可以缓存;每次更新前要重新计算当前 student_logps,再据此刷新 rewards、领域统计和权重。否则,上面的 mean_abs_reward 衡量的是旧 student 与 teacher 的差距。

这个最小实现已经把两类约束放进了同一条训练链:先用 sampled-token log-prob 差或 top- 损失生成密集信号,再根据 token 份额和剩余师生差距调整领域权重。生产系统还会加入 PPO clipping、reference KL、长度控制和 eval gating,并把同一领域的轨迹合成 batch 发送给 teacher server。

OPD 和相邻路线的关系

和 SFT / SeqKD 的关系

SFT 是必要的基础。它便宜、稳定、能快速把 student 拉到合理输出区域。OPD 不是替代 SFT,而是在 SFT 之后解决 student 自己轨迹上的错误修正。

可以简单类比:

  • SFT / SeqKD:把路铺到 teacher 经常走的区域
  • OPD:student 自己开车,teacher 坐副驾逐步纠偏

和 GRPO / RLVR 的关系

GRPO / RLVR 的奖励通常来自外部验证器:答案对不对、代码能不能跑、格式是否合格。这类奖励客观,但常常稀疏。一个 2000-token 的解题过程,最后只得到 0 或 1。

OPD 的奖励来自 teacher,每个 token 都能给信号。它不需要标准答案,也不需要 RM,但上限受 teacher 限制。

所以两者适合组合:数学题可以用规则奖励告诉模型最终答案是否正确,再用 teacher log-prob 给中间过程更密的 shaping 信号。Thinking Machines Lab 也把"蒸馏式 per-token reward + sequence-level environment reward"列为值得继续探索的方向。[2:1]

和 DPO 的关系

DPO 的漂亮之处是:语言模型自己可以被看成隐式奖励模型。OPD 更进一步:直接把一个强 teacher 当作逐 token reward model。

但 DPO 是离线偏好优化,适合已有 chosen / rejected 数据的场景;OPD 是在线采样,适合你有 teacher、但缺少偏好数据或验证器的场景。

和黑盒蒸馏的关系

上面的 OPD 默认你能拿到 teacher log-prob,这是白盒或至少 logprob-access teacher。现实里很多 teacher 只有 API 文本输出,没有 logits。黑盒 OPD 就要换信号:例如 2025 年的 GAD 把 student 当 generator,训练 discriminator 区分 teacher 和 student 的回答,再用 discriminator 作为随 student 演化的 on-policy reward model。[13]

这条线很有实践价值,但工程复杂度也高:你又引入了一个会被 hack、会漂移、需要稳定训练的判别器。

还有 teacher-free 的变体:Self-Distilled Reasoner 让同一个模型在不同上下文下同时扮演 teacher 和 student,用自蒸馏减少对外部强 teacher 的依赖。[14]

什么时候该用 OPD

OPD 最适合这些场景:

场景为什么 OPD 合适
小模型继承大模型推理能力小模型探索弱,teacher 能给密集过程信号
没有规则验证器不需要标准答案,只需要 teacher 能评估 token
领域模型后训练可以用强 teacher 恢复 instruction following 或格式
已经有不错的 SFT 初始化student 能采到 teacher 支持集附近的 token
想降低 RL 成本teacher forward 比完整 RL 探索和稀疏奖励更高效

不适合这些场景:

场景风险
想明显超过 teacherOPD 本质是压缩 teacher 行为,不负责发现新策略
student 太弱,采不到关键 tokenreverse KL 没法强化零概率附近的行为
teacher 和 student 推理风格冲突逐 token 信号可能互相拉扯,训练不稳定
长程任务只靠局部 token reward局部对齐不一定等于全局任务成功
只有黑盒 teacher API需要额外 reward/discriminator 设计
teacher 只是同 pipeline 放大版可能分数更高,但不给 student 新决策边界

2026 年的 OPD survey 把这个领域整理成三个维度:反馈信号(logit-based、outcome-based、self-play)、teacher access(white-box、black-box、teacher-free)和 loss granularity(token-level、sequence-level、hybrid)。[15] 这个分类很好用:一看到一个"OPD 新方法",先问这三个问题,就不会被名字绕晕。

实战指南

一个实用 recipe

实际跑 OPD 时,可以按以下步骤操作:

第一步,选 teacher。 teacher 不一定越大越好,关键是它在目标任务上比 student 强、能提供 student 还没学会的能力,并且和 student 的 tokenizer、输出风格、推理格式尽量兼容。同族模型通常更省心,但"同族更大"不是充分条件。最好做一个小对照:同 pipeline 放大版 teacher vs 额外 RL / 数据增强后的 teacher。如果后者明显更好,说明 OPD 需要的是新决策边界,不只是参数规模。

第二步,做 off-policy 冷启动。 用 teacher 生成一批高质量答案,先 SFT student。目标不是一步到位,而是让 student 进入 teacher 的支持集附近。如果初始 overlap ratio 很低,直接 OPD 往往启动不了;先在 teacher rollout 上做轻量 SFT,等 student 能采到相似思路,再切到 on-policy。

第三步,选 prompt 分布。 prompt 不只是任务数据,也是在选择 student 会进入哪些状态。尽量使用 teacher 训练时熟悉的模板、系统提示和题型,让早期 OPD 有足够 overlap;同时混入一部分 OOD prompt,避免 student 只学会 teacher 的固定口癖和低熵模板。

第四步,采 student rollout。 每个 prompt 采 2-8 条回答,保留 token、log-prob、mask、长度、停止原因。这里和 PPO / GRPO 的 rollout 基础设施基本相同。

第五步,teacher 打分。 对 student 生成的完整上下文做 teacher forward,拿生成 token 的 log-prob。白盒 teacher 可以直接算 logits;黑盒 teacher 需要另设 reward 近似。这里不要把 teacher 当成绝对裁判,而要把它当成一种局部偏好:它正在告诉 student,在 student 自己写出的前缀里,哪些选择更像 teacher 会保留的路径。

第六步,更新 student。 用 teacher_logp - student_logp 做 per-token advantage,再接 PPO-style loss 或 importance-sampling loss。实践中要监控 entropy、KL、response length 和重复率,避免 student 过早塌缩。长回答任务可以只对前若干 token、分段窗口或高置信 overlap token 上 loss,别默认整条 10K token 轨迹都同样可靠。

第七步,和任务奖励混合。 如果任务有验证器,不要浪费它。可以把 final reward 用于序列级方向,把 OPD reward 用于 token-level shaping。这样能弥补 OPD 的一个盲点:teacher 局部认可的 token,不一定保证全局答案正确。

第八步,做 eval gating。 OPD 很容易把 teacher 的风格也压进 student。除了目标 benchmark,还要测通用能力、格式、拒答、安全和长度分布。

快速测试方案

第一次验证 OPD,不建议直接上大规模训练。先用小实验回答三个问题:

  1. teacher 给的 token-level 信号是不是有意义?
  2. offline 近似能不能先跑出正向变化?
  3. online rollout 是否比 offline 缓存明显更好?

0. 先问 teacher 是否真的"可学"

这是最快的 sanity check,几十分钟就能做。重点是形成判断:这个 teacher 是否在 student 当前能力附近说话。

选 50-100 个 prompt,让 student 每题生成 2-4 个回答。teacher 不生成答案,只对 student 的回答逐 token 算 log-prob。然后人工检查:teacher 给高分的 token,是否是 student 本来就有可能写出的合理延续?teacher 给低分的地方,是否确实对应推理分叉、格式偏移或过早下结论?如果高低分主要在惩罚长度、惩罚某种模板,或者把所有 student token 都压低,说明 teacher 和 student 不在同一个思维空间里。

这一步能直接验证论文的核心 insight:OPD 的失败常常不是 teacher"不会评分",而是 teacher 的评分没有落在 student 能调整的位置上。全局上有信息的 reward,如果在每个局部 token 选择处都给不出可吸收的方向,训练仍然会原地打滑。

1. Lightning OPD smoke test

第二步先跑 offline 版本,因为它便宜、稳定、容易复现。

数据规模可以很小:

  • 训练 prompt:200-1000 条
  • 验证 prompt:50-200 条
  • student:0.5B-1.5B 小模型
  • teacher:同族更大模型,或你要蒸馏的强模型
  • 训练方式:LoRA 即可,100-500 steps 先看趋势

流程如下:

text
1. 用 SFT student 生成固定回答
2. 用 teacher 预计算每个生成 token 的 log-prob
3. 训练 student,让它提高 teacher 认可的 token 概率
4. 在 held-out prompt 上重新生成答案
5. 比较训练前后:任务分数、长度、重复率、teacher score

最小可接受结果不是"teacher score 上升",而是:

观察项期望
held-out 任务分数小幅上升,或至少不下降
平均回答长度不明显爆炸,也不极端变短
重复率不上升
teacher score上升,但不能靠变短、套模板或重复来刷分
人工样例至少 20 个样例里,多数看起来更像 teacher

如果 teacher score 上升但任务分数下降,说明 student 可能在学 teacher 的局部口癖,而不是学能力。此时优先检查 mask、长度归一化、prompt 分布和 teacher consistency。

2. Online OPD 小规模对照

如果 offline smoke test 有正向信号,再做一个小规模 online 对照。只跑 2-3 轮:

text
Round 1: 当前 student 生成 rollout → teacher 打分 → 训练 50-100 steps
Round 2: 更新后的 student 重新生成 rollout → teacher 重新打分 → 再训练
Round 3: 可选,观察是否继续提升

对照组是 Lightning OPD:同样 prompt、同样 teacher、同样训练步数,但 rollout 固定不刷新。看 online 是否明显优于 offline。

如果结果是...结论
offline 已经接近 online用 Lightning OPD 更划算,没必要上 live teacher
online 明显更好student 分布漂移较大,刷新 rollout 有价值
online 不稳定,offline 更稳teacher 信号可能噪声大,或 online 采样质量太差
两者都没提升优先回到 SFT 数据、teacher 选择和任务评测

这个测试能帮你决定工程路线:如果 offline 就够好,先走 Lightning;如果 online 明显赢,再考虑搭 teacher server。

3. 最小实验报告模板

每次跑 OPD,至少记录这张表:

项目内容
student / teacher模型名、参数量、是否同族
数据prompt 来源、数量、是否和评测去重
rollout每题采样数、temperature、max tokens
rewardteacher log-prob 是否长度归一化、是否裁剪
训练online 还是 offline、steps、LoRA rank
评测任务分数、长度、重复率、人工样例
insight 记录teacher 学到了什么新东西,student 是否真的吸收了
结论继续 online、用 Lightning、还是回去做 SFT

这张表比单看 loss 重要得多。OPD 的 loss 下降只能说明 student 更像 teacher,不自动说明模型真的更会做题。

开源框架支持

本节列出主流训练框架对 OPD 和 OPSD 的支持情况与使用入口。所有信息均基于各框架仓库当前代码与官方文档的直接查证。

术语速查

缩写含义
OPDOn-Policy Distillation,学生自己生成 rollout,由独立的外部教师模型逐 token 打分。
OPSDOn-Policy Self-Distillation,同一个模型同时扮演 teacher 和 student,teacher 通过 privileged context(如 ground-truth 答案、"简洁"前缀等)获得额外信息来指导学生。

OPD 框架详细说明

1. slime(THUDM)

slime 是 GLM-4.5 / 4.6 / 4.7 的后训练框架,OPD 作为第一等特性内建,设计为可加在任意 RL 算法上的 KL 惩罚项

支持模式:

  • SGLang 模式:teacher 跑在独立 SGLang server 上,通过 --rm_url 在 rollout 阶段拉取 token-level log-probs。适合 teacher 架构不同或显存放不下的场景。
  • Megatron 模式:teacher 直接加载进 Megatron 训练进程,通过 --opd-teacher-load 指定 checkpoint。要求 teacher 与 policy/ref 同架构。

最小启动命令(Megatron 模式):

bash
python train.py \
  --use-opd \
  --opd-type megatron \
  --opd-kl-coef 1.0 \
  --opd-teacher-load /path/to/teacher_ckpt \
  --adv_estimator grpo   # 也可换 ppo / reinforce_plus_plus

关键设计:

  • OPD 与 advantage estimator 正交,只是给 advantage 加一个 reverse KL 惩罚。
  • slime/rollout/on_policy_distillation.py 实现了 SGLang 模式下的 reward_func:对每条 sample 调用 teacher server,trim 到 response span 后写回 Sample。
  • 官方示例用 Qwen3-8B student + Qwen3-32B teacher,在 DAPO-Math-17k 上 Math500 从 76% 提升到 94%。

不支持 OPSD:README 明确要求 "use a different (stronger) model as the teacher",无 privileged context 机制。


2. veRL(ByteDance Seed)

veRL 是目前社区活跃度最高的分布式 RL 框架之一,OPD 作为独立 trainer 提供。

使用入口: examples/on_policy_distillation_trainer/

最小启动脚本:

bash
bash examples/on_policy_distillation_trainer/run_qwen3_8b_fsdp.sh

核心配置(Hydra YAML):

yaml
distillation:
  enabled: True
  teacher_models:
    teacher_model:
      model_path: 'Qwen/Qwen3-32B' # HF 路径或本地路径
  distillation_loss:
    loss_mode: 'k3' # 可选 k1 / k3 / forward_kl_topk 等
    use_policy_gradient: True # 是否与 GRPO PG loss 联合训练
    topk: 64 # teacher 只传 top-k logits,省显存

关键设计:

  • teacher 通过独立 Ray 集群 serving,与 student 训练进程解耦。
  • 支持 topk sparse logits:teacher 只返回 top-64 的 logit 值和索引,student 用这些稀疏值算 KL,避免传输全词表 logits。
  • 支持 FSDP 和 Megatron 两种训练后端,以及 vLLM 推理后端。
  • 官方示例同时提供了纯文本和 VLM(run_qwen3_vl_8b_fsdp.sh)的 OPD 训练。

不支持 OPSD:官方 on_policy_distillation_trainerteacher_models 配置只接受外部模型路径。第三方仓库 HJSang/OPSD_OnPolicyDistillation 虽然基于 veRL 构建,但其 README 明确标注 "TODO: Add OPSD support. Currently only OPD is included"


3. NeMo RL(NVIDIA)

NeMo RL 是 NVIDIA 的工业级后训练框架,OPD 以原生算法模块形式存在。

使用入口: nemo_rl/algorithms/distillation.py

启动方式:

bash
python examples/run_distillation_math.py

核心配置(YAML):

yaml
teacher:
  model_path: 'nvidia/Nemotron-4-340B' # teacher 独立配置
  tensor_parallel_size: 4 # teacher 可用不同 TP
distillation:
  topk_logits_k: 64 # sparse top-k teacher logits
loss_fn:
  kl_type: 'reverse' # forward / reverse / mixed

关键设计:

  • 两阶段执行:Phase 1 加载 teacher 算完所有 micro-batch 的 logits,缓存到 CPU 后卸载 teacher;Phase 2 加载 student 做 forward + backward。避免 teacher 和 student 同时占显存。
  • 独立并行策略:teacher 和 student 可以使用不同的 Tensor Parallelism、Context Parallelism 配置。
  • 支持 multi-turn rollout,通过 max_rollout_turns 配置。
  • 与 NeMo 生态深度集成,适合已有 Megatron 训练基础设施的团队。

不支持 OPSDteacher: PolicyConfig 是独立模型配置,代码中无 privileged context 或同一模型双角色逻辑。


4. TRL(HuggingFace)

TRL 拥有最丰富的实验性 OPD trainer 集合,特点是依赖轻、上手快

使用入口: trl/experimental/gkd/

最小代码示例:

python
from trl import GKDTrainer, GKDConfig

trainer = GKDTrainer(
    model="Qwen/Qwen3-8B",           # student
    teacher_model="Qwen/Qwen3-32B",  # teacher(独立模型)
    args=GKDConfig(
        kl_type="reverse",           # "forward" / "reverse" / "jsd"
        temperature=1.0,
        per_device_train_batch_size=4,
    ),
    train_dataset=dataset,
)
trainer.train()

关键设计:

  • GKDTrainer 继承自 DPOTrainer,架构成熟。
  • 支持 forward KL、reverse KL、JSD 三种散度。
  • 基于 Accelerate,可无缝切换单卡、DeepSpeed、FSDP。
  • 实验性目录下还有 minillm/gold/online_dpo/ 等变体,覆盖不同 OPD 算法。

TRL 同时是以下框架的底层依赖:

  • ms-swiftexamples/train/rlhf/gkd/ 直接调用 TRL GKDTrainer,额外封装了多模态和 Megatron 适配。
  • LLaMA-Factory:通过 TRL 集成支持 OPD,无原生独立实现。

5. 其他原生支持 OPD 的框架

框架定位使用入口特点
rLLM(UC Berkeley Sky)轻量 OPD + OPSDrllm/trainer/distill/单 GPU 用 tinker,多 GPU 接 verl 后端。AwesomeOPD 记录有 OPSD 支持。
AReaL(AntGroup / Tsinghua)大规模 RL 框架examples/distillation/gsm8k_grpo_distill.yaml与蚂蚁内部训练平台对齐。
ROLL(Alibaba)多模态 RL 框架roll/pipeline/distill/原生支持 VLM,内置多种 divergence 库。
SkyRL(UC Berkeley NovaSky)RL 研究框架skyrl-train/examples/on_policy_distillation/NovaSky 实验室出品,与 Sky 计算实验室生态对齐。
KDFlow(BJTU)KD-first 框架examples/on_policy_kd/SGLang teacher + FSDP2 student 解耦,原生支持跨 tokenizer 和 VLM。

6. 不支持 OPD 的框架

OpenRLHF 被 AwesomeOPD 明确排除。其架构虽然支持 rollout / teacher / update 分离,但:

  • teacher 作为 remote Ray worker,传输 full logits 跨进程开销极大。
  • 无原生 on-policy distillation 实现,现有蒸馏路径是离线固定语料。
  • 社区多次讨论后仍未合并 OPD 原生支持。

OPSD 框架详细说明

OPSD(On-Policy Self-Distillation)比 OPD 更稀缺:它要求同一个模型在训练过程中同时扮演 teacher 和 student,teacher 通过 privileged context(ground-truth 答案、额外指令、更长上下文等)获得学生看不到的信息,然后对学生的 rollout 逐 token 打分。

1. TRL(HuggingFace)—— 目前唯一官方实验性实现

使用入口: trl/experimental/self_distillation/

核心机制:

  • SelfDistillationMixin._split_prompt_and_privileged_context() 从 batch 里分离出 promptprivileged_context
  • 同一 model 跑两次 forward:
    • student forward:prompt + completion
    • teacher forward:prompt + privileged_context + completion(teacher 看到更多信息)
  • 计算 reverse KL:KL(teacher || student),只对学生生成部分算 loss。

关键类:

  • BaseSelfDistillationTrainer:在线自蒸馏基类,支持 vLLM rollout。
  • SelfDistillationMixin:公共 loss 计算,支持 grpobnpodr_grpodapo 等 loss type。
  • SDPO(Self-Distillation Policy Optimization):具体 trainer 实现。

数据格式要求: 数据集必须包含 promptprivileged_context 两列。例如数学题中,privileged_context 可以是 ground-truth 解答或推导提示。

代码片段:

python
from trl import SDPOTrainer, SelfDistillationConfig

trainer = SDPOTrainer(
    model="Qwen/Qwen3-8B",
    args=SelfDistillationConfig(
        kl_type="reverse",
        loss_type="grpo",      # 或 bnpo / dapo 等
    ),
    train_dataset=dataset,     # 需包含 "prompt" 和 "privileged_context"
)
trainer.train()

支持的散度:

  • alpha=0:reverse KL(
  • alpha=1:forward KL(
  • 0 < alpha < 1:JSD 混合

限制: 实验性代码,API 可能变动;目前只支持单模型架构,无多 teacher。


2. rLLM(UC Berkeley Sky)

AwesomeOPD 记录 rLLM 在 examples/math_distill/ 下有 OPSD 实现(含 opsd/ 子目录)。该框架定位轻量,适合:

  • 单 GPU 快速验证(tinker 后端)
  • 多 GPU 扩展(verl 后端)

但当前 GitHub 路径可能已迁移,建议以最新仓库为准。


3. 明确不支持 OPSD 的框架

框架原因
slime架构上要求 --opd-teacher-load 指向独立模型,无 privileged context 接口。
veRL官方配置 teacher_models.teacher_model.model_path 只接受外部模型路径。
NeMo RLteacher: PolicyConfig 为独立模型配置块,代码无同一模型双角色逻辑。
ms-swift / LLaMA-Factory通过 TRL GKDTrainer 间接支持 OPD,TRL 的 self_distillation 模块尚未被封装。
OpenRLHF无原生 OPD,更无 OPSD。

选型建议

  • 需要 OPD + 大规模分布式训练(SGLang / Megatron / vLLM teacher server):选 slimeveRLNeMo RL。三者都是生产级实现,slime 和 veRL 的社区活跃度更高。
  • 需要 OPSD(自己教自己):目前只有 TRL 有官方实验性实现。如果已在 TRL 生态内,可直接复用 self_distillation/ 模块。
  • 已有 SWIFT / ModelScope 或 LLaMA-Factory 工作流:OPD 可通过 TRL GKDTrainer 间接使用,但 OPSD 暂无支持。
  • 只是想快速验证 OPD 机制TRLGKDTrainerveRLon_policy_distillation_trainer 都是最小依赖的起步选择。

本章总结

OPD 的核心是一个训练范式选择:

  • off-policy 蒸馏有密集 token 监督,但不训练 student 自己会犯的错。
  • RL 是 on-policy,但奖励常常稀疏,样本效率低。
  • OPD 把两者接起来:student 自己采样,teacher 逐 token 给密集反馈。

它特别适合小模型、专用模型和后训练能力迁移。但 OPD 不是 RL 的替代品,也不是万能压缩器——上限来自 teacher,稳定性来自初始化,价值来自"在 student 自己状态上给密集信号"。

2026 年机制分析论文最值得带走的 insight,是把 OPD 的核心问题从"teacher 是否更强"换成"teacher 是否更可学"。更强的模型可能只是会在自己的轨迹上拿高分;更可学的 teacher 则能在 student 当前轨迹上提供新知识、相近思维路径和可吸收的局部偏好。这个视角会改变你设计整条蒸馏 pipeline 的方式:先冷启动让两者说同一种语言,再用 teacher-aligned prompt 让 teacher 站到熟悉的分布上,最后用任务奖励保证局部偏好没有背离全局目标。

扩展到多个领域 teacher 后,完整流程需要同时处理三件事:从共享 SFT 检查点并行训练同源 teacher,用领域路由和逐 token reverse KL 整合能力,再按回答长度、剩余师生差距和当前策略刷新优化预算。同源性决定 teacher 的信号能否稳定被吸收,路由决定每条样本由谁教,预算平衡决定各种能力最终学到多少。

从本章主线看,DPO、GRPO、RLVR 和 OPD 都在回答同一个问题:当我们不想完整跑传统 RLHF 时,训练信号还能从哪里来? DPO 用偏好对,RLVR 用验证器,OPD 用 teacher。理解这三类信号的边界,才是真正能迁移到新项目里的能力。

参考文献


  1. Agarwal R, Vieillard N, Zhou Y, et al. On-Policy Distillation of Language Models: Learning from Self-Generated Mistakes, ICLR 2024.(GKD:在 student 自生成序列上用 teacher 反馈缓解分布偏移) ↩︎

  2. Lu K, Thinking Machines Lab. On-Policy Distillation, 2025.(工程化 OPD 复现与 Tinker 实现,包含 Qwen3 对比和 personalization 实验) ↩︎ ↩︎

  3. Xu X, Li M, Tao C, et al. A Survey on Knowledge Distillation of Large Language Models, arXiv 2024.(从 algorithm、skill、verticalization 三个角度整理 LLM KD) ↩︎

  4. Yang C, Lu W, Zhu Y, et al. Survey on Knowledge Distillation for Large Language Models: Methods, Evaluation, and Application, arXiv 2024.(按 white-box / black-box KD、评测和应用整理) ↩︎

  5. Ross S, Gordon G, Bagnell D. A Reduction of Imitation Learning and Structured Prediction to No-Regret Online Learning, AISTATS 2011.(DAgger:让 learner 访问到的状态进入训练集) ↩︎

  6. Wu Y, Han S, Cai H. Lightning OPD: Efficient Post-Training for Large Reasoning Models with Offline On-Policy Distillation, arXiv 2026.(把标准 OPD 离线化:预计算 teacher log-prob,避免训练时 live teacher server) ↩︎ ↩︎

  7. Gu Y, Dong L, Wei F, Huang M. MiniLLM: Knowledge Distillation of Large Language Models, ICLR 2024.(用 reverse KL 和 on-policy 优化做生成式 LLM 蒸馏) ↩︎

  8. Li Y, Zuo Y, He B, et al. Rethinking On-Policy Distillation of Large Language Models: Phenomenology, Mechanism, and Recipe, arXiv 2026.(分析 OPD 成功条件、token-level 机制和失败恢复策略) ↩︎ ↩︎

  9. Ko J, Kim S, Chen T, Yun S. DistiLLM: Towards Streamlined Distillation for Large Language Models, ICML 2024.(用 skew KL 和 adaptive off-policy 提升 LLM 蒸馏效率) ↩︎

  10. OpenBMB. MiniCPM5-1B Training Recipe, 2026.(公开 SFT、分领域 RL teacher、OPD 三阶段后训练流程;OPD 将多个专项 teacher 蒸馏回一个发布模型) ↩︎

  11. Ma W, Wei J, Zhao L, et al. MOPD: Multi-Teacher On-Policy Distillation for Capability Integration in LLM Post-Training, arXiv:2606.30406, 2026.(提出通用 SFT、分领域 RL teacher 和多教师在线策略蒸馏三阶段流程,并在 Qwen3-30B-A3B 与 MiMo-V2-Flash 上验证) ↩︎ ↩︎

  12. Gao H, Chi H, Yan Y, et al. Open-MOPD: Diagnosing and Fixing Capability Imbalance in Multi-Teacher On-Policy Distillation, arXiv 2026;项目页。(清华大学 AIR/SIA-Lab 与字节跳动 Seed 提出的首个完整开源多教师 OPD recipe,诊断跨领域 token 预算失衡并给出动态平衡方法) ↩︎ ↩︎

  13. Ye T, Dong L, Chi Z, et al. Black-Box On-Policy Distillation of Large Language Models, arXiv 2025.(GAD:没有 teacher logits 时,用 discriminator 提供 on-policy 奖励) ↩︎

  14. Zhao S, Xie Z, Liu M, et al. Self-Distilled Reasoner: On-Policy Self-Distillation for Large Language Models, arXiv 2026.(单模型在不同上下文下同时扮演 teacher 和 student) ↩︎

  15. Song M, Zheng M. A Survey of On-Policy Distillation for Large Language Models, arXiv 2026.(把 OPD 统一到 f-divergence 框架,并按反馈信号、teacher access、loss 粒度分类) ↩︎

现代强化学习实战课程