Concept units

Gradient clipping

Intermediate

Read these first if they are new to you

The idea in one paragraph

Gradient clipping puts a cap on how long the gradient may be before a training step uses it. Measure the length of the whole gradient. If it is longer than a chosen maximum, scale it down to exactly that maximum, keeping its direction. Ordinary steps pass through unchanged. A rare, huge gradient, such as one caused by a single unusual batch of examples, can then no longer throw the weights far off course.

One huge gradient can undo many good steps

In gradient descent, each step moves the weights by the learning rate times the gradient. Most steps are small. Now and then, one batch of examples produces a gradient a hundred times larger than usual: an odd example, a steep cliff in the loss, or a numerical accident. The step is then a hundred times larger too. The weights land somewhere far away, the loss jumps up, and training may take thousands of steps to recover, or never recover.

Clipping shrinks a long gradient to a fixed length, keeping its direction

Think of the gradient as one long vector, with an entry for every weight in the model. Its length, called its norm, is the square root of the sum of the squares of all entries. That is the distance formula, with as many coordinates as the model has weights.

Choose a maximum length c. Then, before each step:

  • if the norm is c or less, leave the gradient as it is;
  • if the norm is larger, multiply every entry by c / norm.

Multiplying every entry by the same number keeps the direction and changes only the length. The clipped length is exactly c.

Worked example: clip (3, 4) to length 1
  1. Norm: √(32 + 42) = √25 = 5.
  2. 5 is more than c = 1, so scale by 1 / 5 = 0.2.
  3. Clipped gradient: (0.6, 0.8). Its length is √(0.36 + 0.64) = 1, and it points the same way.

Your turn. Clip (6, 8) with c = 5.

Answer

The norm is 10, so scale by 5 / 10 = 0.5. The result is (3, 4).

Try it. The figure clips a gradient to length c = 1, shown as the circle. Predict first: what is the clipped length when the gradient's length is 2.5? Then move the slider and find the length at which the two arrows stop matching.

A gradient and its clipped version, with maximum length 1
gradient before clippingafter clipping

Inside the circle nothing changes; outside, the arrow is cut back to the circle along the same line. The direction is fixed in this figure; clipping never changes it.
Answer

The clipped length is 1, as it is for any gradient longer than 1. The arrows match up to length 1 and separate beyond it.

The norm covers all the weights together

The usual method, clipping by the global norm, computes one norm over every weight in the model and scales all gradients by the same factor. Suppose one layer's gradient is (1, 2) and another's is (2). The global norm is √(1 + 4 + 4) = 3. With c = 1.5, every entry is halved.

A cruder method, clipping by value, caps each entry on its own. It changes the direction: capping (3, 4) at 1 gives (1, 1), which points somewhere else.

In PyTorch, clip after loss.backward() has filled the gradients and before optimizer.step() uses them. The function returns the norm from before clipping. That number is worth printing or logging: a norm that keeps rising warns that training is going wrong.

clip_torch.py

import torch

a = torch.zeros(2, requires_grad=True)   # weights of one layer
b = torch.zeros(1, requires_grad=True)   # weights of another
a.grad = torch.tensor([1.0, 2.0])
b.grad = torch.tensor([2.0])

total = torch.nn.utils.clip_grad_norm_([a, b], max_norm=1.5)
print(total)          # length before clipping: sqrt(1 + 4 + 4) = 3
print(a.grad, b.grad) # every entry scaled by 1.5 / 3 = 0.5
tensor(3.)
tensor([0.5000, 1.0000]) tensor([1.0000])

In a training loop, the three lines go in this order (an excerpt, not a whole program):

loss.backward()
# model.parameters() lists all the model's weights, like [a, b] above.
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
optimizer.step()
Small print

PyTorch scales by c / (norm + 0.000001), so the clipped length is a hair under c. A maximum of 1.0 is a common starting value, but it is a setting to tune. Clipping is used with AdamW too. AdamW already limits each step's size, but one huge gradient still inflates its running average of squared gradients, which then shrinks the steps for hundreds of steps afterwards.

Check yourself

  1. Clip the gradient (0.3, 0.4) with c = 1.

    Answer

    Its norm is 0.5, which is below 1, so it stays (0.3, 0.4).

  2. Clip the gradient (2, 0, 0, 1, 2) with c = 2.

    Answer

    The norm is √(4 + 0 + 0 + 1 + 4) = 3. Scale by 2 / 3 to get (4/3, 0, 0, 2/3, 4/3), about (1.33, 0, 0, 0.67, 1.33).

  3. Why is clipping by the global norm usually preferred to clipping each entry by value?

    Answer

    It keeps the gradient's direction, so the step still goes downhill in the same way, only shorter. Clipping by value can change the direction.

  4. Someone calls clip_grad_norm_ after optimizer.step(). What goes wrong?

    Answer

    The step has already used the unclipped gradient, so the large step happens anyway. Clipping must come between backward() and step().

Go further

Optional background, for readers who want more than this page.

Where this shows up

Units that build on this one