Every model in this series, from the image classifiers of the 2010s to the speech models behind Pocket TTS, is built from one small part repeated millions of times. That part multiplies some numbers, adds them up, and bends the result. We call it a neuron.
This post builds a network from those parts, small enough that you can hold every number in your head: 2 inputs, 2 hidden neurons, 1 output, 9 adjustable numbers in total. We will push one example through it (the forward pass), measure how wrong it is (the loss), work out who is to blame for the error (backpropagation), and fix it a little (a weight update). The same numbers follow us the whole way, so you can check every step.
Here are the numbers we will use everywhere. Keep this box handy.
| What | Value |
|---|---|
| Inputs | , |
| Target (the right answer) | |
| Hidden neuron 1 | weights , , bias |
| Hidden neuron 2 | weights , , bias |
| Output neuron | weights , , bias |
| Activation | ReLU (rectified linear unit) in the hidden layer, none at the output |
| Learning rate |
Do not worry about any of those words yet. Each one gets its own section.
What a neuron computes
A neuron does two things. First it takes a weighted sum of its inputs: each input is multiplied by its own weight, the results are added up, and one extra number called the bias is added on top. Then it passes that sum through an activation function, a fixed rule that bends the number.
The everyday picture is a judge scoring a dish. The judge looks at a few things (taste, smell, look), cares about some more than others (the weights), has a personal mood that shifts every score up or down a little (the bias), and then turns the raw total into a final verdict (the activation).
The weighted sum
In one sentence: multiply each input by how much it matters, add them up, and add a constant.
- : the inputs, the numbers the neuron receives.
- : the weights, one per input, saying how strongly that input pushes the result up (positive weight) or down (negative weight).
- : the bias, a number added no matter what the inputs are. It lets the neuron give a non-zero answer even when every input is zero.
- : the result before bending, often called the pre-activation.
Worked example with hidden neuron 1 from our box: , , , , .
- 1.0
What if a number changes? Raise from to and the second product doubles to , so becomes . Because , every change to moves by twice as much. That fact (the input decides how sensitive is to its weight) will come back when we compute every gradient.
The activation
In one sentence: the activation takes and returns a new number , using a fixed, usually curved, rule.
- : the activation function, the same rule for every neuron in a layer.
- : the neuron’s output, called its activation. This is what the next layer receives.
For hidden neuron 1 we use ReLU, which keeps positive numbers and replaces negative numbers with zero. is positive, so . The next section explains why we bother with at all.
Why we need non-linear activations
Why bend the number at all? Why not just pass straight on? The short answer: without the bend, a hundred layers can do no more than one layer.
Three common activations
Each of these takes any number and returns a number. Here is each one in a plain sentence, then the formula.
Sigmoid squashes any number into the range between 0 and 1: big positive numbers come out near 1, big negative numbers near 0, and zero comes out as exactly one half.
- : the Greek letter sigma, the usual name for this function.
- : a fixed number, about . is multiplied by itself times; you only need to know it is huge when is very negative and tiny when is very positive.
Example: 0.5. 0.731. Raise to and is about ; going further barely changes it. That flat region matters in the vanishing gradients post.
Tanh (hyperbolic tangent) is the same S shape stretched to run from −1 to 1, centred on zero.
Example: , 0.762, . It is symmetric: flip the sign of the input, and the output flips too.
ReLU (rectified linear unit) keeps positive numbers and turns negative numbers into zero.
- : whichever is larger, zero or .
Example: , , . The everyday picture is a one-way valve: water flows through in one direction, and nothing at all comes back the other way. ReLU became the default for deep networks after Nair and Hinton (2010) and Glorot, Bordes and Bengio (2011) showed it trains faster than sigmoid and tanh in deep stacks, partly because its slope for positive inputs is always 1 and never shrinks.
The slopes in that figure are not decoration. When we pass blame backwards, every neuron multiplies the blame by its slope. A slope near zero means almost no blame gets through.
Stacking linear layers stays linear
Suppose we skip the activation, so each layer just multiplies and adds. Take a one-number toy. Layer A doubles its input and adds 1. Layer B triples its input and subtracts 1.
- Layer A:
- Layer B:
Put A’s output into B:
Two layers, and the result is one multiply () and one add (): exactly what a single layer could have done. Check with : A gives , B gives , and the single layer gives . Same answer. This is true however many layers you stack and however many inputs each has: a chain of multiply-and-add steps is always one multiply-and-add step. The everyday picture is stacking magnifying glasses. Two lenses give a stronger zoom, but they still only zoom; they can never bend a straight line into a curve.
Now put a ReLU between them: . For , nothing changes (, ). But for , , ReLU turns it into , and , whereas the straight line would give . The graph now has a corner at . Corners, added up across many neurons, can build any shape we like. This is why every network in this series puts a non-linear activation between its layers.
Layers and the matrix form
A layer is a group of neurons that all read the same inputs. Our hidden layer has two neurons, both reading and . Writing each neuron out separately gets long, so we use two pieces of shorthand: vectors and matrices.
A vector is just a list of numbers written in a column. Our inputs are the vector
A matrix is a table of numbers. We put each neuron’s weights in its own row:
- : the weight from input into hidden neuron . So is “into neuron 2, from input 1”.
- : the list of biases, one per neuron.
In one sentence: multiplying a matrix by a vector means doing one weighted sum per row.
Row 1 times : ; plus gives 1. Row 2 times : ; plus gives 0.5.
So . Then the activation is applied to each entry separately: . The everyday picture is a row of judges at a contest, each holding their own scorecard (a row of ) and all tasting the same dish ().
The matrix form is not a new idea, just a shorter way of writing the same sums. It matters in practice because computers, and especially graphics cards, are built to do these row-times-column sums for thousands of neurons at once.
A forward pass through the tiny network
The forward pass means running the inputs through the network, layer by layer, to get an answer. Our network has two layers of weights.
Hidden layer (done above): , , , .
Output layer: one neuron reading and with weights , and bias , and no activation (we want any number out, not just positive ones).
-
: read “y hat”, the network’s prediction. The hat means “our guess at ”.
-
-
-
0.75
The target was , so the network is short by .
Measuring the error with a loss
To improve, the network needs one number that says how wrong it was. That number is the loss. In one sentence: square the gap between guess and target, and halve it.
- : the loss. Zero means perfect; bigger means worse.
- : the gap, or error. Here .
- The square makes the loss positive whether we guessed too high or too low, and punishes big misses much more than small ones.
- The is only there to make the slope tidy, as we will see in a moment.
Our loss: , halved: 0.03125.
If the guess had been instead, the gap would double to and the loss would quadruple to . This is called squared error, and the next post on loss functions compares it with other choices and explains when each one fits.
The chain rule from scratch
Our goal is to find, for each of the nine weights and biases, how much the loss changes if we nudge that number a tiny bit. That “how much” is called the gradient (or slope, or derivative) of the loss with respect to that number, written and read “the change in per change in ”.
The trouble is that a weight like does not touch the loss directly. It changes , which changes , which changes , which changes . We need a way to combine effects along a chain. That is the chain rule.
An everyday picture: gears
Picture three gears in a row. Turn gear A once and gear B turns 3 times. Turn gear B once and gear C turns 2 times. How many times does C turn when A turns once?
6. You multiply the ratios along the chain.
That is the whole chain rule. In one sentence: when one thing affects another through a middle step, the overall rate of change is the product of the rates of each step.
- : how much B moves per unit move of A (here 3).
- : how much C moves per unit move of B (here 2).
- The left side: how much C moves per unit move of A (here 6).
Check with the numbers from the linear collapse example: changes by per unit of ; changes by per unit of ; so should change by per unit of . And indeed . The chain rule just told us the without expanding anything.
If one gear in the chain is jammed (its ratio is ), the whole product is and nothing downstream moves. A ReLU with a negative input is exactly such a jammed gear.
The local slopes we need
Every step in our network is one of three simple operations, and each has an easy slope.
- Multiply by a constant, : the slope of with respect to is , and with respect to is . (Each factor’s slope is the other factor.) Example: ; nudge by and the product moves by , which is times the nudge.
- Add, : the slope with respect to each part is . Adding passes changes through unchanged.
- ReLU, : slope if , slope if .
- Half square, : slope with respect to is . (The from the square cancels the ; that is why the half was there.) Here it is .
Backpropagation: passing blame backwards
Backpropagation is a way of computing every gradient in the network in one sweep from the output back to the inputs. In one sentence: start with the blame at the loss, and at every step backwards multiply by that step’s local slope.
The everyday picture is a relay team that lost a race by 0.25 seconds. The coach starts at the finish line and walks back down the track. Each runner gets handed the blame from the runner after them, and passes on to the runner before them a share scaled by how much their own leg mattered. Nobody has to re-run the race; one walk back down the track is enough.
Why walk backwards rather than forwards? Because the blame on is needed by every weight feeding into . Computing it once and reusing it is what makes backpropagation fast. The same reuse is why a network with a billion weights can get all billion gradients for roughly the cost of two or three forward passes.
The method was popularised for neural networks by Rumelhart, Hinton and Williams (1986). The underlying idea, reverse-mode differentiation, is older: Linnainmaa described it in 1970, and Werbos proposed applying it to neural networks in his 1974 thesis.
We write (delta) for “blame on this quantity”, meaning .
Every gradient of the tiny network
We walk back from the loss. Each line multiplies the blame arriving from the right by one local slope.
At the output. The blame on the prediction is the slope of the half square:
Negative blame means “if went up, the loss would go down”. That matches what we know: the guess was too low.
The output weights and bias. . The slope of with respect to is (each factor’s slope is the other factor), with respect to is .
- −0.25
- −0.125
- −0.25
Passing blame to the hidden activations. The slope of with respect to is .
Notice is positive. reaches the output through a negative weight, so raising would lower , which is the wrong direction. The blame says “push down”.
Through the ReLUs. Both and are positive, so both ReLU slopes are and the blame passes through unchanged.
The hidden weights and biases. , so the slope with respect to is , with respect to is , with respect to is .
- −0.25
- −0.5
- −0.25
- 0.25
- 0.5
- 0.25
Read one end to end, say : it is the product of every local slope on its path, . That is the gear chain from before, four gears long.
What would change? If had been negative, the ReLU slope would be , and , and would all get gradient : that neuron learns nothing from this example. A neuron stuck in that state for every example is called a “dead ReLU”. And because is twice , the weights on always get twice the gradient of the weights on : larger inputs mean larger blame.
One weight update
Now we know which way to push each number. In one sentence: move every weight a small step against its gradient.
- : the Greek letter eta, the learning rate, how big a step we take. Ours is .
- The minus sign: the gradient points uphill (the direction that increases the loss), so we step the other way.
The everyday picture is walking down a foggy hill: you cannot see the bottom, but you can feel which way the ground slopes under your feet, so you take a small step downhill.
Applying it to all nine numbers:
| Number | Old | Gradient | Step gradient | New |
|---|---|---|---|---|
| 0.5 | −0.25 | +0.025 | 0.525 | |
| 0.25 | −0.5 | +0.05 | 0.3 | |
| 0 | −0.25 | +0.025 | 0.025 | |
| −1 | 0.25 | −0.025 | −1.025 | |
| 0.5 | 0.5 | −0.05 | 0.45 | |
| 0.5 | 0.25 | −0.025 | 0.475 | |
| 1 | −0.25 | +0.025 | 1.025 | |
| −1 | −0.125 | +0.0125 | −0.9875 | |
| 0.25 | −0.25 | +0.025 | 0.275 |
Run the forward pass again with the new numbers:
- 1.15, so
- 0.35, so
- 1.108125
- 0.00585
The loss fell from 0.03125 to about 0.00585, more than five times smaller, after one step.
Notice that the prediction jumped from past the target to about . The step was a little too big: all nine numbers moved at once, and their effects added up. With the steps would be half as large and the overshoot smaller. With the first step flies far past: the loss jumps to 3.45, then 4.88 after the second step, both hidden neurons end up with negative , and the ReLUs shut them off for good. Choosing the step size well is the subject of the optimisers post.
The computational graph view
Everything we did can be drawn as a computational graph: a diagram where each box is one simple operation (multiply, add, ReLU, half square) and arrows show which results feed which.
Reading the graph left to right is the forward pass: each box computes its output from its inputs and remembers them. Reading it right to left is the backward pass: each box receives a blame from its output side, multiplies it by its local slope, and sends the result to each of its inputs. No box needs to know anything about the rest of the network. A multiply box only knows “my slope for one input is the other input”; a ReLU box only knows “pass the blame if my input was positive, otherwise send zero”.
Two rules cover every case:
- Along a chain, multiply. That is the chain rule.
- Where a value feeds two places, add the blames coming back. In our network, feeds both hidden neurons. If we wanted the blame on itself, it would be . (We never update inputs, but deeper networks need exactly this sum for every hidden neuron that feeds more than one neuron in the next layer.)
This is how deep learning libraries work. When you write the forward pass in PyTorch, it quietly records the graph; calling loss.backward() walks it in reverse with these two rules. Every network later in this series, from convolutions to attention, is just a bigger graph made of different boxes, and the same walk computes its gradients.
The last figure ties it together on a problem a single layer cannot solve. XOR (exclusive or) outputs 1 when exactly one of its two inputs is 1. Minsky and Papert (1969) showed that a single-layer perceptron cannot compute it; a hidden layer with a non-linear activation can.
Counting the parameters
The weights and biases are the network’s parameters: the numbers training is allowed to change. A model’s size, as in “124 million parameters”, is just their count.
A dense layer (every input connected to every output) with inputs and outputs has
parameters.
- : one weight per line from an input to an output, the entries of the weight matrix .
- : one bias per output neuron.
Our tiny network has two such layers. The hidden layer, 2 inputs to 2 neurons: . The output layer, 2 inputs to 1 neuron: . In total 9, exactly the nine numbers the weight update changed. The activation (ReLU) has no parameters: it is a fixed rule, not something learned.
In PyTorch every layer keeps its learnable numbers in .parameters(), and p.numel() is how many numbers a tensor holds, so one line adds them up.
import torch.nn as nn
net = nn.Sequential(nn.Linear(2, 2), nn.ReLU(), nn.Linear(2, 1))
for name, p in net.named_parameters():
print(name, tuple(p.shape), p.numel())
print("total", sum(p.numel() for p in net.parameters()))
It prints:
0.weight (2, 2) 4
0.bias (2,) 2
2.weight (1, 2) 2
2.bias (1,) 1
total 9
PyTorch stores a layer’s weight matrix as (outputs, inputs), the transpose of how we wrote above; the count is the same.
What to remember
- A neuron computes a weighted sum plus a bias, , then bends it with an activation.
- Without a non-linear activation, any stack of layers collapses into one layer ().
- A layer is many neurons at once: , one weighted sum per row.
- The chain rule multiplies local slopes along a path, like gear ratios.
- Backpropagation walks from the loss back to the inputs, multiplying the blame by each step’s local slope, and adding blame where paths merge.
- One update moves each weight against its gradient by learning rate times gradient. On our network, loss went from 0.03125 to 0.00585 in one step.
Try it yourself
- SingleNeuron: set , , , , . You should see . Switch to sigmoid: . Now set with ReLU: , .
- ActivationExplorer: move to 4. ReLU’s slope should still read ; sigmoid’s should read , tanh’s .
- LinearCollapse: with , , , and ReLU off, the collapsed layer reads . Turn ReLU on and set : instead of .
- ForwardPassSteps: step through with the defaults and confirm , . Then set : switches off and .
- BackwardPassSteps: with the defaults, Step 10 must show . With , all three of neuron 2’s gradients must read 0.
- WeightUpdateStep: with , one update gives . Try and and watch the 20-update curve; at the first update gives a loss of 3.4453.
- XorTrainer: with the defaults, all four points should be right from epoch 530, and the loss should read 0.0012 at epoch 2000. Choose “None (linear)”: the loss stalls at 0.125. Try seed 2 to see a start that gets stuck.
Sources
- Warren S. McCulloch and Walter Pitts (1943). “A logical calculus of the ideas immanent in nervous activity.” Bulletin of Mathematical Biophysics, 5, 115–133. The first mathematical model of a neuron.
- Frank Rosenblatt (1958). “The perceptron: a probabilistic model for information storage and organization in the brain.” Psychological Review, 65(6), 386–408.
- Marvin Minsky and Seymour Papert (1969). Perceptrons. MIT Press. The XOR limitation of single-layer networks.
- Seppo Linnainmaa (1970). The representation of the cumulative rounding error of an algorithm as a Taylor expansion of the local rounding errors. Master’s thesis (in Finnish), University of Helsinki. The first description of reverse-mode automatic differentiation.
- Paul J. Werbos (1974). Beyond Regression: New Tools for Prediction and Analysis in the Behavioral Sciences. PhD thesis, Harvard University.
- David E. Rumelhart, Geoffrey E. Hinton and Ronald J. Williams (1986). “Learning representations by back-propagating errors.” Nature, 323, 533–536.
- Vinod Nair and Geoffrey E. Hinton (2010). “Rectified linear units improve restricted Boltzmann machines.” Proceedings of ICML 2010 (International Conference on Machine Learning).
- Xavier Glorot, Antoine Bordes and Yoshua Bengio (2011). “Deep sparse rectifier neural networks.” Proceedings of AISTATS 2011 (Artificial Intelligence and Statistics), PMLR 15, 315–323.