Why does an exponential moving average of a network's weights often evaluate better than the live weights?
answer
- the run orbits, it does not land
- average the parameters, not the gradients
- shadow copy, updated every step
- window is about one over one-minus-decay
- gains vanish once the rate decays to zero
basics
~20 sLate in training the weights bounce inside a noise ball around a minimum rather than sitting in it. An exponential moving average cancels much of that mini-batch noise, so the averaged copy lands nearer the basin's centre and generalizes better.
solid answer
~60 sWith a non-trivial learning rate, stochastic gradient descent does not converge to a point - it keeps taking noisy steps and orbits the minimum, so any single iterate carries a random offset. Averaging the iterates cancels much of that offset: keep a shadow copy updated as `shadow <- d * shadow + (1 - d) * w` after each step, with a decay `d` such as 0.999, which weights roughly the last `1 / (1 - d)` steps. Polyak averaging is the uniform version of the same idea, and stochastic weight averaging is the coarse version - an equal-weight average of a handful of checkpoints harvested at the ends of cycles of a high or cyclical rate. On a speech recognizer trained this way the shadow copy typically scores better than the live weights at every evaluation, and the gap is largest when the rate is still high. The shadow is an observer only: gradients are still computed on the live weights, so averaging never changes the optimizer's trajectory.
code
python · 16 linesimport random
random.seed(0)
true_w, live, shadow, decay = 1.0, 1.0, 1.0, 0.99
live_err = shadow_err = 0.0
for step in range(1, 20001):
# one noisy step: pulled toward the optimum, kicked by mini-batch noise
live = live - 0.1 * (live - true_w) + random.gauss(0, 0.05)
shadow = decay * shadow + (1 - decay) * live
if step > 10000: # measure only after both have settled
live_err += (live - true_w) ** 2
shadow_err += (shadow - true_w) ** 2
print('live iterate ', round(live_err / 10000, 5))
print('averaged copy ', round(shadow_err / 10000, 5))go deeper
Know that you can keep a smoothed second copy of the weights during training and that it often scores better on validation than the weights you stopped on. Be able to say it costs nothing extra at inference.
Be ready to write the update rule, state that the effective window is about one over one-minus-decay, and explain the noise-cancellation argument for why the average beats a single iterate.
Show you have operated it: where in the budget you start averaging, how you validate the averaged copy separately, and how the benefit interacts with the learning-rate schedule you are already running.
Own the framing that this buys generalization for zero training and zero inference cost, and set the expectation that the gain is small and must be measured against seed-to-seed variance before it goes into a standard recipe.
## What is being averaged Weight averaging averages the **parameters** produced at successive training steps - not gradients, and not predictions. You keep a second copy of the parameter vector, usually called the shadow or averaged copy, and after every optimizer step you fold the current weights into it. Two forms are standard. **Uniform (Polyak) averaging** takes the plain mean of the iterates over some window: `w_bar = (1/T) * sum_t w_t`. This is the classical form, and the classical result is a variance-reduction claim: for a suitably decaying step size the averaged iterate converges at a better asymptotic rate than the last iterate, because the random component of each step is independent enough to partly cancel. **Exponential moving average (EMA)** is the practical form: `shadow <- d * shadow + (1 - d) * w` after each step, with `d` close to 1. The weight given to a step `k` updates ago is proportional to `d^k`, so the effective averaging window is about `1 / (1 - d)` steps: 100 at `d = 0.99`, 1,000 at `d = 0.999`, 10,000 at `d = 0.9999`. Initialize the shadow to the current weights rather than to zero, otherwise the early shadow is dragged toward zero and needs a bias correction. **Stochastic weight averaging (SWA)** is the coarse-grained cousin: instead of every step, you collect one checkpoint at the end of each cycle of a cyclical or high constant learning rate over the last portion of a run - say eight checkpoints over the last quarter of a 200-class fine-grained bird-species run - and take their **equal-weight** mean. Fewer points, spread much further apart. ## Why the average is better than any single iterate A mini-batch gradient is a noisy estimate of the full-data gradient. With a fixed, non-tiny learning rate, the parameter sequence does not settle onto a point; it reaches a stationary distribution and wanders inside a region around the minimum whose radius grows with the learning rate and with the gradient noise. Each iterate is therefore `minimum + offset`, where the offset is a random draw. Averaging many iterates averages many offsets, and because they are only weakly correlated across a long enough window, the average of the offsets is much smaller than a typical single offset. The averaged parameter vector sits nearer the centre of the region the run was orbiting, and that point usually has lower loss on held-out data than the point the run happened to stop at. A second, purely practical effect: the averaged copy is far less jumpy across evaluations. Validation curves computed on the shadow copy are smooth, which makes model selection and early-stopping decisions much less noisy than validation on the live weights. ## Choosing the decay and the window The decay is the one real knob, and it is a bias-variance trade in time. Too small a decay (short window) and you barely reduce the noise. Too large a decay and the shadow is a stale mixture that still contains weights from a much earlier, much worse stage of training - on a short run an EMA at `d = 0.9999` may never catch up before training ends. Two rules of thumb: size the window so it is a meaningful fraction of the steps you plan to spend at the current learning rate, and start averaging only after the run has stopped making large systematic progress, typically in the last quarter of the budget. Averaging from step zero at a high decay is the classic way to ship a model that is worse than the baseline. The averaging and the schedule interact. If the learning rate is annealed all the way to near zero, the iterates stop moving and the average converges to the same point as the live weights - there is nothing left to cancel, so the gain shrinks toward nothing. This is why SWA deliberately keeps the rate high or cyclical: it wants the iterates spread out so the mean of them is a genuinely different point. ## What it is not Averaging weights is not ensembling. An ensemble keeps several models and averages their **outputs**, paying several forward passes at inference; weight averaging produces one parameter vector and costs exactly one forward pass. It is also not momentum: momentum averages **gradients** into a velocity and feeds that back into the update, changing the trajectory. The weight average is write-only - it never feeds back, so it cannot destabilize training and can always be evaluated alongside the live weights and discarded if it loses. Finally, one operational caveat that catches teams: if the network contains layers that carry running statistics estimated during training, the averaged weight vector has no matching statistics, and evaluating it without recomputing them can look catastrophic. Always evaluate the averaged copy through the same procedure you will ship.
- How do you choose the decay coefficient, and what does it trade off?The effective window is about `1 / (1 - d)` steps, so the decay is really a window length. A short window barely reduces the noise; a long one makes the shadow a stale mixture that still carries weights from a much worse stage, and on a short run it may never catch up. Size the window as a fraction of the steps you will spend at the current learning rate, and start averaging in the last part of the budget rather than from step zero.
- Does the averaged copy feed back into training?No. Gradients are computed on the live weights and the optimizer updates only those; the shadow copy is write-only. That is what makes averaging safe to bolt onto any run - it cannot destabilize the trajectory, and if the averaged copy loses on validation you simply ship the live weights. Feeding it back would change the dynamics into something quite different from plain averaging.
- How does stochastic weight averaging differ from an exponential moving average?Same idea at a different granularity. SWA takes an equal-weight mean of a handful of checkpoints collected at the ends of cycles of a high or cyclical rate over the last part of training; an EMA takes a geometrically weighted mean over every step. SWA needs the rate to stay high so the checkpoints are spread apart and their mean is a genuinely different point; an EMA works under a decaying schedule too, though its benefit shrinks as the rate approaches zero.
- Why does the benefit shrink when the learning rate is annealed to near zero?The gain comes from cancelling the random offset in each iterate, and the size of that offset scales with the learning rate. Once the rate is tiny the weights barely move, every iterate in the window is nearly the same point, and the average converges to the live weights. Nothing is left to cancel, so the averaged copy and the live copy score the same.
A hand-held camera shakes; each frame is off-centre in a random direction. Stacking a hundred frames gives a sharper picture than picking the single frame you happened to stop on.
saying these in an interview costs you the question
- Says the averaged weights are fed back into the update
- Confuses averaging weights with momentum averaging gradients
- Calls it an ensemble of independently trained models
- Starts averaging at step zero with a very high decay
- Claims it helps just as much after the rate decays to zero
- Thinks it needs extra forward passes at inference