ML Systems Lab Open interactive version →
Advanced 29 min read debuggingtraining failuresNaN gradientsmode collapse

DL Training Failure Modes

Loss spikes, NaN gradients, mode collapse, underfitting vs overfitting — debugging

Every module in this course has quietly asked the same question in its own diagnostics — Backprop's gradient norms, Activations' dead-neuron checks, Batch Norm's train/eval mismatch: is the model actually learning, or does it just look like it is? This module closes the sequence by pulling that recurring thread into one systematic discipline — the checks worth running before trusting any model enough to fine-tune, quantize, or serve it.

You start training a new model and the loss just sits there — stuck at log(number of classes), epoch after epoch. The natural instinct is to start turning knobs: a different learning rate, a bigger model, more data. Resist it.

Think of it like an ER doctor's first sixty seconds with a patient: before asking about the specific complaint, check pulse and breathing — the two or three cheap vital signs that rule out the fastest, most catastrophic explanations. A model has its own vital signs, and checking them costs about as little. Before asking "why won't it learn what I want," ask the more basic question: *can this model learn anything at all?* Almost every training bug is easier to catch by answering that first.


Step 1: overfit a single batch

Take one batch. Turn off all regularization. Train on just that one batch for a thousand steps. A working model *must* be able to memorize a handful of examples — the loss should crash to near-zero. If it can't even do that, no hyperparameter will save you; the model is wired wrong. Usual suspects: the loss doesn't match the output (softmax paired with MSE, or cross-entropy fed raw logits), the output layer has the wrong number of classes, the labels are the wrong shape, or a forward-pass bug is zeroing activations. This test takes 60 seconds and rules out every one of those at once. It is the single most valuable habit in debugging deep nets.


Step 2: if that passes but full training won't converge, look at gradient flow

The signal is learning, but maybe it isn't reaching every layer. Log the average gradient size for each layer after a step. In a healthy network the biggest and smallest layer gradients stay within ~10× of each other. See 10,000× instead and the early layers are getting almost nothing — vanishing gradients — and they will not learn no matter how long you train; the fix is ReLU, residual connections, or better initialization, not more epochs. The reverse — early layers with huge gradients — is exploding gradients: clip them (max_norm=1.0) and check your initialization.


Watch both vital-sign checks catch real bugs on one model

Say you're training a small binary classifier — malicious versus benign network traffic — with a 2-unit softmax output and cross-entropy loss. Step 1 shows loss at log(2) ≈ 0.693; step 50 still shows 0.693, unmoved. Following the diagnostic above: the single-batch overfit test also sits frozen at 0.693. You inspect the logits directly across steps on the same fixed input and they never change at all — the output layer is receiving zero gradient. The cause, once you look: the code calls .detach() on the logits before computing the loss, silently cutting the backward pass off at the very last layer. Remove the .detach() call, rerun the single-batch test — loss now crashes to 0.02 in under 200 steps, exactly as the diagnostic predicts for a correctly wired model.

Rerun full training with the fix in place. Loss falls smoothly from 0.693 toward 0.41 over the first few epochs — then, at step 47, it reports NaN. Looking at the gradient norm logged just before the spike: 8.2 two steps earlier, 41.6 one step earlier, then NaN. That is exploding gradients, not corrupted data — the norm was climbing steadily before it overflowed. Adding gradient clipping (clip_grad_norm_, max_norm=1.0) and rerunning: loss now decreases cleanly to 0.19 by epoch 10, no NaN. Two classic failures, one model, one continuous debugging session — not two abstract rules to memorise separately.

A shorter pair worth knowing by the same two-question habit: training loss falling steadily while validation loss stays flat usually means train and validation don't come from the same distribution, or a decision threshold hasn't been tuned on validation data at all. And a NaN that shows up specifically inside a custom loss, rather than from an exploding gradient norm, is often a bare log(0) — add a small ε inside the log before trusting the gradient math further.


And the trap that fools everyone: a falling loss does not mean it's working. Loss can drop steadily while the model learns nothing useful. On an imbalanced dataset, a model that always predicts the majority class hits 95% accuracy and a nicely decreasing loss curve — and is completely useless. So look past the curve: eyeball a few actual predictions, check the confusion matrix (is it just always guessing one class?), and check that gradient norms sit in a sane range (~0.001–10).

Key points

Takeaway

Overfit one batch first, then log gradient norms per layer — these two tests diagnose 80% of training failures in minutes, before touching hyperparameters or architecture, because a model that cannot learn one example has a bug, not a tuning problem.

Recap

Check your understanding

Q1. Training loss is NaN after step 47. List your debugging steps in order, and what is the most common cause in transformer training?

Q2. Your model achieves 99% training accuracy but 51% test accuracy (near-random for binary classification). What is happening, and what are the top 3 most likely causes? Select the TWO correct causes.

Q3. Validation loss oscillates rather than decreasing monotonically. You're using SGD+momentum with lr=0.01 and batch size 64. List three possible causes and fixes.

Try it interactively

ML Systems Lab is a free interview-prep platform for ML engineers — work through the full interactive module, quizzes, and drills.

Open ML Systems Lab →