已关闭
【社区任务】:Qwen3VL系列模型FSDP2后端开发实践 #667
lhj628创建于  8月28日关闭于  24 天前
lhj628
8月28日 创建

Qwen3-VL 模型 FSDP2 后端训练开发实践

本文档记录在 MindSpeed-MM 上为 Qwen3-VL 系列模型 (Qwen3-VL-8B / Qwen3-VL-32B Dense 模型与 Qwen3-VL-235B-A22B MoE 模型) 新增纯 FSDP2(native FSDP2)后端全参 SFT 支持的完整实践过程,包括开发模型实践(模型代码迁移、配置开发、权重转换、精度对齐、Ulysses CP / Ring Attention 序列并行开发、EP 专家并行测试)与性能优化实践(显存触顶导致步时波动的定位与解决、loss 计算 logits 显存尖刺定位与 Chunk Loss 优化、单步性能拆解)两部分。其中 235B 为 MoE 模型,复用仓上已有的 MoE 模型实现,无模型代码改动,开发工作为脚本/配置开发与精度对齐。

本次适配充分复用纯 FSDP2 后端仓上现有特性——Ulysses CP / Ring Attention 序列并行、EP 专家并行、Chunk Loss、激活重计算(recompute)、异步激活卸载(async activation offload)——在 Qwen3-VL 系列模型上全部完成适配:开启后均取得显存/性能优化收益,且精度对齐全部验证达标(loss/norm 相对误差 <1%)。逐项汇总见下文「特性支持与效果总览」。

对标实现为仓上已有的 Megatron + FSDP2 后端:

模型 项目 Megatron + FSDP2 后端(已有) 纯 FSDP2 后端(本次新增)
Qwen3-VL-8B 启动脚本 examples/qwen3vl/finetune_qwen3vl_8B.sh examples/qwen3vl/finetune_qwen3vl_8B_v1.sh
Qwen3-VL-8B 配置文件 examples/qwen3vl/qwen3vl_full_sft_8B.yaml examples/qwen3vl/qwen3vl_8B_config_v1.yaml
Qwen3-VL-32B 启动脚本 examples/qwen3vl/finetune_qwen3vl_32B.sh examples/qwen3vl/finetune_qwen3vl_32B_v1.sh
Qwen3-VL-32B 配置文件 examples/qwen3vl/qwen3vl_full_sft_32B.yaml examples/qwen3vl/qwen3vl_32B_config_v1.yaml
Qwen3-VL-235B(MoE) 启动脚本 examples/qwen3vl/finetune_qwen3vl_235B.sh examples/qwen3vl/finetune_qwen3vl_235B_v1.sh
Qwen3-VL-235B(MoE) 配置文件 examples/qwen3vl/qwen3vl_full_sft_235B.yaml examples/qwen3vl/qwen3vl_235B_config_v1.yaml

各模型的其余维度一致:训练入口 pretrain_transformers.py → mindspeed_mm/fsdp/train/trainer.py;模型实现 mindspeed_mm/models/transformers/qwen3vl/ → mindspeed_mm/fsdp/models/qwen3vl/(8B/32B 共用同一 Dense 实现;235B 复用已有 MoE 实现 mindspeed_mm/fsdp/models/qwen3vl_moe/);配置风格 Megatron 参数 + YAML(gpt_args)→ 纯六段式 YAML(parallel/model/data/features/training/tools)。

目录


特性支持与效果总览

本文档的适配工作以「复用纯 FSDP2 后端仓上现有特性」为原则:CP、EP、Chunk Loss、重计算、异步激活卸载等特性均不为 Qwen3-VL 新增框架机制,模型侧只做接入点适配、配置侧开关即用。六个特性在 Qwen3-VL 系列上全部适配完成,开启后均体现显存/性能优化,且精度对齐全部验证达标,一览如下:

特性(配置开关) 开启后的显存/性能优化 适配情况 精度对齐验证
激活重计算(features.recompute + recompute_plan) 丢弃 block 内部中间激活、反向重算,以计算换显存,是 32B 能在 8 卡训练的显存基础 8B/32B/235B(1.3) ✅ 各对齐长跑均在开启状态下运行,与基线误差 <1%(1.7)
Chunk Loss(enable_chunk_loss + chunkloss_plan) 消除 loss 计算前 (B, S, V) 大 logits 张量:8B 峰值显存降至 20 GB 出头、16k 全层可训;32B 可训层数 8/3 → 32/13(约 4 倍) 8B/32B/235B(2.2.5 / 2.3.5) ✅ 开启前后 loss 曲线一致;1.7 / 1.8.5 / 1.9 各对齐均在开启该特性的交付配置下达成
异步激活卸载(enable_activation_offload + activation_offload_plan) block 入口激活异步 D2H 卸载至 host、反向 prefetch,通信被计算掩盖,可与重计算叠加进一步降低峰值显存 8B/32B/235B(1.2 第 4 点) ✅ 交付配置开启,1.7 各对齐通过
Ulysses CP(ulysses_parallel_size) 头维 All-to-All 序列并行,长序列激活与注意力计算按卡切分,支撑 16k+ 场景 8B/32B 本次开发移植(1.8);235B 复用 MoE 已有能力 ✅ 8B/32B/235B 与 CP 关闭基线对齐(1.8.5)
Ring Attention(ring_attention_size) 序列切分 + ring 组 P2P,与 Ulysses 可组合为混合 CP 同上 ✅ 8B/32B/235B 与基线对齐(1.8.5)
EP 专家并行(expert_parallel_size + ep_plan) MoE 专家参数切分至 ep 组各 rank(alltoall 分发),突破单卡专家权重显存上限 235B(1.3 / 1.9) ✅ EP=2 与 EP=1 基线对齐(1.9)

精度对齐统一方法:与对标基线(1.7 为 Megatron + FSDP2 后端,1.8.5 / 1.9 为特性关闭配置)用 TrainingLogParser 按 ["loss:", "norm:"] 逐 step 对比,均满足相对误差 <1% 的对齐要求。


一、开发模型实践

1.1 改动总览

本次开发的核心改动如下:

类型 文件/目录 说明
新增 mindspeed_mm/fsdp/models/qwen3vl/modeling_qwen3_vl.py Qwen3-VL Dense 版本(8B/32B 共用)纯 FSDP2 模型实现,注册 id 为 qwen3_vl
新增 mindspeed_mm/fsdp/models/qwen3vl_moe/ 原 MoE 版本实现(30B/235B,注册 id qwen3_vl_moe)从 qwen3vl/ 目录迁出,独立成目录,代码逻辑不变;235B 直接复用,无模型代码改动
修改 mindspeed_mm/fsdp/models/qwen3vl/npu_patch.py NPU 融合算子 patch 由 MoE 版切换为 Dense 版
新增 examples/qwen3vl/finetune_qwen3vl_8B_v1.sh、finetune_qwen3vl_32B_v1.sh、finetune_qwen3vl_235B_v1.sh 纯 FSDP2 后端启动脚本
新增 examples/qwen3vl/qwen3vl_8B_config_v1.yaml、qwen3vl_32B_config_v1.yaml、qwen3vl_235B_config_v1.yaml 纯 FSDP2 后端训练配置

目录拆分的原因:此前 mindspeed_mm/fsdp/models/qwen3vl/ 目录下只有 MoE 版实现(modeling_qwen3_vl_moe.py,注册 id qwen3_vl_moe),仅覆盖 Qwen3-VL-30B-A3B/235B-A22B 等 MoE 模型。Qwen3-VL-8B/32B 为 Dense 结构,不含 MoE 专家层,无法复用 MoE 版实现,因此新增 Dense 版 modeling_qwen3_vl.py,并将 MoE 版迁至独立的 qwen3vl_moe/ 目录,使两个目录各自对应一种模型结构:

mindspeed_mm/fsdp/models/
├── qwen3vl/                    # Dense 版(Qwen3-VL-8B/32B)
│   ├── modeling_qwen3_vl.py    # @model_register.register("qwen3_vl")
│   └── npu_patch.py            # apply_qwen3vl_npu_patch()
└── qwen3vl_moe/                # MoE 版(Qwen3-VL-30B-A3B/235B-A22B)
    ├── modeling_qwen3_vl_moe.py# @model_register.register("qwen3_vl_moe")
    └── npu_patch.py            # apply_qwen3vl_moe_npu_patch()

1.2 模型代码开发

mindspeed_mm/fsdp/models/qwen3vl/modeling_qwen3_vl.py 参考 HuggingFace transformers 的 Qwen3-VL 官方实现(commitid 保持与 examples/qwen3vl/README_v1.md 中要求一致),改造为适配纯 FSDP2 后端,8B/32B 共用,关键开发点如下。Qwen3-VL-235B-A22B(MoE)复用已就绪的 mindspeed_mm/fsdp/models/qwen3vl_moe/ 实现(含下文的 CP 适配与融合 MoE 算子 patch),无模型代码改动:

  1. 注册机制:顶层模型类通过装饰器注册到 FSDP2 后端的模型注册表,注册 id 与 YAML 中 model.model_id 对应:

    from mindspeed_mm.fsdp.utils.register import model_register
    
    @model_register.register("qwen3_vl")
    class Qwen3VLForConditionalGeneration(Qwen3VLPreTrainedModel, GenerationMixin):
        ...
    

    注册装饰器只有在对应包被 import_plugin 导入时才会执行,因此 YAML 的 training.plugin 中必须列出 mindspeed_mm/fsdp/models/qwen3vl。

  2. 模块结构:实现为自包含文件,包括:

    • 视觉侧:Qwen3VLVisionPatchEmbed(Conv3d 分块)、Qwen3VLVisionAttention、Qwen3VLVisionBlock、Qwen3VLVisionPatchMerger(含 deepstack merger)、Qwen3VLVisionRotaryEmbedding;
    • 文本侧:Qwen3VLTextAttention、Qwen3VLTextMLP、Qwen3VLTextDecoderLayer、Qwen3VLTextRMSNorm、Qwen3VLTextRotaryEmbedding;
    • 顶层:Qwen3VLModel(视觉特征与文本 embedding 融合、3D RoPE position_ids 处理)与 Qwen3VLForConditionalGeneration(lm_head + loss)。
  3. 重计算支持:Qwen3VLVisionBlock 和 Qwen3VLTextDecoderLayer 继承 transformers 的 GradientCheckpointingLayer,使 YAML 中 features.recompute_plan.apply_modules 可以按模块粒度开启激活重计算。

  4. 异步激活卸载(activation offload)支持:模型结构按异步激活卸载特性的接入约定组织,使 YAML 中 features.enable_activation_offload: true + activation_offload_plan 可以直接生效:

    • 按 block 划分卸载粒度:model.visual.blocks 与 model.language_model.layers 均为同构 block 堆叠(nn.ModuleList),activation_offload_plan.apply_modules 通过 {*} 模式逐 block 匹配,框架侧 FeaturesApplier 为每个 block 分配全局编号(block_idx/depth)并包装其 forward,按 block 组织激活的生命周期与反向预取顺序;
    • forward 签名约定:Qwen3VLVisionBlock 与 Qwen3VLTextDecoderLayer 的 forward 第一个位置参数统一为 hidden_states 张量,满足包装器 with_async_save_on_cpu 的约定(取第一个位置参数作为 hidden_states,仅卸载与其共享数据指针的保存张量,即 block 入口激活);
    • 与重计算组合:block 继承 GradientCheckpointingLayer,offload 可与重计算叠加——offload 将重计算入口的 block 激活异步卸载到 host 侧(D2H/H2D 走独立 swap 流,被计算掩盖,反向时 prefetch 提前加载),重计算丢弃 block 内部中间激活,共同降低峰值显存。

    特性原理、参数与典型收益详见 异步激活卸载。

  5. forward 接口对齐数据pipeline:forward 接收 data collator 产出的 batch keys(input_ids、attention_mask、position_ids、labels、pixel_values、image_grid_thw 等),当 labels 非空时在模型内部通过 loss_function 计算 loss,配合 YAML 中 features.loss_cfg 使用。

  6. NPU 融合算子 patch(npu_patch.py::apply_qwen3vl_npu_patch):将热点小算子替换为昇腾融合实现,减少算子数、提升执行效率(替换清单与 MoE 版的专家层融合详见 2.4 NPU 融合算子适配):

    原始实现 替换为
    apply_rotary_pos_emb_vision(视觉 RoPE) npu_fused_operator.apply_transformers_vision_rope_half_npu
    apply_rotary_pos_emb(文本 RoPE) npu_fused_operator.apply_transformers_rope_half_npu
    Qwen3VLTextRMSNorm.forward npu_fused_operator.rms_norm_forward_npu

    MoE 版目录的 patch 额外包含 Qwen3VLMoeTextExperts.forward 的融合 MoE 替换,Dense 版无专家层,因此不需要。

1.3 训练配置开发

纯 FSDP2 后端由单一六段式 YAML 驱动(examples/qwen3vl/qwen3vl_8B_config_v1.yaml / qwen3vl_32B_config_v1.yaml / qwen3vl_235B_config_v1.yaml),三个模型的配置结构一致,各段关键配置与设计考虑如下:

(1)parallel —— 并行与分片策略

parallel:
  tensor_parallel_size: 1              # TP当前代码未实现,令TP=1
  fully_shard_parallel_size: auto      # 自动按设备数确定分片组大小
  fsdp_plan:
    apply_modules:                     # 需与 model.named_modules() 实际名称严格一致
      - model.visual.blocks.{*}
      - model.visual.merger
      - model.visual.deepstack_merger_list.{*}
      - model.visual
      - model.language_model.embed_tokens
      - model.language_model.layers.{*}
      - model.language_model
      - lm_head
    param_dtype: bf16                  # 参数 bf16 分片
    reduce_dtype: fp32                 # 梯度规约 fp32,保证数值精度

分片粒度按「视觉 block / merger、文本单层、embedding、lm_head」划分,通信桶大小适中,有利于通信与计算 overlap。32B 模型参数量更大(text 64 层、hidden 25600),FSDP 参数分片 + bf16 参数/fp32 规约是其能在 8 卡上训练的基础。

(2)data —— 数据模块

data:
  dataset_param:
    dataset_type: huggingface          # 复用已有 huggingface 数据集插件
    attr: { images: images, messages: messages, role_tag: role, ... }
    basic_parameters:
      cutoff_len: 16384                # 序列长度 16k
      template: qwen3_vl_nothink       # 非思考模式模板
      enable_thinking: false
  dataloader_param:
    dataloader_mode: sampler
    sampler_type: BaseRandomBatchSampler
    num_workers: 8
    pin_memory: true
    collate_param: { model_name: qwen3vl, ignore_pad_token_for_loss: true }
    enable_preload: true               # 数据预取,掩盖cpu get_batch和H2D拷贝

数据侧基本完全复用库上现有代码,无需新增 dataset/template/collator;attr 将 ShareGPT 风格原始字段映射到框架字段。

(3)model / features / training / tools

model:
  model_id: qwen3_vl                   # 对应 @model_register.register("qwen3_vl")
  attn_implementation: flash_attention_2
  freeze: [model.visual]               # SFT 冻结视觉编码器

features:
  loss_cfg: { loss_type: default }
  recompute: true                      # 配置重计算
  recompute_plan:
    apply_modules:
      - model.visual.blocks.{*}
      - model.language_model.layers.{*}
  enable_chunk_loss: true              # chunk loss,消除 loss 计算的 logits 显存尖刺(见二、性能优化实践)
  chunkloss_plan:
    apply_module: lm_head
    chunk_size: 512
  enable_activation_offload: true      # 配置异步激活卸载
  activation_offload_plan:
    apply_modules:
      - model.visual.blocks.{*}
      - model.language_model.layers.{*}

training:
  micro_batch_size: 1
  optimizer: adamw
  adam_fused: true                     # 昇腾融合 AdamW
  init_model_with_meta_device: true    # meta 初始化,避免全量权重驻留 CPU 内存
  load: <Qwen3-VL-*-Instruct-dcp>      # DCP 权重路径(release 的上一级目录)
  plugin:                              # 导入插件式注册的model和data
    - mindspeed_mm/fsdp/models/qwen3vl
    - mindspeed_mm/fsdp/data/datasets/huggingface

tools:
  profile:                             # 采集 profiling,用于性能分析
    enable: true
    profile_type: static
    ranks: [0]
    static_param: { level: level1, with_memory: true, record_shapes: true,
                    start_step: 10, end_step: 11, aic_metrics_type: PipeUtilization }

(4)Qwen3-VL-235B(MoE)配置差异

235B 为 MoE 模型,复用已就绪的 qwen3vl_moe 模型实现,其配置(qwen3vl_235B_config_v1.yaml)相对 8B/32B 的差异点如下:

配置项 235B 配置 说明
model.model_id qwen3_vl_moe MoE 注册 id
training.plugin mindspeed_mm/fsdp/models/qwen3vl_moe 目录拆分后 MoE 模型的 plugin 须指向 qwen3vl_moe 包
parallel.expert_parallel_size 1 MoE 专家并行配置入口,>1 开启 EP(见 1.9)
parallel.ep_plan.apply_modules model.language_model.layers.{*}.mlp.experts 专家模块匹配
parallel.ep_plan.dispatcher alltoall EP token 分发方式,可选 alltoall / mc2 / allgather
training.use_deter_comp true 开启确定性计算

1.4 权重转换

纯 FSDP2 后端使用 DCP(PyTorch Distributed Checkpoint)格式权重。meta 初始化(init_model_with_meta_device: true)训练前,需将 HuggingFace 权重转换为 DCP:

source /usr/local/Ascend/ascend-toolkit/set_env.sh
# Qwen3-VL-8B
mm-convert GenericDCPConverter hf_to_dcp \
    --hf_dir  ckpt/hf_path/Qwen3-VL-8B-Instruct \
    --dcp_dir ckpt/dcp_path/Qwen3-VL-8B-Instruct-dcp
# Qwen3-VL-32B / Qwen3-VL-235B-A22B 同理,替换为对应路径
mm-convert GenericDCPConverter hf_to_dcp \
    --hf_dir  ckpt/hf_path/Qwen3-VL-32B-Instruct \
    --dcp_dir ckpt/dcp_path/Qwen3-VL-32B-Instruct-dcp
mm-convert GenericDCPConverter hf_to_dcp \
    --hf_dir  ckpt/hf_path/Qwen3-VL-235B-A22B-Instruct \
    --dcp_dir ckpt/dcp_path/Qwen3-VL-235B-A22B-Instruct-dcp

# 转换后目录结构:
# Qwen3-VL-*-Instruct-dcp/
# ├── release/
# └── latest_checkpointed_iteration.txt

YAML 中 training.load 配置为 release 文件夹的上一级目录。训练完成后如需导出 HF 权重用于推理,使用 dcp_to_hf 子命令反向转换。

1.5 数据集准备

  • 精度对齐/真实训练:使用 COCO2017 + LLaVA-Instruct-150K 构造的 mllm_format_llava_instruct_data.json,参考 针对VL模型的数据构造 · 使用真实数据集。
  • 性能测试:使用 mock 数据集(随机图片 + 重复文本)控制变量,生成脚本为 mindspeed_mm/fsdp/tools/data_tool/generate_mock_data_for_vlmodel.py,示例命令见 2.1 测试配置。

1.6 启动训练

启动脚本 examples/qwen3vl/finetune_qwen3vl_8B_v1.sh(32B 为 finetune_qwen3vl_32B_v1.sh,235B 为 finetune_qwen3vl_235B_v1.sh)核心内容:

export NON_MEGATRON=true               # 必须:启用纯 FSDP2 所需的算子适配
export PYTORCH_NPU_ALLOC_CONF=expandable_segments:True
export MULTI_STREAM_MEMORY_REUSE=2
export TASK_QUEUE_ENABLE=2
export CPU_AFFINITY_CONF=1
export ASCEND_LAUNCH_BLOCKING=0
export ACLNN_CACHE_LIMIT=100000
export HCCL_CONNECT_TIMEOUT=7200

torchrun $DISTRIBUTED_ARGS mindspeed_mm/fsdp/train/trainer.py \
    examples/qwen3vl/qwen3vl_8B_config_v1.yaml    # 32B/235B 分别替换为 qwen3vl_32B_config_v1.yaml / qwen3vl_235B_config_v1.yaml

两个后端的环境变量差异:纯 FSDP2 后端必须设置 NON_MEGATRON=true,且不再需要 Megatron 侧的 CUDA_DEVICE_MAX_CONNECTIONS。

1.7 精度对齐

各模型数据集均使用 README 中提供的 COCO 数据集(mllm_format_llava_instruct_data.json),分别运行 Megatron + FSDP2 后端基线脚本与本次新增的纯 FSDP2 后端脚本;其中 8B/32B 在平台申请 8 卡,各长跑 2000 步。

对齐方法:使用精度对齐工具 TrainingLogParser,选取默认关键词 ["loss:", "norm:"] 解析两份训练日志并逐 step 对比。

Qwen3-VL-8B(不减层)

8B 不减层运行全层模型,基线为 finetune_qwen3vl_8B.sh,本次新增为 finetune_qwen3vl_8B_v1.sh。对齐结果:

jingdu_duiqi.png

结论:loss 相对误差保持在 1% 以内,纯 FSDP2 后端与 Megatron + FSDP2 后端达到精度对齐,满足精度对齐要求。

Qwen3-VL-32B(减层 32/13)

由于 32B 模型尺寸较大,全层跑测 8 卡必现 OOM,因此精度对齐采用减层训练,两个后端使用相同的减层配置(text 32 层 + vision depth 13,全层为 text 64 层 + vision depth 27),基线为 finetune_qwen3vl_32B.sh,本次新增为 finetune_qwen3vl_32B_v1.sh:

# 纯 FSDP2 后端:mindspeed_mm/fsdp/models/modelhub.py
transformer_config.text_config.num_hidden_layers = 32
transformer_config.vision_config.depth = 13
# Megatron + FSDP2 后端:mindspeed_mm/models/transformers_model.py
self.transformer_config.text_config.num_hidden_layers = 32
self.transformer_config.vision_config.depth = 13

对齐结果:

jingdu_duiqi.png

结论:loss 相对误差保持在 1% 以内,纯 FSDP2 后端与 Megatron + FSDP2 后端达到精度对齐,满足精度对齐要求。

Qwen3-VL-235B(MoE,减层 2/1)

平台申请 8 卡,基线为 finetune_qwen3vl_235B.sh,本次新增为 finetune_qwen3vl_235B_v1.sh。由于 235B 模型尺寸较大,全层跑测 8 卡必现 OOM,因此精度对齐采用减层训练,两个后端使用相同的减层配置(text 2 层 + vision depth 1):

# 纯 FSDP2 后端:mindspeed_mm/fsdp/models/modelhub.py
transformer_config.text_config.num_hidden_layers = 2
transformer_config.vision_config.depth = 1
# Megatron + FSDP2 后端:mindspeed_mm/models/transformers_model.py
self.transformer_config.text_config.num_hidden_layers = 2
self.transformer_config.vision_config.depth = 1

两者长跑 2000 步。对齐结果:

235b_loss_align.png

结论:loss 相对误差小于 1%,绝对误差小于 1%,纯 FSDP2 后端与 Megatron + FSDP2 后端达到精度对齐,满足精度对齐要求。

1.8 Ulysses CP 与 Ring Attention 序列并行开发

1.8.1 开发背景

高分辨率图片/视频输入使 VLM 的序列长度快速增长,注意力计算的显存开销随序列长度近似按 O(S²) 上升,长序列训练需要序列并行(Context Parallel,CP)。仓上 Qwen3-VL-MoE(30B)已在纯 FSDP2 后端打通 Ulysses CP(头维 All-to-All)与 Ring Attention(序列切分 + ring 组 P2P)及其混合模式,本次将该能力扩展到 Dense 系列(8B/32B)。两个特性的原理、框架侧实现与通信原语详见 Ulysses / Ring Attention 特性实现梳理,本节聚焦 Dense 模型的开发过程。

1.8.2 开发思路:框架复用、对标 MoE 参考实现移植

CP 的实现分为框架侧与模型侧两部分:

  • 框架侧(无需改动,直接复用):parallel 段参数校验(parallel_args.py)、5 维 device mesh 与进程组构建(parallel_state.py)、通信原语(communication.py 的 all_to_all / split / gather / packed 系列)、CP 感知的 flash attention 算子(ops/flash_attn/flash_attn.py,已替换 transformers 的 flash_attention_2 实现,内含 ulysses-only / ring-only / 混合三条路径)、loss 的 CP 切分(loss_func.py)。这些组件均与模型结构无关,Dense 模型可直接使用。
  • 模型侧(本次开发):CP 逻辑集中在序列维度发生变化的关键位置(视觉编码器出入口、PatchMerger 下采样、注意力调用点、文本 embedding 入口、DeepStack 特征融合、loss 聚合)。mindspeed_mm/fsdp/models/qwen3vl_moe/modeling_qwen3_vl_moe.py 中这些位置均以 Modification 注释标记且已验证,因此 Dense 版采取对标 MoE 参考实现逐项移植的方式,在 mindspeed_mm/fsdp/models/qwen3vl/modeling_qwen3_vl.py 中完成适配。

1.8.3 模型侧适配点

# 位置 适配内容
1 模块级序列长度注册表 新增 get_seq_len/set_seq_len(total/visual/per_visual 三类),在模型 forward 与注意力算子间传递序列形状信息(注意力内部依赖 total_seq_len 决定 All-to-All 聚合尺寸)
2 视觉编码器入口 hidden_states 与 rotary_pos_emb 经 packed_data_split_forward_gather_backward_with_cp 按每张图片独立切分(ring 逐样本切、ulysses 整体切);cu_seqlens 移至 CPU 避免 NPU 上 host-device 同步;开启 ring 时按本 ring rank 重算 cu_seqlens
3 视觉注意力 TND 布局传入 total_seq_len(视觉 token 总长)与 seq_split_lens(per-image 的 ring 非均匀切分尺寸)、ring_in_bnsd=False
4 PatchMerger 下采样需要连续 merge 窗口:先 packed gather 聚合全序列 → norm + 下采样 → 按新长度在 ulysses 组重新 split
5 视觉编码器出口 经 ulysses 组 gather_forward_split_backward 聚合为完整视觉嵌入(后续按 image_token 掩码 masked_scatter 进文本 embedding 需要全局序列视图)
6 文本注意力 BNSD 布局传入 ring_in_bnsd=True、is_causal=True、total_seq_len
7 文本模型入口 packing 模式下 attention_mask=None;ulysses 的 cu_seqlens 参数须在序列切分前由完整 position_ids 生成;set_seq_len("total") 后对 position_ids/text_position_ids/inputs_embeds 做 CP 切分(先 ring zigzag 负载均衡切分,后 ulysses 切分)
8 DeepStack _deepstack_process 先 gather visual_embeds 与 hidden_states → 按掩码位置叠加视觉特征 → 再切分回去
9 顶层 loss CP 开启时各 rank loss 经 cp 组 gather_forward_split_backward 聚合后求和(补偿 FSDP 在含 CP ranks 的分片组上的梯度平均)

其中第 9 点在 Dense 版顶层 forward 中已具备,本次补齐第 1–8 点。移植完成后与 MoE 参考实现做了 CP 原语使用量的逐项比对,除 MoE 特有的路由辅助损失适配(Dense 无专家层,不需要)外完全一致。

1.8.4 使用方式

qwen3vl_8B_config_v1.yaml / qwen3vl_32B_config_v1.yaml 的 parallel 段已包含开关(默认 1,即关闭),按需调大即可:

parallel:
  tensor_parallel_size: 1
  fully_shard_parallel_size: auto      # 需满足 >= ring_attention_size × ulysses_parallel_size
  ring_attention_size: 2               # >1 开启 Ring Attention(仅 NPU 支持)
  ulysses_parallel_size: 2             # >1 开启 Ulysses CP;两者同时 >1 即混合 CP

关键约束:

  • world_size 须为 ring_attention_size × ulysses_parallel_size 的整数倍;
  • 注意力实现保持 flash_attention_2(CP 逻辑在被替换的注意力实现中);
  • 文本侧开启 ring 时序列长度须为 2 × ring_attention_size 的整数倍(zigzag 负载均衡切分要求);
  • Ulysses 要求 num_query_heads % ulysses_parallel_size == 0(8B 为 32 头、32B 为 64 头,需整除);
  • 建议 序列长度 / cp_size > 8k 以获得最佳效果,序列过短时通信可能无法被计算掩盖。

1.8.5 精度对齐

测试方案:使用 COCO 数据集,分别运行 CP 关闭基线(ring_attention_size: 1、ulysses_parallel_size: 1)与开启 CP 的纯 FSDP2 配置,使用 TrainingLogParser 选取默认关键词 ["loss:", "norm:"] 逐 step 对比 loss/norm 曲线。各模型均采用减层配置、跑测 100 步:8B/32B 减层 LLM 16 层 + ViT 7 层、GBS=4,基线为 4 卡(4 die)、开启 CP 的配置为 8 卡(8 die);235B 沿用 1.7 精度对齐的减层配置(LLM 2 层 + ViT 1 层)、GBS=8,基线为 8 卡(8 die)、开启 CP 的配置为 16 卡(16 die)。

对齐结果:

1. Ulysses CP 精度对比

模型 配置 ring_attention_size ulysses_parallel_size 资源
8B/32B case1(基线) 1 1 4 卡
8B/32B case2 1 2 8 卡
235B case1(基线) 1 1 8 卡
235B case2 1 2 16 卡

Qwen3-VL-8B 减层(LLM 16 层、ViT 7 层),GBS=4,跑测 100 步:

8b_cp_loss_align.png

Qwen3-VL-32B 减层(LLM 16 层、ViT 7 层),GBS=4,跑测 100 步:

32b_cp_loss_align.png

Qwen3-VL-235B 减层(LLM 2 层、ViT 1 层),GBS=8,跑测 100 步:

235b_cp_loss_align.png

2. Ring Attention 精度对比

模型 配置 ring_attention_size ulysses_parallel_size 资源
8B/32B case1(基线) 1 1 4 卡
8B/32B case2 2 1 8 卡
235B case1(基线) 1 1 8 卡
235B case2 2 1 16 卡

Qwen3-VL-8B 减层(LLM 16 层、ViT 7 层),GBS=4,跑测 100 步:

8b_ring_loss_align.png

Qwen3-VL-32B 减层(LLM 16 层、ViT 7 层),GBS=4,跑测 100 步:

32b_ring_loss_align.png

Qwen3-VL-235B 减层(LLM 2 层、ViT 1 层),GBS=8,跑测 100 步:

235b_ring_loss_align.png

结论:Ulysses CP 与 Ring Attention 两种配置下,8B/32B/235B 的 loss/norm 曲线均与 CP 关闭基线对齐,满足精度对齐要求。

1.9 EP 专家并行测试(Qwen3-VL-235B)

235B 为 MoE 模型,专家层(model.language_model.layers.{*}.mlp.experts)在纯 FSDP2 后端支持专家并行(Expert Parallel,EP):expert_parallel_size > 1 时,框架按 ep_plan.apply_modules 将专家参数切分至 ep 组内各 rank(每 rank 仅持有部分专家),token 按路由结果经 ep_plan.dispatcher 指定的通信方式在 ep 组内分发,专家计算完成后汇聚回原 rank。交付配置 qwen3vl_235B_config_v1.yaml 的 parallel 段已包含 EP 入口(默认 expert_parallel_size: 1,即关闭),调大即可启用:

parallel:
  expert_parallel_size: 1              # >1 开启 EP
  ep_plan:
    apply_modules:
      - model.language_model.layers.{*}.mlp.experts
    dispatcher: alltoall               # 可选 alltoall / mc2 / allgather

测试方案:沿用 1.7 精度对齐的减层配置(LLM 2 层 + ViT 1 层),GBS=8,跑测 100 步,使用 TrainingLogParser 选取默认关键词 ["loss:", "norm:"] 与 EP 关闭基线逐 step 对比 loss/norm 曲线:

配置 expert_parallel_size 资源 GBS
case1(基线) 1 8 卡(8 die) 8
case2 2 8 卡(8 die) 8

235b_ep_loss_align.png

结论:expert_parallel_size=2 的 loss/norm 曲线与 EP 关闭基线对齐,满足精度对齐要求。与 1.8.5 中 CP 测试(开启后卡数翻倍至 16 die)不同,EP 是在原设备网格内对专家参数重新划分,world_size 保持不变,EP=2 与基线同样在 8 卡上跑测。


二、性能优化实践

2.1 测试配置

性能测试采用减层模型 + mock 数据集控制变量,排除真实数据加载波动与全层模型资源占用对问题定位的干扰。之所以不使用 COCO 数据集做性能测试:COCO 序列长度较短(通常在 1k 以下),无法对 16k 长序列场景施压。

减层配置:在模型构建处修改 transformer_config(测试完成后需还原):

# 纯 FSDP2 后端:mindspeed_mm/fsdp/models/modelhub.py
transformer_config.text_config.num_hidden_layers = 32   # 8B 全层为 36,32B 全层为 64
transformer_config.vision_config.depth = 13             # 全层为 27

# Megatron + FSDP2 后端:mindspeed_mm/models/transformers_model.py(如需对比测试)
transformer_config.text_config.num_hidden_layers = 32
transformer_config.vision_config.depth = 13

mock 数据集:使用库上数据生成脚本 mindspeed_mm/fsdp/tools/data_tool/generate_mock_data_for_vlmodel.py 生成 16k 序列长度数据集(8B/32B 性能测试共用):

source /usr/local/Ascend/ascend-toolkit/set_env.sh
SAVE_DIR=/home/data/datasets/
mkdir -p $SAVE_DIR
python mindspeed_mm/fsdp/tools/data_tool/generate_mock_data_for_vlmodel.py \
    --tokenizer_path /usr/local/weights/Qwen3-VL-8B-Instruct/ \
    --pic_width 1024 \
    --pic_height 1024 \
    --num_pics 10 \
    --text_length 16384 \
    --num_samples 128 \
    --save_dir $SAVE_DIR

生成文件的实际命名与 --num_pics/--text_length/--num_samples 取值相关,请与 YAML 中 data.dataset_param.basic_parameters.dataset 路径保持一致(本次测试使用 mock_data_pic_num_10_text_len_16384_sample_num_128.json)。

本次测试使用的优化特性配置与训练配置如下(以 Qwen3-VL-8B 测试期配置为例,chunk loss 为优化过程中开启,见 2.2.5;32B 显存压力更大,其交付配置默认开启 chunk loss 与覆盖视觉 block 的激活 offload,并开启确定性计算 use_deter_comp: true):

# 优化特性配置
features:
  loss_cfg:
    loss_type: default                 # 使用模型自带 loss 计算
    router_aux_loss_coef: 0.0          # Dense 模型无路由辅助损失
  recompute: true                      # 开启激活重计算
  recompute_plan:
    apply_modules:
      - model.visual.blocks.{*}
      - model.language_model.layers.{*}
  enable_chunk_loss: false             # 本阶段未开启 chunk loss
  chunkloss_plan:
    apply_module: lm_head
    chunk_size: 1024
  enable_activation_offload: true      # 开启激活 offload
  activation_offload_plan:             # 激活 offload 作用范围:文本 decoder 层
    apply_modules:
      - model.language_model.layers.{*}

# 训练配置
training:
  micro_batch_size: 1
  gradient_accumulation_steps: 1
  seed: 42
  lr: 1.0e-5
  lr_decay_style: cosine
  lr_warmup_ratio: 0.1
  weight_decay: 0
  train_iters: 2000
  clip_grad: 0.0
  init_model_with_meta_device: true    # meta 初始化,配合 DCP 权重加载
  optimizer: adamw
  adam_fused: true                     # 昇腾融合 AdamW
  save_interval: 10000
  use_deter_comp: false
  plugin:                              # 必须包含所有带 @register 装饰器的包
    - mindspeed_mm/fsdp/models/qwen3vl
    - mindspeed_mm/fsdp/data/datasets/huggingface

2.2 Qwen3-VL-8B 性能优化

2.2.1 问题:显存触顶导致单步时间大幅波动

减层配置初始采用 text 32 层 + vision depth 13,训练过程中观察到:

  • 进程显存达到 62557 MB,叠加单卡基础显存后一度达到 65467 MB,触顶 A3 单卡总显存容量;
  • 单步时间出现约 1000 ms 量级的波动尖刺。

单步时间变化:

qwen3vl_8B_16k_fsdp2_32_13_20260811_100720.png

2.2.2 Profiling 定位

采集 rank0 单步 profiling(YAML tools.profile,static 模式、level1、with_memory: true),在 timeline 中发现一段较长的 Free 空隙:

qwen3vl_8B_16k_fsdp2_Free_20260811_100720.png

向上追溯该 Free 段对应的 runtime 调用,发现是 aclrtFreePhysical:

qwen3vl_8B_16k_fsdp2_aclrtFree_20260811_100720.png

原因分析:aclrtFreePhysical 接口用于释放通过 aclrtMallocPhysical 申请的物理内存。显存触顶后,ACL 触发了物理内存的重整/回收管理,该过程在训练主流程中表现为同步的 Free 等待,从而导致单步时间出现约 1000 ms 的波动。

2.2.3 优化措施与效果

措施:为显存预留余量,将减层配置从 32/13 调整为 text 24 层 + vision depth 13:

transformer_config.text_config.num_hidden_layers = 24
transformer_config.vision_config.depth = 13

效果:单步时间波动从约 1000 ms 降至约 40 ms,训练恢复平稳:

qwen3vl_8B_16k_fsdp2_24_13_20260811_100720.png

2.2.4 单步性能拆解

对 24/13 配置采集 rank0 单步 profiling:

qwen3vl_8B_16k_fsdp2_24_13_profiling_20260811_105420.png

单步总耗时拆解为 Free + Communication(Not Overlapped) + Computing = 4463.33 ms:

类别 耗时 占比 说明
Computing 4332.41 ms 97.0% 计算时间占绝对主导
Free 68.85 ms 1.54% Free占比已大幅收敛
Communication (Not Overlapped) 62.07 ms 1.39% 未被计算掩盖的通信

结论:显存余量充足后,aclrtFreePhysical 引起的长 Free 消失(Free 占比仅 1.54%),FSDP2 通信基本被计算掩盖(未掩盖通信仅 1.39%),任务呈计算密集型特征,NPU 利用率极高。当前配置下性能瓶颈在计算本身,通信与内存管理均非瓶颈。

2.2.5 loss 计算显存尖刺定位与 Chunk Loss 优化

减层虽然规避了显存触顶,但也限制了可训练的模型规模。要进一步支持全层训练,需要找到并消除显存占用的大头。

现象:查看内存快照发现,中间部分存在明显的显存尖刺:

qwen3vl_8B_16k_fsdp2_24_13_profiling_20260811_105420.png

尖刺位置正好处于前向和反向的中间位置,推测是计算 loss 时产生了较大的中间变量。如果能消除此尖刺,即可在 8 卡上全层运行 qwen3vl-8b。

定位:查看 loss 计算代码发现,在计算 loss 之前会产生一个临时张量 logits:

logits_code.png

在此处打断点查看其 shape:

logits_bsv.png

logits 的 shape 达到 [1, 16384, 151936],即 (B, S, V):16k 序列长度下,单个 logits 张量元素数约 24.9 亿(bf16 约 4.6 GiB,计算 loss 时若升为 float32 占用翻倍),这正是显存尖刺的来源。

优化措施:对照仓上 qwen3vl MoE 模型的代码实现,并参考 Chunk Loss 特性文档,在模型代码中适配 chunk loss:

shipei_chunkloss.png

并在 YAML 中开启:

features:
  enable_chunk_loss: true
  chunkloss_plan:
    apply_module: lm_head
    chunk_size: 512

其原理是将 tensor 在序列维度切分成长度为 512 的 sub_seq 子序列分段进行 loss 计算,避免一次性生成大张量 logits。

优化效果:开启 chunk loss 后重新实验,不减层也可以在 8 卡上很好地跑下 qwen3vl-8b 全层对 16k 数据集的训练:

  • 单步时间相对均匀,波动范围在 100 ms 左右:

qwen3vl_8B_16k_fsdp2_all_20260811_100720.png

  • rank0 单步 profiling 显示任务仍明显属于计算密集型,NPU 利用率极高:

qwen3vl_8B_16k_fsdp2_chunkloss.png

  • 内存快照显示中间位置的显存尖刺消失,变成了一段段 chunk 之后的 sub_seq 子序列 loss 计算,峰值显存降低到 20 GB 出头:

loss_chunk.png

结论:chunk loss 消除了 loss 计算产生的大 logits 中间张量,峰值显存大幅下降,使 qwen3vl-8b 无需减层即可在 8 卡上进行 16k 序列长度的全层训练,足见 chunk loss 带来的显存优化作用巨大。

2.3 Qwen3-VL-32B 性能优化

32B 性能测试复用 8B 生成的 16k mock 数据集。由于模型更大,使用 16k 数据集需要进一步减层:经测试当 language_model 层数置为 12、visual 层数置为 7(12/7)时可勉强跑下。

2.3.1 问题:内存重整问题复现,训练时间大幅波动

在 12/7 减层配置下训练,仍出现了在 Qwen3-VL-8B 中遇到过的内存重整问题:

  • 训练时间出现较大波动:

qwen3vl_32B_fsdp2_12_7_16k_20260812_093551.png

  • 单卡显存波动幅度较大,推测是由于反复进行显存重整导致。

2.3.2 Profiling 定位

采集 rank0 单步 profiling,可以观察到存在多段 Free:

qwen3vl_32B_fsdp2_12_7_16k_Free.png

向上追溯这些 Free 段对应的 runtime 调用,发现是 aclrtFreePhysical 和 aclrtGetMemInfo:

qwen3vl_32B_fsdp2_12_7_16k_aclrtFree.png

图:Free 段对应 aclrtFreePhysical 和 aclrtGetMemInfo 调用(qwen3vl_32B_fsdp2_12_7_16k_aclrtFree.png)

原因分析:由 8B 的实践已知 aclrtFreePhysical 是在释放物理内存;查阅 CANN 接口文档,aclrtGetMemInfo 用于获取 Device 上应用可用内存的空闲大小和总大小。两者交替出现,说明显存再次触顶后 ACL 反复进行内存重整管理,原因与 8B 相同。

2.3.3 优化措施与效果

措施:进一步下调减层配置为 text 8 层 + vision depth 3:

transformer_config.text_config.num_hidden_layers = 8
transformer_config.vision_config.depth = 3

效果:单步时间相较之前变得非常均匀:

qwen3vl_32B_fsdp2_8_3_16k_20260812_093551.png

2.3.4 单步性能拆解

对 8/3 配置采集 rank0 单步 profiling:

qwen3vl_32B_fsdp2_8_3_16k.png

单步总耗时拆解为 Free + Communication(Not Overlapped) + Computing = 3771.3 ms:

类别 耗时 占比 说明
Computing 3581.90 ms 95.0% 计算时间占绝对主导
Communication (Not Overlapped) 122.03 ms 3.24% 未被计算掩盖的通信
Free 67.41 ms 1.79% 内存释放开销

结论:任务呈计算密集型特征,NPU 利用率极高。与 8B 相比,32B 未掩盖通信占比略高(3.24% vs 1.39%),符合模型更大、单层通信量更多的预期,但整体仍以计算为主,通信与内存管理均非瓶颈。

2.3.5 Chunk Loss 优化

8/3 虽然稳定,但可训练层数被压得过低。查看内存快照发现,与 8B 相同,中间部分同样存在明显的显存尖刺:

loss_jianci.png

其根因与 8B 一致:loss 计算前产生的临时张量 logits 在 16k 序列长度下达到 (B, S, V) 量级的大张量(定位过程详见 2.2.5)。

优化措施:应用 chunk loss 并在 YAML 中开启(配置同 2.2.5:apply_module: lm_head、chunk_size: 512),将 tensor 在序列维度切分成长度为 512 的 sub_seq 子序列分段进行 loss 计算,避免一次性生成大张量 logits。

优化效果:开启 chunk loss 后重新实验,将 language_model 层数置为 32、visual 层数置为 13(32/13),即可在 8 卡上很好地跑下 qwen3vl-32b 对 16k 数据集的训练:

  • 单步时间分布比较均匀:

qwen3vl_32B_fsdp2_32_13_16k_20260812_093551.png

  • rank0 单步 profiling 显示任务仍明显属于计算密集型,NPU 利用率极高:

qwen3vl_32B_fsdp2_32_13_16k.png

  • 内存快照显示中间位置的显存尖刺消失,变成了一段段 chunk 之后的 sub_seq 子序列 loss 计算:

chunk_loss.png

结论:从在 8 卡上只能跑下 8 层 llm 和 3 层 vit,到应用 chunk loss 后可以跑下足足 32 层 llm 和 13 层 vit,可训练层数提升约 4 倍,足见 chunk loss 带来的显存优化作用巨大。

2.4 NPU 融合算子适配

除上述显存类优化外,计算侧对模型中的热点小算子做了 NPU 融合算子适配:将原始 PyTorch 实现替换为昇腾融合算子,减少算子数量与 kernel 启动开销,提升执行效率。适配位于各模型目录的 npu_patch.py,modeling 文件末尾在检测到 NPU 环境(IS_NPU_AVAILABLE)时自动调用 apply_*_npu_patch() 完成模块级函数替换,模型前向代码无需改动,随 training.plugin 导入模型包时生效。

Dense 版(qwen3vl/npu_patch.py::apply_qwen3vl_npu_patch,8B/32B 共用):

原始实现 替换为 说明
apply_rotary_pos_emb_vision npu_fused_operator.apply_transformers_vision_rope_half_npu 视觉侧 RoPE(half 模式)融合实现
apply_rotary_pos_emb npu_fused_operator.apply_transformers_rope_half_npu 文本侧 RoPE(half 模式)融合实现
Qwen3VLTextRMSNorm.forward npu_fused_operator.rms_norm_forward_npu RMSNorm,基于 torch_npu.npu_rms_norm

MoE 版(qwen3vl_moe/npu_patch.py::apply_qwen3vl_moe_npu_patch,30B/235B):在上述三类算子(对应 MoE 类 Qwen3VLMoeTextRMSNorm)替换的基础上,额外将专家层前向 Qwen3VLMoeTextExperts.forward 替换为 npu_fused_operator.fused_moe_forward_npu,把 permute → grouped matmul(gate/up)→ SwiGLU → grouped matmul(down)→ unpermute 的 MoE 计算链路整体以融合算子执行。

2.5 经验总结

  1. 显存需预留余量:显存占用贴近单卡物理上限会触发 ACL 物理内存重整(aclrtMallocPhysical/aclrtFreePhysical),引入毫秒级同步等待,表现为单步时间大幅波动。性能测试与正式训练均建议通过减层、降 batch、开重计算/激活 offload/chunk loss 等方式预留显存余量,而不是贴着上限运行。
  2. 大模型更早触碰显存墙:同为 16k 序列长度,8B 减层到 32/13 才触顶,而 32B 减层到 12/7 仍复现内存重整,需进一步减至 8/3 才稳定。模型越大,显存余量越要留足,减层与稳定性的权衡要更早纳入测试计划。
  3. 波动类问题优先用 profiling 对时间线,方法可复用:单步时间尖刺类问题,直接采集单步 profiling,在 timeline 上找到尖刺对应的 runtime 区间(Free → aclrtFreePhysical/aclrtGetMemInfo),即可定位到显存触顶引发的 ACL 内存重整,避免在计算/通信层面盲目排查;该路径在 8B 与 32B 上均得到验证。
  4. 性能测试用 mock 数据 + 减层控制变量:mock 数据集(固定图片 + 重复文本)与减层模型可以排除数据加载抖动、全层资源占用等干扰,使 profiling 结论聚焦于框架与并行策略本身;结论验证后再回到全层 + 真实数据复核。
  5. 大 logits 是长序列 VLM 的共性隐性显存峰值:大词表(15 万+)+ 长序列(16k)场景下,loss 计算前的 logits 临时张量达数十亿元素,在内存快照中表现为前向与反向之间的显存尖刺。参考 MoE 模型实现适配 chunk loss(apply_module: lm_head,按序列维度分块计算 loss)可消除该尖刺——8B 借此在 8 卡上支撑了 16k 全层训练,峰值显存降至 20 GB 出头。
  6. chunk loss 对大模型的收益更显著:32B 应用 chunk loss 后可训练层数从 8 层 llm + 3 层 vit 提升到 32 层 llm + 13 层 vit(约 4 倍),是支撑大模型长序列训练的关键特性。
  7. 健康训练任务的参考画像:8B 计算占比 ~97%、未掩盖通信与 Free 各 ~1.5% 以内;32B 计算占比 ~95%、未掩盖通信 ~3.2%、Free ~1.8% 以内。可作为纯 FSDP2 后端 VLM SFT 任务调优达标的参考基线;通信占比随模型增大而升高属正常现象,若进一步升高应检查 fsdp_plan 分片粒度与 num_to_forward_prefetch/num_to_backward_prefetch 等预取配置。

附录

A. 本次改动文件清单

新增:
  examples/qwen3vl/finetune_qwen3vl_8B_v1.sh
  examples/qwen3vl/finetune_qwen3vl_32B_v1.sh
  examples/qwen3vl/finetune_qwen3vl_235B_v1.sh
  examples/qwen3vl/qwen3vl_8B_config_v1.yaml
  examples/qwen3vl/qwen3vl_32B_config_v1.yaml
  examples/qwen3vl/qwen3vl_235B_config_v1.yaml
  mindspeed_mm/fsdp/models/qwen3vl/modeling_qwen3_vl.py   # Dense 实现,注册 id qwen3_vl,同时支持 8B/32B
  mindspeed_mm/fsdp/models/qwen3vl_moe/(modeling_qwen3_vl_moe.py、npu_patch.py,由 qwen3vl/ 迁出)
修改:
  mindspeed_mm/fsdp/models/qwen3vl/npu_patch.py   # patch 切换为 Dense 版
参考(未改动):
  examples/qwen3vl/finetune_qwen3vl_8B.sh / qwen3vl_full_sft_8B.yaml     # Megatron + FSDP2 对标实现
  examples/qwen3vl/finetune_qwen3vl_32B.sh / qwen3vl_full_sft_32B.yaml   # Megatron + FSDP2 对标实现
  examples/qwen3vl/finetune_qwen3vl_235B.sh / qwen3vl_full_sft_235B.yaml # Megatron + FSDP2 对标实现

B. 参考资料

likedislike
Llhj628
8月28日 修改了issue 的描述
Llhj628
8月29日 修改了issue 的描述
且奏长歌且奏长歌成员
24 天前 issue状态由 TODO 改变为 DONE
且奏长歌且奏长歌成员
24 天前 关闭了 issue
ascend-robotascend-robot成员
24 天前 添加了label:resolved
且奏长歌且奏长歌成员
24 天前 将 lhj628 设为负责人