Optimization and Calculus for Machine Learning

Adam

Adam as an adaptive optimizer that combines momentum-like gradient memory with per-parameter scaling from squared gradients.

Lesson
29

Goal

Understand Adam as an adaptive optimizer that keeps two memories:

The core idea is:

Adam asks, “Which direction has been consistently useful, and how large are gradients usually in each parameter?”

By the end of the lesson, Adam should feel less like a magic optimizer name and more like a stateful update rule built from ideas you already know: SGD, momentum, exponential memory, and per-parameter scaling.

Motivation

Gradient descent gave us the basic movement rule:

θt+1=θtηL(θt)\theta_{t+1} = \theta_t - \eta \nabla L(\theta_t)

SGD made that rule practical by estimating the gradient from mini-batches:

θt+1=θtηgt\theta_{t+1} = \theta_t - \eta g_t

Momentum added memory:

vt=βvt1+gtv_t = \beta v_{t-1} + g_t θt+1=θtηvt\theta_{t+1} = \theta_t - \eta v_t

That helps when the same downhill direction keeps recurring.

But momentum still uses one shared learning-rate scale. If one parameter usually has huge gradients and another usually has tiny gradients, a single global step size can be awkward:

Adam responds by adapting the update size separately for each parameter.

Prerequisites

Notation / Symbol Table

SymbolPlain-English nameMeaning
θt\theta_tcurrent parametersThe parameter vector at optimization step tt.
gtg_tgradient estimateThe mini-batch gradient estimate at step tt.
mtm_tfirst moment estimateA running average of recent gradients.
vtv_tsecond moment estimateA running average of recent squared gradients.
m^t\hat{m}_tbias-corrected first momentmtm_t adjusted for zero initialization.
v^t\hat{v}_tbias-corrected second momentvtv_t adjusted for zero initialization.
β1\beta_1first-moment decayHow much previous gradient memory is retained.
β2\beta_2second-moment decayHow much previous squared-gradient memory is retained.
η\etabase learning rateThe global scale applied to Adam’s normalized update.
ϵ\epsilonstability constantSmall positive number added to avoid division by zero.
\odotelementwise multiplicationMultiply matching coordinates separately.

Important overload warning: in Lesson 28, vtv_t meant momentum velocity. In Adam, vtv_t usually means the second moment estimate. Same letter, different optimizer state. Always check the local symbol table.

Core Idea

Adam is short for adaptive moment estimation.

The name is useful once the vocabulary is unpacked:

For Adam, the sequence being summarized is the sequence of mini-batch gradients.

The first memory tracks the average gradient direction. The second memory tracks the average squared gradient size.

First Memory: Direction

Adam’s first memory is:

mt=β1mt1+(1β1)gtm_t = \beta_1 m_{t-1} + (1 - \beta_1)g_t

This looks like momentum. It keeps a running average of recent gradients.

If recent gradients keep pointing in the same direction, mtm_t keeps that direction. If gradients alternate, the running average dampens the noise.

The coefficient β1\beta_1 controls how much previous first-moment memory survives. A common default is:

beta_1 = 0.9

That means the new first moment keeps 90 percent of the previous first moment and blends in 10 percent of the current gradient.

Second Memory: Scale

Adam’s second memory is:

vt=β2vt1+(1β2)(gtgt)v_t = \beta_2 v_{t-1} + (1 - \beta_2)(g_t \odot g_t)

The term gtgtg_t \odot g_t means square each gradient coordinate separately.

If:

gt=[10,1]g_t = [10, 1]

then:

gtgt=[100,1]g_t \odot g_t = [100, 1]

So vtv_t tracks which coordinates usually have large gradients and which usually have small gradients.

A common default is:

beta_2 = 0.999

That makes the second memory change slowly. It is trying to estimate a stable scale for each parameter, not react sharply to one noisy mini-batch.

Why Squared Gradients Help

Suppose two parameters have different gradient scales:

gt=[10,1]g_t = [10, 1]

Plain SGD uses the same learning rate for both coordinates:

Δθ=η[10,1]\Delta \theta = -\eta [10, 1]

The first coordinate moves ten times as much as the second.

Adam divides by a running estimate of gradient scale:

m^tv^t+ϵ\frac{\hat{m}_t}{\sqrt{\hat{v}_t} + \epsilon}

The square root matters because vtv_t stores squared gradients. If a coordinate has squared-gradient scale near 100, then 100=10\sqrt{100} = 10.

Very roughly:

[10,1][100,1]=[10,1][10,1]=[1,1]\frac{[10, 1]}{\sqrt{[100, 1]}} = \frac{[10, 1]}{[10, 1]} = [1, 1]

This is the adaptive part. Adam reduces updates in coordinates that usually have large gradients and allows relatively larger updates in coordinates that usually have small gradients.

Bias Correction

Adam usually initializes both memories at zero:

m0=0m_0 = 0 v0=0v_0 = 0

Early in training, that makes the running averages biased toward zero. For example:

m1=β1m0+(1β1)g1m_1 = \beta_1 m_0 + (1 - \beta_1)g_1

Since m0=0m_0 = 0:

m1=(1β1)g1m_1 = (1 - \beta_1)g_1

If β1=0.9\beta_1 = 0.9, then m1m_1 is only 10 percent of g1g_1.

Adam corrects this with:

m^t=mt1β1t\hat{m}_t = \frac{m_t}{1 - \beta_1^t}

and:

v^t=vt1β2t\hat{v}_t = \frac{v_t}{1 - \beta_2^t}

The hats mean “bias-corrected.” They compensate for the fact that the memories started at zero.

The Adam Update Rule

Adam computes:

mt=β1mt1+(1β1)gtm_t = \beta_1 m_{t-1} + (1 - \beta_1)g_t vt=β2vt1+(1β2)(gtgt)v_t = \beta_2 v_{t-1} + (1 - \beta_2)(g_t \odot g_t)

Then it bias-corrects:

m^t=mt1β1t\hat{m}_t = \frac{m_t}{1 - \beta_1^t} v^t=vt1β2t\hat{v}_t = \frac{v_t}{1 - \beta_2^t}

Finally it updates:

θt+1=θtηm^tv^t+ϵ\theta_{t+1} = \theta_t - \eta \frac{\hat{m}_t}{\sqrt{\hat{v}_t} + \epsilon}

Read the last line as:

Move opposite the momentum-like average gradient, scaled coordinate-by-coordinate by recent gradient magnitude.

The division is elementwise. Each parameter gets its own denominator.

A Small Scalar Example

Use toy coefficients to keep the arithmetic small:

beta_1 = 0.8
beta_2 = 0.9
eta = 0.1
epsilon is tiny enough to ignore

Start with:

m0=0m_0 = 0 v0=0v_0 = 0

and suppose:

g1=3g_1 = 3

First moment:

m1=0.8(0)+(10.8)(3)=0.6m_1 = 0.8(0) + (1 - 0.8)(3) = 0.6

Second moment:

v1=0.9(0)+(10.9)(32)=0.9v_1 = 0.9(0) + (1 - 0.9)(3^2) = 0.9

Bias correction:

m^1=0.610.81=0.60.2=3\hat{m}_1 = \frac{0.6}{1 - 0.8^1} = \frac{0.6}{0.2} = 3 v^1=0.910.91=0.90.1=9\hat{v}_1 = \frac{0.9}{1 - 0.9^1} = \frac{0.9}{0.1} = 9

Adam update:

θ2=θ10.139+ϵ\theta_2 = \theta_1 - 0.1 \frac{3}{\sqrt{9} + \epsilon}

Ignoring the tiny ϵ\epsilon:

θ2θ10.1\theta_2 \approx \theta_1 - 0.1

This example shows a useful first-step behavior: after bias correction, Adam has recovered the gradient scale information from the first observation.

Connection To RMSProp

You will often see Adam described as combining momentum and RMSProp.

RMSProp keeps a running average of squared gradients and divides by the root mean square scale:

st=ρst1+(1ρ)(gtgt)s_t = \rho s_{t-1} + (1 - \rho)(g_t \odot g_t) θt+1=θtηgtst+ϵ\theta_{t+1} = \theta_t - \eta \frac{g_t}{\sqrt{s_t} + \epsilon}

Adam adds the momentum-like first moment mtm_t and bias correction:

θt+1=θtηm^tv^t+ϵ\theta_{t+1} = \theta_t - \eta \frac{\hat{m}_t}{\sqrt{\hat{v}_t} + \epsilon}

That is the conceptual compression:

Adam is momentum for direction plus RMSProp-style scaling for step size.

Connection To Programming

Plain SGD needs almost no optimizer state:

theta = theta - learning_rate * gradient

Momentum stores one state vector:

velocity = beta * velocity + gradient
theta = theta - learning_rate * velocity

Adam stores two state vectors:

firstMoment = beta1 * firstMoment + (1 - beta1) * gradient
secondMoment = beta2 * secondMoment + (1 - beta2) * gradient * gradient
firstMomentHat = firstMoment / (1 - beta1 ** step)
secondMomentHat = secondMoment / (1 - beta2 ** step)
theta = theta - learningRate * firstMomentHat / (sqrt(secondMomentHat) + epsilon)

The model stores parameters. The optimizer stores memory about gradients.

What Adam Helps With

Adam is often useful when:

That is why Adam is common in deep learning experiments. It often starts training effectively without as much learning-rate tuning as plain SGD.

What Adam Does Not Solve

Adam is not a guarantee of better generalization.

It can make optimization easier, but it does not replace:

One important warning for later: weight decay interacts differently with adaptive optimizers than with plain SGD. This is why AdamW exists. AdamW is not the topic yet, but the warning is worth naming so “Adam plus weight decay” does not become a blurry phrase.

Why Adam Shows Up In ML

Modern neural networks often have many parameters with very different roles and gradient scales.

For example:

Adam gives each parameter a local scale estimate while still using a momentum-like direction estimate. That makes it a strong default optimizer for many experiments.

Summary

SGD uses the current gradient estimate:

θt+1=θtηgt\theta_{t+1} = \theta_t - \eta g_t

Momentum remembers recent gradient directions:

vt=βvt1+gtv_t = \beta v_{t-1} + g_t

Adam keeps two memories:

mt=β1mt1+(1β1)gtm_t = \beta_1 m_{t-1} + (1 - \beta_1)g_t vt=β2vt1+(1β2)(gtgt)v_t = \beta_2 v_{t-1} + (1 - \beta_2)(g_t \odot g_t)

Then Adam updates with:

θt+1=θtηm^tv^t+ϵ\theta_{t+1} = \theta_t - \eta \frac{\hat{m}_t}{\sqrt{\hat{v}_t} + \epsilon}

The first moment gives direction. The second moment gives scale. Bias correction fixes the early zero-initialization shrinkage.

Exercises

  1. In your own words, explain why Adam needs two memories instead of one.
  2. What is the difference between mtm_t and vtv_t in Adam?
  3. Why does Adam divide by v^t+ϵ\sqrt{\hat{v}_t} + \epsilon instead of by v^t\hat{v}_t?
  4. Compute the first Adam step for the scalar setup in the worked example, but use g1=5g_1 = 5.
  5. Explain why bias correction matters most near the beginning of training.
  6. Give one situation where Adam is likely easier to use than plain SGD, and one situation where Adam still needs careful tuning.
  7. Write TypeScript-style pseudocode for one Adam update over an array of parameters.

Looking Ahead

Adam adapts step sizes using gradient history, but it still has a base learning rate η\eta.

The next question is:

Should the base learning rate itself change over training?

That leads to learning rate schedules: decay, warmup, cosine schedules, and the practical shape of training runs.