mindspore-ai / mindspore-ai/hyper-parallel

clip_grad_norm_ 将 replicate/非切分参数的梯度范数按 shard world size 重复计数,导致 grad_norm 偏大

Open
#279 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

该问题是怎么引起的?

在 FSDP/HSDP(单机 8 卡,DP=8)下,clip_grad_norm_ 计算全局梯度范数时,把非切分参数(replicate 参数,或经 FSDP ignored_params 由用户自管理的参数)的本地 norm² 也放进了 shard 进程组的 all_reduce(SUM)。这些梯度本身在各 rank 上已是全局一致的值,其 norm² 应只在本地计入一次;多做一次 shard 组 SUM 会把它重复计 shard_world_size 次(8 卡即 ×8),使全局 grad_norm 偏大。

两种管理不切参数的方式都复现同一症状(并行策略未改、首步 loss 未变,仅 grad_norm 改变):

  • 通过 replicate_params 管理不切参数 → global grad_norm = 48(基线 28);
  • 通过 FSDP ignored_params 并在训练侧自行额外 all_reduce 其梯度 → grad_norm = 100+

根因:replicate / ()-签名 的梯度应"本地计入、不通信",旧实现却在 shard 组上对其 norm² 求和。注:首步 loss 不变是因为该步 norm 未超过 max_norm、未触发实际裁剪,但计算出的全局 norm 本身是错的——一旦 norm 超过 max_norm 就会按错误的范数过度裁剪。

重现步骤
  1. 单机 8 卡,FSDP/HSDP,DP=8,并行策略保持不变。
  2. 选一批不切分参数(如每层 bias):方式 A 交给 replicate_params;方式 B 放入 FSDP ignored_params 并在训练侧自行对其梯度做一次 all_reduce。
  3. 反向后调用 clip_grad_norm_,读取返回值/日志中的全局 grad_norm
  4. 以"把所有分片梯度 all-gather 还原成全量后直接计算的范数"作为基线对照。

观察:replicate_params → 48,ignored_params + 手动 all_reduce → 100+,基线 → 28。

报错信息

非崩溃类问题,无异常栈,是数值错误:

baseline(all-gather 全量梯度算的范数)   : 28
replicate_params 全局 grad_norm           : 48     (≈ 在 shard 组上重复计数)
ignored_params + 手动 all_reduce          : 100+
首步 loss 不变,仅 grad_norm 偏大

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

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 by locating the Python implementation of clip_grad_norm_ and trace how replicate_params and FSDP ignored_params, including the () signature, contribute to norm aggregation. Reproduce the single-node 8-card FSDP/HSDP cases and compare the reported norm with the all-gather baseline; done means replicate and non-sharded gradients are counted once and the global grad_norm matches the baseline.

Written by the indexing model from the issue text.

Assessment

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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.