Prhub

#6014 [megatron, ckpt] refactor: optimize mcore ckpt manager impl, checkpoint yaml config APIs

原始 PR 作者 ETOgaosion 合并时间 2026-06-02 16:51 文件变更 33 提交数 9 评论 9 代码增减 +2222 / -536

执行摘要

重构 Megatron 检查点管理器,引入 v2 布局与迁移脚本

根据关联 Issue #5630,当前 mcore 检查点管理有多个后端(mbridge、dist_checkpointing、verl 原始实现),且两个模型相关 save_contents API(model、hf_model)实现复杂、命名混乱,使用户难以理解。通过与 FSDP 检查点管理器风格对齐,使代码更清晰、维护更简单。

本 PR 是重要的基础设施重构,推荐所有 Megatron 后端用户和开发者精读。重点关注:

1) 分层构建器模式如何解耦状态字典构造与 save/load;
2) 路径工具函数的组合与目录保障;
3) 迁移脚本的幂等设计和硬链接使用;
4) 配置类的继承设计;
5) ISEEKYAN 对 use_mbridge 命名的质疑提示 API 命名应清晰。

讨论亮点

Review 中主要讨论了以下几点:

  • use_mbridge 命名争议:ISEEKYAN 指出原始含义不再适用,建议改为 save_HF_weights;作者 ETOgaosion 回应将移除该参数,改为在 manager 内部根据 use_dist_checkpointing 推导。
  • _save_transformer_config 安全性:gemini-code-assist[bot] 建议用 try...finally 或字典操作避免就地修改,该建议被采纳。
  • 类型检查冗余:gemini-code-assist[bot] 指出 type(value) in ignore_types(含 Callable)无法正确识别可调用对象,应仅用 callable(),该建议被采纳。
  • 加载时旧布局检测:加载 v1 checkpoint 时给出明确错误提示并指引使用迁移脚本。

实现拆解

  1. 定义新的检查点目录结构 (v2 layout):在 verl/utils/megatron_utils.py 中新增路径工具函数,将检查点拆分为 model/(含 huggingface/ 和 dist_ckpt/)、optimizer/dist_ckpt/、extra/dist_ckpt/ 三个独立子树,并提供规范的路径生成函数。同时保留对 v1 布局的检测。
  2. 重构 MegatronCheckpointManager:将 generate_state_dict 拆分为 _build_model_sharded_state_dict、_build_optimizer_state_dict、_build_extra_state_dict 等构建器,分别构造所需的 sharded state dict。save_checkpoint 和 load_checkpoint 基于 save_contents/load_contents 动态调度到对应后端(mbridge/dist_checkpointing/PEFT)。__init__ 中提前验证 save_contents 与后端矩阵的合法性。
  3. 引入 use_mbridge 标志:废弃 use_dist_checkpointing,新增 use_mbridge 作为正向标志(use_mbridge = not use_dist_checkpointing)。use_dist_checkpointing 作为遗留别名仍接受。hf_model 内容仅在 mbridge 启用时可用。
  4. 新增迁移脚本 scripts/migrate_megatron_checkpoint_layout.py:支持将 v1 布局(dist_ckpt/ 单目录)转换为 v2 布局。利用 Megatron dist_checkpointing.load 的 key-driven 特性,默认使用硬链接(不增加磁盘占用),支持 symlink 和 copy 模式。脚本幂等。
  5. 后端配置抽取:在 verl/workers/config/checkpoint.py 中新增 McoreCheckpointConfig 和 MindSpeedCheckpointConfig 子类,将 mbridge 额外参数从通用配置中分离。
  6. 适配模型合并器等消费者:verl/model_merger/base_model_merger.py 新增 _default_hf_model_config_path 函数,根据后端类型选择正确的 HF 模型配置路径。
  7. 添加全面的单元测试:在 tests/utils/ckpt/test_megatron_checkpoint_manager_on_cpu.py 中新增 657 行测试,覆盖 flag 解析、构建器组合、save/load 调度以及边界情况。
文件 模块 状态 重要度
scripts/migrate_megatron_checkpoint_layout.py 迁移脚本 added 9.08
verl/utils/checkpoint/megatron_checkpoint_manager.py 检查点管理器 modified 9.04
verl/utils/megatron_utils.py 路径工具 modified 8.45
tests/utils/ckpt/test_megatron_checkpoint_manager_on_cpu.py CPU 测试 added 7.76
verl/workers/config/checkpoint.py 配置 added 7.37
verl/model_merger/base_model_merger.py 模型合并器 modified 6.29

关键符号

_is_v2_layout _is_legacy_layout migrate_one should_save_hf_model should_save_dist_ckpt_model _build_model_sharded_state_dict _build_optimizer_state_dict _build_extra_state_dict _default_hf_model_config_path

关键源码片段

scripts/migrate_megatron_checkpoint_layout.py migration-tool

新增迁移脚本,实现从 v1 到 v2 布局的幂等迁移,是整个重构的配套工具,关系用户升级路径。

def _is_v2_layout(ckpt_dir: Path) -> bool:
    # 检测是否为 v2 布局:只需检查是否存在 model/、optimizer/、extra/ 之一
    return any((ckpt_dir / sub).is_dir() for sub in ("model", "optimizer", "extra"))
​
​
def _is_legacy_layout(ckpt_dir: Path) -> bool:
    # 检测是否为 v1 旧布局:通过 dist_ckpt/ 或 huggingface/ 目录的存在来判断
    return (ckpt_dir / "dist_ckpt").is_dir() or (ckpt_dir / "huggingface").is_dir()
​
​
def migrate_one(ckpt_dir: Path, *, dry_run: bool, mode: str) -> str:
    # 迁移单个 checkpoint 目录(幂等),返回状态字符串
    if _is_v2_layout(ckpt_dir):
        return "skipped (already v2)"
    if not _is_legacy_layout(ckpt_dir):
        return "skipped (no recognised layout)"
​
    legacy_dist = ckpt_dir / "dist_ckpt"
    legacy_hf = ckpt_dir / "huggingface"
​
    new_model = ckpt_dir / "model"
    new_optimizer = ckpt_dir / "optimizer"
    new_extra = ckpt_dir / "extra"
​
    # 利用 Megatron key-driven load 特性,三个 dist_ckpt/ 指向同一物理数据
    _link_tree(legacy_dist, new_model / "dist_ckpt", mode)
    _link_tree(legacy_dist, new_optimizer / "dist_ckpt", mode)
    _link_tree(legacy_dist, new_extra / "dist_ckpt", mode)
​
    # huggingface 目录直接移动到 model/huggingface/
    if legacy_hf.is_dir():
        _move(legacy_hf, new_model / "huggingface")
    return "migrated"
verl/utils/checkpoint/megatron_checkpoint_manager.py core-logic

核心重构文件,修改量最大(+806/-436),实现分层构建器、新布局 save/load 和 flag 解析。

class MegatronCheckpointManager(BaseCheckpointManager):
    def __init__(self, ..., use_mbridge: Optional[bool] = None,
                 use_dist_checkpointing: Optional[bool] = None):
        # 处理遗留的 use_dist_checkpointing 参数,将其转换为 use_mbridge
        if use_mbridge is None and use_dist_checkpointing is not None:
            use_mbridge = not use_dist_checkpointing
        self.use_mbridge = use_mbridge if use_mbridge is not None else True
​
        # 验证 save_contents 与 backends 的兼容性
        if "hf_model" in self.save_contents and not self.use_mbridge:
            raise ValueError("hf_model is only supported with mbridge enabled")
        if not self.use_mbridge and "model" not in self.save_contents:
            raise ValueError("When mbridge is disabled, save_contents must include 'model'")
        ...
​
    def _build_model_sharded_state_dict(self, model):
        # 根据后端选择构建模型 sharded state dict
        if self.use_mbridge and "hf_model" in self.save_contents:
            return None # mbridge 模式下 HF 保存不需要 sharded dict
        # 否则使用 dist_checkpointing 的 sharded_state_dict
        if hasattr(model, "module"):
            model = model.module
        return model.sharded_state_dict()
​
    def _build_optimizer_state_dict(self, optimizer):
        # 总是使用 dist_checkpointing 保存优化器状态
        return optimizer.sharded_state_dict() if optimizer else None

评论区精华

use_mbridge 命名争议 设计

ISEEKYAN 指出 `use_mbridge` 原始含义不再适用,建议改为 `save_HF_weights`

结论:ETOgaosion 回应将移除该参数,改为在 manager 内部根据 `use_dist_checkpointing` 推导 · 已解决

_save_transformer_config 安全性 正确性

gemini-code-assist[bot] 建议使用 try...finally 或字典操作避免就地修改

结论:建议被采纳 · 已解决

类型检查冗余 style

gemini-code-assist[bot] 指出 `type(value) in ignore_types` 无法正确识别 callable,应仅用 `callable()`

结论:建议被采纳 · 已解决

风险与影响

兼容性风险:旧版 checkpoint(v1 布局)无法直接加载,必须运行迁移脚本。虽然脚本默认硬链接不增加磁盘,但跨文件系统时回退到 symlink 或 copy 可能带来额外空间/性能开销。use_mbridge 替换 use_dist_checkpointing 可能破坏现有 YAML 配置,但已保留向后别名。回归风险:核心 save/load 路径全面重构,多个 backend 组合可能引入回归,尤其是 PEFT 场景。测试覆盖新增 657 行单元测试,但未覆盖 HDFS 等分布式场景。安全风险:迁移脚本仅使用硬链接/拷贝,无特殊权限操作。性能风险:新布局新增目录创建和原子 rename 步骤,对训练整体影响可忽略。

对用户:新版本 checkpoint 使用 v2 目录结构,需更新自定义消费脚本(模型合并器已适配);从旧版本恢复训练需运行迁移脚本。对系统:内部逻辑简化,消除了复杂条件判断,减少 bug 风险;目录结构标准化便于与 FSDP 共享工具。对团队:代码可读性和可维护性显著提升;配置类分离降低耦合。

旧布局需迁移脚本 核心路径重构 配置字段重命名 跨文件系统硬链接回退 PEFT 场景回归风险

关联 Issue

#5630 [RFC] verl mcore checkpoint manager refactor

完整报告

参与讨论