Step 1 of 5 · Reading · ~3 min
Optimizers & Autograd
The Adam Optimizer
Adam keeps two running averages per parameter and uses them to give every parameter its own step size. It is the default optimizer in most deep-learning code. After this lesson your program applies Adam, including its bias correction, to a single parameter.
Two problems with a single global step
- Noise. Mini-batch gradients jitter from step to step, so the path zig-zags instead of heading for the minimum.
- Scale. Some parameters see gradients of size 100 and others of size 0.001. One learning rate is far too big for the first and far too small for the second.
Two moving averages
For each parameter Adam tracks the gradient g's running mean and running mean of squares:
m <- b1 * m + (1 - b1) * g direction: a smoothed gradient (momentum)
v <- b2 * v + (1 - b2) * g * g magnitude: how large gradients have been lately
The usual values are b1 = 0.9 and b2 = 0.999. m plays the role of momentum: noise cancels and a consistent direction survives. v remembers the typical gradient size; dividing by its square root turns "gradient" into "gradient in units of its own typical size".
Bias correction
Both averages start at 0, so early on they are biased toward 0: after one step m holds only a tenth of the gradient. Adam divides by how much of the average has been filled in so far, using the step count t (starting at 1 on the first step):
m_hat = m / (1 - b1^t)
v_hat = v / (1 - b2^t)
As t grows, b1^t and b2^t shrink to 0 and the correction fades out. On the very first step it exactly undoes the shrinkage: m_hat is g and v_hat is g*g.
The update
theta <- theta - lr * m_hat / ( sqrt(v_hat) + eps )
eps (about 1e-8) only prevents dividing by zero. Because the step is the smoothed gradient divided by the typical gradient size, its magnitude is close to lr regardless of how big or small the raw gradients are. That is why one learning rate such as 0.001 works across so many models.
A worked trace
theta = 2, lr = 0.1, b1 = 0.9, b2 = 0.999, gradients 0.5, then -0.5:
t = 1 g = 0.5 m = 0.05 v = 0.00025 m_hat = 0.5 v_hat = 0.25
step = 0.1 * 0.5 / (0.5 + eps) = 0.1000 theta = 1.9000
t = 2 g = -0.5 m = -0.005 v = 0.00049975 m_hat = -0.02632 v_hat = 0.25
step = 0.1 * (-0.02632) / 0.5 = -0.00526 theta = 1.9053
The first step moves the full lr. When the gradient flips sign, the average m nearly cancels and the step shrinks to about 5% of the first: Adam refuses to chase noise.
Common mistakes
- Bias-correcting with the step count before incrementing it. At
t = 0the denominator1 - b1^0is 0. - Squaring after averaging (
(mean g)^2) instead of averaging the square. - Putting
epsinside the square root, or forgetting it, which breaks ong = 0. - Not resetting
m,vandtwhen a new run starts. - Using the wrong
tforv_hat: both corrections use the samet.
Your exercise
The input is a script of INIT, LR, BETAS, EPS, STEP and PRINT commands. The starter keeps the state in a four-slot list holding theta, m, v and t, and supplies a helper for integer powers. Implement adam_step, which updates that state in place for one gradient.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…