Prhub

#2243 fix: restore args.ckpt_step after load_other_checkpoint

原始 PR 作者 keepkeen 合并时间 2026-08-12 13:45 文件变更 1 提交数 1 评论 0 代码增减 +14 / -8

执行摘要

修复 load_other_checkpoint 未还原 args.ckpt_step 导致后续加载错迭代

PR body 明确指出,--ref-ckpt-step--opd-teacher-ckpt-step 会永久泄漏进 args.ckpt_step,导致同一进程内后续检查点加载解析到错误的迭代。根本原因是 load_other_checkpoint 仅在保存的 old_ckpt_stepNone 时还原,而 args.ckpt_step 默认是 None,所以正常情况下还原永远不会执行。这会导致 get_load_checkpoint_path_by_args 在每次后续加载(如 --keep-old-actor 时的 old_actor 加载)时使用泄漏的 iteration,静默加载错误权重或直接崩溃。

建议合入此修复,因为它修复了一个静默的错误权重加载问题,且改动简单、逻辑清晰。值得关注的设计是使用 old_args 元组统一保存和还原多个参数,这是该模式的一次合理扩展,也提示团队在未来修改 load_other_checkpoint 时注意参数生命周期的管理。

讨论亮点

该 PR 未被合并,且无 review 评论与讨论。

实现拆解

  1. 回归根因定位slime/backends/megatron_utils/actor.pyload_other_checkpoint 中,原本用 old_ckpt_step 临时保存 ckpt_step,但还原操作被错误地放在 if old_ckpt_step is not None 分支内。由于 args.ckpt_step 默认是 Noneold_ckpt_step 通常为 None,因此还原逻辑从不执行。
  2. 修复方案:将 ckpt_step 加入 old_args 元组(该元组已用于保存和还原 loadno_load_optimno_load_rngfinetune),并在 load_checkpoint 之后无条件地从 old_args 还原所有字段,包括 ckpt_step。这消除了原代码中“保存值与是否应用覆盖”条件不一致的问题,使还原逻辑在总是发生。
  3. 代码结构清理:原有 old_ckpt_step 单独保存和条件还原的代码被删除,统一用 old_args 元组的元组解包赋值,保持了与其他参数还原模式的一致性,简化了控制流并避免未来再次引入类似不一致。

该变更没有修改测试文件,PR body 解释了原因:actor.py 依赖真实 Megatron 运行环境,无法在 CPU 单测中导入;且现有 GPU CI 路径(如 test_qwen3_4B_ppo.py)没有设置 --ref-ckpt-step,因此没有暴露此问题。

文件 模块 状态 重要度
slime/backends/megatron_utils/actor.py Megatron 工具 modified 6.94

关键符号

load_other_checkpoint

关键源码片段

slime/backends/megatron_utils/actor.py core-logic

核心修复文件,`load_other_checkpoint` 方法调整了参数保存与还原逻辑。

def load_other_checkpoint(self, model_tag: str, path: str) -> None:
    # 将 ckpt_step 也纳入 old_args,与其他参数一起统一保存与还原
    old_args = (
        self.args.load,
        self.args.no_load_optim,
        self.args.no_load_rng,
        self.args.finetune,
        self.args.ckpt_step,
    )
    self.args.load = path
    self.args.no_load_optim = True
    self.args.no_load_rng = True
    self.args.finetune = True
​
    # 仅当显式指定了 ref/teacher 的 ckpt_step 时才覆盖 args.ckpt_step
    if model_tag == "ref" and self.args.ref_ckpt_step is not None:
        self.args.ckpt_step = self.args.ref_ckpt_step
    elif model_tag == "teacher" and self.args.opd_teacher_ckpt_step is not None:
        self.args.ckpt_step = self.args.opd_teacher_ckpt_step
​
    _, _ = load_checkpoint(
        self.model,
        None,
        None,
        checkpointing_context={},
        skip_load_to_model_and_opt=False,
    )
​
    # 无条件还原所有 args,包括 ckpt_step,避免泄漏到后续加载
    (
        self.args.load,
        self.args.no_load_optim,
        self.args.no_load_rng,
        self.args.finetune,
        self.args.ckpt_step,
    ) = old_args
​
    self.weights_backuper.backup(model_tag)
    self._active_model_tag = model_tag

评论区精华

没有提炼出高价值讨论线程

当前评论区没有形成足够清晰的争议点或结论,后续有更多讨论时会体现在这里。

风险与影响

主要风险是回归风险:在 load_other_checkpoint 被调用的场景(ref 或 teacher 检查点加载)中,此改动改变了 args.ckpt_step 的处理方式。但是修复逻辑与 load 等参数的处理完全一致,且改动极小,风险较低。由于没有相关测试,如果未来有人修改了 load_other_checkpoint 的参数生命周期逻辑,此模式可能再次被破坏。建议在 GPU CI 中增加覆盖 --ref-ckpt-step 的场景,以防回归。

影响范围:直接影响 Megatron 后端的多模型检查点加载流程,特别是使用 --ref-ckpt-step--opd-teacher-ckpt-step 的 PPO/OPD 训练场景。修复后,args.ckpt_step 在加载完 ref/teacher 后能正确还原,后续同一进程内的 actor/rollout_actor 权重加载会使用正确的迭代。对用户而言,它修复了潜在的错误权重加载和潜在崩溃;对系统而言,它保证了多阶段训练中检查点加载的确定性。

缺少测试覆盖

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论