1. Prompt
我是机器人RL方向的博士生正在学习ManiSkill,在学习时我尝试使用相同命令训练同一个任务,但是我发现最终的 success_once 指标并不是相同的,我感到十分焦虑,
我使用的命令如下:
python sac.py --env_id="StackCube-v1" \
--num_envs=32 --utd=0.5 --buffer_size=500_000 \
--total_timesteps=500_000 --eval_freq=50_000 \
--control-mode="pd_ee_delta_pos"
I conducted two independent training runs with the same setup. Here is a comparison of the success_once
metric at different stages:
Run 1:
success_once: 0.00, return: 2.59: 0%
...
success_once: 0.88, return: 39.84: 90%
success_once: 0.88, return: 39.84: : 500032lt [1:04:37, 132.17lt/s]
Run 2:
success_once: 0.00, return: 2.59: 0%
...
success_once: 0.31, return: 36.46: 90%
success_once: 0.31, return: 36.46: : 500032lt [43:38, 199.09lt/s]
The final success rate differs drastically (0.88 vs. 0.31).
请您浏览 this codebase,尤其是 sac.py,请帮我看看是什么原因导致了结果不一致呢?
2. Cursor-Gpt-5-high
我先快速在仓库里搜一下评估时 final_info
的结构、success_once
的生成方式,以及 ManiSkillVectorEnv
的实现细节,确认是否存在评估统计上的问题或非确定性来源。接着我会并行打开这些相关实现的源码片段以验证假设。
[5 tools called]
我将打开并阅读 mani_skill/vector/wrappers/gymnasium.py
的类初始化部分,确认 auto_reset
、ignore_terminations
与指标统计的具体行为,以判断评估是否可能错误聚合或存在非确定性来源。
[1 tool called]
我先并行查找 BaseEnv.reset
的重置与随机种子逻辑、enhanced_determinism
的实现,以及 StackCube-v1
任务的评估与成功判定,确认是否由环境非确定性或控制模式差异导致评估波动。随后我会核对 sac.py
的评估循环在 GPU 向量环境下是否正确聚合 success_once
。
[6 tools called]
结论(为什么同命令两次训练 success_once 会差这么多)
- 训练早期的随机动作未被固定种子控制。
sac.py
在学习开始前用envs.action_space.sample()
探索,但没有对action_space
本身调用seed
,Gymnasium 的Space
默认用自身的 RNG(来自 OS 随机源),这会导致最初缓冲区的数据不同,后续学习发散,导致最终指标差异很大。 - GPU 物理仿真存在不可完全复现的数值非确定性(碰撞、接触、并行归约顺序等)。
StackCube-v1
的成功判定包含静止判定与非常小的几何容差;微小数值差异会放大为“某一轮成功/未成功”的不同,进一步影响采样和学习轨迹。 - 评估环境每次
reset
(默认eval_reconfiguration_freq=1
)都会重新随机对象位姿;尽管这对于公平评估是需要的,但它会使单次评估更敏感于策略的细微差别,从而放大不同 run 之间的差异。 - 两次运行的训练速度差异很大(64min vs 43min),说明系统负载/调度不同;在 GPU 物理和大批量并行时,这种差异也会轻微改变浮点执行顺序,从而进一步增加非确定性。
直接可改的最关键点
- 固定 Gymnasium
action_space
的随机源(最重要,成本最低,收益最大):
if isinstance(envs.action_space, gym.spaces.Dict):
envs = FlattenActionSpaceWrapper(envs)
eval_envs = FlattenActionSpaceWrapper(eval_envs)
envs.action_space.seed(args.seed)
eval_envs.action_space.seed(args.seed + 1)
if args.capture_video or args.save_trajectory:
eval_output_dir = f"runs/{run_name}/videos"
if args.evaluate:
eval_output_dir = f"{os.path.dirname(args.checkpoint)}/test_videos"
print(f"Saving eval trajectories/videos to {eval_output_dir}")
进一步提升复现性的建议
- 将评估(至少评测时)切到 CPU 物理后端,或训练也统一 CPU 后端(速度会慢,但更稳定):
- 运行时传
--env_vectorization=cpu
或在创建环境时设置sim_backend="physx_cpu"
并将num_envs=1
(CPU 模式不支持多并行)。
- 运行时传
- 若必须用 GPU:
- 保持固定
--seed
,并固定action_space.seed
(如上)。 - 避免在同机上并发其它重负载任务,尽量减少运行时抖动。
- 可将评估步数/回合数加大(例如把
num_eval_envs
或num_eval_steps
提高),用均值稀释单次随机性。
- 保持固定
- 若希望评估更稳定、对初始化不太敏感,可暂时设
--eval_reconfiguration_freq=0
(减少每次评估的对象重随机),但要注意这会降低评估的随机覆盖面,长期应使用1
。
你看到的现象如何对应到上述点
两次 run 的
success_once
差异巨大,且 walltime 差异明显,符合“早期随机轨迹不同 + GPU 物理非确定性”叠加放大的模式。sac.py
里已经固定了 Python/NumPy/PyTorch 的种子,但没有固定 Gymaction_space
的种子,这是最直接的缺口。如需我直接帮你把
sac.py
加上action_space.seed
、并提供一个更“稳健评估”的命令行(CPU/GPU 两版),我可以顺手改好并给出完整命令。小提示:你命令里的
--control-mode
映射到Args.control_mode
是可以的;保持与训练/评估端一致即可。若要严格科研可复现,建议最终报告同时给出多种子均值/方差,并在附录说明 GPU 物理的不可完全确定性。
如果修复后你仍看到显著波动,可以把评估阶段切到 CPU 物理或增加评估覆盖度;这两招基本能稳定到小方差区间。
需要我继续把评估改成“CPU-only”(只在评估时切 CPU)并给出对应命令吗?
最后提醒:默认
capture_video=True
会拖慢训练速度且引入 I/O 抖动,如果不需要视频,可加--capture_video=False
提升稳定性。