Skip to content

[bug fix] Compute fp16 batch norm in fp32 instead of downcasting the params - #54

Merged
gokulkrishna98 merged 1 commit into
apple:mainfrom
gokulkrishna98:dev/gokul/batchnorm-fp32-compute
Aug 7, 2026
Merged

[bug fix] Compute fp16 batch norm in fp32 instead of downcasting the params#54
gokulkrishna98 merged 1 commit into
apple:mainfrom
gokulkrishna98:dev/gokul/batchnorm-fp32-compute

Conversation

@gokulkrishna98

Copy link
Copy Markdown
Contributor

Description:

BatchNorm keeps its params in fp32 even when the activation is fp16. ATen promotes the input to fp32, uses the params and eps at full precision, and narrows only the result (native_batch_norm_helper). The composite body did the opposite: it cast the params down to fp16, losing mantissa bits and turning any running_var above the fp16 max of 65504 into inf, which silently zeroes the output.

Fix: use prepare_compute_type_for_norm, as layer_norm and group_norm already do. fp32 inputs are unchanged apart from dropping redundant f32 -> f32 casts.

Tests: numerical case with large/tiny running_var (fails on main with max abs error 4.945 and 0.5), plus an IR case pinning the promote/compute/narrow body.

`nn.BatchNorm2d` keeps gamma/beta/running_mean/running_var in fp32 even
when the activation is fp16. ATen handles that mixed-precision case by
promoting the input to fp32, using the params and eps at full precision,
and narrowing only the final result (`native_batch_norm_helper` in
torch/_decomp/decompositions.py).

The composite body did the opposite: it cast the fp32 params and eps down
to the input element type and ran every op in fp16. That loses mantissa
bits on the params (eps 1e-5 becomes 1.00136e-05), and any channel whose
running_var or running_mean exceeds the fp16 max of 65504 becomes inf --
sqrt(inf) then divides the activation to a silent zero.

Use the existing `prepare_compute_type_for_norm` helper, as layer_norm
and group_norm already do, so the fp16 input is promoted, the arithmetic
runs in fp32, and there is a single narrowing cast before the output.
fp32 inputs are unaffected apart from dropping now-redundant f32->f32
casts, which the optimizer already folded.

Tests: a numerical case with large and tiny running_var (max abs error
4.945 and 0.5 against eager before the fix) and an IR case pinning the
promote/compute/narrow shape of the fp16 composite body.
@gokulkrishna98
gokulkrishna98 force-pushed the dev/gokul/batchnorm-fp32-compute branch from 9b11e80 to b3345b3 Compare August 7, 2026 19:38
@gokulkrishna98
gokulkrishna98 marked this pull request as ready for review August 7, 2026 19:39
@gokulkrishna98
gokulkrishna98 merged commit 46d15b7 into apple:main Aug 7, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants