Module 04 · Part I · Foundations

How a Model Learns From Data

Self-Supervision, Neurons and MLPs, Softmax and Cross-Entropy, Overfitting, Hyperparameters, and How to Read an Experiment

By the end you can train a tiny MLP end to end, diagnose overfitting, and read an experiment's result critically.

Builds onModule 3·
Check yourself ▸

Before you start, can you already…

  • ☐use the rules for expectation and variance, read conditional probability notation, and explain cross-entropy, perplexity and KL divergence. (Module 3)

If any is shaky, the linked module teaches it.

Open in Colab
5 Hours·Core·Foundational·0/7

Modules 2 and 3 supplied the two halves of learning: a loss that measures how wrong a model is, and gradient descent to make it less wrong. This module puts them together on a small neural network, then adds what makes training a science: why fitting the training data is not the goal, which knobs you choose by hand, and how to tell a real improvement from luck.

4.1 Supervised and Self-Supervised Learning

Most machine learning is supervised: each example pairs an input with the wanted answer, its label (an image labelled cat, an email labelled spam), and the loss compares the model's output with the label.

Labels are expensive, so labelled datasets hold millions of examples, not trillions. Language models make labels from the data itself: hide the next token and predict it from the tokens before. A 20-token sentence is 20 training examples, and the whole internet becomes a labelled dataset. This is , and it is why language models could grow so large: the training signal grows with the supply of text. Section 8.4 writes the objective out, and section 8.2 shows a second self-supervised task, filling in blanks.

A third kind, reinforcement learning, has no labels, only a score for how good an output was. Module 21 uses it to tune assistants to people's preferences.

4.2 Neurons, Layers, and the MLPRun this section

A is the smallest unit of a neural network. It takes a vector of inputs, computes a weighted sum plus a constant, and passes the result through a fixed nonlinear function:

a=ϕ(xw+b)a = \phi(\mathbf{x}\mathbf{w} + b)

The weights w\mathbf{w} and bias bb are learned. ϕ\phi is the : ReLU, max⁡(0,z)\max(0, z), zeroes negative values; tanh⁡\tanh squashes into (−1,1)(-1, 1); GELU is a smooth ReLU used in transformers (section 7.4).

A layer is many neurons reading the same input. With their weight vectors as the columns of WW, a layer is one matrix multiplication followed by ϕ\phi entry by entry: a=ϕ(xW+b)\mathbf{a} = \phi(\mathbf{x}W + \mathbf{b}). A (MLP) chains such layers. The layers between input and output are hidden layers, and their size is the network's width.

Why the Nonlinearity Matters

Without ϕ\phi, two layers give (xW1+b1)W2+b2=x(W1W2)+(b1W2+b2)(\mathbf{x}W_1 + \mathbf{b}_1)W_2 + \mathbf{b}_2 = \mathbf{x}(W_1W_2) + (\mathbf{b}_1 W_2 + \mathbf{b}_2): one linear layer again. Any stack of linear layers can only draw straight boundaries; the nonlinearity is what lets depth add power. With enough hidden neurons, one hidden layer can approximate any reasonable function (the universal approximation theorem), but depth makes many functions far cheaper to represent.

Every transformer block contains an MLP, the feed-forward network (section 7.4), which holds most of the model's parameters.

4.3 Logits, Softmax, and Cross-EntropyRun this section

A model produces raw scores called , z=[z1,…,zN]\mathbf{z} = [z_1, \dots, z_N], any real numbers. Softmax turns them into probabilities: positive, and summing to 1.

Softmax⁡(z)i=ezi∑j=1Nezj\operatorname{Softmax}(\mathbf{z})_i = \frac{e^{z_i}}{\sum_{j=1}^N e^{z_j}}

Numerical Catastrophe: Floating-Point Overflow

Floating-point formats have a largest value; anything bigger becomes infinity (inf). Because eze^{z} grows so fast, ordinary logits can overflow:

  • In 32-bit floats (fp32) the largest value is about , so eze^{z} overflows once z>88.72z > 88.72.
  • In 16-bit half precision (fp16) the largest value is 65,504≈e11.0965{,}504 \approx e^{11.09}, so eze^{z} overflows once z>11.09z > 11.09.
  • bf16 keeps fp32's exponent range and gives up precision instead, so it overflows near the same point as fp32.

Once any ezie^{z_i} is inf, softmax computes = NaN ("not a number"). Backpropagation carries it into the gradients and then the weights, and any arithmetic with NaN gives NaN.

Shift-Invariance & Safe Softmax

Subtracting the same number cc from every logit leaves the softmax unchanged:

Softmax⁡(z−c)i=ezi−c∑jezj−c=e−cezie−c∑jezj=Softmax⁡(z)i\operatorname{Softmax}(\mathbf{z} - c)_i = \frac{e^{z_i - c}}{\sum_j e^{z_j - c}} = \frac{e^{-c} e^{z_i}}{e^{-c} \sum_j e^{z_j}} = \operatorname{Softmax}(\mathbf{z})_i

The factor e−ce^{-c} cancels top and bottom. Standard implementations (PyTorch, FlashAttention, TensorRT-LLM) use c=max⁡j(zj)c = \max_j (z_j):

SafeSoftmax⁡(z)i=ezi−max⁡k(zk)∑j=1Nezj−max⁡k(zk)\operatorname{SafeSoftmax}(\mathbf{z})_i = \frac{e^{z_i - \max_k(z_k)}}{\sum_{j=1}^N e^{z_j - \max_k(z_k)}}

Every exponent is now zero or negative, so every exponential lies in (0,1](0, 1] and nothing can overflow.

Architecture Flowchart
Diagram: Raw logits z_i (can be as large as 120); Find m = max(z_k); Subtract: z_i − m ≤ 0; exp(z_i − m) lies in (0, 1]: cannot overflow; Divide each by the sum; Probabilities p_i, identical to plain softmax

In words. Subtract the largest logit from every logit, then exponentiate and divide by the sum. The answer is unchanged; only overflow is prevented.

Temperature Scaling Regimes

When sampling text, the logits are often divided by a T>0T > 0 first:

pi=exp⁡(zi/T)∑jexp⁡(zj/T)p_i = \frac{\exp(z_i / T)}{\sum_j \exp(z_j / T)}
  • T=1T = 1: the model's own probabilities.
  • (greedy decoding, always picking the most likely token): every gap zmax⁡−ziT\frac{z_{\max} - z_i}{T} grows without bound, so all probability lands on the largest logit, a distribution. is the position of the largest logit:
    lim⁡T→0+pi={1,if i=arg⁡max⁡kzk0,otherwise\lim_{T \to 0^+} p_i = \begin{cases} 1, & \text{if } i = \arg\max_k z_k \\ 0, & \text{otherwise} \end{cases}
  • T→∞T \to \infty (uniform): every zi/Tz_i / T goes to 00, so every token gets the same weight:
    lim⁡T→∞pi=1N\lim_{T \to \infty} p_i = \frac{1}{N}
Figure 4.3
Temperature moves the distribution across the simplex

Three logits give three probabilities that add to 1, so every softmax output is a point in this triangle. Lower T slides it to the top corner (greedy); higher T slides it to the center (uniform).

Logits z = [2, 1, 0.1]. The corners are one-hot distributions; the curve is every temperature from about 0.01 to 200.
Predict, then try

Before moving the slider, decide where the point must end up as the temperature grows very large, and why the answer does not depend on the logits at all.

In words. With three logits, every softmax output is a point in a triangle whose corners are the one-hot distributions. Lowering the temperature slides the point toward the largest logit's corner (greedy decoding); raising it slides it to the center, where each probability is a third.

Softmax Jacobian and Gradient Dynamics

How does each probability pi=Softmax⁡(z)ip_i = \operatorname{Softmax}(\mathbf{z})_i change when logit zjz_j changes?

∂pi∂zj=pi(δij−pj)={pi(1−pi),if i=j−pipj,if i≠j\frac{\partial p_i}{\partial z_j} = p_i (\delta_{ij} - p_j) = \begin{cases} p_i(1 - p_i), & \text{if } i = j \\ -p_i p_j, & \text{if } i \neq j \end{cases}

δij\delta_{ij} is 11 when i=ji = j and 00 otherwise. This Jacobian is not diagonal: raising one logit lowers every other probability, since they must still sum to 1. Both cases follow from the quotient rule with S=∑kezkS = \sum_k e^{z_k}: for i=ji = j, eziS−ezieziS2=pi−pi2\frac{e^{z_i} S - e^{z_i} e^{z_i}}{S^2} = p_i - p_i^2; for i≠ji \neq j, −eziezjS2=−pipj\frac{-e^{z_i} e^{z_j}}{S^2} = -p_i p_j.

If one logit dominates, pi≈1p_i \approx 1 and every other pj≈0p_j \approx 0, so every entry of the Jacobian is nearly zero and almost no gradient flows back. The softmax is saturated, and learning through it stalls. This is one reason attention divides its scores by .

Measured (lab 1b, experiments/softmax_overflow.py). The size of the Jacobian (its Frobenius norm) drops from 0.4330.433 for equal logits to 0.0001930.000193 for saturated ones, a factor of 2,2402{,}240, as the largest probability goes from 0.250.25 to 0.99990.9999. Only the two ends are compared: along some paths between them the norm rises before it falls, which is why section 6.3 tracks the top class's p(1−p)p(1-p) instead.

Measured (lab 1b). exp() overflows fp32 at z>88.72z > 88.72 and fp16 at z>11.09z > 11.09, matching the values quoted in the overflow section. Passing z=[120,0,−120]z = [120, 0, -120] to the plain softmax formula returns nan, while the safe version gives the exact answer. Note which code is at risk: torch.softmax already subtracts the row maximum internally, so the model code shipped with this course never overflows. The danger is in writing the formula out by hand, not in calling the library.

Softmax With Cross-Entropy: The Gradient That Trains Every LLM

Saturation matters when the softmax output feeds further computation, as in attention. At a model's output, the softmax feeds the cross-entropy loss for the correct class cc (section 3.5), and differentiating through both at once gives something much simpler:

∂L∂zj=pj−yj\frac{\partial \mathcal{L}}{\partial z_j} = p_j - y_j

yjy_j is 11 for the correct class and 00 otherwise. In one line: −log⁡pc=−zc+log⁡∑kezk-\log p_c = -z_c + \log \sum_k e^{z_k}, and the second term's derivative is pjp_j.

The gradient is "what the model predicted, minus what was right". When the model is confidently wrong (pc≈0p_c \approx 0), the correct class gets a gradient near −1-1, the strongest push; it vanishes only when the model is confidently right. The log cancels the exponential, which is why a saturated output layer is no problem while a saturated softmax inside attention is.

4.4 Generalization, Overfitting, and RegularizationRun this section

The goal is not a low loss on the training data but on data the model has never seen: . A model that memorized its examples would be useless on anything else.

Three Splits

To measure generalization honestly, the data is divided before training:

  • The training set is what gradient descent sees.
  • The validation set is held out and used for decisions: learning rate, model size, when to stop. Its loss is the validation loss.
  • The test set is touched once, at the end, to report the final number.

Making decisions on the test set turns it into a second validation set, and the reported number becomes optimistic. Section 13.3 shows the same failure with public benchmarks.

Overfitting and Underfitting

Watch the training and validation losses together:

  • Underfitting. Both are high, still falling or stalled. The model is too small or undertrained.
  • A good fit. Both fall, and stay close.
  • Overfitting. Training loss keeps falling while validation loss turns and rises: the model is fitting noise in its particular examples.

Overfitting needs a model big enough to memorize and data small enough to be memorized. Large language models see almost every example once and rarely overfit; small models trained for many passes over small datasets overfit readily.

Regularization

A regularizer discourages memorization at a small cost to the training loss:

  • More data, the most effective of all.
  • Weight decay pulls every weight slightly toward zero each step, discouraging the large, finely tuned weights memorization needs (section 10.2).
  • Dropout zeroes a random fraction of activations at each training step, so the model cannot rely on any single feature (Srivastava et al., 2014). GPT-2 used 0.10.1; most modern pretraining uses zero, since it rarely overfits.
  • Early stopping keeps the weights from the step with the lowest validation loss.

4.5 Hyperparameters, Batches, Steps, and Epochs

are what gradient descent learns. Everything chosen by hand is a : learning rate, number and width of layers, batch size, weight decay, dropout, training length. They are chosen by comparing validation loss across several values.

  • A batch is the set of examples for one gradient computation. Larger batches give a less noisy gradient (section 3.2) at a higher cost per step.
  • A step is one weight update: loss on one batch, backpropagate, gradient descent.
  • An epoch is one full pass over the training set: n/Bn / B steps for nn examples and batch size BB.

Small models train for many epochs. Large language models train for about one epoch or less, so progress is counted in tokens seen instead (section 10.5).

Reading a Loss Curve

A loss curve plots loss against step. A healthy one falls fast, then flattens. A flat curve suggests a learning rate too small, or a bug disconnecting loss from weights. Sudden spikes suggest a learning rate too large or overflow (section 4.3); section 10.4 lists the defences.

4.6 Reading an ExperimentRun this section

Rerun any experiment and its numbers come out slightly different. Reading results well means knowing why.

Seeds and Run-to-Run Noise

Initial weights, batch order and dropout are drawn from a pseudo-random generator, which repeats exactly from the same seed. A different seed gives a different but equally valid run, and the final loss moves a little: run-to-run noise.

A difference between methods means something only if it is clearly larger than the noise. If changing the seed moves validation loss by 0.020.02, a single-run improvement of 0.0150.015 shows nothing yet. Run each method with several seeds and compare the spread.

Ablations

An ablation changes one component and keeps everything else (data, seed, steps) identical, isolating that component's effect. Most comparisons in this course are ablations: sinusoidal against learned positions (section 7.1), Post-LN against Pre-LN (section 8.5).

Reading a Measured Box

When you read a Measured box, ask four questions:

  1. What was measured? A derivation checked to floating-point precision (such as an identity holding to 10−710^{-7}) is a different kind of claim from a training comparison.
  2. At what scale? A result on a model with 100,000 parameters may not hold at a billion. The boxes say when this matters.
  3. How many seeds? One seed shows that something can happen, not how reliably.
  4. Compared with what noise? A difference smaller than the run-to-run noise is not a result.

The Caveat boxes exist to answer these questions in advance.

4.7 Worked Example: Training a Tiny MLPRun this section

The figure trains an MLP in your browser to separate two classes of points in a plane: two coordinates in, two logits out, softmax and cross-entropy, then backpropagation and gradient descent on each batch.

Figure 4.7
Train a small neural network, and watch it generalize or overfit

The network sees the training points (solid). The validation points are never trained on; their loss is the dashed curve. Shading shows the class the network predicts.

Mini-batches of 16, plain gradient descent with decoupled weight decay, cross-entropy loss. Each frame is two epochs. Changing the data or the architecture starts a fresh network.
Predict, then try

Set the training set to its smallest size and the hidden layer to its largest width, then train. Before you press play, predict what the two loss curves will do.

In words. A plane of two-coloured points, shaded by the network's current prediction, beside a chart of training and validation loss. Controls set the number of points, network width and depth, activation, learning rate and weight decay. As training runs, the shading reshapes around the data and the curves show underfitting, fitting or overfitting.

Try, in order: with ReLU and moderate width, the boundary bends to follow the data. Switch the activation to none and it stays a straight line however many layers you add (section 4.2). Raise the learning rate and the loss curve jumps around (section 2.4). Finally, make it overfit with the smallest training set and widest network, then cure it.

Browser lab

The same network, two training-set sizes

An ablation: one network (two hidden layers of 32, ReLU, learning rate 0.1, no regularization, 150 epochs) trained on 30 points and on 400 points, with the same 300 held-out validation points. Section 4.4 predicts that the small set overfits (training loss far below validation loss, validation loss rising after an early minimum) and the large set does not.

Each run uses a new seed. Run it a few times: the exact numbers move, which is run-to-run noise (section 4.6), but whether the gap is large or small does not.

Exercises

Check

Why is next-token prediction called self-supervised rather than supervised?

Apply

Show that two linear layers with no activation between them, xW1W2\mathbf{x}W_1W_2 with W1∈R4×16W_1 \in \mathbb{R}^{4 \times 16} and W2∈R16×3W_2 \in \mathbb{R}^{16 \times 3}, are equivalent to one linear layer. What is that layer's shape, and what is the largest rank it can have?

Apply

A model's logits are [2,1,0][2, 1, 0] and the correct class is the first. Compute the gradient of the cross-entropy loss with respect to the first logit.

Apply

A training set has 60,000 examples and the batch size is 128. How many steps is 3 epochs, rounding each epoch up to a whole number of steps?

Apply

Describe what the training and validation loss curves look like when a model overfits, and name two ways to reduce it.

Extend

Two methods are compared with one seed each. Method B's validation loss is 0.0150.015 lower than method A's. Three more seeds of method A give losses spread over a range of 0.040.04. What can you conclude, and what would you do next?

Extend

Why does a saturated softmax stall learning inside attention but not at the output layer of a classifier trained with cross-entropy?

Core Academic References

  • Goodfellow, Bengio & Courville (2016): Deep Learning, chapters 5–7 (Machine Learning Basics, Deep Feedforward Networks, Regularization). MIT Press.
  • Srivastava et al. (2014): Dropout: A Simple Way to Prevent Neural Networks from Overfitting. JMLR 15: 1929–1958.
  • Hornik, Stinchcombe & White (1989): Multilayer Feedforward Networks are Universal Approximators. Neural Networks, 2(5): 359–366.
  • Bouthillier et al. (2021): Accounting for Variance in Machine Learning Benchmarks. MLSys 2021. arXiv:2103.03098

Measured labs

These labs test what this module says by running real code. Each card states the idea, what the experiment found, and what it does not prove. The numbers come from experiments/results.json, the output of running experiments/run_all.sh on the tiny-transformers reference code at version 137f7ef. The project checks fail if a measured number in the module text stops matching that file.

Lab 1b — softmax overflow, reproduced and then fixed

What the module says

Section 4.3 says that computing exp⁡(z)\exp(z) directly overflows to infinity once zz passes 88.72 in fp32 or 11.09 in fp16. It says standard implementations avoid this by subtracting the largest score, max⁡(z)\max(z), first. It also says that when softmax puts almost all probability on one class, its gradient ∂pi/∂zi=pi(1−pi)\partial p_i / \partial z_i = p_i(1 - p_i) shrinks toward zero, so learning stalls.

What the experiment found

Both overflow points reproduce: z=88.72z = 88.72 in fp32 and z=11.09z = 11.09 in fp16, the values the module quotes. The plain softmax from the module returns NaN (not a number) on scores as large as z=120z = 120, while the version that subtracts the maximum gives the exact answer. When the scores are extreme enough that one probability is 1, the gradient term p(1−p)p(1-p) is exactly 0: the stalled-learning effect the module describes.

fp32 overflow point
88.7228

above this zz, exp⁡(z)\exp(z) is too large for a 32-bit float

fp16 overflow point
11.0899

above this zz, exp⁡(z)\exp(z) is too large for a 16-bit float

plain softmax gives a finite answer at z=120z = 120?
false

torch.softmax does: it subtracts the row maximum for you

gradient term p(1−p)p(1-p) for the top class, scores [60,0,−60][60, 0, -60]
0

the term the module says goes to zero, measured at zero

size of the whole gradient matrix, spread-out →\to peaked scores
0.433013 → 0.000193

context only: this is the Frobenius norm ∥J∥F\|J\|_F, not the p(1−p)p(1-p) term

What this does not prove

The model in this repository never overflows, because it calls torch.softmax, which already subtracts the row maximum. The risk is in the plain formula, not in this code. You only see that by running the experiment: reading the module alone could leave you thinking this code base is at risk.

Run it yourself

bash
cd tiny-transformers && .venv/bin/python experiments/softmax_overflow.py

The code being tested