Type at least 2 letters. Enter opens the first result, Esc closes.

    learn.mohebi.ai

    Optimizer race

    Race seven optimizers on six classic loss surfaces. Drag the start point, change the learning rates and the β values, and see why momentum helps in a valley, why Adam rescales the coordinates, and what goes wrong when the step is too large.

    Training a model means finding parameters xx that make a loss f(x)f(x) small. An optimizer does this step by step: it looks at the gradient g=∇f(x)g = \nabla f(x), the direction in which ff grows fastest, and moves the parameters a little against it. How far it moves is set by the learning rate η\eta (the step size).

    Real models have millions of parameters, but the main effects are already visible with two. Here x=(x1,x2)x = (x_1, x_2), and the loss is drawn as a contour map: each line joins points with the same loss, like the height lines of a hiking map, and darker regions are lower. Seven optimizers start at the same point and race to the minimum:

    You will see where each one is fast, where it is slow, and where it fails. The update rules and the math behind the pictures are in section 3.

    1. The race #

    Interactive figure: loading…

    Press Play and watch the race; then drag the start point x0x_0 (the large dot) or tap the map. Colours: GD grey, momentum blue, Nesterov violet, AdaGrad yellow, RMSProp aqua, Adam orange and AdamW magenta (dashed). Stars mark the global minima. The table is the race board: click a name to switch that optimizer on or off, and move its slider to change its learning rate η\eta. The column finish is the first step tt with f(xt)−f⋆<10−4f(x_t) - f^\star < 10^{-4} (the dashed line in the chart), and the numbers 1, 2, 3 are the ranks. The chart shows the loss gap f(xt)−f⋆f(x_t) - f^\star on a log scale. The sliders at the bottom set the momentum β\beta, Adam's β1\beta_1 and β2\beta_2, RMSProp's ρ\rho and AdamW's weight decay λ\lambda for all optimizers that use them.

    The six surfaces:

    Surface f(x1,x2)f(x_1, x_2) What it tests
    Rosenbrock (1−x1)2+100 (x2−x12)2(1 - x_1)^2 + 100\,(x_2 - x_1^2)^2 a long, curved, flat valley; minimum (1,1)(1, 1)
    Beale ∑i=13(ci−x1+x1x2 i)2\sum_{i=1}^{3} (c_i - x_1 + x_1 x_2^{\,i})^2, c=(1.5,2.25,2.625)c = (1.5, 2.25, 2.625) flat plateaus next to steep walls; minimum (3,0.5)(3, 0.5)
    Saddle 12x12−12x22+14x24\tfrac12 x_1^2 - \tfrac12 x_2^2 + \tfrac14 x_2^4 a saddle point at (0,0)(0, 0); minima (0,±1)(0, \pm 1)
    Ill-conditioned 12(1κu2+w2)\tfrac12\big(\tfrac1\kappa u^2 + w^2\big), valley turned by the angle θ\theta the condition number κ\kappa; axis-aligned or tilted valley
    Noisy 12x⊤Ax\tfrac12 x^\top A x, with gradient noise of size σ\sigma stochastic gradients, as with minibatches
    Himmelblau (x12+x2−11)2+(x1+x22−7)2(x_1^2 + x_2 - 11)^2 + (x_1 + x_2^2 - 7)^2 four global minima; which one do you reach?

    2. Try this #

    1. Momentum in a curved valley. Use Rosenbrock with the default settings. GD, momentum and Nesterov all have the same learning rate η=10−3\eta = 10^{-3}. Question: which of them reach the finish line in 1500 steps? What to see: GD does not; after 1500 steps it is still in the valley near (0.60,0.36)(0.60, 0.36) with f−f⋆≈0.16f - f^\star \approx 0.16. Momentum finishes at t=836t = 836 and Nesterov at t=873t = 873. Now set β=0.5\beta = 0.5: momentum does not finish either. With β=0.95\beta = 0.95 it finishes at t=407t = 407. Along the valley the gradient keeps its direction, so momentum makes the step about 1/(1−β)1/(1 - \beta) times longer. Finally set AdamW's weight decay to λ=0.3\lambda = 0.3: AdamW now stops near (0.48,0.23)(0.48, 0.23), because the decay pulls it toward 00.
    2. Too large a step. Choose Ill-conditioned (κ=25\kappa = 25, θ=0°\theta = 0°). The side panel shows the largest stable learning rate of GD, momentum and Nesterov. Question: move GD's η\eta to 1.51.5, 22, 33. What happens? What to see: at 1.51.5 GD zigzags across the valley but still finishes (t=59t = 59). At exactly η=2\eta = 2 it jumps between two points forever. At η=3\eta = 3 the board shows diverged after 8 steps, and the curve in the chart goes up. Momentum diverges above 3.83.8 and Nesterov above 1.361.36 (with β=0.9\beta = 0.9). Adam does not blow up even with η=5\eta = 5 (it finishes at t=82t = 82): its steps are normalized. On Rosenbrock, the curvature near the start is about 15001500, so GD with η=0.007\eta = 0.007 diverges after 4 steps.
    3. Adam rescales the coordinates. Stay on Ill-conditioned and set κ=100\kappa = 100, θ=0°\theta = 0°. Question: compare GD and Adam, then turn the valley to θ=45°\theta = 45°. Who suffers from the turn? What to see: with the axis-aligned valley Adam finishes at t=68t = 68 and AdaGrad at t=26t = 26, while GD needs 290290 steps. Adam's number does not change when you raise κ\kappa. At 45°45° Adam and AdaGrad no longer finish in 400 steps. GD and momentum do not slow down: they even finish earlier (t=161t = 161 and t=85t = 85), only because the start point now has a smaller part along the flat direction. Their speed does not depend on the angle. Adam scales each coordinate; that fixes a valley along an axis, not a tilted one.
    4. Escaping a saddle point. Choose Saddle. The start is (−2,0.01)(-2, 0.01), very close to the line x2=0x_2 = 0 that leads into the saddle. Question: which optimizers leave the saddle first? What to see: the adaptive methods turn toward a minimum at once (∣x2∣>0.5|x_2| > 0.5 after 3 steps for AdaGrad and 11 steps for RMSProp and Adam). Momentum and Nesterov need about 40 steps. GD needs 83 steps and spends 33 of them near the saddle, where the gradient is tiny. Now drag the start point onto the axis x2=0x_2 = 0 (it snaps to steps of 0.010.01). All optimizers run into the saddle and stop there with f−f⋆=0.25f - f^\star = 0.25: the gradient has no x2x_2 part, so nothing pushes them off the line.
    5. Noise and the step size. Choose Noisy. Each optimizer sees the true gradient plus random noise. Question: set GD's η\eta to 0.20.2, then to 0.020.02. How do the cloud of points at the end and the mean column change? What to see: with η=0.05\eta = 0.05 the mean loss of the last 100 steps is about 0.020.02; with η=0.2\eta = 0.2 it is about 0.10.1; with η=0.02\eta = 0.02 it drops to about 0.010.01, but GD needs more steps to arrive. With a constant learning rate, the optimizer does not converge; it moves around in a noise ball whose size grows with η\eta. AdaGrad's steps shrink over time, so it usually ends lowest. Press New noise to see another random run, and set σ=0\sigma = 0 to remove the noise.
    6. Many minima. Choose Himmelblau. Question: from the default start (−1,−0.6)(-1, -0.6), which minimum does each optimizer reach? What to see: GD, momentum and Nesterov go down-left to star 3; AdaGrad, RMSProp, Adam and AdamW go up to star 2. The first steps of the adaptive methods follow the sign of the gradient, not the gradient itself, so they take another route. Move the start to (−1.2,−0.4)(-1.2, -0.4): GD still ends at star 3, but momentum and Nesterov now reach star 2. In a nonconvex problem the solution you get depends on the start and on the optimizer.

    3. The math behind the race #

    All seven optimizers use the gradient gt=∇f(xt−1)g_t = \nabla f(x_{t-1}) at step t=1,2,…t = 1, 2, \dots (on the noisy surface, gtg_t is the noisy gradient). The playground uses exactly the update rules of PyTorch's torch.optim. Squares, square roots and divisions of vectors act per coordinate.

    Gradient descent and the step size #

    Gradient descent (GD) moves against the gradient:

    xt=xt−1−η gt.x_t = x_{t-1} - \eta\, g_t .

    The clearest case is a quadratic f(x)=12x⊤Axf(x) = \tfrac12 x^\top A x with a symmetric positive definite AA. Its gradient is AxAx. Write xx in the eigenvectors of AA, the Hessian of ff (the matrix of second derivatives). Then each coordinate eie_i follows its own rule:

    ei←(1−ηλi) ei,e_i \leftarrow (1 - \eta\lambda_i)\, e_i ,

    where λi\lambda_i is the eigenvalue, the curvature in that direction. Call the largest eigenvalue LL and the smallest μ\mu. Every factor ∣1−ηλi∣|1 - \eta\lambda_i| must be below 1, so GD converges only if

    0<η<2L.0 < \eta < \frac{2}{L} .

    For a larger η\eta the steep direction overshoots more at every step and the iterates grow: this is divergence (experiment 2). But the flat direction shrinks only by the factor 1−ημ1 - \eta\mu, which is close to 1. With the best fixed step η=2/(μ+L)\eta = 2/(\mu + L) the error shrinks by (κ−1)/(κ+1)(\kappa - 1)/(\kappa + 1) per step, where κ=L/μ\kappa = L/\mu is the condition number. So the number of steps grows in proportion to κ\kappa. A long, narrow valley is a valley with a large κ\kappa. In the Ill-conditioned surface L=1L = 1 and μ=1/κ\mu = 1/\kappa.

    Momentum and Nesterov: memory along the valley #

    Momentum (Polyak's heavy-ball method) adds up the past gradients in a buffer bb:

    bt=β bt−1+gt,xt=xt−1−η bt,b0=0.b_t = \beta\, b_{t-1} + g_t, \qquad x_t = x_{t-1} - \eta\, b_t, \qquad b_0 = 0 .

    This is PyTorch's SGD(momentum=β). If the gradient stays the same, btb_t grows to g/(1−β)g/(1 - \beta): the effective step is η/(1−β)\eta/(1 - \beta), ten times larger for β=0.9\beta = 0.9. In a valley the gradient across the valley changes sign at every step, so these parts cancel in bb. The gradient along the valley keeps its sign, so it adds up. The method speeds up exactly where GD crawls (experiment 1). With the best η\eta and β\beta on a quadratic, the error shrinks by about (κ−1)/(κ+1)(\sqrt\kappa - 1)/(\sqrt\kappa + 1) per step: about κ\sqrt\kappa steps per digit of accuracy instead of κ\kappa.

    Nesterov's method takes the gradient at a look-ahead point, where momentum is about to carry the iterate: vt=βvt−1−η∇f(xt−1+βvt−1)v_t = \beta v_{t-1} - \eta \nabla f(x_{t-1} + \beta v_{t-1}), xt=xt−1+vtx_t = x_{t-1} + v_t. PyTorch (nesterov=True) writes the same method in the variables of the look-ahead point:

    bt=β bt−1+gt,xt=xt−1−η (gt+β bt).b_t = \beta\, b_{t-1} + g_t, \qquad x_t = x_{t-1} - \eta\,(g_t + \beta\, b_t) .

    On a quadratic with largest eigenvalue LL, the three methods are stable for

    GD: ηL<2,momentum: ηL<2(1+β),Nesterov: ηL<2(1+β)1+2β.\text{GD: } \eta L < 2, \qquad \text{momentum: } \eta L < 2(1 + \beta), \qquad \text{Nesterov: } \eta L < \frac{2(1+\beta)}{1 + 2\beta} .

    For β=0.9\beta = 0.9 this gives 3.83.8 and about 1.361.36: the limits in experiment 2.

    Quick check

    On the Ill-conditioned surface (L=1L = 1), GD uses η=1.9\eta = 1.9 and momentum uses η=0.5\eta = 0.5 with β=0.9\beta = 0.9. For a constant gradient, which one takes the larger effective step?

    Adaptive methods: one step size per coordinate #

    The adaptive methods divide each coordinate of the step by a running size of its own gradients.

    AdaGrad sums all squared gradients:

    Gt=Gt−1+gt2,xt=xt−1−η gtGt+ε.G_t = G_{t-1} + g_t^2, \qquad x_t = x_{t-1} - \eta\, \frac{g_t}{\sqrt{G_t} + \varepsilon} .

    RMSProp uses an exponential moving average instead, so old gradients are forgotten:

    st=ρ st−1+(1−ρ) gt2,xt=xt−1−η gtst+ε.s_t = \rho\, s_{t-1} + (1 - \rho)\, g_t^2, \qquad x_t = x_{t-1} - \eta\, \frac{g_t}{\sqrt{s_t} + \varepsilon} .

    Adam also keeps a moving average of the gradients (momentum) and corrects both averages for their start at zero (bias correction):

    mt=β1mt−1+(1−β1) gt,vt=β2vt−1+(1−β2) gt2,m_t = \beta_1 m_{t-1} + (1-\beta_1)\, g_t, \qquad v_t = \beta_2 v_{t-1} + (1-\beta_2)\, g_t^2,
    m^t=mt1−β1t,v^t=vt1−β2t,xt=xt−1−η m^tv^t+ε.\hat m_t = \frac{m_t}{1 - \beta_1^t}, \qquad \hat v_t = \frac{v_t}{1 - \beta_2^t}, \qquad x_t = x_{t-1} - \eta\, \frac{\hat m_t}{\sqrt{\hat v_t} + \varepsilon} .

    AdamW adds decoupled weight decay: xt=xt−1−η(m^t/(v^t+ε)+λxt−1)x_t = x_{t-1} - \eta\big(\hat m_t/(\sqrt{\hat v_t} + \varepsilon) + \lambda x_{t-1}\big). The term λx\lambda x pulls every coordinate toward 0. In a neural network this is a regularizer; on a 2D loss it moves the end point away from the minimum of ff (experiment 1). The playground uses ε=10−8\varepsilon = 10^{-8}, and all states start at zero.

    Why Adam rescales the coordinates. At the first step m^1=g1\hat m_1 = g_1 and v^1=g12\hat v_1 = g_1^2, so the step is η g1/(∣g1∣+ε)≈ηsign⁡(g1)\eta\, g_1/(|g_1| + \varepsilon) \approx \eta \operatorname{sign}(g_1): every coordinate moves by about η\eta, whether its gradient is 10001000 or 0.0010.001. Later, as long as a coordinate's gradient keeps a similar size, m^t/v^t\hat m_t/\sqrt{\hat v_t} stays of order one. So Adam acts like gradient descent with a diagonal preconditioner: a different step size for each coordinate. If the valley lies along an axis, this removes the bad scaling, and κ\kappa no longer matters. If the valley is tilted, a steep and a flat direction share each coordinate, and no per-coordinate scaling can separate them (experiment 3).

    Quick check

    At the first step, Adam (with η=0.01\eta = 0.01) sees the gradient g1=(1000, 0.001)g_1 = (1000,\ 0.001). How far does each coordinate move?

    What goes wrong with a too-large step #

    E [f(x)−f⋆]=∑i=1dησ22(2−ηλi)≈ησ2d4,\mathbb E\,[f(x) - f^\star] = \sum_{i=1}^{d} \frac{\eta\sigma^2}{2(2 - \eta\lambda_i)} \approx \frac{\eta\sigma^2 d}{4} ,

    which grows in proportion to η\eta (experiment 5). This is why training uses learning-rate schedules that lower η\eta at the end, and why AdaGrad, whose step shrinks like 1/t1/\sqrt t, ends low.

    Where the noise-ball formula comes from

    In the eigenvector coordinates of AA, one GD step with noise is e←(1−ηλ) e−ησξe \leftarrow (1 - \eta\lambda)\,e - \eta\sigma\xi with ξ∼N(0,1)\xi \sim \mathcal N(0, 1), independent at every step. If the variance VV of ee has settled, it is the same before and after the step: V=(1−ηλ)2V+η2σ2V = (1 - \eta\lambda)^2 V + \eta^2\sigma^2. So V=ησ2/(λ(2−ηλ))V = \eta\sigma^2 / \big(\lambda(2 - \eta\lambda)\big), and this direction adds 12λV=ησ2/(2(2−ηλ))\tfrac12 \lambda V = \eta\sigma^2 / \big(2(2 - \eta\lambda)\big) to the mean loss gap. In the playground (λ=0.25\lambda = 0.25 and 11, σ=1\sigma = 1, η=0.05\eta = 0.05) the formula gives 0.02540.0254; long simulations give 0.0250.025 as well.

    Quick check

    On the Noisy surface, GD with η=0.05\eta = 0.05 ends with a mean loss gap of about 0.0250.025. What do you expect with η=0.1\eta = 0.1, once the run has settled?

    4. Learn more #

    These lessons explain the ideas of this page in depth, with proofs, worked examples and notebooks: