DASH 这个训练方法,核心解决的是推理模型自我蒸馏时一个很实际的问题:模型生成一条长思维链(Chain-of-Thought)之后,训练时到底该监督到哪一步。直接用最终答案做结果监督,信息利用不充分;把整条思维链所有 token 都做监督,又容易在模型发散时把错误分布学进去。DASH 的做法是用“发散度”动态决定监督视野(Supervision Horizon),并且整个过程要求训练数据来自当前策略自身的生成,也就是标题里强调的 On-Policy Self-Distillation。如果你最近在做 R1 风格推理模型的数据构建、SFT 微调或 RL 对齐,这篇文章值得完整读一遍。
1. 先搞清楚 DASH 解决的核心问题:一条思维链到底该监督到哪
1.1 推理模型和普通模型的最大区别:长思维链
微调一个普通指令模型,输入一个问题,期望输出是一个答案,可能几百 token。训练时把问题和答案拼接起来做 teacher forcing,模型每个位置都有明确目标。推理模型就不一样。推理模型在正式输出答案之前,会先生成一条很长的思维链,可能是几千甚至上万 token,里面包含中间步骤、自我检查、回溯、纠错。
R1 这类模型训练的重要环节,就是把“能答对问题的思维链”作为监督数据。问题就在这里:一条思维链的监督信号并不是均匀分布的。有些步骤是决定性的,比如第一步把问题形式化、设定变量;有些步骤只是机械运算;有些步骤甚至包含着错误的前置假设,只是后面被纠正回来。
如果对整条思维链所有 token 施加同等监督,模型会被迫去拟合那些实际上它自己并不会稳定生成的中间状态。尤其是当训练数据来自另一个模型(或者是旧版本模型)时,这种强制对齐会引入大量噪声。
1.2 自我蒸馏里的监督信号从哪来
自我蒸馏的意思是:训练数据不是来自外部大模型,而是来自模型自己。通常流程是:
让当前模型对一批问题做多次采样生成,得到多个带思维链的完整轨迹。然后用一个验证器或奖励模型判断每个轨迹的最终答案是否正确。正确的轨迹保留下来,作为 SFT 阶段的训练数据;或者直接把它放入 RL 框架里做偏好或策略优化。
这个流程简单,但有一个核心矛盾:如果直接用模型自己的输出来训练自己,正确轨迹里也包含大量低质量思维链片段。有些轨迹虽然答案对了,但中间推理有明显的跳跃或堆积;有些轨迹早期方向就偏了,只是后面偶然回到正轨。把这些轨迹全部当黄金数据,模型会逐渐变得“会产出相似形态的废话”。
这就是为什么监督边界变得重要。你需要决定:对每条轨迹,到底从开头到哪个位置施加强监督;之后的部分,是让模型靠结果奖励自由探索,还是完全不监督。
1.3 固定监督深度的三个方案都不够好
把“监督到哪”这个问题拆开,通常有三种固定策略。
第一种是整条轨迹全监督。适合轨迹质量极高、分布特别干净的场景。缺点是成本高,而且一旦轨迹里包含错误假设或无效步骤,模型会被带偏。
第二种是只监督到前 k 个 token 或前 m 个推理步骤。这是“前几步固定、后面自由发挥”的思路。优点是能让模型先学会稳定的开局,缺点是 k 怎么定是个经验值。不同任务的前几步重要性差别很大。
第三种是只做结果监督,完全不管中间过程。这种方案最稳,但信息利用率很低,模型需要大量探索才能学会长链条推理。
DASH 的核心想法是把这三个方案统一起来:监督深度不再是一个固定值,而是根据当前模型和参考轨迹之间的发散度动态变化。发散度小的地方,说明模型已经走在稳定路径上,监督可以放长一点;发散度突然变大,说明模型进入了不确定区域,监督视野就要收短,避免把不稳定行为强行压进模型。
2. On-Policy 为什么是推理模型蒸馏的关键词
2.1 Off-Policy 蒸馏很容易把模型带偏
很多人第一次接触自我蒸馏时会想:先用旧模型生成一批数据,再拿去训练新模型,不就行了吗?这就是典型的 Off-Policy。数据来自旧策略,训练的是新策略,两者分布不完全一致。
分布不一致会带来两个问题。第一个是覆盖偏差:旧策略生成的轨迹只能代表旧策略的行为空间,如果一个推理步骤是当前新策略特别容易产生的,但旧策略从来没生成过,训练数据里就没有对应的正反馈或纠正信号。第二个是目标漂移:随着模型迭代,旧轨迹里“正确”的定义也在变。一条旧轨迹可能通过旧模型的能力结构正好绕到了最终答案前,但新模型不一定能复现这条路径,你拿它做监督反而会让新模型学到一套新模型难以稳定表达的中间行为。
在推理模型训练里,这个问题会被长思维链放大。普通指令模型的一条输出很短,覆盖偏差的影响有限;推理模型一条轨迹几千 token,某一步的策略分布差异就可能让整条轨迹失真。
2.2 PPO 的 On-Policy 特性和策略更新窗口
熟悉 RL 的读者都知道,PPO 是典型的 On-Policy 算法。它的目标函数里有一个重要性采样比例,用来纠正策略更新前后的分布差异。但 PPO 只能在一个有限的更新窗口内纠正这个比例。通常训练时会让同一批 rollout 数据用于多次 epoch 的更新,但 epoch 数不会设太大,一般 1 到 4 个。如果更新窗口拉太长,旧数据对应的旧策略已经不再代表当前策略,训练就会震荡。
这也是为什么 PPO 的每个训练迭代都要重新采样。模型稍微更新一点,就要立刻用新模型重新生成一批轨迹,然后用这些新轨迹计算奖励和梯度。不能拿上一轮的数据在同一份上反复榨取。
这个原则放到自我蒸馏里同样成立:只有当监督轨迹是当前策略刚生成的,你才能真正做到在模型的真实探索空间里修正行为。
2.3 自我蒸馏里的两种 On-Policy
严格一点说,自我蒸馏里有两层 On-Policy 的含义。
第一层是数据来源:训练数据必须由当前迭代的模型自己采样生成,而不是复用上一个版本的数据缓存。在实践操作中,这意味着每次迭代都要包含一个 rollout 阶段,模型先在推理数据集上采样一批完整思维链,然后经过验证器过滤,再进入训练阶段。
第二层是监督目标:训练目标必须与当前策略直接相关。DASH 引入的发散度度量本身也是当前策略和参考轨迹之间的 KL 散度,这一点天然要求你时刻知道当前策略的分布。如果你用的是离线数据,这个发散度根本无法准确计算。
所以 DASH 不是简单地换了一种 loss,它实际上要求整个数据管线都要改成“采样—验证—训练—再采样”的闭环。在工程上,这个改动比换 loss 大得多,后面我会单独讲。
3. Divergence-Adaptive Supervision Horizon 的工作方式
3.1 发散度:衡量当前策略和参考轨迹的偏差
DASH 里的 Divergence,通常是指当前策略模型在每个 token 或每个推理步骤上,与参考轨迹之间的分布偏差。最常见的形式是 KL 散度。
具体计算时可以这样理解:把当前模型在某个位置上的 token 概率分布拿出来,参考轨迹在那个位置的 token 概率分布也拿出来,两个分布之间的 KL 散度越大,说明模型在这个位置上越“不认同”参考轨迹的走法。
实际操作中,我一般不会在单 token 粒度上直接决定监督视野,因为 token 级 KL 噪声太大。更稳妥的做法是先把思维链按句子或换行符切成 step,或者按任务类型切成“第一步设变量—中间运算—最后结论”这样的结构化片段。每个 step 内对 token 的 KL 做平均,得到一个 step 级发散度序列。
如果你的推理轨迹里本身就包含固定的思考和动作格式,比如 ReAct 风格的工具调用,切割 step 时还要尽量按语义边界来,不要把思考片段和动作片段混在一个 step 里。工具调用的成功或失败往往会带来发散度突变,这时候监督视野的调整逻辑要单独考虑。
3.2 监督视野如何自适应调整
拿到 step 级发散度序列之后,DASH 的思路是:从轨迹开头出发,逐个 step 向后检查发散度。只要发散度低于某个动态阈值,就认为这段路径是当前模型信任的,监督可以继续向后延伸。一旦某个 step 的发散度超过阈值,说明模型从这里开始和参考轨迹脱节了,那么监督视野就停在这个 step 之前,后面的部分不再做逐 token 监督,只留结果奖励。
这种“前段监督、后段自由”的结构,实际上是在模仿一个熟练的推理者:开局阶段要跟随经验路径,把问题和已知条件稳定地形式化;一旦进入探索阶段,就不要再强制模型重复某条具体路径,而是让模型自己去搜。
我个人更倾向把监督视野的截止位置做成软边界,而不是硬边界。硬边界是:前 k 个 step 用交叉熵,后面全部不用。软边界是:用 sigmoid 权重或者距离衰减权重,让监督强度在边界附近逐渐下降。这样做的好处是训练 loss 更平滑,不会因为边界抖动造成梯度突变。
3.3 这个机制和 ORM、PRM 的关系
很多训练方案里会给每个中间推理步骤配一个过程奖励模型(PRM),对每一步是否正确打分。DASH 的监督视野跟 PRM 很像,但侧重点不同。
PRM 的目标是评价每一步的正确性,它需要额外训练一个验证模型,而且验证模型的标注质量直接决定整体效果。DASH 则不需要对每一步单独打分,它只需要当前策略和参考轨迹的分布发散度。发散度衡量的是“当前策略是否信任这段轨迹”,而不是“这段轨迹本身是否正确”。
另一种做法是结局奖励模型(ORM),只对最终结果打分。DASH 的视角介于 ORM 和 PRM 之间:监督深度能动态变化时,早期正确性可以由参考轨迹保证,后续正确性由结果奖励兜底,不需要每一步都有人工或模型打分。这个特点让 DASH 在数据标注成本受限的场景里更有吸引力。
4. 落地 DASH 需要准备什么
4.1 模型、数据和验证器条件
如果要按 DASH 的思路跑一轮训练,先确认以下条件。
模型层面,建议从 1.5B 到 7B 规模开始。小模型跑得快,而且发散度信号更敏感,非常适合先验证方法是否成立。如果你直接上 70B,每一轮采样和训练的成本都会高很多,排错也会麻烦。
数据层面,需要一个带标准答案的推理数据集。至少要有几百到几千条题目,每条题目要有清晰的最终答案格式,方便自动验证。常见的数学推理、逻辑推理、代码问题集都可以。关键是要能对最终答案做确定性比较,不能依赖人工阅读。
验证器层面,要么准备一个可用的规则验证器,要么准备一个准确率足够高的奖励模型。这里最容易踩坑的点是验证器本身经常漏判。规则验证器对答案格式敏感,奖励模型对越界回答宽容,两者都会让你把错误轨迹当成正确轨迹收进训练集。
算力层面,DASH 的数据管线是“采样—验证—训练—再采样”闭环,意味着每个迭代都要重新生成轨迹。这个成本通常比一次固定数据集 SFT 高一个数量级。你至少需要保证能在一个迭代周期内完成全量采样和训练,否则训练进度会很拖沓。
4.2 训练循环的大致流程
一次完整的 DASH 风格训练迭代,可以拆成下面几步。
第一步,用当前策略模型对训练题目做多次采样。每条题目可以采样 4 到 16 条轨迹,具体数量取决于你的推理预算和题目难度。我建议先从小采样数开始,4 条左右,跑通整个流水线再往上加。
第二步,用验证器过滤轨迹。只保留最终答案正确的轨迹,作为当前轮的参考轨迹集合。这一步要记录每条轨迹的基础信息,包括采样温度、长度、验证结果。
第三步,计算发散度。用当前策略模型和参考轨迹做一次前向计算,得到每个 step 的 KL 散度序列。这一步会额外消耗一次前向算力,但很有必要,因为它决定了监督视野。
第四步,根据发散度确定每条轨迹的监督视野,构造训练 loss。通常做法是把前段监督的部分转成 SFT 式交叉熵,后段不监督或者用加权方式降低权重。
第五步,用构造好的 loss 更新模型参数。更新之后不要急着做下一轮,先在小验证集上看看发散度变化趋势和正确率,确认没有出现异常漂移。
整个过程每轮迭代都会重复。如果你看到发散度整体在下降,说明模型在逐渐贴近参考轨迹;如果某条任务所有轨迹的发散度都很高,那这条任务可能超出了当前模型能力边界,不适合继续做蒸馏。
4.3 关键超参数和资源估算建议
实际训练里,最需要调的超参数有三个。
第一个是发散度阈值。这个阈值决定监督视野的平均长度。阈值设高了,监督范围大,模型容易被参考轨迹捆绑;阈值设低了,监督范围小,模型学到的东西少。我一般会在一开始统计所有训练 step 的发散度分布,取中位数或 60 分位数附近的量级作为初始阈值,然后观察训练稳定性再微调。
第二个是采样数量。每条题目的采样数影响轨迹多样性。采样少,发散度估计不稳定;采样多,训练成本线性增长。比较经济的方法是固定一个采样预算,按题目难度动态分配,难题多采样,简单题少采样。
第三个是软边界宽度。如果采用软边界,这个宽度控制监督强度从 1 降到 0 的过渡距离。宽度太窄,相当于还是硬边界;宽度太宽,后段轨迹也会得到不少监督,等于退化成几乎全监督。
资源方面,一个 7B 模型做完整的采样和训练迭代,单卡 A100 80G 级别的机器可以跑,但速度不快。我通常建议用 4 卡以上并行采样,训练阶段用张量并行或数据并行。显存不足时,可以把采样阶段的批量大小调小,但训练阶段显存主要受序列长度影响,长思维链轨迹会让梯度检查点和序列打包成为必需品。
5. 怎么判断训练有没有跑对
5.1 训练日志里该看的几个信号
训练过程中不要只盯着总 loss。DASH 场景下,我更建议把 loss 拆成三段去看。
前段监督区的 loss:这部分收敛速度应该比较快,因为监督信号明确。如果前段 loss 一直不降,可能说明发散度阈值设得太宽松,模型正在被大量低确定性轨迹干扰。
边界附近的 loss:这里最容易看到训练不稳定的信号。如果边界附近的 loss 出现周期性的尖峰,通常意味着发散度序列里有异常 step,或者是某些轨迹的采样质量波动太大。
后段无监督区的 loss:这部分没有监督目标时不能直接看 loss,要看模型输出的多样性、验证正确率和 KL 变化趋势。如果后段输出越来越千篇一律,说明模型可能在用降低探索度来压缩学习难度,这是一个需要警惕的信号。
另外,每条迭代之后记录平均发散度。平均发散度应该随着训练推进会下降,但不应该下降得过快。如果第二轮迭代平均发散度就趋近于零,说明监督视野过早收缩,模型失去了自我探索空间,很可能最后只能记住参考轨迹的表层格式,而不是推理能力。
5.2 评测指标和对比基线
评测时不能只看最终答案正确率,至少要同时看三个维度。
一个是最终正确率,比如 Pass@1。不同采样温度下的正确率都要统计,因为推理模型在低温和高温下的表现差异很大。
一个是监督视野的平均长度。这个指标反映模型对参考轨迹的依赖程度。如果正确率上升了,同时平均监督视野也在变长,说明模型确实在把更多路径内部化;如果正确率上升但监督视野没有变化,你可能只是把现有路径拟合得更好了。
另一个是长轨迹稳定性。用一组需要多步骤推理的题目,统计模型在长轨迹上是否容易出现重复、跳跃、自相矛盾。这个维度在普通 SFT 评测里经常被忽略,但推理模型必须关注。
和基线对比时,我建议至少跑三个对照组:整条轨迹全监督的 SFT 基线、只看最终答案的结果监督基线、以及带固定监督视野的版本。只有和这三组对比,才能看出 DASH 的收益是来自自适应机制,还是仅仅来自监督深度的整体调整。
5.3 什么时候应该停止迭代
训练停止的时机比很多人想象的重要。
如果每个迭代都重新采样、重新计算发散度,那么一个明显的停止条件是:连续两到三轮迭代,正确率和平均监督视野都没有显著变化。这说明模型已经到达当前数据分布下的稳定点,继续迭代只会增加成本。
另一种情况是发散度降到很低但正确率没有同步提升。这说明参考轨迹集合已经和模型行为高度一致,但数据本身的信息量不够了。此时正确的做法不是继续加大训练,而是补充新的、更难的任务数据,或者换一个更严格的验证器。
我个人的经验是,训练结束之后留一部分预算再做一次“放宽采样”测试。具体做法是用略高的温度和更大的采样数,看模型能不能生成出训练数据之外的新推理路径。如果能,说明模型保持了探索性;如果不能,说明它在蒸馏过程中把分布收得太窄,后续部署时遇到分布外问题会很容易崩。
6. 常见问题与排查顺序
6.1 发散度计算可能出的问题
发散度是对两个概率分布做比较,所以最容易出问题的环节就是概率分布本身拿得不准。
排查时先确认参考轨迹是不是真的来自当前策略。如果你用了旧缓存数据,或者用了另一个模型的输出,发散度就失去了“当前策略不认同”这个语义,整个自适应机制也会失效。
然后确认发散度计算的粒度。token 级 KL 容易受到特殊 token 和格式 token 的干扰。一个换行符或一个思考标记,可能让 KL 出现不合理的尖峰。建议先把 tokenizer 输出里的格式 token 过滤掉,再按 step 做聚合。如果尖峰依然存在,就要仔细看那一个 step 的内容,通常会发现模型在参考轨迹里插入了一段没有实际意义的重复内容。
最后确认发散度阈值是否随着训练进度在变化。很多固定阈值方案在训练初期有效,到训练后期会出现监督视野过早收缩的问题。最好把阈值绑定到发散度分布的滑动分位数,而不是一个绝对数值。
6.2 训练不稳定怎么办
训练不稳定通常表现为几种现象:训练 loss 尖峰、验证集正确率上下震荡、生成轨迹长度突然暴涨或暴跌。
先放宽采样温度。采样温度过低会让生成轨迹过于集中,发散度估计失真;采样温度过高会让轨迹质量下降。通常 0.7 到 1.0 之间是一个比较常见的区间,但不同模型可能差异很大,需要自己测。
再看验证器的误差。如果验证器把很多错误轨迹判成正确,这些轨迹会进入参考轨迹集合,发散度计算和监督视野都会失真。一个简单的做法是定期抽检验证器的判断结果,随机抽 50 条轨迹人工对比。
最后看学习率和批次大小。DASH 因为引入了动态监督视野,梯度分布比普通 SFT 更不均匀。如果学习率偏大,边界附近的梯度会把模型推离稳定区域。建议从普通 SFT 学习率的一半开始试,同时把批次大小控制在显存允许的上限附近,这样梯度估计会更稳定。
6.3 哪些场景不适合 DASH
DASH 并不是所有推理场景的最佳选择。
如果任务只有一个标准答案,而且中间推理步骤非常短,比如几步以内就能得出答案,那 DASH 的优势不明显。此时整条轨迹全监督或者结果监督都足够简单。
如果参考轨迹的质量无法保证,比如数据来源非常杂乱、同一道题有大量不同形态的中间推理,DASH 的发散度计算会很难稳定。发散度衡量的是策略与参考轨迹的偏差,如果参考轨迹本身就不是一个稳定目标,监督视野的调整就会变成噪声放大。
如果算力非常紧张,连一次完整 rollout 都跑不动,DASH 也不太适合。它本质上是一种在线学习方案,每一次迭代都要重新采样,这个成本是固定的,不像离线 SFT 那样一批数据可以反复使用。
还有一类情况很容易被忽视:如果模型已经收敛到一个不错的推理能力,只是想通过蒸馏把某条新的路径固化下来,那直接用固定监督视野就够了,不需要引入发散度自适应机制。过度复杂的方法在简单问题上往往不会带来额外的收益。
踩过几次之后我发现,很多问题不是 DASH 这个方法本身不对,而是前置条件没有满足:数据不是当前策略生成的,发散度算得太粗,验证器误判太多。先确认这三件事,再去调阈值和训练参数,整个流程会顺很多。