09

Training well: batches, epochs, and optimizers

Which training decisions separate a network that learns from one that does not?

12 min

Training well: batches, epochs, and optimizers

With voice. You can pause and jump to any step.

Lesson complete

Subtitles

In lesson eight we corrected the weights using a single example. A real model learns from millions. How many examples should it look at before each step?

Voice

No voice: the lesson advances on its own with subtitles.

The voice for this lesson is being recorded: for now it advances on its own with subtitles.

0:00 / 0:00

What you will achieve

  • Explain what a batch is and why we train on random batches instead of using every example, and compute how many steps an epoch has.
  • Tell what each optimizer (SGD, momentum, and Adam) does and why each one needs its own learning rate.
  • Predict what warmup and decay of the rate do, and recognize which settings are hyperparameters.

Key ideas

  1. With many examples the slope is not computed from all of them: it is estimated from a random batch (in the lesson, 8 of 256). Its arrow is a little off, but on average it points the right way. That is stochastic gradient descent (SGD).
  2. An epoch is one full pass over the data: with N examples and batches of B, it has N/B steps (256 / 8 = 32). For the same work, a small batch gives many cheap, noisy steps and a large one gives a few exact steps; the best is usually in between.
  3. The optimizer turns the slope into a step. SGD uses it as is; momentum adds inertia; Adam also gives each weight its own step size. Each one wants its own rate.
  4. The rate can change during training: a warmup at the start avoids the jump that throws the model far away, and a decay at the end calms the jitter so it settles at the bottom.
  5. Batch size, rate, optimizer, and schedule are hyperparameters: a person chooses them, they are not learned with gradients, and trying them costs whole training runs.

What many people believe

Common belief The bigger the batch, the better: the gradient is more exact, so it learns more.

What is true More exact is not more efficient. With the same budget of examples, a big batch takes fewer steps. And very large batches call for rescaling the rate and warming up so quality does not suffer.

Check yourself

Four questions. No grade, no account: they just show whether the idea stuck.

ConceptWhy is it useful to train with batches instead of using every example at each step?
CalculationA dataset has 10,000 examples and is trained with batches of 250 for 3 epochs. How many update steps are taken in total?
ConceptWhat does Adam do besides carrying a momentum term?
ApplicationA team doubles the batch and also the rate, as is recommended when scaling up, but the error spikes in the first steps. What should they try first?

Under the hoodFor those who want the math and the code. You do not need it to follow the course.

The gradient of a batch

With NN examples and a per-example error ℓi(w)\ell_i(\mathbf{w}), the total error is L(w)=1N∑iℓi(w)L(\mathbf{w})=\tfrac1N\sum_i \ell_i(\mathbf{w}). A batch B\mathcal{B} of BB randomly chosen examples gives the approximate gradient

gB=1B∑i∈B∇ℓi(w),E[gB]=∇L(w)\mathbf{g}_B=\frac1B\sum_{i\in\mathcal{B}}\nabla\ell_i(\mathbf{w}),\qquad \mathbb{E}[\mathbf{g}_B]=\nabla L(\mathbf{w})

It is an unbiased estimator. Its variance is N−BN−1 σ2B\frac{N-B}{N-1}\,\frac{\sigma^2}{B}, where σ2\sigma^2 is the variance of one example’s gradient: the error falls as 1/B1/\sqrt{B}, while the cost of the step grows as BB. That is why cutting the noise in half takes a batch about four times larger. In the lesson, the arrow’s average error (measured over 24 batches of each size) was 20 %, 10 %, 4 %, and 1.7 % for batches of 2, 8, 32, and 128: it falls a bit faster than 1/B1/\sqrt{B} because the factor N−BN−1\frac{N-B}{N-1} matters when the batch is a large share of the 256 examples. An epoch has ⌈N/B⌉\lceil N/B\rceil steps, and after tt steps tBtB examples have been seen. The idea of updating with noise goes back to Robbins and Monro (1951).

Near a minimum with curvature hh, gradient noise (variance s2=σ2/Bs^2=\sigma^2/B) leaves an error floor: E[L]≈η s2/4\mathbb{E}[L]\approx \eta\,s^2/4 when ηh≪1\eta h\ll 1. It is proportional to η/B\eta/B: lowering the rate or raising the batch lowers it. That is why decaying the rate helps the path settle.

Momentum

vt+1=β vt+gt,wt+1=wt−η vt+1\mathbf{v}_{t+1}=\beta\,\mathbf{v}_t+\mathbf{g}_t,\qquad \mathbf{w}_{t+1}=\mathbf{w}_t-\eta\,\mathbf{v}_{t+1}

With β=0.9\beta=0.9 and a constant gradient, v→g/(1−β)\mathbf{v}\to \mathbf{g}/(1-\beta): the effective step is ten times larger; for the same effective step, momentum’s rate would be about ten times smaller than SGD’s (in practice the ratio is smaller: in the lesson’s canyon, 0.02 versus 0.07). For a quadratic with curvatures λmin⁡≤h≤λmax⁡\lambda_{\min}\le h\le\lambda_{\max} and condition number κ=λmax⁡/λmin⁡\kappa=\lambda_{\max}/\lambda_{\min}, the best fixed rate for SGD shrinks the distance to the bottom per step by a factor κ−1κ+1\frac{\kappa-1}{\kappa+1}, and well-tuned momentum by κ−1κ+1\frac{\sqrt\kappa-1}{\sqrt\kappa+1} (Polyak, 1964). In the lesson’s canyon, near the bottom, κ≈50\kappa\approx 50: about 0.960.96 per step versus 0.750.75 (in theory, without noise).

Adam

mt=β1mt−1+(1−β1)gt,st=β2st−1+(1−β2) gt2\mathbf{m}_t=\beta_1\mathbf{m}_{t-1}+(1-\beta_1)\mathbf{g}_t,\qquad \mathbf{s}_t=\beta_2\mathbf{s}_{t-1}+(1-\beta_2)\,\mathbf{g}_t^{2}
m^t=mt1−β1 t,s^t=st1−β2 t,wt=wt−1−η m^ts^t+ε\hat{\mathbf{m}}_t=\frac{\mathbf{m}_t}{1-\beta_1^{\,t}},\quad \hat{\mathbf{s}}_t=\frac{\mathbf{s}_t}{1-\beta_2^{\,t}},\qquad \mathbf{w}_t=\mathbf{w}_{t-1}-\eta\,\frac{\hat{\mathbf{m}}_t}{\sqrt{\hat{\mathbf{s}}_t}+\varepsilon}

Everything is per weight (the square and the root act component by component). The defaults from Kingma and Ba (2015) are η=0.001\eta=0.001, β1=0.9\beta_1=0.9, β2=0.999\beta_2=0.999, and ε=10−8\varepsilon=10^{-8}. The bias corrections compensate for m\mathbf{m} and s\mathbf{s} starting at zero. On the first step m^/s^=sign⁡(g)\hat{\mathbf{m}}/\sqrt{\hat{\mathbf{s}}}=\operatorname{sign}(\mathbf{g}), so every weight moves by η\eta; in normal operation each weight’s step is at most on the order of η\eta. Two consequences: the method is invariant to rescaling each gradient separately (its authors say so), and near an optimum the signal-to-noise ratio drops and the steps shrink on their own, an “automatic annealing” in Kingma and Ba’s words. With noisy batch gradients that is not enough: in the lesson’s simulation, with a constant η\eta the path keeps jittering around the bottom, with an amplitude that grows with η\eta, and lowering η\eta at the end calms it.

Learning-rate schedules

ηt=ηmax⁡min⁡ ⁣(1,t+1Tw)(linear warmup)\eta_t=\eta_{\max}\min\!\Big(1,\frac{t+1}{T_w}\Big)\quad\text{(linear warmup)}
ηt=ηmin⁡+12(ηmax⁡−ηmin⁡)(1+cos⁡π t−TwT−Tw)(cosine decay, from Loshchilov and Hutter, 2017)\eta_t=\eta_{\min}+\tfrac12(\eta_{\max}-\eta_{\min})\Big(1+\cos\pi\,\frac{t-T_w}{T-T_w}\Big)\quad\text{(cosine decay, from Loshchilov and Hutter, 2017)}

Two real recipes: the original transformer (Vaswani et al., 2017) used Adam with β2=0.98\beta_2=0.98 and warmed up for 4000 steps; GPT-3 (Brown et al., 2020) used Adam with β2=0.95\beta_2=0.95, a linear warmup over the first 375 million tokens, and cosine decay down to 10 % of the peak value over 260 billion tokens. Its batch also grew from 32 thousand tokens to the final value (3.2 million for the 175-billion-parameter model) over the first 4 to 12 billion tokens, depending on model size, and it saw only 0.44 epochs of its web-crawl text (Table 2.2). When the batch grows, Goyal et al. (2017) propose multiplying the rate by the same factor and warming up: with that rule they trained ResNet-50 with batches of 8192 images on 256 GPUs, in one hour, with no loss of accuracy.

Hyperparameters

The weights w\mathbf{w} are learned with ∇L\nabla L; hyperparameters (η\eta, BB, β\beta, TwT_w, the optimizer…) set how learning happens and have no gradient within reach. They are chosen by trying: in spaces of several dimensions, trying random combinations often pays off more per trial than sweeping a grid (Bergstra and Bengio, 2012).

In code

import math

# Eight fixed examples of y ≈ 2x + 1. Batches are taken in order so the
# result is reproducible (in practice you shuffle every epoch).
X = [0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5]
Y = [1.2, 1.8, 3.1, 4.1, 4.9, 6.2, 6.9, 8.1]

def grad(w, b, idx):                       # gradient of the batch mean squared error
    n = len(idx)
    gw = sum(2 * (w * X[i] + b - Y[i]) * X[i] for i in idx) / n
    gb = sum(2 * (w * X[i] + b - Y[i]) for i in idx) / n
    return [gw, gb]

def train(opt, eta, B, epochs, beta=0.9, b1=0.9, b2=0.999, eps=1e-8):
    p = [0.0, 0.0]                         # weights: w and b
    v = [0.0, 0.0]; m = [0.0, 0.0]; s = [0.0, 0.0]; t = 0
    for _ in range(epochs):                # one epoch = go through all the batches
        for start in range(0, len(X), B):
            g = grad(p[0], p[1], range(start, min(start + B, len(X))))
            t += 1
            if opt == "sgd":
                step = [eta * gi for gi in g]
            elif opt == "momentum":
                v = [beta * vi + gi for vi, gi in zip(v, g)]
                step = [eta * vi for vi in v]
            else:                          # Adam
                m = [b1 * mi + (1 - b1) * gi for mi, gi in zip(m, g)]
                s = [b2 * si + (1 - b2) * gi * gi for si, gi in zip(s, g)]
                step = [eta * (mi / (1 - b1 ** t)) / (math.sqrt(si / (1 - b2 ** t)) + eps)
                        for mi, si in zip(m, s)]
            p = [pi - di for pi, di in zip(p, step)]
    return [round(pi, 3) for pi in p]

# the exact least-squares solution is [1.998, 1.042]
for opt, eta in [("sgd", 0.05), ("momentum", 0.005), ("adam", 0.3)]:
    print(opt, train(opt, eta, B=4, epochs=50))
# sgd [1.999, 1.033]
# momentum [2.008, 1.043]
# adam [1.993, 1.028]

On a problem this small and well conditioned, SGD is already enough: the differences show up on landscapes like the canyon, and each optimizer needed its own rate.

What comes next

Lowering the training error is not enough: you have to measure with data the model has not seen. That is lesson 10 (generalizing and evaluating), which adds early stopping and weight decay as ways to regularize.

Decision lens

What this idea means if your job is to decide, not to code.

  • “We trained with the default settings” is not a sufficient answer. Ask which rate, batch, optimizer, and schedule were used and how many trials it took to get there: each trial was a full training run.
  • Doubling the GPUs does not always halve the time: beyond a certain size, a bigger batch no longer saves steps (the “critical batch size,” McCandlish et al., 2018), and it forces you to rescale the rate and warm up.
  • A loss that spikes at the start points to a rate that is too high or a missing warmup; one that oscillates at the end, to a missing decay. These are configuration fixes, not reasons to buy more compute.
  • Compare optimizers with each one’s rate tuned, not at the same value: it is easy to mislead that way. Also ask them to record the seed and the configuration, because two identical runs give somewhat different results.

Terms in this lesson

Full transcript
CommentsThis lesson’s public conversation: questions, facts to check, ideas.

Comments live in GitHub Discussions, in the course’s public repository; writing one requires a GitHub account. They load from giscus.app when you open this section. The course reviewers read them, and if a change is accepted, it is applied in both languages. Open the thread on GitHub

Sources