C.W.K.
Stream
Lesson 07 of 10 · published

Gradient Clipping — Taming Exploding Gradients

~10 min · clipping, stability, norm

Level 0Tensor Curious
0 XP0/62 lessons0/13 achievements
0/120 XP to next level120 XP to go0% complete

When backward overshoots, clip it

Some training setups produce gradients with huge norms — RNNs over long sequences, very deep networks without proper normalization, GANs, transformer training without warmup. Huge gradients × any learning rate produce huge updates that destabilize training (NaN losses, sudden divergence).

Gradient clipping bounds the magnitude of gradients before the optimizer step. Two common variants:

  • Clip by global norm — compute the L2 norm of all gradients combined, scale them down if it exceeds a threshold. Most common in modern training (Transformers, LLM fine-tuning, etc.).
  • Clip by value — clamp each gradient element to [-c, c]. Cruder, but useful when you have outlier elements rather than overall large norms.

Where it goes in the loop

Always between backward() and optimizer.step(). Clipping before backward doesn't make sense (no gradients yet); clipping after step is too late.

A typical max_norm value is 1.0 for transformer training, sometimes 5.0 for RNNs. The right value is empirical — log the actual gradient norm during training and you'll see whether you're hitting the cap often.

Code

Clip by global norm — the standard recipe·python
import torch
import torch.nn as nn

model = nn.Sequential(nn.Linear(100, 50), nn.ReLU(), nn.Linear(50, 10))
optimizer = torch.optim.AdamW(model.parameters(), lr=1e-3)
loss_fn = nn.CrossEntropyLoss()

x = torch.randn(32, 100)
y = torch.randint(0, 10, (32,))

optimizer.zero_grad()
loss = loss_fn(model(x), y)
loss.backward()

# Clip before step — returns the original (pre-clip) total norm
total_norm = torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
print(f"pre-clip norm: {total_norm:.4f}")

optimizer.step()
Clip by value — cruder, sometimes useful·python
import torch
import torch.nn as nn

model = nn.Linear(10, 2)
loss = nn.functional.mse_loss(model(torch.randn(4, 10)), torch.randn(4, 2))
loss.backward()

# Clamps each element of every gradient tensor to [-0.5, 0.5]
torch.nn.utils.clip_grad_value_(model.parameters(), clip_value=0.5)
Logging gradient norms — diagnostic, no cap·python
import torch
import torch.nn as nn

model = nn.Linear(10, 2)
loss = nn.functional.mse_loss(model(torch.randn(4, 10)), torch.randn(4, 2))
loss.backward()

# Compute the global norm WITHOUT clipping (pass max_norm=inf)
norm = torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=float('inf'))
print(f"global grad norm: {norm:.4f}")

# In a training loop, log this every N steps. If it consistently exceeds
# your max_norm, your clip threshold is doing real work — that's a signal
# to investigate (lr too high? warmup needed? mixed-precision underflow?).

External links

Exercise

Train a small RNN on a toy sequence task for 5 epochs without clipping. Log the global grad norm every batch. You'll usually see it spike. Now turn on clip_grad_norm_(model.parameters(), 1.0) and rerun — the loss curve should be visibly smoother.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.