Chapter 2

2Deep Learning, Just Enough

Everything in this book runs on one engine: a differentiable function, a loss that scores its output, and gradient descent turning the loss into small parameter updates. You have seen all of this before. The job of this chapter is to compress it into the specific facts the later chapters lean on — how representations arise, what the training loop actually does, which optimizer LLMs use and why, why giant models generalize at all, and why the field settled on bf16. For the full course, the standard reference is still the Goodfellow, Bengio, and Courville textbook (Goodfellow et al., 2016); this is the refresher.

2.1From features to representations

Classical machine learning splits the work in two: a human designs features, and a model learns weights over them. Deep learning removes the split. You hand the network raw input — pixels, bytes, token ids — and each layer re-describes the output of the previous one, so the features themselves are learned. The early layers of a language model learn things like "these characters form a word"; later layers learn things like "this clause is the object of that verb." Nobody designed those features, and nobody could have designed the millions of stranger ones between them.

A useful geometric picture is the manifold view. Raw inputs are points tangled together in a huge space: sentences with opposite meanings can differ by one token. Each layer applies a learned transformation that untangles the manifold a little, so that by the final layer, things that should be treated the same sit near each other. Prediction becomes easy geometry at the top because the layers below did the unfolding.

Intuition

A deep network is not one clever function; it is a pipeline of small re-descriptions, each making the next layer's job slightly easier. "Depth" is how many times the input gets re-described.

Two interleaved spirals in the input space become two separated clusters divided by a straight line in the last-layer representation.
Figure 2.1What depth buys you. Classes that no straight line can separate in the input become trivially separable after the network's layers untangle them — prediction is easy geometry at the top because the layers below did the unfolding.

2.2The training loop

Every model in this book — from a toy MLP to a frontier LLM — is trained by the same five lines:

for batch in data:
    logits = model(batch.inputs)  # Forward pass.
    loss = cross_entropy(logits, batch.targets)  # How wrong were we?
    loss.backward()  # Backprop: compute every parameter's gradient.
    optimizer.step()  # Nudge every parameter downhill.
    optimizer.zero_grad()

The only non-obvious part is loss.backward(). Backpropagation (Rumelhart et al., 1986) is the chain rule from calculus, applied systematically from the loss backwards through every operation, reusing intermediate results so the whole thing costs about as much as the forward pass. Autograd frameworks are bookkeeping for that chain rule — they record what the forward pass did, then replay it in reverse. There is nothing else in the box: when Chapter 7 talks about a trillion-token pretraining run, it is this loop, run a few million times on a few thousand GPUs.

Analogy

Backprop is blame assignment on an assembly line. A defect is found at final inspection (the loss), and a memo travels backwards station by station, telling each worker how much their step contributed and in which direction to adjust. The analogy leaks in one way: on a real assembly line workers adjust one at a time, while gradient descent adjusts every station simultaneously — which is why each step must be small, or the workers invalidate each other's corrections.

A forward pass from batch to model to loss, with a dashed backward pass carrying gradients from the loss back to update the parameters.
Figure 2.2The whole engine. The forward pass turns inputs into a loss; the backward pass sends a gradient back to every parameter; the optimizer nudges each one downhill. A trillion-token run is just this loop, repeated.

2.3Optimizers and learning rates

Plain stochastic gradient descent takes the gradient of the current batch and steps against it. Two refinements matter, and they are the two knobs worth understanding:

  • Momentum keeps a running average of recent gradients and steps along that instead. Individual batches are noisy; the average points where the loss is consistently going down.
  • Adaptive scaling gives each parameter its own effective step size, normalized by the recent magnitude of its gradients. A parameter whose gradients are habitually huge takes proportionally smaller steps, and vice versa.

Adam (Kingma & Ba, 2015) combines both, and AdamW (Loshchilov & Hutter, 2019) fixes a subtle interaction with weight decay. The subtlety: if you add decay as an L2 penalty in the loss, it enters the gradient and is then divided by Adam's per-parameter denominator, so parameters with large gradients get less effective decay — the opposite of uniform. AdamW decouples the two, subtracting a fixed fraction of each weight directly, independent of gradient scale. AdamW is the default optimizer for essentially every LLM you will meet in this book, and Chapter 7 covers the learning-rate schedule (warmup, then decay) that accompanies it.

The learning rate itself remains the single most important hyperparameter in deep learning. Too high and the loss diverges; too low and you waste compute crawling. Everything else in an optimizer exists to make one global learning rate workable across millions of very different parameters.

Interview

Why is Adam(W) used for transformers instead of plain SGD? Because gradient magnitudes in a transformer vary enormously across parameters — embedding rows for rare tokens see gradients rarely, while LayerNorm gains see them constantly. Adam's per-parameter normalization equalizes these scales so one learning rate serves all of them; with plain SGD, transformers train poorly or need impractical per-layer tuning.

Interview

A model runs a forward pass fine but hits out-of-memory during training. Why, and what are your levers? Training holds far more than the weights: every layer's activations (kept for the backward pass), the gradients, and the optimizer state — for AdamW that is two moments per parameter plus, in mixed-precision training, an fp32 master copy, roughly 16 bytes per parameter before activations. The levers each attack one term: gradient checkpointing recomputes activations instead of storing them, gradient accumulation shrinks the batch's activation footprint, and optimizer-state sharding across GPUs (ZeRO, Chapter 8) splits the moments and master weights. Inference needs none of this, which is why a model that generates comfortably can still be untrainable on the same hardware.

Loss over training steps for three learning rates: a well-tuned rate converges smoothly, too-low barely moves, and too-high wobbles then diverges off the chart.
Figure 2.3Why the learning rate dominates. Set it too high and the loss diverges; too low and you crawl. Every other knob in an optimizer exists to make one global learning rate workable across millions of very different parameters.

2.4Regularization and generalization

The classical story says a model with too many parameters will memorize its training set and fail on new data, so you constrain it: weight decay shrinks weights toward zero, and dropout (Srivastava et al., 2014) randomly silences activations during training so no unit can depend too much on another.

LLMs complicate the classical story twice. First, the puzzle: modern networks are so overparameterized that they can memorize completely random labels — yet the same architectures, trained on real data, generalize well (Zhang et al., 2017). Capacity alone does not decide generalization; what the optimizer finds first, and the structure in the data, matter more than the parameter count. Second, the practice: a pretraining run makes roughly one pass over its enormous corpus, so the model rarely sees any example twice — classic overfitting has little room to happen. Weight decay survives in LLM training more as a stabilizer than as an anti-memorization device, and dropout has largely disappeared from pretraining.

Intuition

Overfitting is a symptom of revisiting the same data with too much capacity. Pretraining starves it from the data side: with trillions of tokens seen once, the model has no choice but to learn patterns, because memorization has nothing to grab onto twice.

A grouped bar chart: on real labels the network reaches 100 percent train and 91 percent test accuracy; on random labels it still reaches 100 percent train but only chance-level test accuracy.
Figure 2.4The generalization puzzle. The same overparameterized network can memorize completely random labels, yet on real data it generalizes. Capacity alone does not decide generalization — structure in the data and what the optimizer finds first matter more.

2.5Numerical precision

A number format spends its bits on two things: dynamic range (how large and small the exponent lets values get) and precision (how many mantissa bits distinguish nearby values). float32 has plenty of both but costs 4 bytes per number — memory and bandwidth you will learn to resent in Parts II and IV.

The two half-size formats split the budget differently:

Format Exponent bits Mantissa bits Range Precision
float32 8 23 huge high
float16 5 10 narrow medium
bfloat16 8 7 same as float32 low

fp16's narrow range is the problem: gradients in a large model span many orders of magnitude, and values silently underflow to zero or overflow to infinity. Training in fp16 requires loss scaling — multiplying the loss so gradients sit inside the representable window — and it is fragile machinery (Micikevicius et al., 2018). bf16 keeps float32's full exponent, sacrificing precision instead (Kalamkar et al., 2019). That trade wins because training is noisy anyway: stochastic gradients already jitter far more than the mantissa error, but a single overflow can kill a run.

Interview

Why did bf16 beat fp16 for training? Because training tolerates noise but not clipped range. bf16 keeps float32's 8-bit exponent, so nothing overflows or underflows and no loss scaling is needed; the precision it gives up disappears into gradient noise the run already has. fp16 spends its bits the other way and buys a failure mode instead.

Bit layouts of float32, float16, and bfloat16 drawn to a common scale, showing that bfloat16's 8-bit exponent matches float32's while its mantissa is the shortest of the three.
Figure 2.5Where the bits go. bfloat16 keeps float32's full 8-bit exponent — so it has the same dynamic range and nothing overflows — and pays for it out of the mantissa. That trade wins because training tolerates low precision but not clipped range.

Precision returns in Chapter 16 from the other side: at inference time, weights are static and the noise argument changes, which is why quantization can push below 8 bits per weight when training cannot.

That is the equipment. Next, the specific way LLMs turn text into the integers this machinery consumes.

References

Check yourself

Interview-style questions on this chapter. Pick an answer to see whether it holds up.

  1. Your model runs a forward pass on a batch just fine, but the same batch runs out of memory during training. What is the primary reason, and which lever most directly addresses it?

  2. What is the actual difference between L2 regularization folded into the loss and AdamW's decoupled weight decay?

  3. The same overparameterized network can be trained to 100% accuracy on data with completely random labels, yet it generalizes well on real labels. What does this most directly imply?

  4. Why did bfloat16 become the default over float16 for training, even though float16 offers more mantissa bits (higher precision)?

  5. Why is Adam(W) preferred over plain SGD for transformers specifically?