已开启
训练栈:examples/ 三个 train.yaml 在最新 master 上报错(dp_shard leaf/grad + vl-moe get_input_embeddings/AC 回退) #230
xuxinglei创建于  6月17日
xuxinglei成员
6月17日 创建

该问题是怎么引起的?

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.py(TorchHSDPParamV2)—— 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

为什么是 bug:Tensor.narrow(...) 返回的是 base(local_tensor)的 autograd 视图(view),仍挂在它的求导图上;这步在 to_empty 期间、torch.no_grad() 下执行 → 是"no_grad 下创建的视图"。之后权重加载 / 混精 upcast 在 grad 模式下原地改写它/它的 base。等构造 torch.optim.AdamW 时,add_param_group 读 param.is_leaf(DTensor 的 is_leaf → self._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.py(TorchHSDPStateV2.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

为什么是 bug:self.is_shard 在 dp_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.py(Qwen3VLMoeModel)—— get_input_embeddings 被误删

这段代码做什么:Qwen3VLMoeModel.forward 在 inputs_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

为什么是 bug:Qwen3VLMoeModel.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/master(eebcca6)。填好三个 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。

likedislike
Xxuxinglei成员
6月17日 关联了pull request:fix(train): restore dp_shard and vl-moe example runs on master
Xxuxinglei成员
6月17日 修改了issue 的描述
Xxuxinglei成员
6月17日 修改标题为 “训练栈:examples/ 三个 train.yaml 在最新 master 上报错(dp_shard leaf/grad + vl-moe get_input_embeddings/AC 回退)”,原标题为“训练栈:examples/ 三个 train.yaml 在最新 master 上全部跑不起来(dp_shard leaf/grad + vl-moe get_input_embeddings/AC 回退)”
MengXY107MengXY107成员
6月22日 关联了pull request:fix(fsdp): detach narrowed shard in reset_sharded_param to avoid is_leaf error
MengXY107MengXY107成员
6月24日 关联了pull request:fix: restore reset_sharded_param .detach() and Qwen3VLMoe get_input_embeddings