skip to content

In AdaGrad, how does the running sum of squared gradients set each parameter's step size?

level: middleimportance: must knowfreq 58%

answer

  1. Every parameter gets its own denominator
  2. Squares add up, never subtract
  3. Divide the learning rate by root of history
  4. Constant gradient gives one-over-root-t

basics

~20 s

AdaGrad divides the global learning rate by the square root of each parameter's own accumulated squared gradients. Consistently large gradients get small steps, quiet parameters keep large ones, and because the sum only grows, every step shrinks over time.

solid answer

~40 s

AdaGrad keeps one accumulator per parameter and updates it elementwise as `acc <- acc + g*g`, then steps with `w <- w - lr * g / (sqrt(acc) + tiny)`. The effective learning rate of a parameter is therefore `lr` divided by the square root of everything that parameter has ever seen, which makes the step scale-free: if a gradient is roughly constant at `c`, then `acc` is about `t*c^2` and the step is `lr/sqrt(t)` no matter whether `c` was 1.0 or 0.001. That is the point of the method — parameters with wildly different gradient scales travel comparable distances per step, so one global learning rate stops being a compromise. The catch is that a sum of squares never decreases, so every parameter's step size is monotonically non-increasing for the whole run.

code

python · 13 lines
python
import math

# Two parameters, same global step size, gradients three orders of magnitude apart.
lr, tiny = 0.1, 1e-8
acc = {"quiet": 0.0, "loud": 0.0}
grad = {"quiet": 0.001, "loud": 1.0}

for t in range(1, 5):
    for name in ("quiet", "loud"):
        g = grad[name]
        acc[name] += g * g                       # a SUM of squares: never shrinks
        step = lr * g / (math.sqrt(acc[name]) + tiny)   # tiny only guards the division
        print(t, name, "acc=%.6f" % acc[name], "step=%.4f" % step)

go deeper

for a junior

Be ready to state the rule in words: each parameter's step is the global learning rate divided by the square root of that parameter's own accumulated squared gradients, and the accumulation is elementwise.

for a middle

Expect to derive it. Show that with a roughly constant gradient the step becomes the learning rate over the square root of the step count, identical for a large-gradient and a small-gradient parameter, and explain why the accumulator can only grow.

for a senior

Demonstrate that you can read a flat loss curve correctly: check whether gradient norms are still large before declaring convergence, and know that a shrinking effective step is the usual cause on long runs.

for a principal

Own the call between letting the optimizer set step sizes from gradient history and keeping that decision explicit and tunable. Be able to say which you would standardise on for a team's long runs and what evidence would change your mind.

## What the rule is AdaGrad keeps one extra number per parameter: an accumulator that sums that parameter's squared gradients over the entire run. Writing `g` for the current gradient of a single parameter `w`, `lr` for the global learning rate, and `acc` for that parameter's accumulator (initialised to zero): ``` acc <- acc + g * g w <- w - lr * g / (sqrt(acc) + tiny) ``` Every operation is elementwise, so `acc` has exactly the same shape as the parameter tensor and no parameter's history mixes with any other's. `tiny` is a small positive constant that only exists so the first division is safe. The quantity `lr / (sqrt(acc) + tiny)` is what people mean by the *effective learning rate* of that parameter: the global `lr` is a ceiling, and each parameter's own gradient history divides it down. ## Why it is called a per-parameter learning rate The problem AdaGrad attacks is that a single global step size has to be chosen for the worst case. In a real network, parameters differ enormously in the scale of the gradients they see: an input feature measured in millions versus one in fractions, a weight in an early layer versus a late one, a row of an embedding matrix touched by every example versus one touched rarely. With one `lr`, the value has to be small enough not to blow up the largest-gradient parameter, which leaves every small-gradient parameter crawling. Dividing by `sqrt(acc)` fixes this in a strong sense. Suppose a parameter's gradient is roughly a constant `c` on every step. After `t` steps `acc = t * c^2`, so `sqrt(acc) = |c| * sqrt(t)`, and the update becomes ``` lr * c / (|c| * sqrt(t)) = sign(c) * lr / sqrt(t) ``` The gradient's magnitude has cancelled. A parameter with a gradient of 1.0 and one with a gradient of 0.001 move the *same distance* on step `t`. Only the sign survives from the gradient itself. That is the property being bought: AdaGrad equalises the distance travelled per step, not the gradient. The global `lr` is therefore closer to "how far should a parameter move per step" than to "how much should I trust this gradient". ## Why the step only ever shrinks `acc` is a sum of squares, so it is non-decreasing by construction — no gradient value can ever reduce it. That makes the effective learning rate of every parameter non-increasing for the entire run. This is deliberate, not an accident: it is a self-tuning anneal driven by observed gradients rather than by wall-clock progress, and in convex online learning it is exactly what gives AdaGrad its regret guarantee. In a deep, non-convex run it is also the method's main weakness. The `lr / sqrt(t)` decay derived above applies to any parameter whose gradient does not die down, which is most of them on a hard problem. Around a few tens of thousands of steps, the step size has been divided by a couple of hundred, and the loss curve goes flat. Note what is *not* true: the total distance is `sum over t of 1/sqrt(t)`, which grows like `2 * sqrt(T)` and diverges, so the parameters are not frozen — training has become asymptotically slow, not stopped. That distinction gives the diagnostic. Genuine convergence means small gradients: the loss is flat because there is nothing left to descend. A stalled AdaGrad run has a flat loss and gradient norms that are still substantial — the slope is there and the optimizer can no longer walk down it. If you suspect the second, look at gradient magnitudes before you conclude the model has finished. ## Which parameters keep their step size Because `acc` sums only that parameter's own gradients, a parameter that receives a gradient of zero contributes nothing on that step and its effective learning rate is unchanged. Parameters that are active on every batch with large gradients are damped hardest and fastest; quiet or rarely active parameters keep a small denominator and therefore a large step for far longer. This is the frequency-equalising behaviour that made AdaGrad the natural choice for models with many rarely seen parameters. ## What to remember Three claims cover almost every interview answer on AdaGrad. First, the denominator is that parameter's own accumulated squared gradients, which makes the step size scale-free. Second, because the accumulator is a sum, the effective learning rate is monotonically non-increasing for the whole run. Third, that monotonicity is a feature in a convex online setting and a liability in a long deep-learning run, where it produces a flat loss curve with a live gradient. Replacing the sum with a forgetful average is the standard fix, and it is what RMSProp does.

  • If the step decays like one over the square root of t, does training actually stop?
    No, it becomes asymptotically slow. Summing `1/sqrt(t)` over t steps grows like `2*sqrt(T)`, so the total distance a parameter can travel still diverges. What you observe is a loss curve going flat while the gradient is still substantial, not parameters pinned in place.
  • How would you tell a stalled AdaGrad run from a genuinely converged one?
    Look at gradient magnitudes, not the loss curve. Real convergence means the gradients have shrunk, so there is nothing left to descend. A stalled AdaGrad run shows a flat loss with gradient norms that have not collapsed: the slope is still there and the effective step size has become too small to walk down it.
  • Does AdaGrad remove the need to choose a global learning rate?
    No. The global rate multiplies every update, and because the normalized direction has magnitude near one early on, it effectively sets how far a parameter moves on its first steps. AdaGrad reduces sensitivity to that choice and to badly scaled features, but a value that is far too large still diverges.

It is a water meter on every tap. The more a tap has ever run, the tighter the optimizer screws that particular valve, so taps that have barely been used keep their full pressure.

saying these in an interview costs you the question

  • Says parameters with larger gradients get larger steps
  • Describes it as one global schedule rather than per-parameter state
  • Claims the accumulator shrinks when gradients get small
  • Reads a flat AdaGrad loss curve as convergence without checking gradients
  • Thinks the accumulator stores gradients rather than their squares

context