Gradient Clipping
One spike in the gradient is enough to send a weight flying off to infinity. Clipping caps the size of the step without changing its direction.
Overview
Context first
Most gradients during training are reasonably sized. Occasionally — a badly scaled batch, a long recurrent chain, a numerically unstable loss — one gradient is enormous. Applied directly, w − lr · g can throw a weight far outside the region the optimiser was making sane progress in, and the next few steps are spent recovering rather than learning.
Run Training
Steps 1-2 are ordinary. Step 3 injects a spike 50x the real gradient — an exploding gradient, the kind deep or recurrent nets produce on their own.
Weight Trajectory on L(w) = w²
—This Step
Gradient Clipping: A Practical Guide
A safety valve for the one bad gradient in a thousand.
What clipping does
if ||g|| > threshold: g ← g · (threshold / ||g||)
The gradient's direction is unchanged — it still points the way steepest descent says to go — only its length is capped. This is norm clipping, the common form; value clipping (capping each component independently) is cruder and used less often.
Capping the size of a step
Gradient clipping is a single guard rail: if the gradient is larger than a threshold, scale it down before applying the update.
if ‖g‖ > c: g ← g × c / ‖g‖
Note what that does and does not change. The direction is preserved exactly — every component is scaled by the same factor. Only the length is limited. That is why clipping by norm is the standard, and clipping each value independently is not: per-value clipping distorts the direction, so the update no longer points downhill.
loss.backward()
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
optimizer.step()The call must come after backward() and before step(). Placing it anywhere else does nothing, silently.
The failure it prevents
A single unusually large gradient can destroy a model in one step. Suppose the gradient norm is normally around 0.5 and one batch produces 4,000 — a rare example, an outlier, an unlucky interaction. With a learning rate of 1e-3 that is a weight change of 4.0 in one step, which pushes the weights far outside the region where the loss is meaningful. The next forward pass produces inf, the loss becomes NaN, and every subsequent update propagates the NaN through the whole model.
Nothing recovers from that. The run is over, and if it happened at hour six of an eight-hour job, the checkpoint is what saves you.
Clipping makes that impossible. The worst case becomes a step of size learning_rate × threshold, whatever the gradient was.
| Situation | Clipping |
|---|---|
| Recurrent networks | Essential — the classic use case |
| Transformers | Standard, usually max_norm 1.0 |
| Reinforcement learning | Standard — rewards are high-variance |
| GANs | Common, alongside spectral normalisation |
| Feed-forward CNNs | Often unnecessary, harmless to include |
Choosing the threshold
The useful way to pick it is to measure rather than guess. Log the gradient norm for a few hundred steps without clipping, look at the distribution, and set the threshold somewhere above the typical value but below the outliers.
total = torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1e9)
# returns the norm BEFORE clipping, so this logs without actually clippingThen watch how often it triggers. Occasional clipping — a few percent of steps — is exactly right: the rare spike is caught and normal steps pass through untouched. Clipping on every step means the threshold is too low and you are throttling all learning, which shows up as slow, stalled convergence.
Common values: 1.0 for transformers and most language models, 5.0 for recurrent networks, 0.5 when training is particularly unstable. These are conventions rather than derived numbers.
Cap the step, keep the direction
One bad batch can produce a gradient large enough to destroy a working model in a single update. Clipping bounds the damage, and the difference between the two ways of doing it matters.
Guided experiments
- Step twice normally. The weight descends the parabola exactly as gradient descent should.
- Step through the spike, unclipped. Uncheck Clip Gradients first. Step 3's gradient is 50x normal, and the update sends w far past the minimum — sometimes far enough that the next raw gradient is even larger, a genuine divergence.
- Reset, turn clipping back on, step through again. The spike is capped at the clip norm before it is applied, and the weight takes a bounded step instead of an unbounded one.
- Lower the clip norm. Smaller thresholds cap harder — including, if set low enough, ordinary gradients that were never a problem. It is a trade between safety and full-speed learning.
Worth remembering
Gradient clipping rescales an oversized gradient down to a maximum norm before the optimiser applies it, preserving direction while bounding step size. It is cheap insurance against the rare exploding gradient that would otherwise throw training off course, and it is standard practice in RNNs and very deep networks, where long chains of multiplication make the occasional huge gradient close to inevitable.
Where the spikes come from
Clipping treats a symptom, and it is worth knowing the causes, because sometimes the cause is fixable.
Recurrent depth. Backpropagating through 100 time steps multiplies 100 Jacobian factors. If their product exceeds 1, it compounds — the original motivation for clipping.
A learning rate that is too high makes weights large, which makes activations large, which makes gradients large. If clipping fires constantly, lower the learning rate before raising the threshold.
An unlucky batch. One example with an extreme feature value or a mislabelled target produces a genuinely enormous gradient. Scaling the inputs and checking the labels both reduce this.
Missing normalisation. Without batch or layer normalisation, activations can drift into ranges where gradients explode.
A loss that can spike. Cross-entropy on a confidently wrong prediction, or a division by something near zero, produces a large loss and a large gradient.
So the sensible order is: scale the inputs, normalise the activations, set the learning rate correctly, add warm-up — and clip as the final safety net rather than the first response.
Clipping and mixed precision
One interaction worth knowing. Mixed-precision training scales the loss up before the backward pass, to stop small gradients underflowing in 16-bit. That means the gradients you see are also scaled.
Clipping must therefore happen after unscaling, or the threshold means something different from what you intended:
scaler.scale(loss).backward()
scaler.unscale_(optimizer) # remove the loss scale
torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0) # now clip
scaler.step(optimizer)
scaler.update()Getting this order wrong is a real bug: with a loss scale of 65,536, a threshold of 1.0 applied before unscaling effectively clips at 1/65,536 and training stops learning entirely.
Questions people ask
Does clipping slow training? Only if the threshold is too low, in which case you are discarding real gradient signal on most steps. Log the trigger rate.
Norm or value clipping? Norm, almost always — it preserves direction. Value clipping is occasionally used in reinforcement learning.
Is clipping the same as gradient normalisation? No. Clipping only intervenes above a threshold; normalisation would rescale every gradient to a fixed length, which discards magnitude information the optimiser uses.
Can clipping hide a real problem? Yes — a model that only trains with aggressive clipping usually has a learning rate, initialisation or data problem underneath.
Do I need it with Adam? Adam's per-parameter scaling already limits step sizes somewhat, but it does not prevent a single enormous gradient from dominating the moving averages. Clipping is still standard with Adam in transformers.
What threshold for a new model? Start at 1.0, measure the trigger rate, and adjust.
Recap in one screen
- If the gradient's norm exceeds a threshold, scale it down — direction preserved, length capped.
- One enormous gradient can produce
NaNweights and end a run; clipping makes that impossible. - Call it between
backward()andstep(), and after unscaling under mixed precision. - Occasional triggering is healthy; constant triggering means the threshold is too low or the learning rate too high.
- Essential for recurrent models, standard in transformers, usually unnecessary in plain CNNs.