Which GradScaler settings matter when choosing float16 over bfloat16 for AMP training?
0 reputation · 06 Oct 2025, 11:52 UTC
0 reputation · 06 Oct 2025, 11:52 UTC
I'm setting up automatic mixed precision training with torch.autocast and torch.cuda.amp.GradScaler and want to make a deliberate choice between two documented configurations: float16 autocast with gradient scaling, or bfloat16 autocast without it. My understanding is that bfloat16's wider exponent range usually makes the scaler unnecessary, while float16 needs scaling to avoid gradient underflow.
What I can't resolve from the documentation alone is how much the scaler's tuning knobs actually matter. GradScaler exposes init_scale, growth_interval, and backoff_factor, and it dynamically raises the scale after successful iterations and backs off when infs or NaNs appear in gradients. The defaults presumably work for common cases, but it's unclear when a model (e.g., one with very small gradients or loss spikes) justifies changing them, and what symptoms indicate the defaults are hurting convergence rather than helping.
Assume a recent PyTorch release on an Ampere-class GPU; I know the AMP API signatures have shifted across versions, so answers should note version assumptions.
Specifically:
init_scale, or is it purely trial and error per model?31275 reputation · 06 Oct 2025, 12:29 UTC
On Ampere-class GPUs with a recent PyTorch release, bfloat16 autocast is normally used without GradScaler, while float16 autocast is used with GradScaler. The scaler knobs that matter are init_scale, growth_interval and backoff_factor. In practice the defaults are sufficient for most models; changes are only justified when the scale trajectory shows repeated backoff or very slow recovery.
Assumption: PyTorch 2.x with device-agnostic autocast torch.amp.autocast and torch.amp.GradScaler. API names moved from torch.cuda.amp to torch.amp across releases, so confirm the constructor defaults for your exact version.
There is no documented per-model formula for init_scale. Empirically, a reasonable heuristic is to start near the largest power of two that does not immediately produce infs on the first few steps. The scaler self-corrects quickly, so the default rarely needs changing.
growth_interval controls how fast the scale recovers. Lowering it, e.g. 100-500, lets models with frequent early instability regain scale faster, at the cost of more risk of overshooting. Raising it makes recovery more conservative.
backoff_factor controls how aggressively the scale drops on an inf/NaN. The default 0.5 is a compromise. Frequent backoff usually signals genuine numerical instability — loss spikes, exploding gradients, or an op that overflows in fp16 — rather than a tuning problem. Tuning knobs only change reaction speed, they do not fix instability that persists in float32.
When both dtypes are supported, bf16 is simpler and usually equally fast on Ampere+. Reasons to still choose float16 + scaler:
Otherwise bf16 without a scaler is the lower-maintenance choice.
scaler = torch.amp.GradScaler('cuda')
for step in range(N):
with torch.amp.autocast('cuda', dtype=torch.float16):
loss = model(...)
scaler.scale(loss).backward()
scaler.step(optimizer)
scaler.update()
print(scaler.get_scale())
One diagnostic detail that would change the recommendation: what is the observed scale trajectory and where infs/NaNs first appear — in forward, backward, or specific layers? If backoff is driven by a single op, fixing that op is preferable to retuning the scaler.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.