RL 的 loss 很容易“正常下降”,行为却越来越差。因为采样、target 和策略(policy)都在变。先做一个小到可以预测答案的环境,比直接跑大 benchmark 更容易发现问题。
一条完整训练循环长什么样
记录策略版本与边界→构造 target
return / TD / GAE→更新网络
固定 target 与 old policy
采样完成后先检查 shape、mask、reward scale,再更新。评估用独立环境和固定设置,不把评估轨迹(trajectory)偷偷塞回训练。Checkpoint 不只是网络权重:optimizer、target network、随机数状态、计数器,以及必要时 replay buffer 都影响能否续跑。
如果还不确定 rollout 应该存什么,先看一条训练记录:20 个输入位置里只有 9 个是策略动作,工具返回和 padding 不该混进 loss。那里也区分了任务终止与收集截断,再回来检查 target 会更容易。
先跑 2 个 CPU 小程序
在仓库根目录运行;第一个只依赖 Python 标准库,第二个需要 PyTorch:
python3 05-post-training/deep-rl/code/rl_checks.py
python3 05-post-training/deep-rl/code/torch_updates.py
算术与边界检查验证 Bellman、return、GAE、Double DQN 和 ESS;PyTorch 检查验证 Actor 梯度方向、target 无梯度、连续动作的梯度路径,并训练一个两动作 bandit。
Bandit 奖励(reward)是 $[1,3]$。初始概率各 0.5,固定随机种子,采样动作,并用以当前期望奖励为 baseline 的 REINFORCE 更新 logits。预期是第二个动作概率上升,而不是要求每一步 reward 都单调增长。它只检查单步 bandit 上的更新机制,不是 DQN / SAC 的完整复现或性能 benchmark。
每个阶段留一个可检查的量
| 阶段 | 至少记录 | 能发现什么 |
|---|---|---|
| 数据 | episode length、termination / truncation、reward 分布 | 提前结束、空奖励、错误 reset |
| Value | target、预测值、TD error、绝对值范围 | 发散或 target 量纲错 |
| Policy | entropy、动作比例、KL / ratio(适用时) | 过早塌缩、更新过猛、无变化 |
| 优化 | gradient norm、学习率、更新次数 | 梯度断开、更新频率错 |
| 评估 | 多 seed return、成功率、失败类型 | 平均数掩盖的不稳定性 |
Loss 和 reward 用不同坐标轴也要标清。高 Q、低真实 return 可能是过估计;低 entropy 可能是学会,也可能是卡死。解释要结合行为,而不是凭一个 scalar 下结论。
比较算法,先把预算说清楚
环境交互数、梯度更新数、wall-clock 和硬件不是同一个预算。每个训练 seed 可能有多个评估 episode;episode 不能当成独立训练 seed 来夸大样本量。报告 seed 间差异和合适的区间,不只挑最好的一条曲线。
调参用 validation 环境或划分,最终 test 尽量少碰。平滑曲线保留原始数据,并说明窗口;失败运行也要计入。多任务汇总时说明尺度归一化,不能让 reward 大的环境自动决定总排名。
两条曲线均值一样,结论也一样吗?
假设各做 5 次独立训练,分别算出每个模型在独立评估回合(episode)里的平均 return:
| 方法 | 5 次训练的评估均值 | 再取平均 | 中位数 | IQM |
|---|---|---|---|---|
| A | 10,10,10,10,50 | 18 | 10 | 10 |
| B | 16,17,18,19,20 | 18 | 18 | 18 |
只报均值,两者相同;只挑最佳运行,A 很亮眼;需要可重复的表现时,B 更稳定。
最后一列 IQM(interquartile mean,四分位均值)是文末那篇统计评估论文在 §4.3 推荐的汇总:去掉最低和最高各 25% 的运行,对中间一半取平均。5 次的 25% 不是整数,论文配套的 rliable 库用 scipy.stats.trim_mean(x, 0.25),两头各去掉 1 次:A 剩 10、10、10,B 剩 17、18、19。它不会被 A 那一次 50 带偏,又比中位数多用了几次运行;论文还建议用按任务分层的 bootstrap 给它配置信区间(§4.1)。即便如此,也没有一个脱离任务的“正确汇总法”:要先说关心典型表现、失败风险,还是充分调参后的最好结果,并报告相应的不确定性。五个 seed 只是算例,不是足够统计功效的保证。
每个训练 seed 下再跑 100 个评估回合,有助于估计那一个训练结果的表现,却不能把 5 个模型说成 500 次独立训练。重采样或区间估计要尊重层级:环境任务、训练 seed、评估回合,不是一堆可以随便混的独立行。
一次可以复查的消融怎么安排?
例如想检查 GAE 的 $\lambda$,先固定环境、奖励、网络、采样量、优化设置和评估规则,只改 $\lambda$。然后才单独比较 Critic 容量或 rollout 长度。
| 记录 | 一个具体约定 |
|---|---|
| 主问题 | 同样交互预算下,改变估计长度能否改善稳定性? |
| 比较对象 | $\lambda=0$、一个中间值、$\lambda=1$;不是同时换三个网络 |
| 主要结果 | 预先选定预算点的独立评估表现及跨 seed 差异 |
| 辅助诊断 | Advantage 分布、Critic 误差、KL、梯度尺度 |
| 异常处理 | 保留失败运行,说明提前停止和排除条件 |
若训练 loss 下降、任务变差,保留 rollout 看模型实际在干什么。数字能定位方向,却不能代替观察:机器人可能不动也能拿稳定小分,语言模型(language model, LM)可能把回答写长来讨好 judge。
续训测试为什么也算算法正确性?
在小环境中做两组:一组连续训练,另一组在同一步保存并恢复。比较接下来的采样、target、参数和计数器。确定性设置下可以期待严格对齐;非确定性设备或并行环境下,先定义可接受的差异,不承诺所有配置逐 bit 相同。
只保存 Actor 权重,可能漏掉 optimizer 动量、Critic、target network、replay、随机数状态和探索日程。恢复后策略还能输出动作,不代表训练真的从同一个状态继续。应该把“能加载”和“能按原训练语义恢复”分开测试。
一个有顺序的排错清单
先随机策略与简单基线(baseline);再单条 transition 手算;再固定小 batch 过拟合(overfitting);再检查随机性与边界;最后扩到完整训练。一次改一个因素。
如果单 batch 都拟合不了,先查代码。如果 train return 好、eval 很差,查分布与评估配置。如果 reward 涨、任务没改善,查 reward specification。三种情况不该用同一种“再加训练步数”处理。
扩展实验怎么做
给 bandit 加奖励噪声,比较带 baseline 与不带 baseline 的多 seed 梯度方差(variance);把 GAE 的终止改成截断,看 bootstrap 与 trace 是否分别正确;改变 Double DQN 的两个排序,检查选动作和估值是否真的分离。
参考:Deep RL 的统计评估问题。最后接到语言模型与工具交互。
本页术语 8
- 策略 policy
- 给定状态,规定怎样选动作;可以输出一个动作,也可以输出动作的概率分布
- 轨迹 trajectory
- 与环境交互得到的一串状态、动作和奖励;采样时也可能只保存其中一段
- 奖励 reward
- RL 里指导策略更新的标量信号
- 回合 episode
- 从初始状态开始的一次交互过程;采样片段结束不一定代表任务真正结束
- 语言模型 language model, LM
- 对文本序列建模,用上下文预测或生成后续内容
- 基线 baseline
- 用来比较新方法的参考方法或配置
- 过拟合 overfitting
- 训练集越来越好,没见过的数据反而变差
- 方差 variance
- 估计量的波动幅度;重要性加权最容易在这里爆炸