Repository navigation
fix(group_norm): keep the backward statistics and reductions in fp32 - #1608
Open
bhargavchintam wants to merge 1 commit into
Open
bhargavchintam wants to merge 1 commit into
bhargavchintam wants to merge 1 commit into
Conversation
The forward already computes the group mean and rstd in fp32 (linkedin#1538), but group_norm_forward stored them in the input dtype, so a bf16/fp16 backward read rounded statistics. The backward kernel also ran every reduction in the input dtype: in fp16 a channel of 65536 elements can overflow tl.sum when the upstream gradient is large (the input gradient becomes -inf), and in bf16 the sums only keep 8 bits. Store Mean and RSTD in fp32, as layer_norm has done since linkedin#1498, and upcast X, the upstream gradient and W in the backward kernel before the reductions. The fp32 vectors are twice as wide, and at the old 65536 block limit the kernel gets about ten times slower: on a B200 a (16, 32, 256, 256) bf16 backward goes from 139 us to 1518 us. Cap the backward block at 16384, which brings it to 124 us and also takes the fp32 backward at that shape from 2068 us to 134 us.
1 of 3 tasks
Author
|
I ran None of the 22 is in |
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.
Fixes #1606
Summary
_group_norm_backward_kernelruns itstl.sumreductions in the input dtype, andgroup_norm_forwardstoresMeanandRSTDin the input dtype. In fp16 a channel sum can overflow, which turns the whole input gradient into NaN. In bf16 the sums keep only 8 bits, and the backward reads rounded statistics. luca-888 pointed at theMean/RSTDpart in the review of #1538 as a possible follow-up.Source changes, all in
src/liger_kernel/ops/group_norm.py(the new test is under Testing Done):W,Xand the upstream gradient are upcast to fp32 when they are loaded, in both backward loops.MeanandRSTDare allocated as fp32.MAX_FUSED_SIZE_BACKWARD = 16384limits the backward block. The casts double the vector width, and at the old limit of 65536 the backward gets about ten times slower (table below). My guess is register spills, but I did not measure register use.triton_dtypeanddW.to(dtype)are unchanged because #1253 works on those lines.Details
F.group_normin fp64 on the same input and upstream gradient, then prints the largest absolute difference):group_norm_backwardlaunched 100 times after a warmup, withtorch.cuda.synchronize()around the loop, mean in µs). "Casts only" is this PR without the block limit:I timed block limits of 65536, 16384, 8192 and 4096. 16384 was the fastest at 256×256, and at the other two shapes it matched 65536. The forward kernel is not changed here.
Testing Done
make testto ensure correctnessmake checkstyleto ensure code style (I ran its two check-only steps, shown at the end, because the other two steps rewrite files)make test-convergenceto ensure convergenceI did not run
make testormake test-convergence. The B200 was rented for this run, and on it I ran only the changed kernel's test file,test/transformers/test_group_norm.py, as below.New test
test_liger_group_norm_low_precision_backwardhas three cases (fp16 and bf16 at (1, 16, 256, 256) with an upstream of ones, and bf16 at (2, 8, 16) with an offset), and compares with fp32 autograd. The bias gradient is only compared where the true value fits in the dtype, since 65536 does not fit in fp16.The two snippets from the issue, run on this branch (on main they print
finite x.grad: 0 / 1048576andmax |x.grad| (expected 0): 466.0):