已关闭
【实践文档】Qwen2.5-VL 接入 Megatron 后端开发实践(组装式接入 + TP/PP 切分 + 权重转换) #481
Ruiyu_Qiu创建于  7月5日关闭于  13 天前
Ruiyu_Qiu
7月5日 创建

【实践文档】Qwen2.5-VL 接入 Megatron 后端开发实践(组装式接入 + TP/PP 切分 + 权重转换)

本文是《MindSpeed MM Megatron 后端迁移指南》(PR !2773)的配套开发实践:以 Qwen2.5-VL(qwen2.5vl,examples/qwen2.5vl/)为案例,按指南流程完整走一遍"新 VLM 接入 Megatron 后端"的开发过程,重点展示组装式模型接入与 TP/PP 切分 + 权重转换的实际做法。全流程已在 4×Atlas 910B3(64GB)环境实机走通:第 5 节为 3B 端到端跑通实录,第 6 节为 7B 与 8 卡 ST 基线的精度对齐复现实录(前 2 步 loss 6 位有效数字一致),踩坑记录均为实测踩中。代码引用对应仓库 master 分支;实测数值仅作方法示意,具体数值随权重/数据版本变化。阅读本文前建议先通读迁移指南正文,环境安装与前置条件同指南,不再重复。

1. 迁移对象与目标

源模型为 HF Transformers 的 Qwen2.5-VL(参考实现见 examples/qwen2.5vl/README.md"版本说明":transformers commit_id=fa56dcc、LLaMA-Factory commit_id=52f2565)。目标是接入 Megatron 后端:支持 TP/PP、分布式优化器与融合算子,3B/7B/32B/72B 全规格可训。

与 FSDP2 后端"每模型一目录"不同,Megatron 后端的模型是组装出来的:pretrain_vlm.py::model_provider(L35)构建通用组装类 VLMModel(mindspeed_mm/models/vlm_model.py L47),各子模块采用哪个实现,由 model.json 中对应配置段的 model_id 查表决定:

VLMModel                              # 通用组装类,按 model.json 各段是否非空决定组装什么
├── image_encoder                     # "image_encoder" 段
│   ├── vision_encoder: Qwen2VLViT   #   "model_id": "qwen2vit" 查表命中
│   └── vision_projector             #   "model_id": "lnmlp"
└── text_decoder                      # "text_decoder" 段,"model_id": "qwen2_5_lm"

五个回调(pretrain_vlm.py:model_provider L35 / get_batch L134 / loss_func L164 / forward_step L190 / train_valid_test_datasets_provider L199)与共享训练循环 mindspeed_mm/training.py::pretrain(L86)全部复用,接入过程不触碰训练循环。

2. 模型接入:组装式复用而非拷贝 modeling

与 FSDP2 路线"拷贝 HF modeling 进仓改造"本质不同:Megatron 后端的模型接入是组装式的——通过查表登记组件,model.json 里的 model_id 决定组件实现与 layer spec。

(a) model_id 查表机制。 两级表都是普通 dict,真实条目如下:

# mindspeed_mm/models/vision/vision_model.py L17:组件实现表
VISION_ENCODER_MAPPINGS = {
    "clip": CLIPViT,
    "qwen2vit": Qwen2VLViT,      # qwen2.5vl 直接复用 qwen2vl 的 ViT 实现
    ...
}

# mindspeed_mm/models/common/module_spec/get_layer_spec.py L13-26:layer spec 表
vit_layer_specs = {'qwen2vit': get_qwen2vl_layer_spec, ...}
llm_layer_specs = {'qwen2lm': get_qwen2vl_llm_layer_spec,
                   'qwen2_5_lm': get_qwen2vl_llm_layer_spec,   # 同一函数,零新代码
                   ...}

qwen2_5_lm 与 qwen2lm 指向同一个 qwen2vl_layer_spec.py 中的函数——qwen2.5vl 的 LLM 侧对 Megatron 而言与 qwen2vl 同构。

(b) qwen2.5vl 相对 qwen2vl 的模型侧全部增量。 ViT 的 MLP 从普通 MLP 变为 GLU 结构,这个增量(delta)完全由 examples/qwen2.5vl/model_7b.json 的 vision_encoder 段两个字段表达:

"gated_linear_unit": true,
"activation_func": "silu",

没有新增一行组件代码——Megatron 的 TransformerConfig 原生支持这两个字段。这是"增量迁移"的理想形态:新模型 = 最近的已迁移基线 + 配置化增量。

(c) 全新结构时的接入点。 若目标模型没有可复用的组件:在 VISION_ENCODER_MAPPINGS 登记新实现类、在 vit_layer_specs/llm_layer_specs 登记新 spec 函数,spec 文件以 qwen2vl_layer_spec.py 为起点复制修改。改动仍收敛于"新组件文件 + 两处 dict 登记"。

3. TP/PP 切分与权重转换实践

(a) pipeline_num_layers 语义与 PP 层平衡。 model.json 中每个模块的 pipeline_num_layers 数组长度必须等于 PP 数,各 stage 之和必须等于该模块 num_layers。7B(TP1/PP2,finetune_qwen2_5_vl_7b.sh)的决策:vit_pp_layers [32, 0] + llm_pp_layers [12, 16]——stage0 承担了整个 ViT,故少分 LLM 层(12 对 16)。VLMModel 按此数组推导各 rank 的 pre/post_process(vlm_model.py L186 附近的判定逻辑)。

(b) 转换器跟着模型增量走。 mm-convert 即 checkpoint/convert_cli.py(pyproject.toml 注册);框架逻辑在 checkpoint/vlm_model/hf_to_mm.py/mm_to_hf.py,每模型一个转换器文件。checkpoint/vlm_model/converters/qwen2_5vl.py 的注释直接说明了增量与转换器的对应关系:

def create_qwen2_5_vl_ops(...) -> List[Operator]:
    """qwen2.5vl在qwen2vl的基础上vit的mlp变成了glu模式、需要增加合并处理逻辑"""
    ops = [UpGateMergeOp(...), ...]          # 仅新增 gate/up 合并
    ops += create_qwen2vl_ops(...)           # 其余全部复用 qwen2vl
    return ops

#  qwen2.5vl的tp切分在qwen2vl的tp切分基础上,修改了vit中mlp的tp切分逻辑,适应glu结构
qwen2_5_vl_tp_patterns = {**qwen2vl_tp_patterns,
                          **{r"...mlp.linear_fc1.weight": GLUSplit, ...}}   # L96-100

模型侧增量是 ViT MLP GLU 化,转换器增量就是一组合并 op 加两条 GLUSplit 切分 pattern,其余整体继承。

(c) 转换命令实操。 7B PP2 的离线转换(examples/qwen2.5vl/README.md"权重转换"节):

mm-convert Qwen2_5_VLConverter hf_to_mm \
  --cfg.mm_dir "ckpt/mm_path/Qwen2.5-VL-7B-Instruct" \
  --cfg.hf_config.hf_dir "ckpt/hf_path/Qwen2.5-VL-7B-Instruct" \
  --cfg.parallel_config.llm_pp_layers [[12,16]] \
  --cfg.parallel_config.vit_pp_layers [[32,0]] \
  --cfg.parallel_config.tp_size 1

注意:llm_pp_layers/vit_pp_layers/tp_size 三参数必须与训练脚本的 TP/PP 及 model.json 的 pipeline_num_layers 一致。训练后如需重新切分权重,使用 mm-convert Qwen2_5_VLConverter resplit(不支持 VPP 场景,README 原文)。

(d) 踩坑记录(实测踩中):

  • PP≠1 必须走离线转换:在线加载 HF 权重(bridge_patch: true)目前仅支持 TP 切分方式(README 原文),PP=2 时必须先 mm-convert。
  • 转换切分与训练配置必须一致:三参数与训练脚本 TP/PP、model.json 不一致时,或加载失败,或静默错位。
  • pipeline_num_layers 之和错误是静默故障:各 stage 之和与该模块 num_layers 不一致时不报错,会构建出缺 post_process(最终 layernorm/输出头)的错误结构,到下游才暴露。
  • 跨卡数复现精度的关键是保持 TP/PP 拓扑不变:DP 可变(用 GRAD_ACC_STEP 补偿 GBS),TP/PP 一旦改变,初始化与权重切分就随之改变(实测见 §6)。

4. 数据接入:零新代码

qwen2.5vl 数据侧三个组件(数据集构建、对话模板、collator)全部复用,纯 data.json 配置:dataset_type: huggingface(通用多模态数据集)+ template: qwen2vl + collate_param.model_name: qwen2vl。若模板/collator 需要新增,登记点分别是 mindspeed_mm/data/data_utils/func_utils/template.py 的 _register_template()(qwen2vl 模板在 L325-326 登记)与 mindspeed_mm/data/dataloader/data_collator.py 的 DATA_COLLATOR 字典(L681-694)。

真实数据管线:COCO2017 train2017(118,287 张图)+ LLaVA-Instruct-150K 标注,经 mindspeed_mm/fsdp/tools/data_tool/llava_instruct_2_mllm_demo_format.py(无 shuffle/random,输出确定性保序)转出 mllm_format_llava_instruct_data.json,实测得到 157,712 个样本,无样本被跳过。

数据转换踩坑:

  • 必须确认图片数为 118,287 张后再跑转换:解压未完成就转换时,转换脚本对缺图样本只打印 skipping 并静默跳过(实测被跳 3,987 个),样本顺序随之改变,与 CI 基线固定的前 20 条样本对不上(基线取样规则见 §6)。

5. 配置、启动与跑通(4×910B3 实测)

先用 3B + mock 数据打通全链路(4×Atlas 910B3 64GB):

项 实测配置/结果
mock 数据 仓库自带工具 mindspeed_mm/fsdp/tools/data_tool/generate_mock_data_for_vlmodel.py(用法见 docs/zh/features/building_data_for_VLModel.md"使用虚构数据"节),tokenizer 用 3B 原始权重,448×448 单图、文本长 1024、32 样本
权重转换 python3 -m checkpoint.convert_cli Qwen2_5_VLConverter hf_to_mm,llm_pp_layers [[18,18]](36 层均分)、vit_pp_layers [[32,0]]、tp_size 1,产物 7.6GB
配置改造 model_3b.json 的 pipeline_num_layers [32]→[32,0]、[36]→[18,18];脚本 NPUS_PER_NODE 8→4、PP 1→2、GRAD_ACC_STEP 32→8(GBS=16)、train-iters=20,即 4 卡 TP1/PP2/DP2
结果 20/20 步完成;loss 首步 5.28E-02,随后在 0.059~0.066 区间内波动(mock 重复数据,绝对值无意义);grad norm 稳定 1.19~1.30;0 skipped/0 nan;单步 1.6~1.8s,8.444 samples/s、8646 tokens/s;checkpoint 按 [t 1/1, p 1/2] 分片保存成功

跑通判定口径与指南 §8 一致:日志按 iteration 持续打印、loss 有限且无 NaN/Inf、grad norm 稳定、无 skipped iteration、checkpoint 能按 TP/PP 分片落盘。

6. 精度对齐记录(方法与结论口径)

结论:保持 TP2/PP2 拓扑不变,4 卡复刻与 8 卡 ST 基线前 2 步 loss 6 位有效数字完全一致(而非仅落入下文 CI 门禁的 1% 容差内);iter3=9.436471 正常下降,grad norm 142.082/115.587/55.825。方法与配置如下。

方法:不与 GPU loss 曲线对比,而是复现仓库 ST 的 loss 基线,以验证训练链路数学等价。ST 用例(tests/st/shell_scripts/finetune_qwen2_5_vl_7b.sh)为 8 卡 TP2/PP2/CP1、MBS=1、GRAD_ACC_STEP=2(GBS=4)、--train-iters 3、--seed 42、无 --load(随机初始化),且开着 --use-distributed-optimizer/--use-fused-rmsnorm/--use-fused-swiglu/--use-flash-attn;数据配置 max_samples: 20、shuffle: False。

项 ST 基线(8 卡) 本次复刻(4 卡) 判定
拓扑 TP2/PP2/DP2 TP2/PP2/DP1 TP/PP 保持一致
GBS 4(MBS1×ACC2×DP2) 4(MBS1×ACC4×DP1) 相同
iter1 loss 1.205116E+01 1.205116E+01 6 位有效数字一致
iter2 loss 1.200050E+01 1.200050E+01 6 位有效数字一致

复现成立依赖三点:

  • 初始化 RNG 按 TP/PP rank 播种,保持 TP2/PP2 则各分片初始权重与基线逐位一致;
  • shuffle: False + 保序数据保证每步 global batch 内容一致;
  • DP 拓扑(2→1)只改变样本在卡间的分布,不改变 batch 组成。

日常回归由 CI 门禁量化把关(tests/st/test_tools/test_ci_st.py L10-12:margin_loss = 0.01、margin_time_percent = 0.05、margin_memory_percent = 0.1)。本文为开发过程实践的方法沉淀,具体 loss 数值以 tests/st/baseline_results/ 当前基线为准。

7. 经验总结

  • 增量迁移优先:先找仓内最近的已迁移基线(qwen2.5vl 之于 qwen2vl)做增量。本例模型侧增量是 model.json 两个字段,转换器增量是一组合并 op 加两条切分 pattern,数据侧增量为零。
  • 转换切分与训练配置一致性是第一坑源:llm_pp_layers/vit_pp_layers/tp_size、训练脚本 TP/PP、model.json 的 pipeline_num_layers 三处必须一致;pipeline_num_layers 之和错误还是静默故障,排障先查它。
  • 跨卡数复现精度的关键是保持 TP/PP 拓扑:卡数变化时只动 DP、用 GRAD_ACC_STEP 补偿 GBS,TP/PP 一变初始化与权重切分就全变。
  • 数据管线确定性是基线可比的前提:转换脚本保序、解压全量确认后再转换;任何静默跳样本都会移动样本序,让 loss 基线失去可比性。
  • 首跑最小化:mock 数据 + 最小规格 + 20 步,先打通"转换→加载→训练→存 ckpt",再上真实数据与大规格。

参考

  • 《MindSpeed MM Megatron 后端迁移指南》:PR !2773(docs/zh/features/megatron_developer_migration_guide.md)
  • examples/qwen2.5vl/README.md(权重下载、转换命令、启动步骤)
  • docs/zh/pytorch/weight_conversion.md、docs/zh/features/building_data_for_VLModel.md
  • 任务 issue:#383
likedislike
ascend-robotascend-robot成员
7月7日 关联了看板:MindStudio ISSUE管理
young256young256成员
7月8日 关联了里程碑:MindSpeed 26.2.0
yeqm成员
7月14日 评论:

感谢您对 MindSpeed-MM 社区的贡献!Qwen2.5-VL Megatron 后端开发实践及关联 PR 已收到,后续将由社区 Maintainer 进行审核,请关注评审意见并及时更新。

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