(trying a clearer explanation of backprop)

terms:

- $J$ = cost function
- $w_i$ = weights for layer $i$
- $b_i$ = biases for layer $i$
- $f_i$ = activation function for layer $i$
- $l_i = w_i f_{i-1}(l_{i-1}) + b_i$
- $n$ = number of layers, i.e. $i=n$ is the output layer
- $d$ = number of input dimensions

The goal of backprop is to compute weight and bias updates, i.e. to compute $\frac{\partial J}{\partial w_i}$ and $\frac{\partial J}{\partial b_i}$ for all $i \in [1, n]$. We basically do so through applying the chain rule of derivatives.

For a layer $i$ we want to compute $\frac{\partial J}{\partial l_i}$ because we can easily compute $\frac{\partial J}{\partial w_i}$ and $\frac{\partial J}{\partial b_i}$ from it:

$$
\begin{aligned}
\frac{\partial J}{\partial w_i} &= \frac{\partial J}{\partial l_i} \frac{\partial l_i}{\partial w_i} \\
\frac{\partial J}{\partial b_i} &= \frac{\partial J}{\partial l_i} \frac{\partial l_i}{\partial b_i}
\end{aligned}
$$

Also note that (these derivatives are quite easy to work out on your own):

$$
\begin{aligned}
\frac{\partial l_i}{\partial w_i} &= f_{i-1}(l_{i-1}) \\
\frac{\partial l_i}{\partial b_i} &= 1
\end{aligned}
$$

To clarify, $\frac{\partial l_i}{\partial w_i} = f_{i-1}(l_{i-1})$ just means that it equals the output of the previous layer.

With these in mind, we can carry out backpropagation.

We start with the output layer, i.e. with $i=n$.

$$
\begin{aligned}
\frac{\partial J}{\partial l_n} &= \frac{\partial J}{\partial f_n(l_n)} \frac{\partial f_n(l_n)}{\partial l_n} \\
&= \frac{\partial J}{\partial f_n(l_n)} f_n'(l_n)
\end{aligned}
$$

Note that $\frac{\partial J}{\partial f_n(l_n)}$ is just the derivative of the cost function with respect to the network's predicted values, i.e. $J'(h(x))$.

With $\frac{\partial J}{\partial l_n}$ we can compute the updates for $w_n$ and $b_n$ using the relationship shown above.

Now we move backwards a layer to compute $\frac{\partial J}{\partial l_{n-1}}$. Starting with the full chain rule version:

$$
\frac{\partial J}{\partial l_{n-1}} = \frac{\partial J}{\partial f_n(l_n)} \frac{\partial f_n(l_n)}{\partial l_n} \frac{\partial l_n}{\partial f_{n-1}(l_{n-1})} \frac{\partial f_{n-1}(l_{n-1})}{\partial l_{n-1}}
$$

But we can simplify this a bit, especially because we've already computed $\frac{\partial J}{\partial l_n}$:

$$
\begin{aligned}
\frac{\partial J}{\partial l_{n-1}} &= \frac{\partial J}{\partial l_n} \frac{\partial l_n}{\partial f_{n-1}(l_{n-1})} \frac{\partial f_{n-1}(l_{n-1})}{\partial l_{n-1}} \\
&= \frac{\partial J}{\partial l_n} \frac{\partial l_n}{\partial f_{n-1}(l_{n-1})} f_{n-1}'(l_{n-1})
\end{aligned}
$$

Also note that because:

$$
l_n = w_n f_{n-1}(l_{n-1}) + b_n
$$

Then (again, this is easy to show with basic derivative rules):

$$
\frac{\partial l_n}{\partial f_{n-1}(l_{n-1})} = w_n
$$

Therefore:

$$
\frac{\partial J}{\partial l_{n-1}} = \frac{\partial J}{\partial l_n} w_n f_{n-1}'(l_{n-1})
$$

Then we can again go from this to $\frac{\partial J}{\partial w_{n-1}}$ and $\frac{\partial J}{\partial b_{n-1}}$ using the relationship described earlier.

We can generalize what we just did for any layer $i$:

$$
\frac{\partial J}{\partial l_i} = \frac{\partial J}{\partial l_{i+1}} w_{i+1} f_i'(l_i)
$$

And then use the relationship described earlier to go from this to $\frac{\partial J}{\partial w_i}$ and $\frac{\partial J}{\partial b_i}$.

To summarize:

Compute for the output layer, i.e. $i=n$:

$$
\frac{\partial J}{\partial l_n} = J'(h(x)) f_n'(l_n)
$$

Then for all other layers $i \neq n$ (except for the input layer, that has no parameters):

$$
\frac{\partial J}{\partial l_i} = \frac{\partial J}{\partial l_{i+1}} w_{i+1} f_i'(l_i)
$$

Then, for all layers $i \in [1, n]$, compute the weight and bias updates:

$$
\begin{aligned}
\text{weight update} &= \frac{\partial J}{\partial l_i} \frac{\partial l_i}{\partial w_i} = \frac{\partial J}{\partial l_i} f_{i-1}(l_{i-1}) \\
\text{bias update} &= \frac{\partial J}{\partial l_i} \frac{\partial l_i}{\partial b_i} = \frac{\partial J}{\partial l_i}
\end{aligned}
$$

Note that $f_0(l_0) = X$ (the input layer's output is just the input $X$).