Back propagation sounds like something invented to scare undergraduates and sell GPU clusters. It is not. It is just the chain rule with better accounting.

The problem is not that back propagation is mathematically hard. The problem is that it is usually introduced backwards. People show you a wall of matrix notation, sprinkle some deltas on top, and then expect you to feel enlightened. That is like explaining cooking by starting with restaurant logistics.

So let us do it by hand once.

No PyTorch. No TensorFlow. No "the optimizer handles it." Just a tiny neural network, one training example, and enough arithmetic to see what is actually moving.

The network

We will use the smallest network that still feels like a neural network:

  • Two inputs
  • Two hidden neurons
  • One output neuron
  • Sigmoid activation everywhere
  • Mean squared error loss

![[backprop-tiny-network.excalidraw]]

The training example is:

x1 = 1
x2 = 2
target y = 1

The network looks like this:

backprop-network

We start with these weights:

h1:
  w1 = 0.10
  w2 = 0.20
  b1 = 0.00

h2:
  w3 = -0.10
  w4 = 0.10
  b2 = 0.10

output:
  v1 = 0.30
  v2 = -0.20
  b3 = 0.05

The sigmoid function is:

sigmoid(z) = 1 / (1 + exp(-z))

Its derivative is beautifully convenient:

sigmoid'(z) = sigmoid(z)(1 - sigmoid(z))

That little fact is why old neural network textbooks were obsessed with sigmoid. It makes the derivative cheap once you already have the activation.

First, go forward

Before the network can learn, it has to be wrong.

For the first hidden neuron:

z1 = w1*x1 + w2*x2 + b1
z1 = 0.10*1 + 0.20*2 + 0.00
z1 = 0.50

a1 = sigmoid(0.50)
a1 = 0.622459

For the second hidden neuron:

z2 = w3*x1 + w4*x2 + b2
z2 = -0.10*1 + 0.10*2 + 0.10
z2 = 0.20

a2 = sigmoid(0.20)
a2 = 0.549834

Now the output neuron:

z3 = v1*a1 + v2*a2 + b3
z3 = 0.30*0.622459 + (-0.20)*0.549834 + 0.05
z3 = 0.126771

y_hat = sigmoid(0.126771)
y_hat = 0.531650

The network predicts 0.531650. The target is 1.

So the network is wrong by:

y_hat - y = 0.531650 - 1
y_hat - y = -0.468350

We will use the half squared error:

L = 0.5 * (y_hat - y)^2
L = 0.109676

The 0.5 is not philosophical. It is there because the derivative of a square creates a 2, and the 0.5 cancels it. Mathematicians like cleanliness. Occasionally they deserve credit.

What learning actually means

Learning means changing each weight in the direction that reduces the loss.

That is it.

Every weight asks the same question:

If I move a little, what happens to the loss?

That question is a derivative:

dL/dweight

If dL/dweight is positive, increasing the weight increases loss, so we should decrease it.

If dL/dweight is negative, increasing the weight decreases loss, so we should increase it.

The update rule is:

new_weight = old_weight - learning_rate * gradient

We will use:

learning_rate = 0.5

In compact notation:

\[ \theta_{\text{new}}=\theta_{\text{old}}-\eta \nabla_{\theta}L \]

Yes, that is large. This is a toy example, not a production trading system.

Now go backward

Back propagation starts at the loss and walks backward through the network.

The final output depends on z3. z3 depends on v1, v2, b3, a1, and a2. The hidden activations depend on their own weights. The loss sits at the end of this chain.

So we apply the chain rule from the end back to the beginning.

Start with:

L = 0.5 * (y_hat - y)^2

The derivative with respect to the prediction is:

dL/dy_hat = y_hat - y
dL/dy_hat = -0.468350

The prediction came from a sigmoid:

y_hat = sigmoid(z3)

So:

dy_hat/dz3 = y_hat * (1 - y_hat)
dy_hat/dz3 = 0.531650 * (1 - 0.531650)
dy_hat/dz3 = 0.248998

Combine them:

dL/dz3 = dL/dy_hat * dy_hat/dz3
dL/dz3 = -0.468350 * 0.248998
dL/dz3 = -0.116618

This value is often called the output delta:

delta3 = -0.116618

The word "delta" makes it sound more profound than it is. It just means "the loss sensitivity at this neuron before activation."

Update the output weights

The output pre-activation was:

z3 = v1*a1 + v2*a2 + b3

So:

dz3/dv1 = a1
dz3/dv2 = a2
dz3/db3 = 1

Therefore:

dL/dv1 = dL/dz3 * dz3/dv1
dL/dv1 = delta3 * a1
dL/dv1 = -0.116618 * 0.622459
dL/dv1 = -0.072590
dL/dv2 = delta3 * a2
dL/dv2 = -0.116618 * 0.549834
dL/dv2 = -0.064121
dL/db3 = delta3
dL/db3 = -0.116618

All three gradients are negative. That means increasing these parameters will reduce the loss.

Now apply the update rule:

new_v1 = 0.30 - 0.5*(-0.072590) = 0.336295
new_v2 = -0.20 - 0.5*(-0.064121) = -0.167940
new_b3 = 0.05 - 0.5*(-0.116618) = 0.108309

The output layer is done.

This is where most people think the difficult part begins. It does not. The hidden layer is the same idea with one extra link in the chain.

Send blame backward

The hidden neurons affected the loss only through the output neuron.

That sentence is the whole point of back propagation.

The loss does not directly know what w1 or w2 are. It only knows the final prediction was too low. The output neuron then tells the hidden neurons how much they contributed to that mistake.

For hidden neuron h1, we need:

dL/dz1

The chain is:

z1 -> a1 -> z3 -> y_hat -> L

backprop-chain-rule

In LaTeX, the same path is:

\[ \frac{\partial L}{\partial z_1} = \frac{\partial L}{\partial z_3} \frac{\partial z_3}{\partial a_1} \frac{\partial a_1}{\partial z_1} = -0.116618 \cdot 0.30 \cdot 0.235004 = -0.008222 \]

So:

dL/dz1 = dL/dz3 * dz3/da1 * da1/dz1

We already know:

dL/dz3 = delta3 = -0.116618

Since:

z3 = v1*a1 + v2*a2 + b3

we get:

dz3/da1 = v1 = 0.30

And:

da1/dz1 = a1 * (1 - a1)
da1/dz1 = 0.622459 * (1 - 0.622459)
da1/dz1 = 0.235004

Therefore:

dL/dz1 = -0.116618 * 0.30 * 0.235004
dL/dz1 = -0.008222

Call it:

delta1 = -0.008222

Now do the same for h2:

dL/dz2 = dL/dz3 * dz3/da2 * da2/dz2

Here:

dz3/da2 = v2 = -0.20

and:

da2/dz2 = a2 * (1 - a2)
da2/dz2 = 0.549834 * (1 - 0.549834)
da2/dz2 = 0.247517

So:

dL/dz2 = -0.116618 * (-0.20) * 0.247517
dL/dz2 = 0.005773

Call it:

delta2 = 0.005773

Notice the sign changed. That is not a typo. The second hidden neuron connects to the output with a negative weight. If a2 goes up, z3 goes down. Since the output was already too low, increasing a2 makes the prediction worse. The gradient reflects that.

This is why back propagation matters. It does not simply say "make everything bigger because the prediction was too low." It assigns responsibility through the actual wiring of the network.

Update the hidden weights

For h1:

z1 = w1*x1 + w2*x2 + b1

So:

dL/dw1 = delta1 * x1 = -0.008222 * 1 = -0.008222
dL/dw2 = delta1 * x2 = -0.008222 * 2 = -0.016443
dL/db1 = delta1 = -0.008222

Update:

new_w1 = 0.10 - 0.5*(-0.008222) = 0.104111
new_w2 = 0.20 - 0.5*(-0.016443) = 0.208222
new_b1 = 0.00 - 0.5*(-0.008222) = 0.004111

For h2:

z2 = w3*x1 + w4*x2 + b2

So:

dL/dw3 = delta2 * x1 = 0.005773 * 1 = 0.005773
dL/dw4 = delta2 * x2 = 0.005773 * 2 = 0.011546
dL/db2 = delta2 = 0.005773

Update:

new_w3 = -0.10 - 0.5*(0.005773) = -0.102886
new_w4 = 0.10 - 0.5*(0.011546) = 0.094227
new_b2 = 0.10 - 0.5*(0.005773) = 0.097114

And that is one full back propagation step.

Did it work?

Let us run the network forward again with the new weights.

The prediction moves from:

old y_hat = 0.531650

to:

new y_hat = 0.556745

The target is 1, so the prediction moved in the right direction.

The loss moves from:

old loss = 0.109676

to:

new loss = 0.098237

backprop-update-step

That is learning. No magic. No sentience. No tiny ghost in the GPU having opinions. Just a prediction, an error, and a disciplined way of distributing blame.

The general pattern

Every neuron does the same three things.

First, during the forward pass:

z = weighted input + bias
a = activation(z)

Then, during the backward pass, it receives a signal from the layer after it. That signal says:

Here is how much the loss cares about your output.

The neuron multiplies that by the derivative of its activation:

delta = incoming_error * activation_derivative

Then each incoming weight gets a gradient:

gradient_for_weight = delta * input_that_used_that_weight

That is the whole machine.

Deep networks look more intimidating because they contain millions or billions of these operations. But the idea does not change. Back propagation is not a new kind of intelligence. It is the chain rule applied at industrial scale.

Why doing it by hand matters

You do not need to do this by hand every day. That would be a waste of a good life.

But you should do it once.

Because after you do, neural networks stop looking like black boxes in the lazy sense. They may still be hard to interpret at scale, but they are not mystical. Each parameter is nudged according to how much it contributed to the final error. Each layer passes responsibility backward through the weights that connected it to the next layer.

The forward pass makes a claim.

The loss measures how wrong the claim was.

Back propagation computes who should be blamed.

Gradient descent applies the punishment.

That is the training loop.

The rest is scale, architecture, data, hardware, and a frightening amount of matrix multiplication. Important, yes. But conceptually downstream.

If you understand the tiny example above, you understand the central trick. Everything else is engineering.