Repository navigation
fix(sparsemax): preserve low-precision gradients by subtracting the mean in fp32 - #1609
Open
wqh-creator wants to merge 1 commit into
Open
wqh-creator wants to merge 1 commit into
wqh-creator wants to merge 1 commit into
Conversation
Preserve small FP16/BF16 gradient differences by delaying the output cast until after mean subtraction. Add exact regression tests for last and non-last dimensions. Assisted-by: OpenAI Codex
This branch has not been deployed
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.
Summary
Fix FP16/BF16 Sparsemax input gradients by subtracting the support mean in FP32 before the final output cast. The previous backward kernel rounded that mean to the output dtype first, losing small differences between upstream gradients.
For a zero input row with upstream BF16 gradients
[1, 1.0078125], the correct input gradient is[-0.00390625, 0.00390625]. Current main returns[0, 0.0078125], incorrectly producing a nonzero row sum. FP16 has the same failure with adjacent representable upstream values. The forward path is unchanged.Add exact regression coverage for FP16/BF16, last/non-last dimensions, and an inactive support entry. The tests compare against the existing PyTorch reference computed in FP32 and assert the zero-sum gradient invariant.
Duplicate check: searched open and merged Sparsemax PRs/issues. #1545 fixes forward shift invariance; #1497 fixes row offsets; #1459 changes cuTile contracts. None fixes this premature cast in the native Triton backward.
AI assistance: this change, tests and GPU experiments were prepared with OpenAI Codex.
Testing Done
e44de705ecc9b43eb33aa35ad10ee9d59a054c3b.CUDA_VISIBLE_DEVICES=4 python -m pytest test/transformers/test_sparsemax.py -k preserves_small_gradient_differences -v→ 4 failed, 10 deselected, with the expected gradient mismatches.CUDA_VISIBLE_DEVICES=4 python -m pytest test/transformers/test_sparsemax.py -xvs→ 14 passed, including the large-row-offset test; no skips.make testto ensure correctness — the scoped Sparsemax suite above was run; the full repository suite was not run.make checkstyleto ensure code stylemake test-convergenceto ensure convergence — not run; no model patching or forward behavior is changed.Bounded benchmarks reuse
benchmark/scripts/benchmark_sparsemax.py::setup_sparsemaxand the repository speed/memory helpers for BF16 shapes 128×1024 and 1024×4096. Forward, backward and full modes were measured with three repeats per configuration, including alternating base/patched processes. Full-mode peak allocations were unchanged: 2.508 MiB and 80.031 MiB respectively.Small-shape eager full-mode timings were variable: approximately 0.045 ms on the base and 0.045–0.175 ms on the patch, despite isolated backward timings remaining around 0.006 ms. This host-inclusive variability has not been conclusively explained, so this PR makes no performance improvement claim. Additional fixed-input CUDA Graph full forward/backward measurements (two alternating runs per version, five measurements each) did not show a corresponding GPU execution regression:
Correctness is the purpose of this change. Model-training convergence and throughput are not claimed.