Skip to content

[PyTorch] Quantize distributed BF16 weights in the fused grouped MLP - #3695

Closed
xrennvidia wants to merge 2 commits into
NVIDIA:mainfrom
xrennvidia:xren/fused_grouped_mlp_gtp_bf16_weight_requant
Closed

xrennvidia wants to merge 2 commits into
NVIDIA:mainfrom
xrennvidia:xren/fused_grouped_mlp_gtp_bf16_weight_requant

Conversation

@xrennvidia

Copy link
Copy Markdown
Collaborator

A distributed (e.g. GTP) weight whose shards are not natively quantized is re-gathered as BF16. The fused grouped-MLP op assumed gathered weights were already quantized: forward quantized the shards before the gather, and backward read _columnwise_data / the NVFP4 amax straight off the gathered tensor, which fails for a module whose per-module recipe (e.g. MXFP8 experts under a global NVFP4 recipe) does not create native-quantized params.

Gather first, then quantize whatever is still unquantized with the op's per-expert weight quantizers, mirroring module/linear.py's handling of a re-gathered BF16 distributed weight. Native-quantized shards gather as quantized tensors and pass through unchanged. The FC2 backward re-gather moves ahead of the NVFP4 global-scale computation so that path no longer reads un-gathered shards.

Description

Please include a brief summary of the changes, relevant motivation and context.

Fixes # (issue)

Type of change

  • Documentation change (change only to the documentation, either a fix or a new content)
  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Infra/Build change
  • Code refactoring

Changes

Please list the changes introduced in this PR:

  • Change A
  • Change B

Checklist:

  • I have read and followed the contributing guidelines
  • The functionality is complete
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

@xrennvidia
xrennvidia requested a review from timmoon10 as a code owner October 16, 2026 06:17
@github-actions github-actions Bot added the community-contribution PRs from external contributor outside the core maintainers, representing community-driven work. label Oct 16, 2026
@greptile-apps

greptile-apps Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no new blocking issues were found.

Summary

The fused grouped MLP now gathers distributed BF16 weights before quantizing them with each expert’s quantizer. Already-quantized weights pass through unchanged.

  • Distributed grouped MLP weights are gathered before plain weights are quantized.
  • Backward saves only the grouped input data its weight gradients use.

The three earlier, unnumbered findings are addressed: regression tests were added, CI runs both files, and both saved-input test layers set split_alignment=256. No new actionable issues were found. CUDA tests were not run.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Distributed weight shards] --> B[Gather full weights]
  B --> C{Already quantized?}
  C -->|Yes| E[Use weights unchanged]
  C -->|No| D[Quantize with per-expert quantizers]
  D --> F[Run grouped GEMM]
  E --> F
  G[Quantizers saved in forward] --> D
Loading

Reviews (12) · Last reviewed commit: "[PyTorch] Drop NVFP4 rowwise input copie..." · Reviewed by Greptile

Comment thread transformer_engine/pytorch/ops/fused/grouped_mlp.py
@xrennvidia
xrennvidia force-pushed the xren/fused_grouped_mlp_gtp_bf16_weight_requant branch 2 times, most recently from b0d0f7d to a11d19c Compare October 17, 2026 08:59
Comment thread tests/pytorch/test_ops_grouped_mlp_distributed_weight.py
@xrennvidia
xrennvidia force-pushed the xren/fused_grouped_mlp_gtp_bf16_weight_requant branch 3 times, most recently from 70020d5 to b4ae3f7 Compare October 17, 2026 09:25
Comment thread tests/pytorch/test_ops_grouped_mlp_nvfp4_saved_tensors.py
@xrennvidia
xrennvidia force-pushed the xren/fused_grouped_mlp_gtp_bf16_weight_requant branch 2 times, most recently from 5ea48b1 to c159f66 Compare October 18, 2026 06:34
xrennvidia and others added 2 commits October 18, 2026 02:00
A distributed (e.g. GTP) weight whose shards are not natively quantized is
re-gathered as BF16. The fused grouped-MLP op assumed gathered weights were
already quantized: forward quantized the shards before the gather, and backward
read _columnwise_data / the NVFP4 amax straight off the gathered tensor, which
fails for a module whose per-module recipe (e.g. MXFP8 experts under a global
NVFP4 recipe) does not create native-quantized params.

Gather first, then quantize whatever is still unquantized with the op's
per-expert weight quantizers, mirroring module/linear.py's handling of a
re-gathered BF16 distributed weight. Native-quantized shards gather as quantized
tensors and pass through unchanged. The FC2 backward re-gather moves ahead of
the NVFP4 amax / global-scale computation so that path no longer reads
un-gathered shards.

The per-expert weight quantizer lists are built once in forward and saved on
the FC1/FC2 contexts; backward quantizes the re-gathered weights with those
rather than reading the live op. A later forward on the same module under a
different recipe, or with quantization disabled, resets the ops' quantizers
before the earlier backward runs (pipelined microbatches, or a module shared
across recipe scopes), which would otherwise hit None or quantize in the wrong
format.

Regression tests: a fake routed-expert DistributedWeight group whose materialize
hooks return BF16 tensors (or MXFP8 tensors for the native-quantized case)
drives the CuTeDSL fused grouped MLP under per-module MXFP8, optionally inside a
global NVFP4 autocast. Distinct power-of-two scales in the forward/backward
hooks make the gathered weights observable: output, dgrad and the main_grad
wgrads must match a plain reference bit-exactly up to those scales. A unit
check confirms already-quantized gathered weights pass through the helper
unchanged. A further case runs a second forward under no recipe or NVFP4
between an MXFP8 forward and its backward and requires bit-exact agreement
with a reference. Fails on the unpatched op with AttributeError on
_columnwise_data in backward; passes with the fix.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Signed-off-by: Xiaowei Ren <[email protected]>
…saved state

Every wgrad path reads only the columnwise tiles of the saved FC1/FC2 inputs:
the cuDNN grouped wgrad kernel, the single-group dense GEMM (general_gemm with
the NT layout unwraps both operands columnwise) and the generic grouped-GEMM
fallback (NVFP4 forces a TN layout by taking the columnwise data for a
non-transposed A and a transposed B, which is the wgrad case). The save path
already pruned the rowwise data and scales for MXFP8 but kept them for NVFP4,
citing the generic fallback, so each quantized NVFP4 input was held (and, under
activation offloading, copied to host and back) twice.

Prune them for NVFP4 as well, unconditionally. The amax tensors are untouched.
Per decoder expert layer of a Nemotron hybrid proxy (83k tokens, hidden 3072)
this removes a 121 MiB data and a 15 MiB scale tensor from the ~1.9 GiB saved
set, with unchanged numerics on both the cuDNN kernel and the forced generic
fallback (NVTE_DISABLE_CUTEDSL_WGRAD_FUSED_GROUPED_MLP=1).

Adds a saved_tensors_hooks test asserting exactly one FP4 data and one scale
tensor is saved per quantized input on the fused NVFP4 path, for group sizes
1 and 4, with token counts chosen so the inputs cannot be confused with weights
in the count, and registers it in the L0 unit-test runner. Grouped storage is
saved as raw buffers; the single-group dense path (cuDNN frontend >= 1.27)
saves a GroupedTensor wrapper whole, so the test inspects the wrapper's buffers
directly and requires its rowwise data and scales to be None.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Signed-off-by: Xiaowei Ren <[email protected]>
@xrennvidia
xrennvidia force-pushed the xren/fused_grouped_mlp_gtp_bf16_weight_requant branch from c159f66 to cb1ffc4 Compare October 18, 2026 09:01
@xrennvidia

Copy link
Copy Markdown
Collaborator Author

Close because the changes have been cherry-picked into PR3545.

@xrennvidia xrennvidia closed this Oct 18, 2026
@xrennvidia
xrennvidia deleted the xren/fused_grouped_mlp_gtp_bf16_weight_requant branch October 23, 2026 07:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community-contribution PRs from external contributor outside the core maintainers, representing community-driven work.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant