The goal

We have a loss that depends on billions of parameters. We want to find parameter values that make small. We can’t solve this analytically. So we do it iteratively.

Gradient descent

The gradient tells you, for each parameter, “if I increase this weight slightly, does the loss go up or down, and by how much?” The update rule:

Breaking down the formula

PieceMeaning
One weight in the network
”Replace the left side with the right side”
The slope: how much changes per tiny nudge of . Positive means “increasing makes things worse”
Learning rate, the step size (e.g. )
Go the opposite way of the slope, i.e. downhill

Move each weight a small step in the direction that reduces the loss. Repeat. Imagine standing on a foggy hillside and always stepping downhill. You never see the whole landscape, you just feel the local slope.

Backpropagation

The question is how to compute for every one of billions of weights efficiently. Answer: the chain rule from calculus, applied cleverly.

A network is a chain of functions:

The chain rule says the derivative of a composition is the product of derivatives along the chain. For a weight that feeds through :

Think of it as a chain of “how much does this change affect the next thing,” multiplied together.

Tiny example. Say and loss .

flowchart LR
    X["x"] --> H["h = w₁·x"]
    H --> A["a = ReLU(h)"]
    A --> Y["y = w₂·a"]
    Y --> L["L = (y − target)²"]

Forward pass (compute left to right, saving every value):

Backward pass (walk right to left, reusing each piece):

You run the forward pass (compute outputs, saving intermediate values), then the backward pass (walk the chain in reverse, reusing each computed piece). The reuse is the trick. The backward pass costs about 2× a forward pass regardless of how many parameters there are. That’s what makes training billion-parameter networks feasible at all.

Modern frameworks (PyTorch, JAX) do this automatically (autograd). You write the forward pass, and loss.backward() hands you every gradient.

Practical optimizers

Plain gradient descent is rarely used. Real training uses:

  • Mini-batches: compute the gradient on a batch of examples (millions of tokens), not the whole dataset. It’s a noisy estimate, but good enough and way cheaper. This is stochastic gradient descent.
  • Adam / AdamW: keeps running averages of each parameter’s gradient (momentum) and squared gradient (scale), then adapts the step size per parameter. This is the standard for LLMs.
  • Learning rate schedule: warm up from tiny, ramp to a peak, then decay (often cosine). Too high a rate and training diverges. Too low and it crawls.

← 3. Loss and how wrong we are · Contents · 5. From generic networks to language →