已关闭
【实践文档】:glm4.5v开发模型实践 #560
SuperKoalaKing创建于  8月4日关闭于  9月15日
SuperKoalaKing
8月4日 创建

MindSpeed MM FSDP2 模型迁移实践(GLM-4.5V,megatron 后端 → 纯 FSDP2 后端)

概述

本文记录将 GLM-4.5V(106B MoE 多模态理解模型) 从 MindSpeed MM 的 megatron-FSDP2 路径
(mindspeed_mm/models/transformers/glm4v_moe/ + pretrain_transformers.py)迁移到
插件式纯 FSDP2 后端(mindspeed_mm/fsdp/ + trainer.py)的完整过程与验证方法
(对应 issue #370)。

本文不重复共性内容,聚焦 GLM-4.5V 的差异点、双后端等价性验证方法与实测踩坑。

源模型结构与迁移重点

GLM-4.5V 的参考实现为 Transformers(≥4.57)原生 Glm4vMoeForConditionalGeneration:

Glm4vMoeForConditionalGeneration
├── model.visual                      # 视觉编码器(ViT)
│   ├── patch_embed / blocks.{*} / merger
├── model.language_model              # 语言模型(46 层,MoE:128 路由专家 + 1 共享,top-8)
│   ├── embed_tokens
│   └── layers.{*}                    # layers.{i}.mlp.experts 为稀疏专家(首层 dense)
└── lm_head
工作项 落点
模型接入 mindspeed_mm/fsdp/models/glm4_5v/modeling_glm4_5v.py(注册 id:glm4v_moe)
数据接入 无新增文件:复用 huggingface 数据集 + template glm4v_moe + collator qwen3vl
权重转换 复用 mm-convert ExpertMergeDcpConverter,转换脚本 examples/glm4.5v/convert_glm4_5v_ckpt.sh
训练配置/脚本 examples/glm4.5v/glm4_5v_full_sft_106B.yaml + finetune_glm4_5v_106B_fsdp.sh

模型接入采用薄封装继承 HF 原生类:__init__ 内将 RMSNorm、多模态/视觉 RoPE、视觉 embeddings
替换为 NPU 融合版(自已验证的 megatron 侧实现移植并去除 Megatron 依赖),支持 chunk-loss 前向,
非 NPU 环境自动回退原生实现。

差异点一:MoE 专家权重布局

同一个模型在两个后端需要两种专家布局,这是 GLM-4.5V 迁移与 Qwen3-VL 接入最大的不同:

后端 专家参数布局 原因
megatron 后端(基线) 2D 展平 (E*in, out) 既有 glm_4v_moe_text 实现的契约
纯 FSDP2 后端(本迁移) 3D 堆叠 (E, in, out) 框架专家并行沿专家维 Shard(0) 切分 + npu_grouped_matmul 融合前向的契约(同 Qwen3VLMoeTextExperts)

共享转换器 checkpoint/vlm_model/converters/moe_expert.py 据此做成环境变量门控:
默认保持上游 2D 展平(megatron 侧转换不受影响),GLM4V_DCP_3D=1 时输出 3D
(convert_glm4_5v_ckpt.sh 已内置)。若把 3D 做成无条件默认,会直接破坏 megatron
侧的既有转换——我们在实测中踩过这一回归(加载报
Size mismatch saved (8,4096,2816) vs current (32768,2816)),迁移同类双后端共存模型时务必注意。

3D 布局契约可零卡时静态验证:python examples/glm4.5v/verify_expert_layout.py
(转换器输出形状 == 模型参数形状,且堆叠前向与 HF 逐专家前向数值等价,实测 max_err ≈ 2.9e-6)。

差异点二:DCP 键前缀

DCP checkpoint 的键 = Stateful 包裹前缀 + 模块 FQN,而两个后端的包裹层数不同:

后端 键形态 转换参数
纯 FSDP2 model. + FQN → model.model.language_model.*(两层) --dcp_prefix ""
megatron model. + model.(TransformersModel 包装) + FQN → 三层 model.model.model.* 转换器默认值

两个必须养成的习惯:

  1. 上卡前先验键名与形状(几秒钟,零卡时):
    from torch.distributed.checkpoint import FileSystemReader
    md = FileSystemReader("<dcp_dir>/release").read_metadata()
    # 检查键前缀层数、experts 形状维度是否符合目标后端
    
  2. 训练 yaml 设 training.load_strict: true:键不匹配立即报错。默认宽松模式下键全 miss
    会静默随机初始化起训,loss 曲线看起来完全正常,是最难发现的一类错误。
    同理,load_format: hf / from_pretrained 路线务必检查日志无
    newly initialized 告警(我们实测过:对堆叠布局模型用逐专家布局的原始 HF 权重
    from_pretrained,全部 experts 被静默随机初始化且不报错)。

meta-device 初始化(init_model_with_meta_device: true)下权重只从 training.load
加载,model.model_name_or_path 仅提供 config——yaml 里漏掉 load 字段同样表现为静默随机起训。

差异点三:MoE 融合的开关位置

纯 FSDP2 后端中,模型内没有 MoE 融合开关:融合(grouped-GEMM)由框架专家并行在
EP≥2 时替换 experts 前向提供,EP=1 时走模型自带的逐专家循环。因此融合/非融合的有效 A/B 是
parallel.expert_parallel_size: 2(含 ep_plan) vs 1(删 ep_plan),而不是模型配置里的
任何布尔开关(megatron 侧的 use_npu_fused_moe 在纯 FSDP2 侧无消费者)。

双后端等价性验证方法

精度验收(与 megatron 基线对拍 <2%)的前提是逐级排除变量,我们采用四级验证链,
每级都有对应工具,全部随迁移代码提供:

级 验证内容 方法/工具 GLM-4.5V 实测结果
1 权重加载数值正确 同 seed 两跑:meta+DCP 加载 vs meta+HF safetensors 加载(load_format: hf),确定性模式比逐步 loss 30 步 loss 逐位一致
2 融合/非融合路径等价 EP=2 vs EP=1 同 seed 对照 首步相对差 4e-5,前段逐位贴合 3-4 位有效数字
3 双后端 token 流一致 两后端各 dump 前 N batch 的 input_ids/labels/loss_mask,CPU 逐位 diff 8 batch 逐位一致
4 双后端 loss/性能对拍 compare_runs.py(解析两侧日志,自动判精度<2% + 性能≥基线) 见性能实践篇

第 3 级的两个实测要点:

  • loss 归一化口径必须先统一。两后端 loss_type: default 的实现同构(微批内
    sum(CE)/有效 token → 梯度累积内平均 → DP 组平均),但 megatron 侧样例配置默认
    per_token_loss(按 DP 全局平均 token 数归一),两种口径不可由日志事后换算,
    对拍时须把基线配置临时覆盖为 default。
  • megatron 侧 dump 不要挂在 DataLoader 层(adaptor 环境下会静默失效),用
    get_batch 层的专用入口 examples/glm4.5v/dump_entry_megatron.py;纯 FSDP2 侧可用
    examples/glm4.5v/token_dump.py wrap。

训练 YAML 要点(相对 Qwen3-VL 篇的增量)

parallel:
  fsdp_plan:
    apply_modules:                 # 按 GLM 模块树填写
      - model.visual
      - model.visual.blocks.{*}
      - model.language_model
      - model.language_model.embed_tokens
      - model.language_model.layers.{*}
      - model.language_model.layers.{*}.mlp.experts
      - lm_head
  expert_parallel_size: 2          # EP>=2 走框架融合 grouped-GEMM
  ep_plan:
    apply_modules:
      - model.language_model.layers.{*}.mlp.experts
training:
  init_model_with_meta_device: true
  load: <DCP 目录>                  # meta 初始化下权重唯一来源,勿漏
  load_format: dcp
  load_strict: true                # 强烈建议:杜绝静默随机起训
  no_load_optim: true
  no_load_rng: true

fsdp_plan/ep_plan 的模块路径可用 examples/glm4.5v/preflight_check.py 零卡时校验
(meta 构建模型后逐条模式匹配,避免上卡后才报 [FSDP2] No module named ...)。

实测踩坑

现象 根因 处理
loss 正常下降但精度对不上基线 yaml 缺 training.load 或 DCP 键前缀不匹配 → 静默随机起训 load_strict: true;上卡前验键名
megatron 侧加载报 experts 形状 mismatch 用了 3D 布局 DCP megatron 侧转换不要带 GLM4V_DCP_3D
from_pretrained 后 experts 精度全错 原始 HF 为逐专家键,堆叠模型静默随机初始化 先 GLM4V_DCP_3D=1 mm-convert moe_expert --style merge 得堆叠 safetensors;grep newly initialized 把关
数据预处理(tokenize)卡死,CPU 空转 datasets 5.x 在 NPU 已初始化的进程 fork 预处理 worker 死锁 preprocessing_num_workers: null 走主进程内联
融合 EP 反向偶发 MoeTokenUnpermuteGrad ... shape has 0 小数据下某专家单步 0 token 的算子边界 换 seed/加大数据;真实负载基本不触发

交付与验证状态

小规模验证(tiny 同构模型 + 真实 LLaVA/COCO 图文样本,单卡,token 流逐位一致前提下):
精度以验收原文的 2000 步口径达标——2048 样本 × 2000 步平均相对误差 0.378%(<2%);
性能在 tiny 单卡为持平量级(±2~4%,符号随硬件翻转,非固有回归),权威性能结论待满规模。
数据与复现命令、多组结果详见性能优化实践篇。

likedislike
SSuperKoalaKing
8月4日 修改了issue 的描述
yeqm成员
8月21日 评论:

👋 您好,欢迎向 MindSpeed MM 提交 Issue!
我们已收到您的反馈,感谢你对开源社区的支持。🎉
📅 处理时效: 维护团队将在 24 小时内 查看并回复您的问题(工作日)。
🚨 紧急联系: 如果您的问题非常紧急,可通过微信联系我们。
请确保 Issue 描述清晰,包含复现步骤和日志,这将帮助我们更快定位问题。谢谢!

likedislike
且奏长歌且奏长歌成员
9月15日 issue状态由 TODO 改变为 DONE
且奏长歌且奏长歌成员
9月15日 关闭了 issue
ascend-robotascend-robot成员
9月15日 添加了label:resolved