← All Posts
Deep Learning · OptimizersChapter 02 / 11

SGD with Momentum: Remembering a Direction

Unroll the momentum buffer, distinguish velocity from displacement, and see why narrow valleys are difficult.

In a narrow valley, SGD repeatedly crosses the steep direction while inching along the flat direction. Momentum gives the optimizer a memory: gradient components that keep pointing the same way accumulate, while rapidly reversing components partially cancel. That is the mechanism behind the familiar “rolling ball” picture—but the actual recurrence is more useful than the analogy.

1. A buffer is a weighted history

Start with a zero buffer $b_0=0$. The heavy-ball convention used in this chapter is

$$\begin{aligned}b_t&=\beta b_{t-1}+g_t,\\w_t&=w_{t-1}-\eta_t b_t,\qquad 0\le\beta<1.\end{aligned}$$

The gradient $g_t$ is evaluated at the current parameter $w_{t-1}$. The coefficient $\beta$ controls how much of the previous buffer survives. The buffer has gradient units; the displacement $\Delta w_t=-\eta_t b_t$ has parameter units. A positive buffer component moves that parameter in the negative direction.

Substitute the recurrence into itself:

$$b_t=g_t+\beta g_{t-1}+\beta^2g_{t-2}+\cdots+\beta^{t-1}g_1.$$

Older gradients receive exponentially smaller weights. They were evaluated at older parameter values, so this history provides persistence at the cost of lag. Momentum can keep moving through a point where the current gradient is zero.

For a constant gradient $g$, the geometric series sums to the steady-state limit:

$$b_t=g\sum_{k=0}^{t-1}\beta^k=g\,\frac{1-\beta^t}{1-\beta}\xrightarrow{t\to\infty}\frac{g}{1-\beta}.$$

Consequently, adding momentum while keeping the same learning rate $\eta$ multiplies the sustained step magnitude along a steady direction by $1/(1-\beta)$.

2. Two conventions that look deceptively similar

1. Heavy-ball ($b_t$) vs. EMA ($m_t$): equivalent after rescaling the learning rate. Many papers write momentum as an exponential moving average with $m_0=0$:

$$m_t=\beta m_{t-1}+(1-\beta)g_t,\qquad w_t=w_{t-1}-\eta_{\text{EMA}}m_t.$$

Unrolling $m_t$ and comparing with $b_t=\beta b_{t-1}+g_t$ ($b_0=0$) shows that $m_t$ is simply $b_t$ scaled by $(1-\beta)$:

$$\begin{aligned}m_1&=(1-\beta)g_1=(1-\beta)b_1,\\m_2&=\beta(1-\beta)g_1+(1-\beta)g_2=(1-\beta)(\beta g_1+g_2)=(1-\beta)b_2,\\m_t&=(1-\beta)\bigl(g_t+\beta g_{t-1}+\cdots+\beta^{t-1}g_1\bigr)=(1-\beta)b_t.\end{aligned}$$

Therefore, the two rules produce the exact same parameter updates if and only if the learning rates are scaled by $1-\beta$:

$$-\eta_{\text{EMA}}m_t=-\eta_{\text{EMA}}(1-\beta)b_t=-\eta b_t\iff\eta_{\text{EMA}}=\frac{\eta}{1-\beta}.$$

2. Gradient buffer ($b_t$) vs. velocity buffer ($v_t$): equivalent for fixed $\eta$, different under a schedule $\eta_t$. Another convention puts the learning rate inside the recurrence ($v_0=0$):

$$v_t=\beta v_{t-1}-\eta_t g_t,\qquad w_t=w_{t-1}+v_t.$$

Compare the step-2 displacement $\Delta w_2$ under both conventions:

$$\begin{aligned}\text{Gradient buffer }(-\eta_t b_t):\quad\Delta w_2&=-\eta_2(\beta g_1+g_2)=-\beta\eta_2 g_1-\eta_2 g_2,\\\text{Velocity buffer }(v_t):\quad\Delta w_2&=\beta(-\eta_1 g_1)-\eta_2 g_2=-\beta\eta_1 g_1-\eta_2 g_2.\end{aligned}$$

If $\eta_1=\eta_2=\eta$ is constant, both give $v_t=-\eta b_t$ and are identical. When $\eta_t$ changes (such as during warmup or decay), $-\eta_t b_t$ rescales the entire stored history by the new rate $\eta_2$, whereas $v_t$ keeps the old rate $\eta_1$ on past gradients.

3. Momentum changes the dynamics, not the loss

For $L(w)=\tfrac12\lambda w^2$ and constant learning rate, combine the parameter and buffer recurrences to eliminate the buffer:

$$w_t=(1+\beta-\eta\lambda)w_{t-1}-\beta w_{t-2}.$$

Unlike SGD, the next position depends on two earlier positions. A suitably chosen pair $(\eta,\beta)$ can make progress in flat directions much faster while controlling sharp-direction oscillations. But old direction information can also cause overshoot and slow the response to a turn. Large momentum is not a guarantee of faster convergence.

For a single positive-curvature quadratic mode, $0\le\beta<1$ and $0<\eta\lambda<2(1+\beta)$ give stable heavy-ball dynamics. Stability only means eventual decay in this idealized setting; the transient can still oscillate substantially. Stochastic gradients and a changing neural-network Hessian complicate this picture.

4. Lab: watch the stored direction

This uses the same eight-example quadratic dataset as the SGD chapter. The default full gradient removes sampling noise so you can first see the effect of curvature. The readout exposes the current buffer and the displacement separately.

Momentum in a narrow valley

Compare runs from the same starting point. A control change clears the buffer and resets the trajectory.

5. Where Nesterov momentum differs

The conceptual Nesterov form asks for a gradient at a lookahead point. Using a displacement velocity and a constant rate:

$$\begin{aligned}\widetilde w&=w_{t-1}+\beta v_{t-1},\\v_t&=\beta v_{t-1}-\eta\nabla L(\widetilde w),\\w_t&=w_{t-1}+v_t.\end{aligned}$$
Side-by-side vector diagram comparing heavy-ball momentum, which evaluates the gradient at w_{t-1} and overshoots across the valley, against Nesterov momentum, which steps to the lookahead point w-tilde first and evaluates a corrective gradient pointing back toward the valley.
Scroll the figure horizontally on small screens.
Heavy-ball momentum evaluates $-\eta\nabla L(w_{t-1})$ before applying $\beta v_{t-1}$, whereas Nesterov momentum first steps to the lookahead point $\widetilde w=w_{t-1}+\beta v_{t-1}$ and evaluates $-\eta\nabla L(\widetilde w)$ there to correct overshoot early.

The gradient can correct the direction after accounting for where the stored velocity is about to take us. This is different from simply increasing $\beta$. Libraries may implement an algebraically rearranged form in different parameter coordinates. For example, PyTorch forms a buffer and uses $g_t+\beta b_t$ for its Nesterov update. Do not implement both a lookahead move and that extra term without deriving the intended convention.

The classic Sutskever et al. study discusses momentum and initialization in deep learning. Its results support taking momentum dynamics seriously; they do not turn a particular coefficient into a universal choice.

6. Implementation and state

# Heavy-ball convention used in this chapter.
# b persists across batches and starts at zero.
b = beta * b + grad
parameter -= learning_rate * b

# Corresponding PyTorch configuration:
optimizer = torch.optim.SGD(
    model.parameters(), lr=0.03, momentum=0.9,
    dampening=0, nesterov=False, weight_decay=0
)

Unlike plain SGD (which stores no state between steps), momentum stores one extra number—the buffer value $b_{t,i}$—for every individual model parameter $w_i$. In code, each parameter tensor W of shape $(d_{\text{out}}, d_{\text{in}})$ gets a matching buffer tensor b of the same shape $(d_{\text{out}}, d_{\text{in}})$, doubling the parameter memory footprint compared to stateless SGD ($+4\text{ bytes/parameter}$ in FP32). Because b carries the accumulated direction across steps, always save optimizer.state_dict() alongside model weights when checkpointing.

7. Questions worth answering

Can momentum move uphill relative to the current gradient?

Yes. The buffer can point against the current gradient because it contains earlier gradients. That may carry the iterate through a small local reversal or create harmful overshoot. The method does not guarantee loss reduction on every step.

Does $\beta=0.9$ mean “average the last ten gradients”?

Only as a rough memory-scale intuition. Contributions decay geometrically and extend beyond ten steps. Their weights do not form a hard ten-step window, and the unnormalized buffer’s weights do not sum to one.

Why might changing the learning rate after warmup produce a sudden change?

In the gradient-buffer convention, the new rate multiplies the entire current buffer. The stored history remains, but the displacement scale changes immediately.

Sources

Sutskever et al., On the Importance of Initialization and Momentum in Deep Learning; PyTorch SGD: momentum conventions and Nesterov implementation. The sequence, figures, and quadratic experiments are original teaching examples.