Skip to content

Count replicated GPTBigCode K/V rows once in clip_grad_norm_ - #9030

Open
mvanhorn wants to merge 3 commits into
deepspeedai:masterfrom
mvanhorn:fix/9024-autotp-replicated-kv-grad-norm
Open

mvanhorn wants to merge 3 commits into
deepspeedai:masterfrom
mvanhorn:fix/9024-autotp-replicated-kv-grad-norm

Conversation

@mvanhorn

@mvanhorn mvanhorn commented Nov 7, 2026

Copy link
Copy Markdown

Background

Tensor-parallel multi-query GPTBigCode training clips gradients more aggressively than the same step on one device. The key and value rows of the fused attention projection sit on every tensor-parallel rank, and the norm that sets the clip coefficient adds every copy, so updates are scaled down relative to single-device training. The gradient values already match that single-device run; the mismatch is the clip scale, and it grows with the key/value share of the model gradient and with the tensor-parallel degree.

Clipping totals a power of each local gradient and all-reduces that total across the tensor-parallel group, which is correct when every model-parallel row belongs to exactly one rank. The fused attention weight and bias are marked model-parallel, and each rank's shard holds its query rows plus the full key/value block that the backward hook has already reduced.

Query rows from different ranks are disjoint and combine into one copy, while the key/value block is identical on every rank, so the all-reduce counts it once per rank and the reported norm is too large.

Summary

The fused linear layer used for this projection now stores a mark on both the weight and the bias: the first replicated row, how many rows are replicated, and the tensor-parallel world size. In the finite-norm accumulation, a model-parallel parameter carrying that mark contributes its leading rows at full power and its replicated rows at that power divided by the tensor-parallel size, so the later sum across ranks counts those rows once.

Parameters without the mark still contribute their full gradient power, and each stored term is already raised to the norm power so the stacked terms are summed directly. Refs #9024.

Tests

  • Ran make test locally on macOS for the DeepSpeed fused_LinearLayer change; it fails the same way on the base branch, so the failure predates this change (it fails the same way on upstream/master).
  • A run with the DeepSpeed fused_LinearLayer change reverted gave no clear result, so no test is shown to catch this bug.
  • The changed test tests/unit/runtime/test_runtime_utils.py did not run here: the torch module is not installed.

Refs #9024

AI was used for assistance.

The fused linear layer used for this projection now stores a mark on
both the weight and the bias: the first replicated row, how many rows
are replicated, and the tensor-parallel world size. In the finite-norm
accumulation, a model-parallel parameter carrying that mark contributes
its leading rows at full power and its replicated rows at that power
divided by the tensor-parallel size, so the later sum across ranks
counts those rows once. Parameters without the mark still contribute
their full gradient power, and each stored term is already raised to the
norm power so the stacked terms are summed directly. Fixes deepspeedai#9024.

Refs deepspeedai#9024

Signed-off-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 7, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-07T10:58:30.154302Z 079df62 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 079df62743

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread deepspeed/runtime/utils.py Resolved
The single-device comparison ran with clipping off, so it could not see a
clip coefficient computed from a norm that counts the shared K/V rows once
per rank. Run it again with gradient_clipping 0.5, which is below every
step's gradient norm, and clip the reference with
torch.nn.utils.clip_grad_norm_ at the same threshold.

Refs deepspeedai#9024

Signed-off-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Raising each parameter's norm to norm_type one at a time added a scalar
kernel per parameter on every clipped FP32 step, even for models without
replicated K/V rows. Unmarked parameters go back to one stacked pow, and
only parameters marked with ds_tp_replicated_rows take the per-row path.

Refs deepspeedai#9024

Signed-off-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
@VenusTZZ

VenusTZZ commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

Thanks for this fix! I’ve verified the norm accounting is correct for the path it touches.
I went ahead and ran the GPTBigCode MQA clipping-parity test (TP=2, DP=2, max_norm=1.0, tol=1e-5) on both 4×Ascend 910B4 and 4×A100 setups, built on my AutoTP branch (#8962) with your changes applied on top. Here are the results:

  • With your fix applied: ZeRO-0 + fp32 passes successfully on both platforms.
  • Without the utils.py change: ZeRO-0 fails on both platforms, with a consistent ~4.7e-4 loss gap.

Test environment details for reference:

  • A100: torch 2.14.1+cu126, transformers 5.20.2
  • 910B4: CANN 8.5.1, torch_npu 2.10.0.post7, transformers 5.19.0

A quick note on scope

This fix specifically resolves the ZeRO-0 fp32 case. The take_model_step function only calls clip_grad_norm when both fp16/bf16 and ZeRO are disabled; ZeRO-1/2/3 and fp16/bf16 workflows use their own separate norm logic, which remains untouched here. Stages 1/2/3 still fail with the same ~4.7e-4 gap on both setups.

Two possible paths forward

  1. Extend the replicated-row accounting logic to cover all norm calculation paths. Note that ZeRO shards/partitions will require offset-aware K/V handling, so the current arithmetic cannot be reused directly.
  2. Keep this PR scoped to the ZeRO-0 fp32 fix: update the title and description accordingly, mark unfixed stages as skipped/xfail, and leave [BUG] AutoTP counts replicated GPTBigCode key/value rows once per tensor-parallel rank in the global gradient norm #9024 open for follow-up work.

Small refinements to suggest

  • Unify the duplicated marker name — there is a hardcoded literal in utils.py and the DS_TP_REPLICATED_ROWS constant in layers.py that would be cleaner to align.
  • Parameterize the new test across stages 0–3 (or mark unfixed stages as xfail) to align test coverage with the scope of the fix.

Per the guidelines in AGENTS.md, could you please run the new integration test on a GPU setup and share the hardware specs, dependency versions, and the exact run command? For context, my validation used the clipping-parity test from my branch, rather than the test case added in this PR.

@mvanhorn

Copy link
Copy Markdown
Author

Thanks for running this on both A100 and 910B4. Option 2 fits best: this PR covers only the ZeRO-0 fp32 clip_grad_norm_ path, and the ZeRO 1/2/3 and fp16/bf16 norm paths need offset-aware K/V handling that belongs in a separate change, with #9024 left open for it. I don't have GPU hardware on this machine, so your A100 and 910B4 runs are the only device results for this so far, and you're right that utils.py still hardcodes 'ds_tp_replicated_rows' instead of importing DS_TP_REPLICATED_ROWS from layers.py.

@gpu-test-labeler gpu-test-labeler Bot added the skip-gpu-test Requires positive evidence that every changed behavior can be validated without a GPU. label Nov 14, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip-gpu-test Requires positive evidence that every changed behavior can be validated without a GPU.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants