When backward goes wrong, you have tools
Training that diverges, NaN losses, gradients that mysteriously become zero or infinity — these are the classic "what's wrong with my model" symptoms. Four PyTorch-specific tools cover most of the diagnostic surface:
- Gradient norm logging. Add
clip_grad_norm_(model.parameters(), float('inf'))after backward and log the returned value. A spike to 1e6 is your "what just happened" signal. - NaN / Inf detection.
torch.isfinite(loss)at every step is cheap insurance — it catches both Inf and NaN.torch.autograd.set_detect_anomaly(True)at the top of training will tag the exact op that produced a NaN backward — slow, but priceless when you're stuck. Watch the scope mismatch: anomaly detection only fires on NaN, not Inf (see "Inf vs NaN" below). - Per-parameter gradient stats. Walk
model.named_parameters()after backward and print mean/std/max for each. Vanishing-gradient bugs are obvious in this view. - Gradient hooks for shape mismatches. Register a hook on a layer's output to verify gradient shapes match what you expect — useful when adding skip connections or custom layers.
Inf vs NaN — anomaly detection has a narrow scope
The gap most tutorials skip: set_detect_anomaly(True) raises only on NaN values in backward output, not Inf. The same function can land in either bucket depending on its input.
Inf sources — anomaly detection stays silent; use torch.isfinite() to catch:
sqrt(0).backward()— derivative is 1/(2·sqrt(x)) = ∞ at x=0. Fix:(x + 1e-9).sqrt().log(0).backward()— derivative is 1/x = ∞ at x=0. Fix:(p + 1e-9).log().
NaN sources — anomaly detection fires and points at the exact op:
sqrt(-1),log(-1)— NaN in forward, propagates to backward.0 / 0— usually from a normalize-by-norm where the norm is zero.0 * log(0)— 0 × (-∞) = NaN.log(softmax(x))where softmax collapses to ~0 on an entry — produces NaN backward (useF.log_softmaxfor numerical stability — see the exercise).- fp16 overflow that turns into 0×∞ — switch to bf16 or add a GradScaler.