d2l::interactive

Chapter 4 // Foundations

Softmax Classification

Regression asks how much. Classification asks which one. Same linear machinery, three new parts: a softmax to make probabilities, cross-entropy to score them, and accuracy to keep us honest.

Adapted from the original chapter of Dive into Deep Learning · CC BY-SA 4.0 · the Python shown is the book's; the figures run a faithful TypeScript port in your browser

Linear regression predicts a number. But most questions aren't how much — they're which category: cat or dog, spam or inbox, one of ten garments. The plumbing barely changes. We just need the output to be a probability distribution over classes, and a loss that rewards putting mass on the truth.

Act 1 · An image becomes a probability

01/05

8 × 8 image64 pixels

Here is the input: an 8-by-8 grayscale image of a garment. To the model it isn't a picture — it's just 64 numbers, one brightness per pixel.

The whole operation, in two formulas — squash logits into probabilities, then score them by the surprise of the correct class:

y^i=exp(oi)jexp(oj)l(y,y^)=logy^true\hat{y}_i = \frac{\exp(o_i)}{\sum_j \exp(o_j)} \qquad l(\mathbf{y}, \hat{\mathbf{y}}) = -\log \hat{y}_{\text{true}}

That log is doing real work. It's gentle when you're right and unbounded when you're confidently wrong.

Act 2 · Cross-entropy is surprise

01/05

the truth is t-shirtt-shirt99.3%trouser0.4%bag0.3%

The image really is a t-shirt, and the model put 99% there. Good — but what exactly makes 99% good and 10% bad? We need a loss.

One prediction is easy to score. The real job is turning every prediction right — moving the weights until the boundaries between classes land where the data says they should.

Act 3 · Carving up the space

01/05

feature 1 →feature 2

A simpler dataset so we can see everything: two features, three classes, 180 points. The book trains on 784-pixel photos — same math, more dimensions. Where should the boundaries go?

That live run hid something dangerous. Exponentiating raw logits is a fast path to numerical disaster — and the fix is the reason frameworks ask for logits, not probabilities.

Act 4 · The trick that dodges NaN

01/05

ooologit o2.01.00.1exp(o)7.392.721.11÷ sum0.660.240.10friendly logits → the naive recipe is fine

Softmax exponentiates. For friendly logits like these, the three-step recipe just works — positive, normalized, done.

The lab

Two claims to poke at, yours to break: that the classifier really reads pixels — so corrupting them fools it — and that softmax has a temperature you can turn.

▸ lab

Read the model's mind

Pick a garment, then corrupt it — watch the probabilities wobble and finally flip.

t-shirt99.9%trouser0.1%bag0.0%

prediction: t-shirt ✓ correct

garment

▸ lab

Hot and cold softmax

Same logits, one temperature dial. Cool it toward a confident pick; heat it toward a shrug.

cat65.9%chicken24.2%dog9.9%
decisiveuniform

entropy 0.85 / 1.10 nats

low T → one-hot argmax · high T → flat

logits

How good is "good"?

Accuracy on the training set is worthless — a model can memorize it. What we report is accuracy on a held-out test set, and that number is itself an estimate with error bars. The book works out how many examples you need: to be 95% sure your test error is within ±0.01 of the truth, about 10,000 — which is exactly why so many benchmarks ship a 10,000-image test split.

▸ lab

How many test images?

Slide the test-set size and watch the 95% error bar tighten — the O(1/√n) law, live.

±0.01true error-0.03-0.02-0.010+0.01+0.02+0.03

95% margin

±0.0100

on n = 10,000 test examples

4× the data buys 2× the precision. To guarantee ±0.01 (95%), Hoeffding demands ~14,979 — more than the asymptotic 10,000.

And a test set is only honest once. Reuse it to pick among many models and it quietly turns into a training set — adaptive overfitting.

When the world shifts

Even a perfectly trained, well-tested model can fail the moment it meets the real world, because the world doesn't hold still. The book names three ways the distribution can drift:

The real code

Every act above carries a </> chip with its section's exact PyTorch. The heart of it — softmax and cross-entropy from scratch — is just a few lines:

softmax-regression-scratch.md (PyTorch)
def softmax(X):
    X_exp = torch.exp(X)
    partition = X_exp.sum(1, keepdims=True)
    return X_exp / partition            # broadcasting
 
def cross_entropy(y_hat, y):
    return -torch.log(y_hat[list(range(len(y_hat))), y]).mean()
The dataset, the model, and the concise version (the full tour)
image-classification-dataset.md · softmax-regression-scratch.md · softmax-regression-concise.md (PyTorch)
# Fashion-MNIST: 60,000 train / 10,000 test, 28×28, ten classes
data = d2l.FashionMNIST(batch_size=256)
 
# from scratch: a 784×10 weight matrix + softmax forward pass
class SoftmaxRegressionScratch(d2l.Classifier):
    def __init__(self, num_inputs, num_outputs, lr, sigma=0.01):
        self.W = torch.normal(0, sigma, size=(num_inputs, num_outputs),
                              requires_grad=True)
        self.b = torch.zeros(num_outputs, requires_grad=True)
 
    def forward(self, X):
        X = X.reshape((-1, self.W.shape[0]))
        return softmax(torch.matmul(X, self.W) + self.b)
 
# accuracy: fraction of argmax predictions that match the label
def accuracy(self, Y_hat, Y, averaged=True):
    preds = Y_hat.argmax(axis=1).type(Y.dtype)
    return (preds == Y.reshape(-1)).type(torch.float32).mean()
 
# concise: let the framework fold softmax into a numerically stable loss
class SoftmaxRegression(d2l.Classifier):
    def __init__(self, num_outputs, lr):
        self.net = nn.Sequential(nn.Flatten(), nn.LazyLinear(num_outputs))
 
def loss(self, Y_hat, Y, averaged=True):
    return F.cross_entropy(Y_hat, Y.reshape((-1,)))   # Y_hat are logits
 
model = SoftmaxRegression(num_outputs=10, lr=0.1)
trainer = d2l.Trainer(max_epochs=10)
trainer.fit(model, data)

Three things to remember

  1. Softmax turns scores into a distribution; cross-entropy scores the distribution. Exponentiate to make everything positive, normalize to make it sum to one, then measure the loss as the surprise of the true class — minus its log-probability.
  2. The gradient is prediction minus truth. Softmax and cross-entropy are built for each other: their combined gradient is just ŷ − y, the same clean signal as linear regression, which is what makes classifiers so easy to train.
  3. Trust the test set, but only barely. A held-out score is an estimate that sharpens like 1/√n, so ±0.01 needs ~10,000 examples — and it stops being a true test the moment you reuse it to choose a model.

Adapted from Chapter 4 of Dive into Deep Learning by Zhang, Lipton, Li, and Smola (CC BY-SA 4.0). The figures run this site's verified TypeScript port; all computation happens live in your browser.