Skip to content

fix(utils): accumulate param L2 norm in float32 for bf16/fp16 models - #3625

Open
ralovets wants to merge 2 commits into
NVIDIA-NeMo:mainfrom
ralovets:ralovets/fix/param-norm-dtype
Open

fix(utils): accumulate param L2 norm in float32 for bf16/fp16 models#3625
ralovets wants to merge 2 commits into
NVIDIA-NeMo:mainfrom
ralovets:ralovets/fix/param-norm-dtype

Conversation

@ralovets

@ralovets ralovets commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

What does this PR do ?

Fixes the Param L2 norm logged by print_trainable_parameters. _get_model_param_stats accumulates local_sq_norm += p.detach().norm(2) ** 2, which takes the parameter dtype, so the value drifts for bf16 models and is inf for fp16 models — a real 3.62B-parameter bf16 checkpoint logs 3007.3191 against a true value of 3167.8195, 5.1% low.

The fix passes an accumulation dtype to norm(), using torch.promote_types(p.dtype, torch.float32) rather than a literal torch.float32 to avoid narrowing float64 parameters, which linalg.vector_norm rejects. Regression from #1463, which dropped the .float() upcast along with a per-parameter host sync. Metric only, no effect on training numerics.

Performance

Benchmarked on GPU against the real 1120-tensor / 3.62B-param shape distribution, bf16, variants interleaved within each round:

variant implementation median min p25
before #1463 .float() upcast copy, plus a .item() host sync per parameter 27.59 ms 27.37 ms 27.52 ms
current main norm(2), accumulating in the parameter dtype — the bug 11.65 ms 11.57 ms 11.64 ms
this PR norm(2, dtype=...), upcast folded into the reduction 11.80 ms 11.71 ms 11.78 ms

+1.3% (0.15 ms) against current main, from the per-parameter promote_types call. One-time cost, since all call sites run once after sharding.

Changelog

  • _get_model_param_stats accumulates the squared parameter norm in at least float32 instead of the parameter dtype, fixing the reported Param L2 norm for bf16 (drifts >1%) and fp16 (reports inf) models.
  • Added unit tests asserting the reported norm is dtype-independent against a float64 reference.

Before your PR is "Ready for review"

Pre checks:

  • Make sure you read and followed Contributor guidelines
  • Did you write any new necessary tests?
  • Did you add or update any necessary documentation?

_get_model_param_stats accumulated the squared parameter norm in the
parameter dtype, so the reported "Param L2 norm" drifted for bf16 models
and overflowed to inf for fp16 models.

Pass an accumulation dtype to norm() instead. promote_types rather than a
literal float32, because vector_norm rejects a narrowing dtype: float64
parameters would raise and be silently dropped by the surrounding except.

The dtype= kwarg folds the upcast into the reduction, so this keeps the
device-side accumulation introduced in NVIDIA-NeMo#1463.

Signed-off-by: Roman Ralovets <roman@ralovets.com>
@copy-pr-bot

copy-pr-bot Bot commented Aug 22, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@ralovets
ralovets marked this pull request as ready for review August 22, 2026 05:41
@ralovets
ralovets requested a review from a team as a code owner August 22, 2026 05:41
@svcnvidia-nemo-ci svcnvidia-nemo-ci added the waiting-on-maintainers Waiting on maintainers to respond label Aug 24, 2026
@akoumpa

akoumpa commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

/ok to test 0c5f12c

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community-request waiting-on-maintainers Waiting on maintainers to respond

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants