## Background

Backpropagation is a common method for training a neural network. There is no shortage of papers online that attempt to explain how backpropagation works, but few that include an example with actual numbers. This post is my attempt to explain how it works with a concrete example that folks can compare their own calculations to in order to ensure they understand backpropagation correctly.

If this kind of thing interests you, you should sign up for my newsletter where I post about AI-related projects that I’m working on.

## Backpropagation in Python

You can play around with a Python script that I wrote that implements the backpropagation algorithm in this Github repo.

## Backpropagation Visualization

For an interactive visualization showing a neural network as it learns, check out my Neural Network visualization.

## Additional Resources

If you find this tutorial useful and want to continue learning about neural networks and their applications, I highly recommend checking out Adrian Rosebrock’s excellent tutorial on Getting Started with Deep Learning and Python.

## Overview

For this tutorial, we’re going to use a neural network with two inputs, two hidden neurons, two output neurons. Additionally, the hidden and output neurons will include a bias.

Here’s the basic structure:

In order to have some numbers to work with, here are the initial weights, the biases, and training inputs/outputs:

The goal of backpropagation is to optimize the weights so that the neural network can learn how to correctly map arbitrary inputs to outputs.

For the rest of this tutorial we’re going to work with a single training set: given inputs 0.05 and 0.10, we want the neural network to output 0.01 and 0.99.

## The Forward Pass

To begin, lets see what the neural network currently predicts given the weights and biases above and inputs of 0.05 and 0.10. To do this we’ll feed those inputs forward though the network.

We figure out the *total net input* to each hidden layer neuron, *squash* the total net input using an *activation function* (here we use the *logistic function*), then repeat the process with the output layer neurons.

*net input*by some sources.

Here’s how we calculate the total net input for :

We then squash it using the logistic function to get the output of :

Carrying out the same process for we get:

We repeat this process for the output layer neurons, using the output from the hidden layer neurons as inputs.

Here’s the output for :

And carrying out the same process for we get:

### Calculating the Total Error

We can now calculate the error for each output neuron using the squared error function and sum them to get the total error:

For example, the target output for is 0.01 but the neural network output 0.75136507, therefore its error is:

Repeating this process for (remembering that the target is 0.99) we get:

The total error for the neural network is the sum of these errors:

## The Backwards Pass

Our goal with backpropagation is to update each of the weights in the network so that they cause the actual output to be closer the target output, thereby minimizing the error for each output neuron and the network as a whole.

### Output Layer

Consider . We want to know how much a change in affects the total error, aka .

By applying the chain rule we know that:

Visually, here’s what we’re doing:

We need to figure out each piece in this equation.

First, how much does the total error change with respect to the output?

Next, how much does the output of change with respect to its total net input?

The partial derivative of the logistic function is the output multiplied by 1 minus the output:

Finally, how much does the total net input of change with respect to ?

Putting it all together:

You’ll often see this calculation combined in the form of the delta rule:

Alternatively, we have and which can be written as , aka (the Greek letter delta) aka the *node delta*. We can use this to rewrite the calculation above:

Therefore:

Some sources extract the negative sign from so it would be written as:

To decrease the error, we then subtract this value from the current weight (optionally multiplied by some learning rate, eta, which we’ll set to 0.5):

We can repeat this process to get the new weights , , and :

We perform the actual updates in the neural network *after* we have the new weights leading into the hidden layer neurons (ie, we use the original weights, not the updated weights, when we continue the backpropagation algorithm below).

### Hidden Layer

Next, we’ll continue the backwards pass by calculating new values for , , , and .

Big picture, here’s what we need to figure out:

Visually:

We’re going to use a similar process as we did for the output layer, but slightly different to account for the fact that the output of each hidden layer neuron contributes to the output (and therefore error) of multiple output neurons. We know that affects both and therefore the needs to take into consideration its effect on the both output neurons:

Starting with :

We can calculate using values we calculated earlier:

And is equal to :

Plugging them in:

Following the same process for , we get:

Therefore:

Now that we have , we need to figure out and then for each weight:

We calculate the partial derivative of the total net input to with respect to the same as we did for the output neuron:

Putting it all together:

You might also see this written as:

We can now update :

Repeating this for , , and

Finally, we’ve updated all of our weights! When we fed forward the 0.05 and 0.1 inputs originally, the error on the network was 0.298371109. After this first round of backpropagation, the total error is now down to 0.291027924. It might not seem like much, but after repeating this process 10,000 times, for example, the error plummets to 0.000035085. At this point, when we feed forward 0.05 and 0.1, the two outputs neurons generate 0.015912196 (vs 0.01 target) and 0.984065734 (vs 0.99 target).

If you’ve made it this far and found any errors in any of the above or can think of any ways to make it clearer for future readers, don’t hesitate to drop me a note. Thanks!

Thanks dude! finally, i could understand very well this issue.

yes … me too… very incredible tutorial… thx

THANK’S I helped a lot

Greetings from Peru

Great explaination, Thanks!

Great post, clear concepts! Thanks!!

Helps a lot, thanks

nice one

It was a revelation. Equations with many Greek letters does not explain much alone. Keep going!

Btw, why dont you calculate gradients for biases ?

Some guides do that, others don’t. I didn’t for this tutorial.

Is there a possibility you can show us how to calculate the gradients for the biases? Love this tutorial by the way it is very informative and clear!

Can you please do a tutorial for back propagation in Elmann recurrent neural networks!!…. It would be of a lot of help…

Great article! I struggle with one aspect though and that is calculating the partial derivatives from out/net, using (output * (1.0 – output). In cases where output is 0 or 1, it effectively kills the pass through of error. How do you handle this when, especially at startup, the network can devolve (or even start) in this state?

Thanks!

How do you get an output of exactly 0 or 1? The only neuron that can output them is the input neuron (because they output what they take in), but we don’t do anything with them. For any other neuron, output is the result of sigmoid function so it cannot be exactly 0 or 1.

You can round the output to get 0 or 1.

should be the first stop for anyone to understand backward propagation, very well explained…thanks a lot

Very informative, Thank you

In trying to understand NNs better I have produced a Google Docs spreadsheet that almost does what this link talks about:

I was hoping that going through that exercise will give me a better mental picture of what a NN is doing when it is working. I should be able to see easily by direct comparison what happens to the set of numbers with each iteration (ie each new pair of FP and BP sheets). I think I have got it nearly working except for the stuff in the dashed purple box. I want to confirm that the purple arrow is pointing to the wrong blue arrow? I also want someone to tell me how to implement the purple box . . If I could work that out I think I could then repeat the FR and BP sheets and see how the Diff column evolves . . here is the current SS:

https://docs.google.com/spreadsheets/d/1-YxT_PuzDt3VXrOucOBHzxBpSiB5USiy2ULaqE75Wcg/pubhtml

I still found your description above heavy going – could you help me finish my spreadsheet or turn your example into a spreadsheet?

Thanks,

Phil.

This is how any teaching or learning has to happen! Great work buddy!

Many thanks. The best tutorial I have ever seen

It seems that the learningrate of “0.5” in this example here is too high. because it diverges with the given values. Maybe 0.05?

sorry, forgot to set the energy variable in the neuron back to zero after training

I found this post very helpful but there is one thing that is confusing me. You apply the logistic function on the output nodes to compute out_o1 and out_o2 as the sigmoid function applied to the weighted sum of the hidden nodes. My question is, what if you are predicting an output that has a range wider than 0 to 1. I had thought the output layer was simply a weighted sum of the outputs of the final hidden layer. In that case you could output a value in any range, but this seems very limiting.

Yeah, this only works on a range of 0 to 1. If you have a range from say 0 to 100 you can divide by 100 to get it down to a range of 0 to 1 so that you can use this neural network.

what does the Who mean in the expression for the hidden layers?

The “ho” is small

Weight of the hidden output.

Shouldn’t it be the weights connecting the last hidden layer and the output layer? If the network had an input of 200, hidden of 100, and another hidden of 50, and an output of 10; it wouldn’t work. Because the layer of 1 hundred would add the outputs multiplied by 10 of the 50 layer weights, right? Sorry if I am not very clear. What I’m just trying to ask is this: Is the Who referring to the weight connecting the last layer and output layer or is it connecting the current layer and next layer?

You have used a squared error function. When using that as a cost function along with the sigmoid function, won’t your cost function have a lot of local minima ?

Thanks, it’s the best and clear step by step tutorial of the backpropagation alg I have ever read

Thanks so much!! It’s a very clear and thorough explanation :)

Great Post with the step by step explanation.

Based on your explanation I am able to implement the same for LOGICAL GATE solution where I have used 2 Input Nodes (for logical Inputs), 1 Hidden Layer with 2 Nodes and one Output Node.

Thanks for this detailed explanation.

Regards

Neevan

Couldn’t agree less with Hajji.

Its one the best and simplified article.

Thanks for beautiful workout .algorithm has become transparent and very easy

Amazingly simple explanation to something everyone tends to complicate! :) Thanks a lot!!

It was a great explanation, Mazur. Thank you very much for the effort.

It would be great if you could response my following query.

I have been trying to train a model using neuralnet package. My model contains five input one output and a hidden layer with 10 nodes. logistic transfer function is selected as an activation function. All data are normalized in 0,1 range. As the logistic function is bounded between 0,1 the results are expected to be in the range 0,1. But some of the training results are negative. What is causing the model to compute negative output?

Regards

H. Paudel

How would you compute the weight for a hidden layer in a multi-layer network?

反向传播算法入门资源索引 | 我爱自然语言处理

Building a multi-class text classifier from scratch using Neural Networks – Stokastik

you’re the saviour!

Many thanks for tutorial!

But I have a problem, when im trying use more neurons (e.g. 20 inputs and 8 outputs) with more training data, NN total error is almost stagnates after few cycles. Any solution? (adaptive eta?)

Hi – Thanks much , this is the only place I got some valid information in a way I can understand. I have few questions, if you don’t can you please check these out.

Question 1:

You have tried to find derivative of Etotal WRT to W5.

In this how did you get this “-1”(Minus 1), doesn’t the derivate rule equate to following

2*1/2(targeto1-out01) ( No Minus)

Question 2:

In chain rule. To find derivative of Etotal WRT to W5 following was used.

DEtotal/Dw5 = Dnet01/Dw5 * Dout01/ Dnet01 * DEtotal/DOut01

Here please note : DOut01/Dnet01 , Out01 was used and It makes sense.

But to To find derivative of Etotal WRT to W1 following was used.

DOut01/Dnet01 was not used, why is that.

Question 1: Using chain rule, since out_01 has a negative sign before it in the parenthesis, you need to propagate (no pun intended) that negative sign. In other words:

\frac{d}{dx} {(1-x)}^2 = (-1) \times (2-1) \times {(1-x)}^{(2-1)}

Question 2:

Check out the image beneath the writing “Next, we’ll continue the backwards pass by calculating new values for w1, w2, w3, and w4”. He explains that D(E_total)/D(out_h1) = D(E_o1)/D(Out_h1) + D(E_o2)/D(Out_h1). And then he breaks out that first term, D(E_o1)/D(Out_h1), in the next images, which includes your term “DOut01/Dnet01”

Hi Hari – Thank you for your patience and answer. May be I am missing something here, on question 1.

Here derivate of E total WRT Out01 is (target01 – Out01), so the correct value is “-0.74136507”.

Minus sign stating derivative in decreasing , going in downward direction.

I am still not sure how “-0.74136507” changed to “0.74136507”

Mazur, Thank you for your explanation, but i have a little question, i have calculated exactly the same steps you described, but the second round i calculated the total error is 0.292165448

different from what you have written, i have done something wrong in your opinion?

神经网络101 手把手快速入门神经网络基础 | David 9的博客 --- 没有"过拟合"

If you read the original 1986 paper (Learning representations by back-propagating errors), you will find that they calculate the error function in the reverse (out – target). I believe the math works out the same, but the general consensus from a variety of sources is to calculate it as -(target – out).

I would be curious what the original authors, or even Yann LeCun, has to say about this.

You allude to this curiosity early in your rundown. I figured it would be worthwhile to point out its potential origin.

Thank you =))))