PyTorch Weight Decay
Weight decay is a penalty that pulls weights toward zero during training. Smaller weights make a simpler model, and a simpler model often generalises better.
Weights that fit noise
Plain training puts no cost on large weights. A weight can grow just to fit one noisy row.
loss = loss_fn(model(x), y)
loss.backward()
optimizer.step() # every weight is free to growDecay in the optimizer
The optimizer takes a weight_decay value at creation. The stored value reads back from the param groups.
import torch
torch.manual_seed(0)
model = torch.nn.Linear(3, 1)
optimizer = torch.optim.SGD(model.parameters(), lr=0.05, weight_decay=0.1)
print(optimizer.param_groups[0]["weight_decay"])Decay as a loss penalty
A penalty adds a term built from the squared weights to the loss. Training then pays for large weights on every step.
import torch
torch.manual_seed(0)
model = torch.nn.Linear(3, 1)
penalty = 0.1 * sum((p ** 2).sum() for p in model.parameters())
print(f"Penalty added to the loss: {penalty.item():.4f}")Example
import torch
torch.manual_seed(0)
x = torch.randn(32, 20)
true = torch.zeros(20)
true[0] = 2.0
true[1] = -1.0
true[2] = 0.5
y = x @ true + torch.randn(32) * 0.5
loss_fn = torch.nn.MSELoss()
def train(decay):
torch.manual_seed(1)
model = torch.nn.Linear(20, 1)
optimizer = torch.optim.SGD(
model.parameters(), lr=0.05, weight_decay=decay)
for step in range(300):
optimizer.zero_grad()
loss = loss_fn(model(x).squeeze(1), y)
loss.backward()
optimizer.step()
norm = sum(p.norm().item() ** 2 for p in model.parameters()) ** 0.5
return loss.item(), norm
for decay in [0.0, 0.1]:
loss, norm = train(decay)
print(f"Decay {decay}: final loss {loss:.4f}, weight norm {norm:.4f}")Key points
- Weight norm: the length of all weights treated as one list, found with norm()
- Use the weight_decay argument in an optimizer to turn decay on
- Decay shrinks every weight a little on every step, before the gradient update
- A penalty term in the loss is the other route, and it adds the weight sizes to the loss
- Generalise: to work well on new data, not only on the training data
- Weight decay trades a little training fit for smaller weights, as the closing example shows
In this example, the data has 20 input columns and only 3 true weights. The seed fixes the data, and the other 17 columns are pure noise. A train function builds the same Linear model for each decay value. Each run trains for 300 steps with SGD. Decay 0 ends with the lower loss, 0.0702, and the larger weight norm, 2.6155. Decay 0.1 ends with loss 0.1131 and a smaller norm, 2.2646. Decay gave up a little fit and kept the weights smaller.
Note
Weight decay is not a smaller learning rate, because decay pulls toward zero while the rate sets step size.
Try it Yourself
Edit the code, then press Run.
Running is simulated ยท Python Playground
Exercise
What does weight decay do on every training step?