Training Deep Networks Effectively
Learning Rate Schedules & Warmup
Chapter 2's optimizers still require choosing a base learning rate, held constant throughout training in the simplest setup. In practice, the ideal learning rat
Jr Codex Deep Learning Notes
Level: Intermediate Prerequisites: Chapter 4: Regularization Time to complete: ~20 minutes
Table of Contents
- Why a Fixed Learning Rate Is Suboptimal
- Step Decay
- Cosine Annealing
- Learning Rate Warmup
- Combining Warmup with Decay
- Learning Rate Schedules in PyTorch
- Finding a Good Initial Learning Rate
- Summary & Next Steps
1. Why a Fixed Learning Rate Is Suboptimal
Chapter 2's optimizers still require choosing a base learning rate, held constant throughout training in the simplest setup. In practice, the ideal learning rate typically changes over the course of training:
The Intuition
─────────────────────────────────────────
EARLY in training: weights are far from any good
solution — LARGE steps make fast
progress and are relatively safe
LATE in training: weights are CLOSE to a good
solution — LARGE steps risk
overshooting and oscillating
around the minimum instead of
settling into it precisely
─────────────────────────────────────────
A learning rate schedule adjusts the learning rate over time, typically starting higher and decreasing — directly generalizing the ML Notes' hyperparameter tuning discipline into something applied during a single training run rather than only between runs.
2. Step Decay
The simplest schedule: multiply the learning rate by a fixed factor every fixed number of epochs.
def step_decay(initial_lr, epoch, drop_factor=0.5, epochs_per_drop=10):
return initial_lr * (drop_factor ** (epoch // epochs_per_drop))
for epoch in [0, 5, 10, 15, 20, 25, 30]:
lr = step_decay(initial_lr=0.1, epoch=epoch)
print(f"Epoch {epoch}: learning rate = {lr:.5f}")
# Epoch 0: 0.10000
# Epoch 10: 0.05000 (dropped after 10 epochs)
# Epoch 20: 0.02500 (dropped again after 20 epochs)
# Epoch 30: 0.012503. Cosine Annealing
Cosine annealing smoothly decreases the learning rate following a cosine curve, from an initial value down to (near) zero — a popular, smoother alternative to step decay's abrupt drops.
import numpy as np
def cosine_annealing(initial_lr, epoch, total_epochs):
return initial_lr * 0.5 * (1 + np.cos(np.pi * epoch / total_epochs))
for epoch in [0, 10, 20, 30, 40, 50]:
lr = cosine_annealing(initial_lr=0.1, epoch=epoch, total_epochs=50)
print(f"Epoch {epoch}: learning rate = {lr:.5f}")Step Decay vs Cosine Annealing
─────────────────────────────────────────
Learning Rate
│████ Step decay — abrupt DROPS at
│ ████ fixed intervals
│ ████
│ ████
│
│╲ Cosine annealing — smooth,
│ ╲__ continuous decrease
│ ╲___
│ ╲______
└──────────────────────────► Epoch
─────────────────────────────────────────
4. Learning Rate Warmup
Warmup does the opposite at the very start of training: it begins with a small learning rate and gradually increases it over the first several hundred or thousand steps, before switching to a decay schedule.
def warmup_schedule(step, warmup_steps, target_lr):
if step < warmup_steps:
return target_lr * (step / warmup_steps) # LINEAR ramp-up
return target_lr
for step in [0, 250, 500, 750, 1000, 1500]:
lr = warmup_schedule(step, warmup_steps=1000, target_lr=0.001)
print(f"Step {step}: learning rate = {lr:.6f}")Why Warmup Helps, Especially for Transformers (Module 6)
─────────────────────────────────────────
At the VERY START of training, weights are randomly
initialized (Chapter 1) and the running statistics used by
Adam (Chapter 2) and BatchNorm/LayerNorm (Chapter 3) haven't
stabilized yet. Taking large steps immediately, before these
statistics have settled, can push the network into a poor,
hard-to-escape region early on. A brief warmup period lets
these statistics stabilize gently before applying full-sized
updates.
Warmup is now considered close to MANDATORY for training
Transformers (Module 6) — most published training recipes
for BERT, GPT, and similar architectures explicitly specify
a warmup period.
─────────────────────────────────────────
5. Combining Warmup with Decay
The standard modern recipe combines both: warmup for the first portion of training, followed by a decay schedule (often cosine annealing) for the remainder.
def warmup_then_cosine_decay(step, warmup_steps, total_steps, target_lr):
if step < warmup_steps:
return target_lr * (step / warmup_steps) # Section 4's warmup
# Section 3's cosine decay, over the REMAINING steps after warmup
progress = (step - warmup_steps) / (total_steps - warmup_steps)
return target_lr * 0.5 * (1 + np.cos(np.pi * progress))Learning Rate Over the FULL Training Run
─────────────────────────────────────────
Learning Rate
│ ╱‾‾╲
│ ╱ ╲___
│ ╱ ╲______
│╱ ╲__________
└──────────────────────────────────────► Step
↑warmup↑ cosine decay
─────────────────────────────────────────
6. Learning Rate Schedules in PyTorch
import torch
import torch.nn as nn
model = nn.Linear(10, 1)
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
# Step decay (Section 2)
step_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.5)
# Cosine annealing (Section 3)
cosine_scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=50)
# A typical training loop, applying the scheduler AFTER each epoch
for epoch in range(50):
# ... training steps for this epoch ...
cosine_scheduler.step() # update the learning rate for the NEXT epoch
current_lr = optimizer.param_groups[0]["lr"]
print(f"Epoch {epoch}: lr = {current_lr:.6f}")7. Finding a Good Initial Learning Rate
A Practical Technique: the Learning Rate Range Test
─────────────────────────────────────────
1. Start training with a VERY small learning rate
2. GRADUALLY increase it every few steps (e.g. exponentially)
3. Plot loss against learning rate
4. Pick a learning rate from JUST BEFORE the point where
loss starts increasing sharply — this is typically a
strong INITIAL learning rate to use with a proper
schedule (Sections 2-5)
─────────────────────────────────────────
This range test is a fast, empirical alternative to the ML Notes' systematic hyperparameter search methods — specifically adapted for the learning rate, given how large its search range typically spans (often several orders of magnitude).
8. Summary & Next Steps
Key Takeaways
- A fixed learning rate is a compromise; ideal learning rates tend to be larger early in training and smaller as training approaches a good solution.
- Step decay drops the learning rate at fixed intervals; cosine annealing decreases it smoothly and continuously — both are common decay schedules.
- Learning rate warmup starts small and ramps up, letting optimizer and normalization statistics stabilize before large updates begin — close to mandatory for training Transformers.
- The learning rate range test provides a fast, empirical way to choose a strong initial learning rate before applying a full schedule.
Concept Check
- Why might a learning rate that works well early in training cause problems later in training?
- What specific training instability does warmup help prevent, and why is it especially important for Transformers?
- How does the learning rate range test help you pick an initial learning rate?
Next Chapter
→ Chapter 6: Vanishing/Exploding Gradients & Diagnosing Training Problems
Jr Codex — 1-on-1 Personalized Coaching | Back to Module Index | Back to DL Index