Asked in: ML breadth at LLM-team and infra interviews.
Explain how mixed-precision training assigns numerical formats while preserving useful gradients and stable optimization.
Problem
Context. A model-training workload seeks the speed or memory benefits of FP16 or BF16 without silently corrupting training. Activations, reductions, gradients, parameters, and optimizer state can have different numerical requirements.
Task. Specify a precision policy, explain range and rounding failure modes, and describe how to validate it against a higher-precision baseline. Compare the role of loss scaling for FP16 and BF16, including the order of unscaling, clipping, and stepping.
Constraints. Distinguish storage dtype, compute dtype, and accumulation dtype; automatic casting does not choose every policy for you. Account for nonfinite gradients and skipped steps, and do not assume every operation or parameter needs identical precision.
What an L4 answer sounds like
“Mixed precision uses FP16 or BF16 instead of FP32, which is faster and uses less memory. PyTorch has
torch.cuda.ampfor it.”
True, no mechanism. You’ve turned it on, never debugged it.
What an L5 answer sounds like
“One common mixed-precision recipe keeps FP32 parameters and optimizer state while autocasting eligible operations to BF16 or FP16. The exact policy depends on the device, kernel, framework, and operation:
- Persistent parameters. FP32 model parameters can already be the authoritative weights; a separate master copy is not always allocated.
- Operation-specific compute. Autocast chooses eligible lower-precision operations; it does not make every forward/backward value low-precision.
- Gradient accumulation. FP32 leaf parameters normally accumulate FP32 gradients, even though intermediate arithmetic may use lower precision. Casting after underflow cannot recover a lost gradient.
- Guarded update. Unscale before clipping, reject non-finite updates, and update the persistent parameters with the intended optimizer-state precision.
FP16 training commonly uses loss scaling before backward to preserve small intermediate gradients, followed by unscaling before the optimizer step. BF16’s wider exponent range often removes that need, but finite precision, overflow, and unstable operations still require checks. Preserving a gradient also does not guarantee its weight update is representable; the mixed-precision mechanism separates those cases.
Some operations stay in FP32 for stability:
- LayerNorm/RMSNorm: variance computation needs precision.
- Softmax: especially in attention; cast scores to FP32, softmax, cast back.
- Loss function: usually FP32.
Frameworks (PyTorch autocast) handle most of the casting automatically.”
This is L5. You’ve described the full recipe with the stability ops named.
Learning objective: trace one FP16 training step in the order that preserves small gradients without letting scaled or non-finite values corrupt the FP32 parameters.
One FP16 training step
Unscale before clipping; skip nonfinite parameter updates
Reference drawing
One FP16 training step
Unscale before clipping; skip nonfinite parameter updates
Derivation
What an L6 answer sounds like
“…practical things that bite people:
Use a byte ledger. Two FP32 Adam moment arrays cost eight bytes per parameter; FP32 parameters and their FP32 gradient buffers add four bytes each. Activation savings and temporary casts depend on the implementation. Mixed precision and state sharding address different memory terms.
BF16 changes range, not mathematical stability. It often avoids FP16 range failures, but it does not prevent every NaN or guarantee a throughput ordering. Check device support, kernel behavior, reductions, and task quality.
FP8 (H100+) is the next step. Two variants: E4M3 (more mantissa, less range, for forward) and E5M2 (more range, for backward gradients). Per-tensor scaling factors that update during training. Frameworks like Transformer Engine handle the bookkeeping.
Mixed precision can introduce silent quality regressions. Differences appear in long-context attention (numerical stability of softmax over many positions), in very deep networks, and in RL where reward signals are subtle. Always evaluate on the production task, not just on training loss.
Gradient scaler + grad clipping interact. If you clip gradients, do it on the unscaled gradients (after
scaler.unscale_()in PyTorch). Clipping the scaled gradient gives wrong results.”
Tells that get you a strong-hire vote
- You name the FP32 master weights + low-precision compute pattern explicitly.
- You explain why FP16 needs loss scaling but BF16 doesn’t.
- You list ops that stay in FP32 for stability.
- You acknowledge optimizer state as the memory dominator, not weights.
- You bring up FP8 for H100-era training.
Tells that get you down-leveled
- “Just use AMP” with no further detail.
- Suggesting FP16 over BF16 on modern hardware.
- No mention of loss scaling for FP16.
- Confusion between mixed precision (training) and quantization (inference).
Common follow-up
“You said optimizer state dominates memory. Can you mixed-precision the optimizer too?”
The L6 answer:
“Yes, but carefully. Adam’s second moment v is the largest concern; storing it in BF16 loses precision and causes training instability. Approaches: 8-bit Adam (Dettmers et al.) quantizes optimizer state to INT8 with per-block scales, recovering most of the memory with minor quality loss. AdaFactor uses a factored approximation of v with much less memory. For pretraining at scale, ZeRO sharding of FP32 optimizer state across data-parallel ranks is more common than quantizing it.”
Related: Mixed precision training, Walk me through how you’d train a 100B parameter model, Quantization.