[moe training] Add backward_override to the MXFP8 grouped GEMM - #10
Closed
wolfcomos wants to merge 2 commits into
Closed
[moe training] Add backward_override to the MXFP8 grouped GEMM#10wolfcomos wants to merge 2 commits into
wolfcomos wants to merge 2 commits into
Conversation
Support ablating the backward computation of the grouped MXFP8 op without touching the forward: - backward_override: None/"quantized" keeps the quantized backward (default, unchanged); "high_precision" saves the unpadded high-precision operands and computes both gradients with plain grouped GEMMs; "dequantized" saves the quantized forward operands and dequantizes them for the same two grouped GEMMs, which measures the effect of differentiating the quantized-forward function itself (train/inference consistency for RL-style recipes). - The dequantized arm quantizes activations with the Triton dim0 kernel (bitwise identical qdata to the CuTe DSL 1x32 rceil path) because backward needs logical scales and the CuTe DSL kernel returns blocked-only scales with no unswizzler. - Thread backward_override through MXFP8TrainingOpConfig (including __eq__/__hash__, so overridden configs never alias the default) and _quantize_then_scaled_grouped_mm. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The CUDA dim1 cast kernel behind the default quantized wgrad checks input.is_contiguous(), and autograd can hand backward an expanded or otherwise non-contiguous grad_output view (sum().backward() produces a stride-0 expanded ones gradient), which crashed the default backward. Call .contiguous() on the dim1-cast wgrad operands and on the dgrad CuTe DSL quantize input before quantizing; this is a no-op for the already-contiguous tensors these paths normally see, and keeps the kernel choices unchanged for existing callers. test_noncontiguous_grad_output_wgrad pins the behavior with a direct expanded-view wgrad call and an end-to-end sum().backward() that also drives the dgrad quantizer with the stride-0 gradient. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
wolfcomos
force-pushed
the
4over6/ao4-mxfp8-backward-override
branch
from
August 31, 2026 02:07
8e0ce88 to
b8d7f07
Compare
Owner
Author
|
Closing to tidy the fork; the branch is preserved for a future upstream cut of the MXFP8 backward_override. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Independent PR (no stack). Companion TorchTitan converter PR: wolfcomos/torchtitan#15.
Summary
Adds a
backward_overrideknob to the MXFP8 grouped GEMM(
_to_mxfp8_then_scaled_grouped_mm/_MXFP8GroupedMM) so the backwardcomputation can be ablated without touching the forward. This is the op-level
support needed for recipe ablations that hold the quantized forward fixed and
vary only where gradients come from (e.g. RL fine-tuning recipes that need the
training backward to be consistent with a quantized inference forward).
The knob is threaded through
MXFP8TrainingOpConfigand the_quantize_then_scaled_grouped_mmdispatcher, so model-conversion callers(e.g. torchtitan converters) reach it the same way they reach the existing
MXFP8 knobs.
A second commit fixes a latent crash class on the default quantized backward:
the CUDA dim1 cast kernel checks
input.is_contiguous(), and autograd canhand backward an expanded (stride-0) or otherwise non-contiguous
grad_outputview (out.sum().backward()produces one). The fixmaterializes with
.contiguous()on both the dim1-cast wgrad operands andthe dgrad CuTe DSL quantize input (whose stride-0 handling is unverified) —
a no-op for the contiguous tensors these paths normally see — keeping the
kernel choices unchanged for existing callers.
Design notes
None/"quantized"leave the default quantized backward byte-for-byteunchanged;
"quantized"is just an accepted alias forNone."high_precision"saves the unpadded high-precision operands and computesboth gradients with plain
torch._grouped_mmcalls (upper-bound gradientquality arm).
"dequantized"differentiates the quantized-forward function itself: itsaves the quantized forward operands and dequantizes them for the same two
grouped GEMMs. This is the arm that matters for RL train/inference
consistency — the gradients correspond to the function the quantized
forward actually computes.
because the backward needs logical (pre-blocking) scales; the CuTe DSL 1x32
kernel returns blocked-only scales and no unswizzler exists. The forward
output is unchanged (see Numerics evidence).
backward_overrideparticipates inMXFP8TrainingOpConfig.__eq__/__hash__so overridden configs never alias the default config (the config is a
registered pytree constant used under dynamo nonstrict trace).
choices or availability gates change in this PR beyond the
.contiguous()fix described above (kept as its own commit).
bf16-only dim0 Triton cast, so non-bf16 operands are rejected at the
op-level validation point rather than deep inside the kernel.
Numerics evidence
qdata bitwise identical to the CuTe DSL 1x32 rceil kernel used by the stock
forward;
test_forward_bitwise_identical_across_overridespins that theforward output is
torch.equalacross all override arms.test_high_precision_backward_matches_grouped_mm_referencepins thehigh-precision arm bitwise against the same two-
torch._grouped_mmformulation.
Test plan
CPU (no GPU; module-level skip hygiene):
Import/threading check (CPU):
MXFP8TrainingOpConfigeq/hash asymmetry withand without
backward_override,backward_overridepresent in the_to_mxfp8_then_scaled_grouped_mmsignature and in the_quantize_then_scaled_grouped_mmdispatcher — verified in a CPU-onlycontainer.
Lint:
ruff check+ruff format --check(ruff 0.11.6, the version pinned by.github/workflows/ruff_linter.yml) are green on all touched files.GPU results come from the assembled stack tip on GB200, since ao CI has no
SM100 runner (the GPU legs run SM86 collection and an SM90 curated list). The
override arms and the forward-bitwise test passed there on the exploratory
branch, and the override arms also have end-to-end GB200 evidence from RL
recipe-ablation runs. Two caveats on this exact branch: (1) these tests gate
on
torchao::mx_block_rearrange_2d_M_groups, a compiled CUDA extension —source-checkout environments (and CI without SM100 runners) skip the whole
module, so a per-branch GPU run needs a built torchao on SM100; (2) the
exploratory branch ran the quantized wgrad dim1 casts with the Triton kernel,
while this PR keeps the default CUDA cast kernel. The stride-0 crash class is
closed by construction on both backward paths (the same
.contiguous()materialization on wgrad and dgrad), and
test_noncontiguous_grad_output_wgradpins it wherever the suite runs with a built torchao. The tests additionally
require the CuTe DSL runtime (nvidia-cutlass-dsl) for the stock forward, like
the sibling
test_mxfp8_grouped_mm.py.Known follow-ups
backward_overrideis a free string that overlaps semantically withwgrad_with_hp("high_precision"subsumes it for both gradients); thereis no interaction validation or
Literaltyping yet. We kept the op-levelassert as the single validation point for now and can tighten to
Literalplus an interaction check in a follow-up if preferred.
"dequantized"parity has only been exercised with RCEIL scale calculation;FLOOR is reachable but untested.
pad_token_groups_for_grouped_mm=Truecombined with the overrides isuntested (the override backward feeds unpadded offsets where the default
uses padded ones).
Draft on the fork to stage the upstream submission; supersedes the exploratory stack (#7/#8/#9)
Review pass (2026-08-30)
.contiguous()materialization now also covers the dgrad CuTe DSLquantize input (the e2e
sum().backward()leg drives it with the samestride-0 gradient the wgrad fix pins); the fix commit message says so.
(they quantize saved operands with the bf16-only dim0 Triton cast).
_backward_override_grads_tuplespells out the per-argumentNones likethe default return (no magic arity constant).
bound against seed sensitivity.