Why AdaGrad / RMSProp / Adam Matters
A parameter vector can contain coordinates with very different gradient magnitudes and frequencies. A learning rate that works for one coordinate may move another too aggressively. AdaGrad, RMSProp, and Adam use a separate history-dependent denominator for each coordinate, rather than applying the same scalar multiplier to every gradient component.
The three methods remember different information. AdaGrad keeps all squared gradients accumulated so far. RMSProp replaces that unbounded sum with an exponential moving average. Adam combines a moving average of signed gradients with a moving average of squared gradients and corrects both for zero initialization.
These methods appear in numerical model fitting and machine learning, particularly when gradient scales or feature frequencies differ. Adaptation does not eliminate the learning rate, guarantee decreasing loss, or establish that the returned parameters are optimal. This lesson defines one exact convention for each method, follows all three on the same quadratic, and implements them in Python, C++, and Java. Full gradients keep the example deterministic; a mini-batch gradient can occupy the same update interface in a larger system.
All three optimizers rescale updates using coordinatewise squared-gradient history: a cumulative sum for AdaGrad, an exponential average for RMSProp, and a bias-corrected exponential average paired with a first moment for Adam. Keep the history, step counter, and epsilon convention explicit; a correct update rule is separate from a convergence guarantee.
Core Concepts and Terminology
Let $f:\mathbb R^d\to\mathbb R$ be a differentiable objective. Use a one-based update index $t=1,2,\ldots$: the incoming gradient is $\mathbf g_t=\nabla f(\mathbf x_{t-1})$, and the update produces $\mathbf x_t$. All vector squares, roots, divisions, and products in the optimizer formulas are coordinatewise.
Write $\eta\gt0$ for the base learning rate and $\epsilon\gt0$ for the additive denominator constant. We choose $\sqrt{s}+\epsilon$ throughout, with epsilon outside the square root. Other conventions exist; $\sqrt{s+\epsilon}$ is a different normalization.
AdaGrad. Initialize $\mathbf s_0=\mathbf0$, accumulate every squared gradient, and update using the new accumulator:
$$s_{t,j}=s_{t-1,j}+g_{t,j}^2,\qquad x_{t,j}=x_{t-1,j}-\eta\frac{g_{t,j}}{\sqrt{s_{t,j}}+\epsilon}.$$
The coefficient $\eta/(\sqrt{s_{t,j}}+\epsilon)$ cannot increase as $t$ grows in exact arithmetic. Coordinates with large accumulated squared gradients receive smaller multipliers. A rarely active coordinate can retain a relatively larger multiplier. This is the diagonal cumulative variant motivated by Duchi, Hazan, and Singer's AdaGrad paper.
Uncentered RMSProp without momentum or bias correction. Choose $0\leq\rho\lt1$ and initialize $\mathbf s_0=\mathbf0$:
$$s_{t,j}=\rho s_{t-1,j}+(1-\rho)g_{t,j}^2,\qquad x_{t,j}=x_{t-1,j}-\eta\frac{g_{t,j}}{\sqrt{s_{t,j}}+\epsilon}.$$
The reference programs use the input named beta2 for $\rho$. Old contributions decay, so the denominator can shrink as well as grow. This is a moving second moment, not a sum and not a centered variance. The original RMSProp lecture, slide 29 describes its squared-gradient moving average.
Adam. Initialize $\mathbf m_0=\mathbf s_0=\mathbf0$ and choose $0\leq\beta_1,\beta_2\lt1$:
$$m_{t,j}=\beta_1m_{t-1,j}+(1-\beta_1)g_{t,j},\qquad s_{t,j}=\beta_2s_{t-1,j}+(1-\beta_2)g_{t,j}^2.$$
Correct the zero-initialized moments before forming the update:
$$\widehat m_{t,j}=\frac{m_{t,j}}{1-\beta_1^t},\qquad \widehat s_{t,j}=\frac{s_{t,j}}{1-\beta_2^t},$$ $$x_{t,j}=x_{t-1,j}-\eta\frac{\widehat m_{t,j}}{\sqrt{\widehat s_{t,j}}+\epsilon}.$$
This is the explicit corrected-moment form of Kingma and Ba's Adam, Algorithm 1. The first moment retains direction; the second moment estimates squared magnitude. It is not a Hessian estimate. If gradient means and second moments are constant over time, division by the factors $1-\beta_1^t$ and $1-\beta_2^t$ removes the expectation bias caused by zero initialization. It does not make the ratio an unbiased gradient estimator, and it does not guarantee stationarity or convergence.
For a shared vocabulary, let $n_{t,j}$ denote the update numerator and $u_{t,j}$ the second statistic actually used: $(n,u)=(g,s)$ for AdaGrad and RMSProp, and $(n,u)=(\widehat m,\widehat s)$ for Adam. Define
$$D_{t,j}=\sqrt{u_{t,j}}+\epsilon,\qquad \delta_{t,j}=\eta\frac{n_{t,j}}{D_{t,j}},\qquad x_{t,j}=x_{t-1,j}-\delta_{t,j}.$$
The signed correction $\boldsymbol\delta_t$ is subtracted from the old point. Adam's numerator can retain a previous direction after the current gradient changes sign.
The teaching objective is $f(\mathbf x)=\tfrac12\sum_{j=1}^d a_j(x_j-c_j)^2$, with nonnegative curvatures $a_j$ and gradient $g_j=a_j(x_j-c_j)$. If all curvatures are positive, its unique minimizer is $\mathbf c$; zero-curvature coordinates are flat. The worked scalar case uses $a=1$, $c=0$, so $f(x)=x^2/2$ and $g=x$.
| Term | First Adam step in this lesson | Role |
|---|---|---|
| Parameter $x$ | $x_0=2$ | Quantity being optimized |
| Gradient $g_t$ | $g_1=2$ | Slope at the old point |
| First moment $m_t$ | $m_1=(1-0.5)\,2=1$ | Exponential average of signed gradients |
| Second statistic $s_t$ | $s_1=(1-0.75)\,4=1$ | Uncentered squared-gradient history |
| Update index $t$ | $1$ on the first update | Determines Adam's two bias factors |
| Bias-corrected moments | $\widehat m_1=2$, $\widehat s_1=4$ | Remove the zero-initialization factors |
| Epsilon $\epsilon$ | $1$ in the hand trace | Positive additive constant outside the square root |
| Denominator $D_t$ | $\sqrt4+1=3$ | Divides the numerator coordinate by coordinate |
| Signed correction $\delta_t$ | $2/3$ with $\eta=1$ | Subtract from the old point once |
| Second moment versus variance | $\mathbb E[g^2]$ versus $\mathbb E[(g-\mathbb E[g])^2]$ | These are different statistics; this lesson uses the first |
| Optimizer checkpoint | Parameters, raw moments, and step counter | Required for an exact continuation of Adam |
How AdaGrad / RMSProp / Adam Works, Step by Step
Inputs are the initial point, center and curvature vectors, base rate, two moment coefficients, epsilon, number of updates $K$, and one mode: ADAGRAD, RMSPROP, or ADAM. AdaGrad ignores both coefficients after validating them; RMSProp uses beta2 as $\rho$ and ignores beta1; Adam uses both.
- Validate and initialize. Check shapes, finite values, ranges, integer update count, and mode. Copy inputs. Set the two raw moment vectors to zero and the two stored powers to $1$.
- Record the starting loss. Evaluate $f(\mathbf x_0)$ once. The initial row has empty gradient, numerator, normalized-second-statistic, denominator, and correction fields because no update has occurred.
- Check the cap. Return after exactly $K$ completed updates. A zero-update call still returns the initial point and its loss, with zero gradient evaluations.
- Evaluate one gradient at the old point. Compute all coordinates before changing parameters. All three algorithms in this lesson use $\mathbf x_{t-1}$; there is no look-ahead probe.
- Update raw history. AdaGrad adds $\mathbf g_t^2$ to the cumulative accumulator. RMSProp and Adam decay the old second moment and add the weighted new square. Adam also updates the raw signed first moment.
- Advance Adam's powers and correct. On the first Adam update the powers become $\beta_1$ and $\beta_2$, not $1$. Form both corrected moments using $1-\beta_i^t$. The programs maintain powers by repeated multiplication in the same order in every language. Non-Adam modes keep dummy powers at $1$ and never use them in a bias division.
- Normalize and construct a candidate. Use the newly updated statistics. For each coordinate, take the square root of the selected nonnegative second statistic, add epsilon, divide the numerator, multiply that quotient by the learning rate, then subtract the signed correction from the old parameter.
- Commit, monitor, and record. Replace the whole parameter vector together, evaluate the new loss, and append copies of the state. The loss monitor does not accept, reject, or alter updates.
A row numbered $t\geq1$ records the committed $\mathbf x_t$ and its loss, together with the incoming $\mathbf g_t$ evaluated at $\mathbf x_{t-1}$. It also records raw first and second moments, numerator, the second statistic actually used, denominator, correction, and Adam's powers. The gradient field is not the residual at the new point.
There is no early exit on a zero gradient, a numerical no-op, or an increased objective. Adam can move with a zero current gradient because its first moment can remain nonzero. A production stopping test needs a stated residual and state policy; one zero sampled gradient is particularly weak evidence in stochastic training.
flowchart TD
A["Validate mode, shapes, numbers, and controls"] --> B["Copy parameters; zero moments; powers = 1; record starting loss"]
B --> C{"Completed K updates?"}
C -->|Yes| Z["Return final state, counters, and history"]
C -->|No| D["Evaluate one gradient at old parameters"]
D --> E{"AdaGrad?"}
E -->|Yes| F["Add squared gradient to accumulator"]
E -->|No| G["Exponential-average the squared gradient"]
F --> H{"Adam?"}
G --> H
H -->|Yes| I["Update signed first moment and powers; correct both moments"]
H -->|No| J["Numerator = current gradient; use raw second statistic"]
I --> L["Denominator = square root of second statistic plus epsilon"]
J --> L
L --> M["Divide numerator, multiply rate, subtract from old parameters"]
M --> N["Commit candidate; evaluate loss; copy state; count update"]
N --> C
Worked Example with a Full Trace
Use $f(x)=x^2/2$, $g=x$, start $x_0=2$, base rate $\eta=1$, epsilon $\epsilon=1$, $\beta_1=1/2$, $\beta_2=\rho=3/4$, and $K=3$. All raw moments start at zero. These deliberately simple coefficients and large teaching epsilon make the arithmetic visible; they are not recommended training settings. No random seed is needed because the gradients and starting point are deterministic.
Initially $f(2)=2$. Below, $s$ is the raw second statistic, $n$ the chosen numerator, $u$ the second statistic used for normalization, $D=\sqrt u+1$, and $\delta=n/D$ because $\eta=1$. The approximate values are rounded to ten decimal places. Exact fractions describe the real-arithmetic formulas; the programs apply the stated binary64 operation order and can differ in the last bits from exact fractions. Do not round the state between updates.
AdaGrad: keep the entire squared-gradient sum.
- Gradient $g_1=2$. Accumulator $s_1=0+2^2=4$. Thus $n_1=2$, $u_1=4$, $D_1=3$, $\delta_1=2/3$, $x_1=2-2/3=4/3$, and $f(x_1)=8/9$.
- Gradient $g_2=4/3$. Accumulator $s_2=4+16/9=52/9\approx5.7777777778$. Thus $n_2=4/3$, $u_2=s_2$, $D_2=1+2\sqrt{13}/3\approx3.4037008503$, and $\delta_2=4/(3+2\sqrt{13})\approx0.3917304699$. Therefore $x_2\approx0.9416028635$ and $f(x_2)\approx0.4433079763$.
- Gradient $g_3\approx0.9416028635$. Its square is approximately $0.8866159525$, so $s_3=s_2+g_3^2\approx6.6643937303$. With $n_3=g_3$ and $u_3=s_3$, the denominator is $D_3\approx3.5815487077$, the correction is $\delta_3\approx0.2629038275$, the new parameter is $x_3\approx0.6786990360$, and the loss is approximately $0.2303161907$.
AdaGrad keeps a dummy first moment of zero. It has no bias correction here.
RMSProp: decay the old square history.
- Gradient $g_1=2$. Second moment $s_1=(3/4)0+(1/4)4=1$. Thus $n_1=2$, $u_1=1$, $D_1=2$, $\delta_1=1$, $x_1=1$, and $f(x_1)=1/2$.
- Gradient $g_2=1$. Second moment $s_2=(3/4)1+(1/4)1=1$. Thus $n_2=1$, $u_2=1$, $D_2=2$, $\delta_2=1/2$, $x_2=1/2$, and $f(x_2)=1/8$.
- Gradient $g_3=1/2$. Its square is $1/4$, so $s_3=3/4+1/16=13/16=0.8125$. Thus $n_3=1/2$, $u_3=13/16$, $D_3=1+\sqrt{13}/4\approx1.9013878189$, and $\delta_3=2/(4+\sqrt{13})\approx0.2629658164$. The new parameter is $x_3\approx0.2370341836$, and the loss is approximately $0.0280926021$.
This uncentered RMSProp variant also keeps a dummy first moment of zero and applies no startup bias correction. Its second moment decreases on the third update.
Adam: combine and correct both moments.
- Gradient $g_1=2$. Raw moments $m_1=(1/2)2=1$ and $s_1=(1/4)4=1$. Powers are $p_1=1/2$, $p_2=3/4$. Hence $n_1=\widehat m_1=1/(1/2)=2$ and $u_1=\widehat s_1=1/(1/4)=4$. Thus $D_1=3$, $\delta_1=2/3$, $x_1=4/3$, and $f(x_1)=8/9$.
- Gradient $g_2=4/3$. Raw moments are $m_2=(1/2)1+(1/2)(4/3)=7/6$ and $s_2=(3/4)1+(1/4)(16/9)=43/36$. Powers are $p_1=1/4$ and $p_2=9/16$. Thus $n_2=(7/6)/(3/4)=14/9\approx1.5555555556$ and $u_2=(43/36)/(7/16)=172/63\approx2.7301587302$. The denominator is $D_2\approx2.6523191974$, the correction is $\delta_2\approx0.5864888197$, the new parameter is $x_2\approx0.7468445136$, and its loss is approximately $0.2788883638$.
- Gradient $g_3\approx0.7468445136$, with square approximately $0.5577767275$. Raw moments are $m_3=(1/2)m_2+(1/2)g_3\approx0.9567555902$ and $s_3=(3/4)s_2+(1/4)g_3^2\approx1.0352775152$. Powers are $p_1=1/8$ and $p_2=27/64$. Correcting gives $n_3=m_3/(7/8)\approx1.0934349602$ and $u_3=s_3/(37/64)\approx1.7907502966$. Therefore $D_3\approx2.3381891857$, $\delta_3\approx0.4676417832$, $x_3\approx0.2792027305$, and $f(x_3)\approx0.0389770823$.
Each run uses exactly three full-gradient evaluations and four scalar objective evaluations. RMSProp has the smallest final loss for these particular inputs. AdaGrad and Adam agree on step $1$ because Adam's corrected first-step moments are $g_1$ and $g_1^2$; their different history rules separate them afterwards. The finite comparison establishes neither a general ranking nor convergence.
Trace It by Hand
Before reading the result, predict the first update if the initial scalar parameter is already $x_0=0$, while all other controls and zero initial moments remain unchanged.
The gradient is $g_1=0$. Every raw moment remains zero. Adam's powers advance to $1/2$ and $3/4$, and its corrected moments are still zero. In all three modes, the denominator is $\epsilon=1$, the correction is zero, and $x_1=0$ with loss zero. There is no division by zero because epsilon is positive.
Now test the role of state with a different Adam run: restore $x_0=2$ but change $\eta$ to $3$. Step $1$ has the same moments and denominator as the main example, so its correction is $3(2/3)=2$ and it reaches $x_1=0$. On step $2$, $g_2=0$ but $m_2=1/2$, $s_2=3/4$, $\widehat m_2=2/3$, and $\widehat s_2=12/7$. The next correction is $2/(1+\sqrt{12/7})\gt0$, so the method leaves the minimizer. A zero current gradient is not the same as a zero Adam update.
Implementation in Python, C++, and Java
Each panel is complete and independently runnable. The shared callable is adaptive(initial, center, weights, rate, beta1, beta2, epsilon, steps, mode). It returns the last point, raw moments, powers, objective, counters, and snapshots. The history field second_used stores $u_t$; it is an uncentered second statistic, not a centered variance. For AdaGrad and RMSProp, first remains zero and both dummy powers remain one; those modes never divide by the dummy bias factors.
The teaching API accepts $1\leq d\leq32$, an integer $0\leq K\leq2000$, finite coordinates and centers of magnitude at most $10^4$, curvatures in $[0,10^4]$, $0\lt\eta\leq1000$, $0\leq\beta_1,\beta_2\lt1$, and $10^{-12}\leq\epsilon\leq1000$. These bounds keep the demonstration within a controlled numeric range; they are not a stability theorem or universal optimizer restrictions. The routine validates coefficients even in a mode that ignores them. Python rejects boolean numeric controls and requires an actual integer update count; C++ and Java express the count type in their signatures.
There is no general callback, mini-batch sampler, projection, weight decay, gradient clipping, schedule, momentum option for RMSProp, AMSGrad maximum, or stopping tolerance in these programs. The loop always runs the requested number of updates, including zero-gradient and numerical no-op updates. Changing the objective requires replacing both its value and gradient and revisiting numeric assumptions.
Run Python 3 with python main.py. Compile C++17 with g++ -std=c++17 -ffp-contract=off main.cpp -o adaptive and run ./adaptive; avoid fast-math so the stated arithmetic order is retained. Save Java as Main.java, use Java 17 or later, compile with javac Main.java, and run java Main. Java's decimal helper only formats stored binary64 values with eight decimal places and half-even rounding; the optimizer itself uses double arithmetic.
The three programs print the same parameter vector and objective at each iteration. Their sample is the full trace above. Counters count full gradient and scalar value calls, not individual coordinates. Initial history has no gradient because computing the starting objective does not request one. All returned snapshots own their arrays.
Expected standard output, identical in all three languages:
ADAGRADk=0 x=(2.00000000) f=2.00000000k=1 x=(1.33333333) f=0.88888889k=2 x=(0.94160286) f=0.44330798k=3 x=(0.67869904) f=0.23031619steps=3 gradients=3 losses=4RMSPROPk=0 x=(2.00000000) f=2.00000000k=1 x=(1.00000000) f=0.50000000k=2 x=(0.50000000) f=0.12500000k=3 x=(0.23703418) f=0.02809260steps=3 gradients=3 losses=4ADAMk=0 x=(2.00000000) f=2.00000000k=1 x=(1.33333333) f=0.88888889k=2 x=(0.74684451) f=0.27888836k=3 x=(0.27920273) f=0.03897708steps=3 gradients=3 losses=4
Complexity, Correctness, and Invariants
Let $d$ be the dimension and $K$ the requested number of updates. Each iteration evaluates one gradient, updates $O(d)$ optimizer state, performs $O(d)$ normalization and parameter work, evaluates one scalar objective for monitoring, and copies $O(d)$ data into history. For the diagonal quadratic, gradients and objective each cost $O(d)$.
There is no early exit, so best, average, and worst time are all $\Theta((K+1)d)$ for fixed valid $K,d$. The actual counts are $K$ gradient calls, $K+1$ value calls, and $K+1$ snapshots. A zero-step call still costs $O(d)$ to validate, copy, and evaluate the start. Optimizer state and temporary vectors need $O(d)$ working memory; retaining the full returned trace raises total storage to $\Theta((K+1)d)$. Streaming states instead of storing them reduces memory but not the work needed to produce them. The demonstration keeps a dummy first vector for non-Adam modes; a minimal implementation could omit it.
For another objective with gradient cost $C_g(d)$ and value cost $C_f(d)$, total time is $O(d+C_f(d)+K(C_g(d)+C_f(d)+d))$. Full-loss monitoring may cost a dataset pass even when training uses a small mini-batch, so it should be budgeted separately.
State invariant. After $t$ completed updates, the parameters equal the chosen recurrence's $\mathbf x_t$, and raw history summarizes the gradients already consumed. In exact arithmetic,
$$s_{t,j}=\sum_{i=1}^t g_{i,j}^2\quad\text{for AdaGrad},$$ $$s_{t,j}=(1-\rho)\sum_{i=1}^t\rho^{t-i}g_{i,j}^2\quad\text{for RMSProp},$$ $$m_{t,j}=(1-\beta_1)\sum_{i=1}^t\beta_1^{t-i}g_{i,j},\qquad s_{t,j}=(1-\beta_2)\sum_{i=1}^t\beta_2^{t-i}g_{i,j}^2\quad\text{for Adam}.$$
For Adam, the stored powers are $p_{1,t}=\beta_1^t$ and $p_{2,t}=\beta_2^t$. Expanding one recurrence step proves these sums by induction, with the empty sums zero initially. The powers begin at one; multiplying once per update produces the one-based correction factors. When a coefficient is zero, its update keeps only the new observation; interpret an exponent of zero as $1$ in the finite-sum expression, including when the coefficient itself is zero.
Implementation correctness. Assume the invariant before an update. The program evaluates the gradient using the old parameters, updates moments from that gradient once, and then selects exactly the defined raw or corrected statistics. Nonnegative square history and positive epsilon make every denominator positive in the mathematical recurrence. Subtracting the normalized correction from the frozen old point produces the next parameters. Committing the candidate, copying history, and incrementing counters preserves the invariant. In binary64 the invariant is the explicitly rounded sequence of operations; the exact weighted sums describe the corresponding real-arithmetic algorithm, not a promise that floating accumulation is exact.
Descent requires more than positive denominators. For AdaGrad or RMSProp on one quadratic coordinate with error $e_{t-1}=x_{t-1}-c$ and curvature $a\gt0$, define $\lambda_t=\eta/D_t$. Their current-gradient update gives
$$e_t=(1-a\lambda_t)e_{t-1}.$$
If $0\lt a\lambda_t\lt2$, this coordinate's squared error strictly decreases whenever its old error is nonzero. At $a\lambda_t=2$ its magnitude is unchanged; a larger value can increase the loss. This conditional calculation does not ensure that the chosen rate and evolving denominator satisfy the condition at every step. Positive normalization alone cannot guarantee finite-step descent. Adam uses $\widehat m_t$ in place of $g_t$, so the same scalar gradient-descent argument does not apply to it; the experiment card shows an update away from a minimizer.
AdaGrad's online convex regret guarantees require their stated loss, domain, and update assumptions. They should not be transferred automatically to this unconstrained fixed-horizon example. RMSProp and Adam also need suitable assumptions and parameter choices for convergence results. In particular, Reddi, Kale, and Kumar exhibit convex settings in which Adam fails to converge and introduce AMSGrad. An optimizer can implement the intended recurrence perfectly while failing to reach an optimum on a given problem.
| Method / reporting policy | Per-update cost beyond gradient evaluation | Persistent optimizer state excluding parameters | Main distinction / limit |
|---|---|---|---|
| Gradient descent / plain SGD | $O(d)$ | No moment vector | One common scalar rate; stochastic and full-gradient guarantees differ |
| Heavy-ball momentum | $O(d)$ | One $O(d)$ displacement or gradient buffer | Accumulates direction without adaptive squared-gradient normalization |
| Diagonal AdaGrad | $O(d)$ | One $O(d)$ squared-gradient accumulator | All gradient squares persist; the multiplier cannot increase |
| Uncentered RMSProp here | $O(d)$ | One $O(d)$ second-moment vector | Exponential forgetting; no momentum or bias correction |
| Adam here | $O(d)$ | Two $O(d)$ moment vectors plus scalar clock/powers | Corrects both zero-initialized moments; no universal convergence guarantee |
| Full-matrix adaptive scaling | Up to $O(d^2)$ updates plus matrix-function work | $O(d^2)$ | Captures cross-coordinate structure at a much larger cost |
| These programs with trace | $\Theta(d)$ arithmetic, loss monitoring, and copies for this quadratic | $\Theta((K+1)d)$ including history | Exactly $K$ updates; one gradient and one monitored value per update |
The invariant is the parameter vector together with its cumulative or exponentially weighted gradient history and, for Adam, the matching update clock. Induction proves the code consumes each gradient once and applies the specified normalization to the old parameters. This proves recurrence correctness and accounting; convergence, monotone loss, and the quality of a finite returned point require separate arguments.
Variants, Pitfalls, and Optimizations
Epsilon placement and square roots. The normalizer here is $\sqrt u+\epsilon$, not $u+\epsilon$ and not $\sqrt{u+\epsilon}$. On the first AdaGrad step, $u=4$ and $\epsilon=1$ give denominator $3$; placing epsilon inside the root instead gives $\sqrt5$. Both produce plausible decreasing losses on this example, so a loss-decrease smoke check would not catch the formula mismatch. With positive epsilon, exact gradient-scale invariance is also lost: rescaling a gradient and its square history leaves an additive epsilon term with a different relative size.
Correct both Adam moments at the correct time. Update index $1$ belongs to the first update. Using $t=0$ creates zero bias denominators. Correcting only the first moment, only the second, or neither defines a different rule. Retain raw moments in storage; do not feed corrected moments back into the raw recurrences. The denominator must use the newly updated second statistic, not yesterday's value. Repeated multiplication of the powers gives a specified floating-point convention; exponentiation or rearranged fused arithmetic can differ in the last bits.
Cumulative memory versus exponential forgetting. AdaGrad's squared sum never decreases, which can make later updates very small after a long history of nonzero gradients. RMSProp can recover larger multipliers as old large gradients fade. A coefficient close to one has a long effective memory, approximately $1/(1-\rho)$ updates for a second-moment average, and slow startup. The uncentered RMSProp variant here intentionally retains its zero-initialization effect; adding bias correction changes it. Centered RMSProp subtracts a squared moving mean from its second moment to estimate variance and requires an additional state convention and care with roundoff.
Sparse gradients and missing gradients. An explicit zero gradient still decays RMSProp and Adam moments; Adam's first moment can continue moving the parameter. Omitting the whole parameter update, as some systems do for a missing gradient, has different clock and state semantics. A dense implementation does not become $O(\operatorname{nnz}(\mathbf g))$ merely because many gradient entries are zero. Truly sparse or lazy implementations need to reproduce the intervening decay and parameter dynamics under their own specified semantics.
AMSGrad and other variants. AMSGrad adds a coordinatewise running maximum of second-moment estimates to retain longer memory in the denominator. A conventional raw-statistic definition is $\widetilde s_{t,j}=\max(\widetilde s_{t-1,j},s_{t,j})$; specify any bias-correction convention as well when comparing implementations. AdaMax uses a different norm-based scale. These are different optimizers, not flags silently enabled in this lesson.
Regularization and engineering. Coupled $L_2$ regularization adds its derivative to the gradient before the optimizer statistics are updated, so it also changes those statistics. Decoupled weight decay, as in AdamW, applies a separate parameter shrinkage and is not generally equivalent under adaptive normalization. Gradient clipping before the moment updates changes the statistics; clipping the final correction changes something else. State which operation you implement.
Save raw moments and the update counter or powers with model parameters for exact continuation. Resetting moments while retaining trained parameters restarts adaptation. In lower precision, squaring can overflow or underflow, epsilon can round away, and near-one coefficients can round to one. Maintain optimizer statistics in a suitable precision and check finite values in a general implementation. Fused kernels and reordered vector reductions can preserve the mathematical algorithm while changing bitwise reproducibility.
The reference programs make no automatic optimizer recommendation. Compare learning-rate schedules, gradient work, monitoring work, final and best losses, residuals, and held-out metrics under a stated experimental protocol. Feature scaling or preconditioning can still improve conditioning before applying any adaptive optimizer.
Use the square root of the newly updated second statistic, put epsilon in the declared location, and apply both Adam bias corrections starting at update one. Keep raw moments and their clock consistent across checkpoints. Small current gradients, small parameter motion, or a decreasing sample loss do not by themselves certify an optimum or a correct implementation.
Practice Problems and Real-World Use
Start by reproducing the trace, then change one convention at a time. For model training, compare optimizers under the same data order and gradient-work budget, tune their base rates fairly, and account for full-loss monitoring separately. A three-step result on one quadratic is an arithmetic example, not evidence of a generally superior optimizer.
Use deterministic gradients to test the update logic before adding mini-batch sampling or automatic differentiation. When comparing a framework implementation, match epsilon placement, accumulator initialization, bias correction, schedules, missing-gradient behavior, and decay conventions before expecting matching trajectories.
| Exercise or system | Concrete task / expected check | Idea tested | Difficulty |
|---|---|---|---|
| First-step comparison | Reproduce $x_1=4/3$, $1$, and $4/3$ for AdaGrad, RMSProp, and Adam | New statistics, normalization, startup correction | Beginner |
| Zero-gradient continuation | Start Adam at $2$ with $\eta=3$ and show it leaves zero on step $2$ | Current gradient versus retained first moment | Intermediate |
| Epsilon-placement audit | Compare $D=3$ and $D=\sqrt5$ on the first AdaGrad step | Both plausible losses can hide a formula mismatch | Beginner |
| Flat-coordinate quadratic | Use start $(7,3)$, center $(0,1)$, and curvatures $(0,2)$; check coordinate one remains $7$ from zero moments | Nonunique minimizers and inactive coordinates | Intermediate |
| Moment-history identity | Reconstruct every second statistic from its cumulative or weighted-sum formula | State invariant and history integrity | Intermediate |
| Sparse-feature model | Compare frequent and rare feature coordinates, counting skipped and explicit-zero updates separately | Adaptive scales and sparse-state semantics | Advanced |
| Checkpoint and resume | Save parameters, raw moments, and clock; match an uninterrupted run | Persistent optimizer state | Intermediate |
| AMSGrad experiment | Implement an explicitly defined running-maximum normalizer and compare on a documented difficult objective | Long memory and convergence assumptions | Advanced |