
8 月中旬我差点把整套蒸馏流程推倒重来。过去三个月我一直在打磨一条在线策略蒸馏on-policy distillation的训练线轻量 student 策略一边在环境里做强化学习一边从 teacher 策略的 logits 上学习为了让稀疏奖励能训下去我又给环境加了一个基于“到目标距离”的 reward shaping。前 20 万步曲线非常漂亮KL 散度和稀疏回报双双下降我还以为路子走对了。结果 30 万步以后把 shaping 一关用纯任务回报一评估成绩比不加 shaping 的老基线还低。这篇文章就是 6 到 9 月这段折腾的记录怎么从固定 shaping 一步步改成自适应监督中间哪些设计被推翻哪些坑值得后来人绕开。如果你也在做强化学习里的知识蒸馏、模仿学习或者正打算给稀疏奖励任务加 auxiliary bonus这篇值得花十分钟翻一遍。我会把失败过程和参数细节都摊开来说因为这些内容跟论文里“我们提出的方法有效”之间隔了太多日志文件和深夜调参。1. 问题起点6 月的 on-policy 蒸馏基线把时间拨回 6 月。当时我在做的是一个典型设置teacher 策略是一个参数更多、训练更久、带额外观测输入的成熟策略student 是一个需要部署的轻量网络。最朴素的想法当然是 behavior cloning拿 teacher 的长轨迹直接监督式训练 student。那套方案在简单任务上没什么问题但一旦场景带点随机性student 很容易在一个很小的高频状态分布里过拟合一旦跑出 teacher 没去过的区域行为就完全失智。1.1 基线管线在 student 自己的 rollouts 上学老师所以我把行为克隆换成了 on-policy distillation。这个翻译可能有点绕其实一句话就能说清student 用自己当前的策略做 rollout收集到一批状态和动作teacher 对这些状态实时给一个软化后的 logits 输出student 的更新目标是“自己的 policy gradient 加上对 teacher logits 的 KL 匹配”。这样保证了一个关键性质——监督信号永远发生在 student 真实访问到的状态上而不是离线数据里的固定分布上。早期我看到不少资料管这叫 policy distillation也有叫 imitation learning 变体的但核心殊途同归你始终在把“teacher 的偏好”移植到“student 的轨迹范围”里。这个改动带来的第一个收益是分布覆盖明显变好。我把 teacher 对 student 访问状态的置信度拉出来统计6 月基线里大约 72% 的状态落在 teacher 的高置信区而纯 behavior cloning 只有 54%。代价是训练效率降低每步都要跑一次 teacher 前向batch 又不能太大整体耗时比离线蒸馏高了不少。这个代价换来的是“student 在探索路径上始终有人带”我觉得值得。1.2 那个让我走错路的 shaping 设计问题出在任务端的奖励设计。我的实验环境是一个稀疏奖励导航任务智能体要从随机起点出发在 200 步内到达目标区域到达才能拿到 1其余全是 0。这个设定下纯强化学习非常难训因为绝大多数 rollout 都是“零信息”。于是我给环境加了一个 potential-based reward shapingF(s, a, s) γΦ(s) - Φ(s)Φ(s) -1.2 * distance_to_target(s)从理论上讲Ng 等人 1999 年的结论是potential-based shaping 不会改变原环境的收敛最优策略听起来非常安全。我当时也这么想直接设了一个固定系数 β0.1 加到总奖励里。前 20 万步效果确实惊艳不加 shaping 的 PPO 平均稀疏回报还在 0.3 上下挣扎加了 shaping 的已经爬到 0.85收敛速度快了将近两倍。我甚至在 6 月底的记录里写了一句“这路线的瓶颈已经解决了”。现实很快教育了我。到 30 万步左右在关闭 shaping、只用真实任务回报的评估里固定 shaping 的版本不仅没有继续上升反而从 0.85 回退到 0.62 附近。同一时间无 shaping 基线还在慢慢蹭到 0.58。也就是说做了三个月的辅助奖励设计最终收益几乎是零。当时也有同事建议用课程学习把目标距离从近到远手动排课但我需要的是能自动适配多个任务、不需要人工设计的方案。课程学习要在环境侧做难度调度和蒸馏的 teacher 信号耦合太紧很容易引入新的偏置所以我没有采纳。2. 复盘为什么静态 reward shaping 在蒸馏里会失效当时第一反应是 shaping 系数太大、把最优策略带偏了。我把 β 从 0.1 降到 0.02 重新跑结果依然回退只是回退得慢一点。这说明问题不是“度”的把握而是“机制”本身出了问题于是我开始翻定理的边界条件。2.1 potential-based 定理的成立条件在“师生共训”里破功Ng 的结论有非常明确的前提它讨论的是固定 MDP、固定策略迭代下的最优策略不变性。可我的系统里有两个东西每轮都在变一是 student 策略本身每轮梯度更新后行为分布都在移动二是 teacher 策略我当时还没有冻结 teacher而是让它每隔 200k 步继续用混合数据微调。也就是说训练过程是非平稳的老师也在变、学生也在变、shaping 的 Φ 又是先验写死的。三者叠加后shaping 的“不改变最优策略”保证在严格意义上已经不成立了。打个比方如果比赛的正规规则只有“到达终点算赢”shaping 相当于沿途放了一些“前进计分点”。在固定规则下加计分点确实不影响最终赢家。但如果你一边比赛一边改计分点位置、还一边暗示选手哪些路径容易拿分选手自然会去跑那些“在计分意义上很占便宜”的路线而不是终点意义上的最优路线。这正是我观测到的现象。2.2 分布漂移teacher 高置信区在缩小shaping 在带偏用数据说话。我把 student 访问的所有状态按 teacher 置信度排序定义“高置信区”为 teacher 输出概率最高 bin 的前 30%。6 月基线里72% 的 student 状态落在高置信区7 月初固定 shaping 跑 300k 步后这个数字降到 41%。与此同时另一个指标更难看shaping 回报占总回报的 EMA 从 20% 爬到了 65%。这两个数字指向同一个机制student 在 shaping 信号引导下发现了“能持续拿到距离缩减 bonus”但最终走不到目标区域的轨迹模式并在这个模式上越走越远而 teacher 因为大部分时间看到的是这些被带偏的状态能给的监督也越来越像是在配合这个错误模式。分布漂移和 shaping 偏置互相喂形成恶性循环。这时候无论把 KL 系数调多大都只能压制问题不能解决根源。2.3 拆解出的真正病灶把 teacher 当成了“静态权威”复盘到这一步我意识到自己犯了个认知错误我把 teacher 当成了永远正确的静态权威但工程实现里它根本不是。teacher 每 200k 步用混合数据微调一次参数会漂移student 被 shaping 带偏后teacher 看到的状态分布变差微调后的 teacher 也跟着变差。相当于老师和学生一起学坏而这个“坏”的方向恰恰是 shaping 提供的。我把这三点分开验证先冻结 teacher单独看 shaping 的影响再去掉 shaping单独看在线 teacher 的影响最后两者都不改只把 shaping 从“全程固定”改成“按状态自动收放”。前两组的最终评估回报都在 0.58~0.63 之间说明单独修任何一个都不够必须把“教学信号”和“环境奖励辅助信号”当成一个整体来设计。这个认知直接导向了后面的自适应监督框架。3. 设计转变把“固定监督”替换成“自适应监督”7 月中旬我开始动手改框架。目标很明确不能再让 shaping 和 teacher 监督以一种静态强度参与训练它们需要随 student 能力、状态差异和任务阶段动态变化。我给这套机制取了个朴素的名字自适应监督。下面三个小节是我按优先级排序的思考路径。3.1 监督强度应该跟着师生差距走而不是定死第一条改动落在 KL 监督系数上。此前 α_KL 是固定的 0.2student 学得快、学得慢都是一样的“讲课音量”。这带来的问题是后期 student 已经能独立完成任务却还被迫贴着 teacher 的分布均值策略熵被压得很低探索能力几乎为零。这跟蒸馏研究里常提到的“过度正则导致探索坍塌”是同一件事。我把 α 改成师生 KL 散度的一个单调函数α_t α_min α0 * sigmoid((KL_t - KL_ref) / T)其中 KL_t 是每个更新阶段 student 与 teacher 之间的平均 KL 散度KL_ref 是参考水位T 控制响应灵敏度。举个具体计算α00.2α_min0.002KL_ref0.5T0.1。当 KL_t0.2 时sigmoid(-3)0.047α≈0.011老师几乎“闭嘴”让 student 自己探索当 KL_t0.8 时sigmoid(3)0.953α≈0.19student 明显跑偏老师声音立刻放大。这个函数的好处是单调、连续、没有额外学习参数最小化工程侵入。3.2 shaping 从“油门”改“刹车”只在偏移时介入第二个改动更难做因为 shaping 的问题不只是“给太多”而是“一直给”。我把 shaping 定位成一种“临时导引”它只应该在 student 完全盲飞的时候提供方向一旦 student 的真实回报开始产生就必须逐步退场。落地方式是我自己项目中反复试出来的用一个 EMA 统计 shaping 收益占总收益的比例 shape_ratio。每 50 步判断一次如果 shape_ratio 超过 0.35意味着 student 正在靠 shaping 刷分而真实回报没跟上就把 β_t 往下退退的方式是乘性衰减 β_t * 0.995低于阈值时β_t 缓慢回升但封顶不超过 β00.08。这里为什么用 EMA 而不是单步瞬时值因为瞬时值抖动太大单步里 ratio0 或 1 都很常见直接做开关会把训练变成一场振荡。3.3 把两个通道装回同一个框架教学资源的动态分配等两个改动都跑通我再回头看它们本质上是在做同一件事把“监督力度”当作一种随训练阶段动态分配的资源。teacher 的 KL 监督负责纠偏方向环境里的 shaping bonus 负责在稀疏信号早期提供梯度两条通道合起来就是“teacher 传递给 student 的完整偏好”。最后我把框架整理成一句话监督通道应该根据 student 与 teacher 的差异和 student 的真实绩效自动增大或减小参与强度。这句话听起来平淡但它直接决定了后面所有实验的损失函数长什么样。8 月的最终损失可以写为L L_policy_gradient - α_t * KL_loss(student || teacher) - β_t * E[F_shaping]其中 α_t、β_t 都不是常数而是随 KL_t、shape_ratio、真实回报 EMA 动态变化的状态量。这里有个经验值α_min 不要设成 0留一个小尾巴纯粹为了防止 student 在冷启动阶段完全失去 teacher 约束β_t 的衰减不要用线性 decay乘性衰减更稳。也许有人会问既然都叫自适应了为什么不把 threshold 和 kl_ref 直接学成一个网络参数我在 8 月初试过用一个小的 meta 网络输出这两个参数训练很不稳定它容易过拟合短期回报造成周期性崩塌。最终结论是训练阶段用规则式自适应比学一个复杂 meta 网络更稳等数据足够、回报曲线足够平滑再考虑端到端自动调参。4. 7—8 月的落地实现与实验对比纸上推演再完美落到训练管线里又是另一回事。这一节我把 8 月跑通的关键代码逻辑和参数直接贴出来你可以按这个顺序复现也可以只挑其中一半用到自己的项目里。4.1 训练管线的关键改动伪代码大概长这样我省略了 PPO 内部实现只标出与“自适应监督”相关的部分# student 与环境交互收集 batch states, actions, real_rewards, dones student_env_rollout() # teacher 前向冻结或按 EMA 更新 with torch.no_grad(): teacher_logits teacher(states) # 计算师生 KL 与自适应 alpha kl_t kl_divergence(softmax(teacher_logits), softmax(student_logits)) alpha_t alpha_min alpha0 * sigmoid((kl_t - kl_ref) / temp) # 计算 shaping 收益占比EMA 平滑 shape_bonus gamma * phi(next_state) - phi(state) shape_ratio ema(shape_bonus / (real_rewards shape_bonus 1e-8)) if shape_ratio threshold: beta_t max(beta_min, beta_t * 0.995) # 乘性衰减 else: beta_t min(beta0, beta_t * 1.0005) # 缓慢恢复 # 最终 loss loss (- advantage * pg_log_prob - alpha_t * kl_loss - beta_t * shape_bonus_mean)每一行改动都有对应动机。比如 shape_ratio 分母里那个 1e-8纯粹是为了防止真实回报和 shaping 同时为 0 时除零kl_ref 和 threshold 是最影响行为的两个参数我建议先固定 threshold0.35 这个保守值跑 100k 步观察曲线再决定要不要往下调不要一上来就压到 0.2。4.2 关键参数与调参心得下面这张表是 8 月在导航任务上稳定复现的参数组合。参数取值作用调试建议α00.2KL 监督最大强度先按 PPO 原 KL 系数的 2 倍设α_min0.002最小监督残留不要设 0冷启动需要兜底kl_ref0.5师生 KL 参考水位以初始 KL 的 40% 作起点T0.1sigmoid 响应温度T 太大不敏感T 太小易震荡β00.08shaping 初始强度比静态基线低一档threshold0.35shaping 占比触发阈值先保守再收紧decay0.995β 乘性衰减速率0.99 激进0.999 太慢这里的调参顺序有个讲究先调 kl_ref 和 T让 α_t 曲线在训练中呈现“前期高、后期低”的自然形态再调 threshold 和 decay因为 shaping 的退场节奏只有建立在 KL 得体的前提下才有意义。反过来调会很难受你会分不清是哪个机制在拖累收敛。4.3 三个版本的最终对比实验我做了四组对照无 shaping 基线、固定 shaping6 月方案、只改 α 的自适应、完整的 αβ 自适应监督。每组开 3 个随机种子跑 500k 步评估时关闭一切 shaping只看纯任务回报。版本300k 评估回报500k 评估回报终期师生 KL备注无 shaping 基线0.470.580.31收敛慢后段稳定固定 shaping0.710.580.12前期最快后期回退只改 α0.660.700.21中段提升仍受 shaping 拖累完整自适应0.730.820.19前期接近固定版本后期不掉数字能看出两件事第一固定 shaping 在 300k 时确实领先但到 500k 打回原形这正是前文那个恶性循环的延迟体现第二完整自适应方案在前期没有牺牲太多速度后期还把 6 月方案丢掉的收益捡回来了。终期师生 KL 保持 0.19 而不是降到 0.1 以下这点我特别满意——说明 student 保留了相当一部分自主行为空间没有完全被 teacher 均值捆死。这里还要强调一个评估细节评估时不但要关掉 shaping还要把 teacher 的 logits 监督也关掉只让 student 用自己的策略做 rollout。很多复现翻车都是因为评估时还把 KL loss 开着student 白拿了 teacher 的信息评估结果虚高。我一开始顺手用了训练采样器做评估差点踩进去后来才改成独立评估器。这个框架随后又在另一个离散动作任务上跑了一遍threshold 和 kl_ref 零改动直接迁移效果从原来的“能训但不稳”变成“稳定不掉”验证了它不是针对某个连续控制任务的特调方案。5. 这三个月踩过的坑与排查手法自适应监督听起来高级落地时一坑接一坑。这一节把我踩过的几个典型问题原原本本列出来每个都带排查思路和最终解法。5.1 α 更新太频繁训练会震荡一开始我把 α_t 放进每个 mini-batch 里实时计算结果 KL 曲线高频抖动回报曲线跟着一起抖看上去像 PPO 本身不稳定浪费了整整两天排查是不是 advantage 归一化出了问题。最后把 α_t 的更新频率降到每 100 步一次并且对 KL_t 本身做 EMA 平滑抖动立刻消失。经验公式KL_t_ema 0.95 * KL_t_ema 0.05 * KL_t再配合滞后触发——只有当 KL_t_ema 与上次触发值偏差超过 ±0.05 时才真正更新 α_t。这样 α_t 不再跟随单次 batch 的噪声而是只响应一个阶段性的趋势变化。提示遇到“自适应导致训练震荡”不要先怀疑 PPO 超参先看自适应变量是不是在用高频瞬时值当输入。5.2 teacher 被 student 反向污染越教越差7 月中旬我把 teacher 也放开在线微调结果跑着跑着 teacher 自己的评估回报开始下滑跟 student 一起完蛋。排查后发现teacher 的微调数据 90% 来自 student 的 rollout一旦 student 被 shaping 带偏teacher 学到的全是偏置状态下的反馈等于学生把老师带坏了。最终方案是 teacher 冻结为主、离线参考校验为辅teacher 每 200k 步才允许用一小撮离线 reference 数据做低学习率微调student 数据一律不进 teacher 的更新集合。代价是 teacher 对任务新场景的适应能力下降但换来的是教学信号稳定这个交换在蒸馏场景里非常划算。5.3 日常监控五个必看指标排查到最后我沉淀出一份监控清单每天训练前先确认这五条曲线是正常的再离开。你完全可以抄作业指标看什么异常信号shape_ratio EMAshaping 占总回报比例持续 0.5 说明在刷分师生 KL EMA监督需求水位剧烈震荡 α 更新过快student 策略熵探索空间过早塌到 0 监督过强teacher 高置信覆盖比分布是否漂移连续下降 student 飞出教学范围真回报−shaping回报差辅助信号是否“兑现”差距持续扩大 shaping 在误导这五个量全部写进 tensorboard 或者 wandb 之后很多问题不再需要靠“感觉”看一眼曲线就能定位。尤其是 shape_ratio 和真实回报−shaping 回报之差这两个量它们相当于给 shaping 装了一个“业绩考核表”是这套自适应方案的守护者。我后来养成的习惯是每次训练启动前先花十秒钟扫一眼这五条曲线的滚动趋势再决定今天要不要动参数三个月下来帮我省了大量试错时间。6. 一点个人体会把“何时收回监督”当作一等公民三个月折腾下来我的主要体会不是“reward shaping 不好”也不是“自适应监督是万能药”而是在非平稳训练过程中“什么时候收回一个辅助信号”和“给多大辅助信号”同等重要甚至前者更难。静态 shaping 之所以翻车不在于它给了梯度而在于它给了太久的梯度让 student 在错误方向上越走越自信。如果你也要在项目里引入类似的机制我的建议是先把“退出条件”和“监控指标”写进设计文档再谈奖励函数怎么设计。我在 6 月犯的最大错误就是把 shaping 当作纯奖励工程问题忽略了它与 teacher 监督、student 分布漂移之间的耦合。改到自适应监督之后代码其实只多了一个 sigmoid、一个 EMA 和一个乘性衰减真正的难度在于想清楚这三个量各自该观察什么信号。这个框架后续我还在往上叠东西把 threshold 和 kl_ref 从手工设定改成根据回报 EMA 自动调整类似让“老师自己决定什么时候该闭麦”。目前看来方向可行但还不够稳等 10 月的实验数据出来再整理一篇。到时候再来填这个“续”的最后一个坑。