Optimization2011intermediate12 min read

Adaptive Subgradient Methods for Online Learning and Stochastic Optimization

أساليب التدرّج الفرعي التكيُّفية للتعلّم عبر الإنترنت والأمثَلة العشوائية

Duchi, J. · Hazan, E. · Singer, Y. — JMLR

The problem

Standard uses a single for all parameters. In problems with sparse features — natural language, recommender systems, click-through prediction — some features appear in nearly every example while others show up once in a million. A learning rate tuned for the frequent features is far too small for the rare ones, and vice versa. Practitioners spent enormous effort hand-tuning learning rate schedules per group, with no theoretical guidance.

The contribution

AdaGrad: an that gives every its own adaptive learning rate. It accumulates the sum of squared past gradients for each parameter; parameters with large accumulated gradients get smaller steps, parameters with small or rare gradients get larger steps. This is equivalent to preconditioning the by the inverse square root of a diagonal approximation to the empirical Fisher information matrix. The method comes with provable regret bounds that match the best fixed learning rate chosen in hindsight, and it requires essentially no learning rate tuning.

The impact

AdaGrad opened the era of adaptive optimizers. fixed the monotonic decay by using an exponential moving average; combined that fix with and became the default optimizer for . Shampoo extended AdaGrad's full-matrix version to practical scales. Every modern adaptive optimizer traces its lineage to AdaGrad's core insight: let the gradient history shape the geometry of the update step.

Standard is like a coach who shouts the same instruction to every player on the field — "run faster!" — regardless of whether they're a sprinter who's been running all game or a goalkeeper who just got the ball for the first time.

AdaGrad replaces that one-size-fits-all coaching with a personal diary for each player. The sprinter's diary is full of entries, so the coach whispers "small adjustments now." The goalkeeper's diary is nearly empty, so the coach shouts "big move — make it count!"

The result: every player gets exactly the guidance their history warrants. Frequent features converge smoothly; rare but informative features catch up fast.

The problem: one learning rate cannot serve all parameters

In standard SGD, every parameter gets the same learning rate η\eta. The update rule is simple:

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

where gtg_t is the gradient at step tt. This works well when all parameters receive gradients of similar magnitude at similar frequencies. But in the real world, parameters live very different lives.

Consider a language learning word embeddings. The word "the" appears in almost every sentence — its gets updated thousands of times. The word "serendipity" might appear once in the entire corpus. With a shared learning rate, you face a dilemma: set η\eta high enough for "serendipity" to learn, and "the" oscillates wildly. Set it low enough for stability, and "serendipity" barely moves.

This is the sparse feature problem: in NLP, recommender systems, and click-through prediction, most of the predictive power lives in rare features — but a uniform learning rate starves them of the updates they need.

Open in Lab
Watch how SGD gives the same step size to frequent and rare features. The rare feature barely moves while the frequent one oscillates.
The demo wakes as you arrive…

The insight: let gradient history shape the step size

AdaGrad's core idea is deceptively simple: keep a running sum of squared gradients for each parameter, then divide the learning rate by the square root of that sum.

Think of the squared gradient sum as a "mileage counter" for each parameter. A parameter that has been updated many times with large gradients has high mileage — it's already close to where it needs to be, so take smaller steps. A parameter with low mileage has barely been explored — give it a bigger step to compensate.

This is the essence of per-parameter adaptive learning rates: instead of one global η\eta, each parameter effectively has its own ηi\eta_i that shrinks in proportion to how much gradient signal it has accumulated.

The algorithm: three lines that changed optimization

Before we see symbols, let's trace the logic step by step. At each training step, AdaGrad does three things:

Step 1 — Compute the gradient for each parameter, exactly as in standard SGD.

Step 2 — Accumulate: add the square of each gradient component to a running sum. This sum grows monotonically — it never forgets.

Step 3 — Scale the update: divide the gradient by the square root of the accumulated sum, then multiply by the global learning rate. Parameters with large accumulated gradients get smaller effective learning rates; parameters with small accumulated gradients get larger ones.

Gt=Gt−1+gt⊙gtG_{t} = G_{t-1} + g_t \odot g_t
Step 2: Accumulate squared gradients — G_t is a vector (one entry per parameter) that accumulates the element-wise square of every gradient seen so far. The ⊙ denotes element-wise multiplication. This is the "diary" — it remembers how much gradient signal each parameter has received.
θt+1=θt−ηGt+ϵ⊙gt\theta_{t+1} = \theta_t - \frac{\eta}{\sqrt{G_t + \epsilon}} \odot g_t
Step 3: Scaled update — the AdaGrad rule — AdaGrad automatically adapts the learning rate for each parameter based on its training history. Parameters that have already received many large updates become more conservative over time, while parameters that are updated only rarely continue to learn aggressively. This makes AdaGrad particularly effective for sparse data, where some features appear very frequently and others only occasionally.

Notice what happened: we started with a single global learning rate η\eta and ended with a different effective learning rate η/Gt,i\eta / \sqrt{G_{t,i}} for every parameter ii. The adaptation is fully automatic — no manual schedule, no per-feature tuning. The only that matters is the initial η\eta, and AdaGrad is far more robust to its choice than SGD.

Open in Lab
Watch how the accumulated gradient grows for frequent vs rare features, and how the effective learning rate changes accordingly.
The demo wakes as you arrive…

Geometric view: reshaping the loss landscape

There's a deeper way to understand AdaGrad. Standard SGD takes the gradient and steps in that direction with a fixed stride. Geometrically, it treats the surface as if it curves equally in all directions — using the identity matrix as a .

But real loss surfaces are anisotropic: they curve steeply in some directions and gently in others. Think of a long, narrow valley — SGD bounces off the steep walls while crawling along the valley floor.

AdaGrad implicitly builds a diagonal approximation to the curvature from gradient history. The accumulated GtG_t acts like the diagonal of the : large values mean the surface curves sharply there (take shorter steps), small values mean it's flat (take longer steps). The effect is to stretch the narrow valley into a rounder bowl where gradient descent converges naturally.

The full-matrix version of AdaGrad uses the outer product ∑gtgtT\sum g_t g_t^T as a preconditioner — a complete second-order approximation. The diagonal version approximates this with just the diagonal, trading some accuracy for O(d)O(d) memory instead of O(d2)O(d^2).

Open in Lab
Left: SGD bounces in the narrow valley. Right: AdaGrad rescales, stretching the valley into a rounder bowl where convergence is smooth.
The demo wakes as you arrive…

Why it shines: sparse features get their due

The magic of AdaGrad becomes clearest with sparse data. Consider a feature that appears in only 0.1% of training examples. In standard SGD, its gradient is zero 99.9% of the time, so its accumulated Gt,iG_{t,i} stays small. When the feature finally does appear and produces a nonzero gradient, AdaGrad divides by a small number — giving it a large effective learning rate. The rare feature gets a proportionally larger update, compensating for its infrequent appearances.

Conversely, a feature present in every example accumulates a large Gt,iG_{t,i} quickly. Its effective learning rate drops, preventing the oscillation that would occur with a fixed high learning rate.

This is why AdaGrad was transformative for NLP and recommender systems: these domains are dominated by sparse, high-dimensional feature spaces where the frequency of features varies by orders of magnitude.

Open in Lab
Simulate sparse vs dense features. Notice how AdaGrad automatically assigns larger effective learning rates to the sparse features.
The demo wakes as you arrive…

The limitation: a learning rate that only shrinks

AdaGrad has a critical drawback: the accumulated sum GtG_t only grows. Every step adds a non-negative value to it, so the effective learning rate η/Gt\eta / \sqrt{G_t} can only decrease — it never recovers. In convex this is fine: as you approach the minimum, you want to take smaller and smaller steps. In non-convex deep learning, however, the loss landscape has many regions, and a learning rate that decayed in a plateau region can't speed back up when it enters a steep descent.

After many iterations, the effective learning rate can become so small that the model essentially stops learning — the updates become negligible. This is why AdaGrad on its own struggles with deep neural networks that require thousands of epochs.

Open in Lab
Compare how the effective learning rate evolves in AdaGrad vs RMSProp over training steps. AdaGrad's rate drops to near-zero; RMSProp stabilizes.
The demo wakes as you arrive…

The same idea in code

AdaGrad optimizer, completepython

Simplified to show the idea — not the real implementation.

import numpy as np

class AdaGrad:
    """Per-parameter adaptive learning rate optimizer."""

    def __init__(self, lr=0.01, eps=1e-8):
        self.lr = lr          # global learning rate (the only hyperparameter that matters)
        self.eps = eps        # small constant to avoid division by zero
        self.cache = {}       # accumulated squared gradients per parameter

    def step(self, params, grads):
        """Update params in-place given their gradients."""
        for name in params:
            g = grads[name]

            # First time seeing this parameter? Initialize its diary to zeros
            if name not in self.cache:
                self.cache[name] = np.zeros_like(g)

            # Step 2: accumulate squared gradients (the diary grows, never shrinks)
            self.cache[name] += g ** 2

            # Step 3: scale each gradient component by its accumulated history
            params[name] -= self.lr * g / (np.sqrt(self.cache[name]) + self.eps)

# That's it. Three lines of math, one powerful idea:
# frequent-gradient parameters → large cache → small step
# rare-gradient parameters    → small cache → large step

Full-matrix AdaGrad: the ideal we approximate

The paper actually proposes a more general form: instead of tracking only the diagonal, accumulate the full outer product of gradients:

Ht=∑τ=1tgτgτTH_t = \sum_{\tau=1}^{t} g_\tau g_\tau^T

The update then uses Ht1/2H_t^{1/2} (the matrix square root) as the preconditioner:

θt+1=θt−η Ht−1/2 gt\theta_{t+1} = \theta_t - \eta \, H_t^{-1/2} \, g_t

This is the optimal preconditioning — it captures correlations between parameters, not just individual magnitudes. But computing and inverting a d×dd \times d matrix every step is infeasible for millions of parameters. The diagonal version Gt=diag(Ht)G_t = \text{diag}(H_t) is the practical compromise that became "AdaGrad" in common usage.

The full-matrix idea didn't die, though — it re-emerged years later in Shampoo, which finds efficient ways to approximate the full preconditioner for large-scale training.

Theoretical guarantee: regret that matches hindsight

AdaGrad comes with a formal guarantee from theory. The regret measures how much worse the performs compared to the single best fixed decision in hindsight. For standard SGD with a fixed learning rate, the regret bound depends on knowing the scale of the gradients in advance. Pick a rate too high and regret explodes; too low and is slow.

AdaGrad's regret bound is:

R(T)≤2∑i=1d∥g1:T,i∥2R(T) \leq 2 \sum_{i=1}^{d} \|g_{1:T,i}\|_2

where ∥g1:T,i∥2=∑t=1Tgt,i2\|g_{1:T,i}\|_2 = \sqrt{\sum_{t=1}^{T} g_{t,i}^2} is the cumulative gradient for parameter ii. This bound is dimension-dependent but data-adaptive: if most parameters have small gradients (sparse data), the bound is small even in very high dimensions. For sparse problems, this can be dramatically better than SGD's O(T)O(\sqrt{T}) bound.

In plain language: AdaGrad is provably as good as the best learning rate schedule you could have chosen if you could see the future.

The family tree: from AdaGrad to modern optimizers

AdaGrad planted the seed, but its descendants grew taller:

RMSProp (Hinton, 2012) — Replaced the ever-growing sum with an exponential moving average of squared gradients: vt=βvt−1+(1−β)gt2v_t = \beta v_{t-1} + (1-\beta) g_t^2. This "forgets" old gradients, so the learning rate stabilizes instead of decaying to zero. Think of it as AdaGrad with a leaky memory.

AdaDelta (Zeiler, 2012) — Similar to RMSProp but also adapts the numerator using an exponential average of squared parameter updates, eliminating the global learning rate entirely.

Adam (Kingma & Ba, 2014) — Combined RMSProp's adaptive second moment with momentum's first-moment moving average, plus bias correction for the initial steps. Became the default optimizer for deep learning.

Shampoo (Gupta et al., 2018) — Resurrected AdaGrad's full-matrix preconditioner, making it practical for large-scale training through Kronecker-product approximations.

Every one of these is a direct answer to AdaGrad's question: how should gradient history shape the update step? They differ only in how much history to remember and how to structure the preconditioner.

Open in Lab
Click each optimizer to see how it modifies AdaGrad's core idea.
The demo wakes as you arrive…

Timeline: how one idea reshaped optimization

  1. 2011

    AdaGrad

    Duchi, Hazan, and Singer introduce per-parameter adaptive learning rates based on accumulated squared gradients. Dominates sparse NLP tasks immediately.

  2. 2012

    RMSProp

    Hinton proposes replacing the cumulative sum with an exponential moving average, preventing the learning rate from decaying to zero in deep networks.

  3. 2012

    AdaDelta

    Zeiler eliminates the global learning rate hyperparameter entirely by using a ratio of running averages. No manual tuning needed at all.

  4. 2014

    Adam

    Kingma and Ba combine RMSProp's second moment with momentum's first moment, plus bias correction. Becomes the default optimizer for deep learning.

  5. 2018

    Shampoo

    Gupta et al. revive AdaGrad's full-matrix preconditioner using Kronecker-product factorizations, making it practical for large-scale training.

Practical guide: when to use AdaGrad today

Despite its age, AdaGrad remains the right choice in specific settings:

Use AdaGrad when your features are sparse and high-dimensional — click-through rate prediction, large-vocabulary NLP, recommender systems with millions of items. The automatic per-feature adaptation handles the frequency imbalance perfectly, and the monotonic decay is actually a feature: in convex problems, it guarantees convergence.

Use Adam or RMSProp instead when training deep neural networks on dense data (images, audio, continuous signals). The non-convex landscape benefits from a learning rate that can recover after flat regions.

The key intuition: if your problem has features that vary wildly in frequency and your loss is (approximately) convex, AdaGrad is still hard to beat. If you're in deep learning territory with dense gradients and non-convex loss, reach for Adam.

CitationDuchi, Hazan, Singer. Adaptive Subgradient Methods for Online Learning and Stochastic Optimization. JMLR, 2011.

Terms in this paper