Vocabulary for this course

The terms the chapters assume, in plain words. You can open this from any chapter.

activation
A rule applied to each number in a layer that lets the network bend, rather than only scale and add.
autograd
Software that records operations during a forward pass and later computes their gradients automatically.
backpropagation
Working backwards through a network to find how each weight affects the loss, by multiplying local slopes.
batch
A group of examples processed together in one step, averaged into a single update.
epoch
One full pass over the training examples.
forward pass
Running inputs through a network to produce outputs, before any error is measured.
gradient
The direction and steepness in which a value rises fastest; training moves weights the opposite way.
learning rate
How far each update moves the weights; too large overshoots, too small crawls.
loss
One number saying how wrong the current predictions are, which training tries to reduce.
optimiser
The procedure that uses gradients to decide how each weight changes.
overfitting
When a network memorises accidental details of its training examples and does worse on new ones.
regularisation
Any penalty or limit that discourages a network from chasing noise in its training examples. The aim is better performance on new data.
tensor
A nested list of numbers with a shape that says what each axis means.

Deep learning from the ground up

Capstone: a tiny autograd

A scalar autograd engine is a hundred lines: a value, its gradient, the operation that made it, and a reverse walk over the graph. Training a network then needs nothing else.

Part 10 of 10

Advanced18 min readBack to Courses

A value and its history

The central object is a number with three extra fields. It has a gradient, a list of the values that produced it, and a function that knows how to push a gradient back to them. Every operation returns a new value and records the connection. Addition and multiplication are enough to build a surprising amount, and a tanh supplies the nonlinearity.

The forward pass is ordinary arithmetic with bookkeeping attached. When you multiply two values, the result stores its parents and the local derivative. Its parents are the two inputs, and the derivative with respect to one is the other. Nothing is computed yet. The graph is a record of what happened, waiting for a gradient to flow backward through it.

A scalar engine is enough to see the mechanism, because every value is one number. There is no shape to check and no broadcasting to reason about. The only things left are the chain rule and the order of the walk. Those are the right things to learn first: the bugs are conceptual, not dimensional.

Backward is a topological walk

To run backward, collect every node reachable from the output. Order them so that each node comes after its parents, then walk the reverse. Seed the output gradient with one. Each node calls its own backward function, which adds the incoming gradient times the local derivative to each parent. Addition accumulates, which is exactly what a shared node needs.

The order matters. If a node is visited before one of its consumers has sent a gradient, it forwards a partial value and the rest is lost. A depth-first post-order gives a valid order for a graph without cycles, and a forward pass always produces such a graph. A framework does the same walk, with tensors instead of scalars.

Cycles are impossible in a forward pass. Every operation creates a new value from existing ones, so time only moves forward. That is what makes the topological sort terminate. A recurrent network unrolled over time is still a finite acyclic graph. Acyclic means it has no loops. It has one copy of the cell per step. Backpropagation through time is therefore the same algorithm with a longer chain.

The same graph in PyTorch

import torch

w = torch.tensor([0.5, -0.5], requires_grad=True)
x = torch.tensor([1.0, 2.0])

loss = (w * x).sum().tanh()
loss.backward()

print(loss.item())
print(w.grad)   # d loss / d w

A scalar autograd engine

Run the expression and read the gradients, then compare them with a finite difference. The engine has no special cases: every derivative comes from the local rule of one operation.

Output
Click "Run" to execute your code

What a real engine adds

A production engine works on arrays rather than scalars. One node then represents a whole tensor, and its backward function is a matrix operation. The engine fuses kernels, runs on a GPU, reuses memory, and handles broadcasting by summing over the axes that were stretched. None of that changes the idea. It changes the constant factor by many orders of magnitude.

It also adds numerical care. A softmax is computed with the maximum subtracted. A log-sum-exp avoids overflow, and a division guards against a zero denominator. A scalar engine written for teaching can ignore those details, because the numbers are small. A real one cannot. That care is most of the difference between a demo and a library.

Memory is the other difference. A production engine frees intermediate buffers as soon as their gradient has been consumed. It can also recompute an activation instead of storing it when memory is tight. Checkpointing trades compute for memory at the granularity of a block. The scalar engine keeps everything, which is fine for four examples and impossible for a real batch.

Build the scalar version once. When a real framework gives you a shape error or a silent zero gradient, you will know exactly which part of the machine is talking.

From engine to network

A network is a function that composes values. Its parameters are leaves that no operation produced. Training is the same loop as before: forward, loss, backward, update, clear. The only new requirement is that the parameters are reachable from the loss through the graph. They are, if every layer used them.

This is the whole stack. Batching, optimisers and regularisation are additions around this loop, not replacements for it. Once the scalar version trains an XOR, a framework is a faster implementation of the same idea, not a black box.

The natural next step is to replace the scalar with a small array class and let the operations work elementwise. Broadcasting and matrix multiplication are the two additions that make it useful. Both are more about shape bookkeeping than about calculus. The backward rules stay the same. Only the data they move grows.

Train XOR with the engine

Run the tiny MLP and watch the loss fall. The four XOR points cannot be separated by a line; two tanh layers are enough to fit them.

Output
Click "Run" to execute your code

Check what you learned

One question at a time. A wrong answer explains itself — take another go.

Part 1 of 5

What does a scalar autograd value need besides its number?