The problem
LLMs (large language models) are not trained with a fixed learning rate. It changes at every step, following a schedule: it grows in a straight line during a short warmup, then follows half a cosine wave down to a minimum, and stays there. The theory page explains why each phase helps.
You will write the function that a training loop calls once per step to get the learning rate: lr_at(step, max_lr, min_lr, warmup_steps, total_steps), with steps counted from 0.
In the formulas below:
- is
step, iswarmup_steps, istotal_steps; - (eta max) is
max_lrand ismin_lr.
You can assume total_steps > warmup_steps >= 0.
Phase 1: warmup, when step < warmup_steps
The rate rises in equal increments, so that step 0 gets and step gets exactly :
Example. and : steps 0, 1, 2, 3 get , , and .
Phase 2: cosine decay, when warmup_steps <= step <= total_steps
where:
- is the progress through the decay: 0 on the first step after warmup, 1 on the last step;
- goes from (at ) to (at ), so the bracket goes from 2 to 0;
- so slides from down to . Halfway, at , and is exactly the average of the two.
Phase 3: after the end, when step > total_steps
Stay at .
Example. The whole schedule with , , , :
step phase learning rate 0 warmup 0.0005 1 warmup 0.001 2 cosine 0 0.001 4 cosine 0.25 0.000868 6 cosine 0.5 0.00055 8 cosine 0.75 0.000232 10 cosine 1 0.0001 11 after 0.0001 Step 6 is halfway through the decay: .
What to implement
lr_at(step, max_lr, min_lr, warmup_steps, total_steps): the learning rate of step step, as a Python float.
Requirements
warmup_steps = 0means no warmup: step 0 is already in phase 2, with , and getsmax_lr. Do not divide by zero.- Use
math.cosandmath.pi, no loops: this function is called once per training step.