mindspore-ai / mindspore-ai/hyper-parallel

训练栈:examples/ 三个 train.yaml 在最新 master 上报错(dp_shard leaf/grad + vl-moe get_input_embeddings/AC 回退)

Open
#248 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
53
Forks
63
Avg merge
23h 45m
Merged PRs (30d)
63

Description

该问题是怎么引起的?

PR #599(训练栈 + qwen3.5 dense/moe/vl-moe 对齐,描述中显式依赖 PR #633 的 dp_shard==1 修复)合入时,examples/ 下三个 train.yaml 是验证过能跑通 + 1c↔FSDP4 对齐的。此后 master 上的若干提交把四处回退/移除了,导致现在直接跑这三个 example 在 init / parallelize 阶段全部崩溃,无法开始训练。下面逐文件说明每一处为什么是 bug。


1. hyper_parallel/platform/torch/fully_shard/param.pyTorchHSDPParamV2)—— sharded _local_tensor 丢了 .detach()

这段代码做什么:meta-init(to_empty)+ FSDP 分片后,要把"切出来的本地分片"绑到 DTensor 参数的 _local_tensor 上。当前 master:

if updated_local_tensor:
    with torch.no_grad():
        local_view = local_tensor.narrow(dim=shard_dim, start=0, length=length)   # ← 缺 .detach()
    set_requires_grad_if_needed(self.sharded_param, local_view)
    self.sharded_param._local_tensor = local_view

为什么是 bugTensor.narrow(...) 返回的是 base(local_tensor)的 autograd 视图(view),仍挂在它的求导图上;这步在 to_empty 期间、torch.no_grad() 下执行 → 是"no_grad 下创建的视图"。之后权重加载 / 混精 upcast 在 grad 模式下原地改写它/它的 base。等构造 torch.optim.AdamW 时,add_param_groupparam.is_leaf(DTensor 的 is_leafself._local_tensor.is_leaf),PyTorch autograd 检测到"no_grad 下建的视图、其 base 又在 grad 模式下被原地改"这种自相矛盾状态,直接抛 RuntimeError

为什么这样改能修.narrow(...).detach() 返回共享 storage 但脱离求导图的张量(干净 leaf),不是 local_tensor 的 autograd 视图——is_leaf 读取不再触发该检查。PR #633 原本就有这个 .detach(),master 上某次 fully_shard 重构把它丢了。本质是恢复 PR #633。

2. hyper_parallel/platform/torch/fully_shard/state.pyTorchHSDPStateV2.lazy_init)—— dp_shard==1 时漏 reset

这段代码做什么lazy_init 在 meta 物化后把每个 sharded 参数 reset_sharded_param() 重新包回正确的 DTensor nn.Parameter,让优化器和 forward 拿到对的对象。当前 master:

def lazy_init(self):
    if self.is_shard and not self._reset_sharded_params:    # ← 多了 self.is_shard
        for hsdp_param in self.hsdp_params:
            hsdp_param.reset_sharded_param()
        self._reset_sharded_params = True

为什么是 bugself.is_sharddp_shard==1(world_size==1、实际不分片)时为 False,于是整段 reset 被跳过——单卡路径下 param 没被重新包/重置,留在 meta(HSDP params still on meta device),且第 1 点的 leaf 不变量也建立不起来。PR #599(380840b)时这里是 if not self._reset_sharded_params:没有 self.is_shard。world_size==1 时同样要 reset/re-wrap(unshard/shard hook 要把模块属性换回普通 nn.Parameter,避免 DTensor 漏进 forward),故去掉守卫。本文件 #2 与上面 #1 合起来就是 PR #633 的"dp_shard==1 leaf/grad"修复,缺一不可。

3. hyper_parallel/models/qwen3_vl_moe/model.pyQwen3VLMoeModel)—— get_input_embeddings 被误删

这段代码做什么Qwen3VLMoeModel.forwardinputs_embeds is None 时按 HF 标准做法取文本词嵌入:

def forward(self, input_ids, ..., inputs_embeds=None, pixel_values=None, ...):
    if inputs_embeds is None:
        inputs_embeds = self.get_input_embeddings()(input_ids)   # ← 调用 get_input_embeddings
    ... # 之后再把图像特征 merge 进 inputs_embeds

为什么是 bugQwen3VLMoeModel.get_input_embeddings(返回 self.language_model.embed_tokens)被 d464b132 fix(codecheck): add missing docstrings for C0116 warnings(一次性动了 66 个文件的 codecheck/docstring 清理)连带删掉了,但 forward 仍在调它 → vl-moe 第一步 forward 就崩AttributeError: 'Qwen3VLMoeModel' object has no attribute 'get_input_embeddings'

为什么这样改能修:把 get_input_embeddings(+ 对称的 set_input_embeddings)补回 Qwen3VLMoeModel,指向 self.language_model.embed_tokens——纯属恢复 PR #599 就有的方法。

4. hyper_parallel/platform/torch/activation_checkpoint/activation_swap.py_check_and_mark_wrapped)—— 不容忍跨层共享的 rotary

这段代码做什么:激活重计算逐层 checkpoint_wrapper(layer) 时,_check_and_mark_wrapped 遍历该层子模块:先"重叠保护"(任一子模块已 _is_wrapped 就报错),再把所有子模块标记为已包裹。

为什么是 bug:Qwen3-VL-MoE 的所有 text decoder 层共享同一个 MultiModalRotaryEmbedding 实例(有意设计:位置编码算一次、各层共用)。包第 0 层时该 rotary 被标记 _is_wrapped;包第 1 层时,同一个实例被判"已包裹" → ValueError: Submodule 'MultiModalRotaryEmbedding' of 'Qwen3VLMoeTextDecoder' is already wrapped。这个 overlap 守卫是 PR #599 之后才加的(PR #599 没有它),加时没考虑"合法的跨层共享子模块"。vl-moe example 用 activation_checkpoint: full 必然走到这里(dense/moe 用 none 不触发)。

为什么这样改能修:被共享的 rotary 是无状态的(只算位置编码、没有要 checkpoint 的参数),包一次(随第 0 层)、后续层跳过它是正确的,不存在真正的"重叠区域"风险。所以把"子模块已包裹就 raise"改成"continue(跳过)"。


重现步骤

环境:8×Ascend 910B3,CANN 9.0.0,torch 2.7.1 + torch_npu 2.7.1,最新 upstream/mastereebcca6)。填好三个 example 的 weights_path(dense/moe 另需 preset_pt batch 文件),确定性变量 HCCL_DETERMINISTIC=true LCCL_DETERMINISTIC=1 ASCEND_LAUNCH_BLOCKING=1 HCCL_OP_BASE_FFTS_MODE_ENABLE=false,分别跑:

torchrun --standalone --nproc_per_node=4 scripts/train_lm.py examples/qwen3_5_0_8b_base/train.yaml
torchrun --standalone --nproc_per_node=4 scripts/train_lm.py examples/qwen3_5_35b_a3b_base/train.yaml
torchrun --standalone --nproc_per_node=4 scripts/train_vl.py  examples/qwen3_vl_30b_a3b_instruct/train.yaml

三个全部崩溃,无一能开始训练。

报错信息

dense / moe —— optimizer 构造(文件 #1+#2):

File ".../hyper_parallel/trainer/base.py", in _build_optimizer
    self.optimizer = torch.optim.AdamW(param_groups, ...)
  ... add_param_group ...  param.is_leaf or param.retains_grad
File ".../hyper_parallel/platform/torch/dtensor.py", line 187, in is_leaf
    return self._local_tensor.is_leaf
RuntimeError: A view was created in no_grad mode and its base or another view of its base
has been modified inplace with grad mode enabled. ...

vl-moe —— parallelize 的 AC 包裹(文件 #4):

File ".../hyper_parallel/models/qwen3_vl_moe/parallelize.py", in _apply_ac
    model.layers[i] = checkpoint_wrapper(layer)
File ".../activation_checkpoint/activation_swap.py", in _check_and_mark_wrapped
    raise ValueError(...)
ValueError: Submodule 'MultiModalRotaryEmbedding' of 'Qwen3VLMoeTextDecoder' is already wrapped.
Wrapping overlapping module regions is not allowed.

vl-moe —— AC 修复后 forward(文件 #3):

File ".../hyper_parallel/models/qwen3_vl_moe/model.py", in forward
    inputs_embeds = self.get_input_embeddings()(input_ids)
AttributeError: 'Qwen3VLMoeModel' object has no attribute 'get_input_embeddings'

修复(恢复 #1 .detach() + #2 去 self.is_shard 守卫 + #3 补回 get_input_embeddings + #4 容忍共享子模块)后,三个 example 各 20 步 Training completed,1c↔FSDP4 自洽通过(dense/moe ULP 级 ~1e-6,vl-moe 逐位)。修复见关联 PR。

schema_version: 1
source: gitcode
gitcode_repo: mindspore/hyper-parallel
gitcode_issue: 230
source_url: https://gitcode.com/mindspore/hyper-parallel/issues/230

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the four named files: fully_shard/param.py and state.py, models/qwen3_vl_moe/model.py, and activation_checkpoint/activation_swap.py. Run the three listed train.yaml commands in the specified environment and compare the failures with the described leaf, meta-parameter, embedding, and shared-rotary paths. Done means all three examples complete 20 training steps and the stated 1c↔FSDP4 consistency checks pass.

Written by the indexing model from the issue text.

Assessment

Tech stack
python, pytorch
Domain
distributed-systems, machine-learning
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.