Concept units

Learning-rate schedules: warm-up, decay

Intermediate

Read these first if they are new to you

The idea in one paragraph

A learning-rate schedule changes the learning rate as training goes on, by a plan fixed before training starts. The most common plan has two parts. During warm-up, the rate rises in a straight line from near zero to its peak over the first few thousand steps. During decay, it falls back toward zero by the last step, often along a cosine curve. Small early steps keep the random starting weights from being thrown off; small late steps let the weights settle.

No single learning rate suits the whole run

In gradient descent, the learning rate η scales every step. A large rate makes fast progress but overshoots and bounces around a low point. A small rate is precise but slow. A learning-rate schedule uses different rates at different times: a function that gives η for each step number t.

  • Warm-up starts small. At the start the weights are random, so gradients are large and point in changing directions. Optimizers such as AdamW also have only a few gradients in their running averages. Large steps now can throw the weights into a region where training fails.
  • Decay ends small. Late in training, each step's gradient comes from a small random batch of examples, so it is noisy. With a large rate the weights keep jumping around near a low point. Shrinking the rate lets them settle into it.

Warm-up is a straight rise; cosine decay is half a wave down

Write W for the number of warm-up steps, T for the total number of steps, and ηpeak for the highest rate. During warm-up (t < W):

ηt = ηpeak × t / W

After warm-up, let p = (t − W) / (T − W) be the fraction of the decay that is done, from 0 to 1. Cosine decay to zero is:

ηt = ηpeak × ½(1 + cos(πp))

The factor ½(1 + cos(πp)) falls smoothly from 1 at p = 0 to 0 at p = 1. It falls slowly at first, fastest in the middle, and slowly again at the end. (A cosine is a sine wave shifted by a quarter turn.) Some schedules stop at a small floor ηmin instead of 0.

Worked example: four points on one schedule

Take ηpeak = 0.001, W = 1,000 and T = 10,000.

  1. Step 500 is in warm-up: 0.001 × 500 / 1,000 = 0.0005.
  2. Step 1,000 ends warm-up: 0.001 × 1,000 / 1,000 = 0.001, the peak.
  3. Step 5,500: p = 4,500 / 9,000 = 0.5. Factor ½(1 + cos(π/2)) = ½(1 + 0) = 0.5. Rate 0.0005.
  4. Step 10,000: p = 1. Factor ½(1 + cos π) = ½(1 − 1) = 0. Rate 0.

Your turn. What is the rate at step 7,750? Use cos(3π/4) ≈ −0.707.

Answer

p = 6,750 / 9,000 = 0.75. Factor ½(1 − 0.707) ≈ 0.146. Rate ≈ 0.000146.

The warm-up length and the decay shape are settings to choose

Try it. The figure draws the schedule over 10,000 steps with a peak of 0.001. Predict first: with no warm-up at all, what rate does the very first step use? Then find the warm-up length at which step 500 uses half the peak rate.

The learning rate at every step of a 10,000-step run

The shaded band is warm-up; the rest is decay. A longer warm-up leaves fewer steps at high rates. The dot marks step 500.
Answer

With no warm-up, the first step uses the full peak, 0.001. Step 500 uses half the peak when warm-up lasts 1,000 steps.

Other schedules exist. Step decay multiplies the rate by a fixed factor, such as 0.1, at chosen steps. The original Transformer paper used warm-up followed by a fall in proportion to 1 / √t. All of them share the same two aims: careful at the start, settled at the end.

What is known and what is folklore

That warm-up and decay help is well established in practice. Why warm-up helps is still studied, and the reasons given above are the common explanations, not proofs. The warm-up length, the peak and the shape are tuned for each problem. In PyTorch, AdamW's weight decay is multiplied by the current rate, so the schedule changes it too.

In PyTorch, a scheduler sets the rate before each step

LambdaLR multiplies the optimizer's rate by a function of the step count. Call sched.step() once after each opt.step(). If you call it once per pass over the data instead, the function's t counts passes, not steps.

schedule_torch.py

import math
import torch

peak, warmup, total = 0.001, 1000, 10_000

def factor(step):                        # multiplies the peak rate
    if step < warmup:
        return step / warmup             # straight rise from 0
    p = (step - warmup) / (total - warmup)
    return 0.5 * (1 + math.cos(math.pi * p))  # cosine fall to 0

w = torch.zeros(1, requires_grad=True)
opt = torch.optim.AdamW([w], lr=peak)    # the optimizer that updates w, with rate peak
sched = torch.optim.lr_scheduler.LambdaLR(opt, factor)

for step in range(total):
    if step in (0, 500, 1000, 5500, 7750):    # print only at these steps
        print(step, f"{opt.param_groups[0]['lr']:.6f}")   # the optimizer's current rate
    w.grad = torch.zeros(1)              # stands in for loss.backward()
    opt.step()
    sched.step()                         # after each optimizer step
0 0.000000
500 0.000500
1000 0.001000
5500 0.000500
7750 0.000146

With this function, step 0 has a rate of exactly 0, so the first step changes nothing. Some code uses (t + 1) / W instead to avoid that wasted step.

Check yourself

  1. A run has ηpeak = 0.002 and 1,000 warm-up steps. What is the rate at step 250?

    Answer

    0.002 × 250 / 1,000 = 0.0005.

  2. With cosine decay, what fraction of the peak is left a quarter of the way through the decay (p = 0.25)? Use cos(π/4) ≈ 0.707.

    Answer

    ½(1 + 0.707) ≈ 0.854, so about 85% of the peak. Cosine decay stays high for a while, then falls faster.

  3. Someone writes the schedule above but calls sched.step() once per pass over a data set that takes 200 steps per pass. What happens to warm-up?

    Answer

    Warm-up now lasts 1,000 passes, which is 200,000 steps. The run will likely end long before the rate reaches its peak.

  4. Why end training with a small learning rate instead of the peak?

    Answer

    Each step's gradient is noisy, so at a high rate the weights keep jumping around a low point of the loss. A small rate lets them settle into it, which usually lowers the final loss.

Go further

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

Where this shows up

Units that build on this one