CS6910 · Deep Learning · IIT Madras

Feedforward networks and backpropagation

A feedforward network is a chain of matrix-vector products and element-wise non-linearities, so the chain rule gives its gradient one layer at a time. This lecture fixes the notation, chooses output and loss functions, and derives every gradient that backpropagation needs, ending with a few lines of pseudocode.

Lecture 4 Instructor Prof. Mitesh M. Khapra About 152 min of video 8 modules

At a glance

  • A feedforward network with \(L-1\) hidden layers computes, layer by layer from \(\mathbf{h}_0 = \mathbf{x}\), a pre-activation \(\mathbf{a}_k = \mathbf{b}_k + W_k\mathbf{h}_{k-1}\) and an activation \(\mathbf{h}_k = g(\mathbf{a}_k)\), and ends with \(\hat{\mathbf{y}} = O(\mathbf{a}_L)\). Written out, the whole network is one composite function of \(\mathbf{x}\).
  • Training uses the gradient descent of Lecture 3, \(\theta_{t+1} = \theta_t - \eta\nabla\theta_t\), with \(\theta\) now holding every weight and bias. It needs a loss function and a way to compute \(\nabla\theta\).
  • Real-valued outputs use a linear output with squared error. Classification uses a softmax output with cross-entropy, which for a one-hot label is \(-\log\hat y_\ell\).
  • Backpropagation applies the chain rule one layer at a time, starting at the loss: \(\nabla_{\mathbf{a}_L}\mathcal{L} = -(\mathbf{e}(\ell) - \hat{\mathbf{y}})\), then \(\nabla_{\mathbf{h}_{k-1}}\mathcal{L} = W_k^\top\nabla_{\mathbf{a}_k}\mathcal{L}\) and \(\nabla_{\mathbf{a}_{k-1}}\mathcal{L} = \nabla_{\mathbf{h}_{k-1}}\mathcal{L}\odot g'(\mathbf{a}_{k-1})\).
  • The parameter gradients are an outer product and a copy: \(\nabla_{W_k}\mathcal{L} = \nabla_{\mathbf{a}_k}\mathcal{L}\,\mathbf{h}_{k-1}^\top\) and \(\nabla_{\mathbf{b}_k}\mathcal{L} = \nabla_{\mathbf{a}_k}\mathcal{L}\). A forward pass stores every \(\mathbf{a}_k, \mathbf{h}_k\) and the backward pass reuses them. For the logistic function \(g' = g(1-g)\); for tanh \(g' = 1-g^2\).

The network: layers, units and parameters

The course so far went from McCulloch–Pitts neurons and perceptrons (Lecture 2) to sigmoid neurons, gradient descent for a single neuron, and the representation power of networks of sigmoid neurons (Lecture 3). This lecture fixes the notation for a deep network of such neurons and then works out how to learn all of its weights. The same notation is used for the rest of the course, so it is worth learning carefully. The slides credit Hugo Larochelle's video lectures on backpropagation and Andrej Karpathy's CS231n (Winter 2016) lecture as sources.

A feedforward neural network, also called a multilayered network of neurons, has these parts:

  • Input. An \(n\)-dimensional vector \(\mathbf{x} \in \mathbb{R}^n\), for example \(n\) measurements of a site (salinity, pressure, temperature, …) used to decide whether there is oil there.
  • Hidden layers. \(L-1\) of them, each with \(n\) neurons. The running example has two hidden layers, and the drawings use \(n = 3\).
  • Output layer. \(k\) neurons, for example one per class. With two hidden layers the network has \(L = 3\) layers in total.
  • Two parts per neuron. Every hidden and output neuron is split into a pre-activation and an activation. For layer \(i\) these form vectors \(\mathbf{a}_i = [a_{i1}, \dots, a_{in}]\) and \(\mathbf{h}_i = [h_{i1}, \dots, h_{in}]\), both in \(\mathbb{R}^n\).
  • Layer numbers. The input is layer 0 and the output is layer \(L\). The input is also written \(\mathbf{h}_0 = \mathbf{x}\). The output activation has three names, \(\mathbf{h}_L = \hat{\mathbf{y}} = \hat f(\mathbf{x})\).
  • Weights and biases. Every neuron in layer \(i-1\) is connected to every neuron in layer \(i\). Those \(n \times n\) weights form a matrix \(W_i \in \mathbb{R}^{n\times n}\), and each neuron of layer \(i\) has one bias, giving \(\mathbf{b}_i \in \mathbb{R}^n\), for \(0 < i < L\). Into the output layer the weights are \(W_L \in \mathbb{R}^{k\times n}\) and the biases \(\mathbf{b}_L \in \mathbb{R}^k\).
W₁, b₁ℝⁿˣⁿ, ℝⁿW₂, b₂ℝⁿˣⁿ, ℝⁿW₃, b₃ℝᵏˣⁿ, ℝᵏx₁x₂xₙ⋮⋮⋮⋮a₁₁ | h₁₁a₃₁ | ŷ₁Layer 0: inputh₀ = x ∈ ℝⁿHidden layer 1a₁, h₁ ∈ ℝⁿHidden layer 2a₂, h₂ ∈ ℝⁿLayer L = 3: outputa₃, h₃ = ŷ ∈ ℝᵏWhite half: pre-activation a. Shaded half: activation h = g(a), or O(a) at the output.
Notation for a feedforward network with \(L = 3\). Every hidden and output neuron has a pre-activation (white half) and an activation (shaded half). \(W_i\) and \(\mathbf{b}_i\) connect layer \(i-1\) to layer \(i\). With \(n\) neurons in each hidden layer, \(W_1, W_2 \in \mathbb{R}^{n\times n}\) and \(W_3 \in \mathbb{R}^{k\times n}\).

Giving every hidden layer the same \(n\) is only for convenience. Layers can have different sizes, and nothing in the derivations depends on them being equal (see the dimension check further down).

Layer \(i\) takes the previous layer's activation, forms a weighted sum plus a bias, and passes the result through a non-linearity:

\[ \mathbf{a}_i = \mathbf{b}_i + W_i\,\mathbf{h}_{i-1}, \qquad \mathbf{h}_i = g(\mathbf{a}_i), \qquad \hat f(\mathbf{x}) = \mathbf{h}_L = O(\mathbf{a}_L) \]

\(g\) is the activation function of the hidden layers (logistic, tanh, linear, …; later lectures add ReLU and its relatives, see Lecture 7). \(O\) is the output activation function, for example softmax or linear. All of these quantities depend on the input, \(\mathbf{a}_i(\mathbf{x})\) and \(\mathbf{h}_i(\mathbf{x})\), but the \((\mathbf{x})\) is dropped to keep the notation light.

For layer 1 with \(n = 3\), the pre-activation is one matrix-vector product and one vector addition:

\[ \begin{bmatrix} a_{11}\\ a_{12}\\ a_{13} \end{bmatrix} = \begin{bmatrix} b_{11}\\ b_{12}\\ b_{13} \end{bmatrix} + \begin{bmatrix} W_{111} & W_{112} & W_{113}\\ W_{121} & W_{122} & W_{123}\\ W_{131} & W_{132} & W_{133} \end{bmatrix} \begin{bmatrix} x_1\\ x_2\\ x_3 \end{bmatrix} \]

The weight \(W_{1ij}\) sits in row \(i\) and column \(j\), so it carries input \(j\) into neuron \(i\) of layer 1. The same reading holds in every layer: \(W_{kij}\) connects neuron \(j\) of layer \(k-1\) to neuron \(i\) of layer \(k\). The derivations below rely on this. The dimensions match, since an \(n\times n\) matrix times a vector in \(\mathbb{R}^n\), plus a vector in \(\mathbb{R}^n\), is a vector in \(\mathbb{R}^n\).

The activation applies \(g\) to each entry separately; \(g\) is an element-wise function:

\[ \mathbf{h}_1 = g\left(\begin{bmatrix} a_{11}\\ a_{12}\\ a_{13} \end{bmatrix}\right) = \begin{bmatrix} g(a_{11})\\ g(a_{12})\\ g(a_{13}) \end{bmatrix}, \qquad \text{logistic: } g(a_{11}) = \frac{1}{1 + e^{-a_{11}}} \]

Each step on its own is simple. The network becomes a complicated function only because many such steps are stacked.

The output layer gets its own function \(O\) because the right range for the output depends on the problem. A logistic output only produces values between 0 and 1, which is no use for predicting a movie rating out of 100 or a test score. Module 4.3 chooses \(O\) for two common kinds of problem.

Lecture 3 described supervised learning with five components: data, model, parameters, learning algorithm and objective. A network diagram may look like a different kind of object, but following its arrows gives the model as an explicit formula. Start from \(\mathbf{x}\), multiply by \(W_1\) and add \(\mathbf{b}_1\) to get \(\mathbf{a}_1\), apply \(g\) to get \(\mathbf{h}_1\), and repeat for each layer:

\[ \hat{\mathbf{y}}_i = \hat f(\mathbf{x}_i) = O\Big(W_3\, g\big(W_2\, g(W_1\mathbf{x}_i + \mathbf{b}_1) + \mathbf{b}_2\big) + \mathbf{b}_3\Big) \]
ComponentFor a feedforward network
Data\(\{\mathbf{x}_i, \mathbf{y}_i\}_{i=1}^N\)
ModelThe composite function above
Parameters\(\theta = W_1, \dots, W_L,\ \mathbf{b}_1, \dots, \mathbf{b}_L\) (here \(L = 3\))
AlgorithmGradient descent with backpropagation (this lecture)
ObjectiveFor example \(\min \frac1N\sum_{i=1}^N\sum_{j=1}^k(\hat y_{ij} - y_{ij})^2\); in general \(\min \mathcal{L}(\theta)\) for some function \(\mathcal{L}\) of the parameters

So a deep network is one more family of functions in the machine learning toolbox, alongside linear or quadratic functions. It happens to be a very complex composite function. Even a 100-layer network can be written down this way; the formula is just long.

Checking dimensions when layers differ in size

Let the input have \(n\) entries, the first hidden layer \(m\) neurons and the second \(p\). Each neuron of layer 1 receives all \(n\) inputs, so \(W_1 \in \mathbb{R}^{m\times n}\) and \(\mathbf{b}_1 \in \mathbb{R}^m\). Layer 2 receives \(m\) values into each of its \(p\) neurons, so \(W_2 \in \mathbb{R}^{p\times m}\):

\[ \underbrace{\mathbf{a}_1}_{m} = \underbrace{\mathbf{b}_1}_{m} + \underbrace{W_1}_{m\times n}\,\underbrace{\mathbf{x}}_{n}, \qquad \underbrace{\mathbf{h}_1}_{m} = g(\mathbf{a}_1), \qquad \underbrace{\mathbf{a}_2}_{p} = \underbrace{\mathbf{b}_2}_{p} + \underbrace{W_2}_{p\times m}\,\underbrace{\mathbf{h}_1}_{m} \]

In general \(W_i\) has one row per neuron of layer \(i\) and one column per neuron of layer \(i-1\). However many connections the diagram shows, the network is a series of matrix-vector products, each followed by an element-wise non-linearity.

The parameters are all the \(W\)'s and \(\mathbf{b}\)'s. Everything else in the network is computed from them and the input.

Learning the parameters: the same algorithm with a bigger \(\theta\)

Lecture 3 learned the two parameters \(w, b\) of a single sigmoid neuron by gradient descent, repeating \(w_{t+1} \leftarrow w_t - \eta\nabla w_t\) and \(b_{t+1} \leftarrow b_t - \eta\nabla b_t\). Collecting the parameters into one vector \(\theta = [w, b]\) turns the two updates into one:

\[ \theta_{t+1} \leftarrow \theta_t - \eta\,\nabla\theta_t, \qquad \nabla\theta_t = \left[\frac{\partial\mathcal{L}(\theta)}{\partial w}, \frac{\partial\mathcal{L}(\theta)}{\partial b}\right]^\top \text{ evaluated at } w = w_t,\ b = b_t \]

\(\nabla\theta_t\) is shorthand for the gradient of the loss evaluated at the current values. For a feedforward network, \(\theta = [W_1, \dots, W_L, \mathbf{b}_1, \dots, \mathbf{b}_L]\) instead of \([w, b]\). The algorithm does not care how long \(\theta\) is, because adding and subtracting very long vectors works exactly like adding two-dimensional ones.

  1. gradient_descent() for a feedforward network
  2. \(t \leftarrow 0\); max_iterations \(\leftarrow 1000\)
  3. Initialize \(\theta_0 = [W_1^0, \dots, W_L^0, \mathbf{b}_1^0, \dots, \mathbf{b}_L^0]\)
  4. While \(t\)++ \(<\) max_iterations: \(\theta_{t+1} \leftarrow \theta_t - \eta\,\nabla\theta_t\)
\[ \nabla\theta_t = \left[\frac{\partial\mathcal{L}(\theta)}{\partial W_{1,t}}, \dots, \frac{\partial\mathcal{L}(\theta)}{\partial W_{L,t}}, \frac{\partial\mathcal{L}(\theta)}{\partial \mathbf{b}_{1,t}}, \dots, \frac{\partial\mathcal{L}(\theta)}{\partial \mathbf{b}_{L,t}}\right]^\top \]

What changes is the size of \(\nabla\theta\). It holds one partial derivative per parameter: \(\partial\mathcal{L}/\partial W_{111}\) through \(\partial\mathcal{L}/\partial W_{1nn}\) for the first layer, the same for the second, \(\partial\mathcal{L}/\partial W_{L,11}\) through \(\partial\mathcal{L}/\partial W_{L,kn}\) for the last, and one for each bias. Grouped by layer:

\[ \nabla W_1, \dots, \nabla W_{L-1} \in \mathbb{R}^{n\times n}, \quad \nabla W_L \in \mathbb{R}^{k\times n}, \quad \nabla\mathbf{b}_1, \dots, \nabla\mathbf{b}_{L-1} \in \mathbb{R}^n, \quad \nabla\mathbf{b}_L \in \mathbb{R}^k \]

With \(L = 3\) that is \(2n^2 + kn + 2n + k\) numbers. The layers have different sizes, so these numbers form a collection of matrices and vectors rather than one big matrix. For a single neuron, deriving \(\partial\mathcal{L}/\partial w\) and \(\partial\mathcal{L}/\partial b\) already took some work. Deriving a separate formula for each of thousands of weights is out of the question, so the aim is a formula that gives a whole matrix of partial derivatives at once.

Two questions

  1. How should the loss function \(\mathcal{L}(\theta)\) be chosen? This comes first, because a derivative cannot be written down before the function is known. (Module 4.3)
  2. How can \(\nabla\theta\) be computed, that is, all of \(\nabla W_1, \dots, \nabla W_L\) and \(\nabla\mathbf{b}_1, \dots, \nabla\mathbf{b}_L\)? (Modules 4.4 to 4.8)

Real-valued outputs: linear output, squared-error loss

The right loss depends on the problem. The lecture takes the two kinds of problem that cover most of practice, regression and classification, and chooses a loss and an output function for each.

Regression example. Predict a movie's ratings from features of the movie (is the actor Damon? is the director Nolan? …). Each movie has three targets, its IMDb rating, its critics' rating and its Rotten Tomatoes rating, so \(\mathbf{y}_i \in \mathbb{R}^3\), for example \(\mathbf{y}_i = [7.5,\ 8.2,\ 7.7]\). The output layer has \(k = 3\) neurons, one per rating.

The loss should measure how far \(\hat{\mathbf{y}}_i\) is from \(\mathbf{y}_i\). For \(\mathbf{y}_i \in \mathbb{R}^k\) the natural choice is the squared error, averaged over the \(N\) training examples:

\[ \mathcal{L}(\theta) = \frac1N\sum_{i=1}^N\sum_{j=1}^k(\hat y_{ij} - y_{ij})^2 \]

Which output function? The logistic function would squash every \(\hat y_{ij}\) into \((0, 1)\), while ratings run from 0 to 10, or 0 to 100. So \(O\) should be linear:

\[ \hat f(\mathbf{x}) = \mathbf{h}_L = O(\mathbf{a}_L) = W_O\,\mathbf{a}_L + \mathbf{b}_O \]

Here \(W_O\) is a \(3\times 3\) matrix and \(\mathbf{b}_O \in \mathbb{R}^3\), and \(\hat y_{ij}\) is no longer bounded between 0 and 1.

Since nothing bounds a linear output, what stops the network from predicting a rating of 1000? The loss does. If the true IMDb rating is 9.5 and the network says 1000, the term \((9.5 - 1000)^2\) is enormous, and gradient descent moves the parameters away from settings that produce it. The loss can only become small when the predictions sit in the targets' natural range. With a logistic output, no setting of \(W_1, W_2, W_3, \mathbf{b}_1, \mathbf{b}_2, \mathbf{b}_3\) could ever output 9.5, however strongly the loss pushed.

Before this part the lecture sends students to a separate video, from an earlier course, on information content, entropy and cross-entropy. These notes only need the resulting formula.

Classification example. Classify an image into one of \(k\) classes, say apple, mango, orange and banana. For an apple image the label is the one-hot vector \(\mathbf{y} = [1, 0, 0, 0]\). Squared error could measure the gap between \(\hat{\mathbf{y}}\) and \(\mathbf{y}\) here too, but there is a better choice.

Read \(\mathbf{y}\) as a probability distribution over the classes: the label puts probability 1 on the true class and 0 on the others. Then \(\hat{\mathbf{y}}\) should also be a probability distribution, with every entry in \([0, 1]\) and the entries summing to 1. The output function that ensures this is the softmax:

\[ \mathbf{a}_L = W_L\,\mathbf{h}_{L-1} + \mathbf{b}_L, \qquad \hat y_j = O(\mathbf{a}_L)_j = \frac{e^{a_{L,j}}}{\sum_{i=1}^{k} e^{a_{L,i}}} \]

\(O(\mathbf{a}_L)_j\) is the \(j\)-th entry of \(\hat{\mathbf{y}}\) and \(a_{L,j}\) is the \(j\)-th entry of \(\mathbf{a}_L\).

Example. Suppose the four output pre-activations are \(\mathbf{a}_L = [-10, 10, 20, 30]\). This is clearly not a distribution: it has a negative entry and entries above 1. The softmax turns it into one:

\[ \hat{\mathbf{y}} = \frac{\big[e^{-10},\ e^{10},\ e^{20},\ e^{30}\big]}{e^{-10} + e^{10} + e^{20} + e^{30}} \approx \big[4.2\times10^{-18},\ 2.1\times10^{-9},\ 4.5\times10^{-5},\ 0.99995\big] \]

The decimal values are computed for these notes; the lecture leaves the fractions unevaluated.

Two properties make the result a distribution. Every \(e^{a}\) is positive, even for negative \(a\), since \(e^{-10} = 1/e^{10}\). And all entries share one denominator, the sum of all the numerators, so they add up to 1. Positive numbers that add up to 1 each lie between 0 and 1.

Why exponentiate at all? Dividing each raw value by the sum of the raw values also gives entries that add up to 1, but a negative value stays negative: \(-10/(-10 + 10 + 20 + 30) = -0.2\), which cannot be a probability.

Cross-entropy, and why it reduces to \(-\log\hat y_\ell\)

With \(\mathbf{y}\) and \(\hat{\mathbf{y}}\) both probability distributions, a loss from probability theory measures how different they are. It is the cross-entropy:

\[ \mathcal{L}(\theta) = -\sum_{c=1}^{k} y_c\log\hat y_c \]

For a one-hot label, \(y_c = 1\) if \(c = \ell\), the true class, and \(y_c = 0\) otherwise. All terms but one vanish:

\[ \mathcal{L}(\theta) = -\log\hat y_\ell \]

Minimizing \(-\log\hat y_\ell\) is the same as maximizing \(\log\hat y_\ell\):

\[ \min_{\theta}\ \mathcal{L}(\theta) = -\log\hat y_\ell \qquad\text{or}\qquad \max_{\theta}\ -\mathcal{L}(\theta) = \log\hat y_\ell \]

Is \(\hat y_\ell\) a function of the parameters? Yes. It is entry \(\ell\) of the network's output,

\[ \hat y_\ell = \Big[O\big(W_3\, g(W_2\, g(W_1\mathbf{x} + \mathbf{b}_1) + \mathbf{b}_2) + \mathbf{b}_3\big)\Big]_\ell \]

so changing any weight changes it. \(\hat y_\ell\) is the predicted probability that \(\mathbf{x}\) belongs to class \(\ell\), and training should push it toward 1. \(\log\hat y_\ell\) is called the log-likelihood of the data, so this loss is also called the negative log-likelihood.

For instance, if the network outputs \([0.2, 0.3, 0.4, 0.1]\) for an apple image, the loss is \(-\log 0.2 \approx 1.61\) (our arithmetic). Here and below, \(\log\) is the natural logarithm, which is what gives \(\frac{d}{dx}\log x = \frac1x\) in Module 4.5.

OutputsOutput activationLoss function
Real valuesLinearSquared error
ProbabilitiesSoftmaxCross-entropy

Other problems call for other losses, but these two pairs cover a very large share of what comes up in practice. The rest of the lecture works with a softmax output and the cross-entropy loss. The lecture notes that the results carry over to the regression pair with one small change (see the note at the end of Module 4.5).

The loss is now fixed, which leaves the gradient. Take one weight, say \(W_{112}\) (layer 1, from input 2 into neuron 1). Gradient descent needs a formula for \(\partial\mathcal{L}(\theta)/\partial W_{112}\). The aim is more ambitious than one formula: once the derivative is known for one weight, the same work should give it for every weight in that layer, and then for every layer.

Start with a network that is deep but thin, with one neuron in each layer. Then \(\hat y\) is a plain chain of functions of \(x_1\), and the derivative for the first weight is the chain rule applied link by link, just as for a nested function such as \(\sin(\cos(e^{\log x}))\):

\[ \frac{\partial\mathcal{L}(\theta)}{\partial W_{111}} = \frac{\partial\mathcal{L}(\theta)}{\partial\hat y}\,\frac{\partial\hat y}{\partial a_{L1}}\,\frac{\partial a_{L1}}{\partial h_{21}}\,\frac{\partial h_{21}}{\partial a_{21}}\,\frac{\partial a_{21}}{\partial h_{11}}\,\frac{\partial h_{11}}{\partial a_{11}}\,\frac{\partial a_{11}}{\partial W_{111}} \]

The first five factors together are the derivative of the loss with respect to \(h_{11}\), so the chain compresses to two factors. The same compression works for the weights further up:

\[ \frac{\partial\mathcal{L}(\theta)}{\partial W_{111}} = \frac{\partial\mathcal{L}(\theta)}{\partial h_{11}}\,\frac{\partial h_{11}}{\partial W_{111}}, \qquad \frac{\partial\mathcal{L}(\theta)}{\partial W_{211}} = \frac{\partial\mathcal{L}(\theta)}{\partial h_{21}}\,\frac{\partial h_{21}}{\partial W_{211}}, \qquad \frac{\partial\mathcal{L}(\theta)}{\partial W_{L11}} = \frac{\partial\mathcal{L}(\theta)}{\partial a_{L1}}\,\frac{\partial a_{L1}}{\partial W_{L11}} \]
x₁a₁₁h₁₁a₂₁h₂₁aL1ŷLW₁₁₁gW₂₁₁gWL11Oloss∂h₁₁/∂W₁₁₁ (local factor)∂L/∂aL1∂L/∂h₂₁ = ∂L/∂aL1 · ∂aL1/∂h₂₁∂L/∂h₁₁ = ∂L/∂h₂₁ · ∂h₂₁/∂a₂₁ · ∂a₂₁/∂h₁₁∂L/∂W₁₁₁ = ∂L/∂h₁₁ × ∂h₁₁/∂W₁₁₁: a shared part from the loss end times a local part
A deep, thin network. With one neuron per layer, \(\partial\mathcal{L}/\partial W_{111}\) is a single chain of seven factors. The brackets show how the pieces nest: \(\partial\mathcal{L}/\partial h_{11}\) contains \(\partial\mathcal{L}/\partial h_{21}\), which contains \(\partial\mathcal{L}/\partial a_{L1}\). Computed once from the loss end, each serves its own weight after one more local factor.

This is the idea backpropagation is built on. The chains are long, but most of each chain has already been computed for the weights closer to the loss. Compute from the loss end once, store the partial results, and each weight then needs only its short local factor. In a wide network several weights feed the same neuron, and all of them can share the stored part.

The lecture builds intuition with a story. Feed an input \(\mathbf{x}\) through the network with its current weights, somewhere in the middle of training. It produces \(\hat{\mathbf{y}}\) and a non-zero loss \(-\log\hat y_\ell\). Something is wrong, and we want to find out who is responsible.

  1. Ask the output layer why its output is bad. It accepts its share, but points out that it is only as good as the hidden layer and weights below it, since \(\hat{\mathbf{y}} = O(W_L\mathbf{h}_{L-1} + \mathbf{b}_L)\).
  2. Ask \(W_L\), \(\mathbf{b}_L\) and the last hidden layer what is wrong with them. The weights and biases take full responsibility: they are the only parts of the network that can be adjusted. The hidden layer's activation says it is only as good as its pre-activation.
  3. The pre-activation in turn says it is only as good as the hidden layer and weights below it. The questioning continues down the network.
  4. The input is never blamed, since it is given. In the end the responsibility lies with all the weights and biases, the parameters of the model.

The slides name \(h_L\) in step 2; the layer that feeds the output is \(\mathbf{h}_{L-1}\), which is what is written here.

Questioning the first-layer weights directly is hard; questioning them through the output and hidden layers is easy. That is exactly what the chain rule does:

\[ \frac{\partial\mathcal{L}(\theta)}{\partial W_{111}} = \underbrace{\frac{\partial\mathcal{L}(\theta)}{\partial\hat y}\,\frac{\partial\hat y}{\partial a_3}}_{\text{talk to the output layer}}\ \underbrace{\frac{\partial a_3}{\partial h_2}\,\frac{\partial h_2}{\partial a_2}}_{\text{previous hidden layer}}\ \underbrace{\frac{\partial a_2}{\partial h_1}\,\frac{\partial h_1}{\partial a_1}}_{\text{previous hidden layer}}\ \underbrace{\frac{\partial a_1}{\partial W_{111}}}_{\text{now the weight}} \]

This is written loosely, as on the slides. With several neurons per layer, each factor is a vector or matrix of partial derivatives; Modules 4.5 to 4.7 make each one precise.

Why is a derivative the right measure of responsibility? \(\partial\mathcal{L}/\partial w\) says how much the loss changes when \(w\) changes a tiny bit. If a small change in \(w\) moves the loss a lot, \(w\) has a strong influence on the loss, and adjusting it is an effective way to reduce the loss.

Roadmap

The remaining modules compute three kinds of quantity, each once, in a form that works for every layer:

  1. the gradient with respect to the output units (Module 4.5);
  2. the gradient with respect to the hidden units (Module 4.6);
  3. the gradient with respect to the weights and biases (Module 4.7).

Throughout, the output function is softmax and the loss is cross-entropy.

Gradient with respect to the output \(\hat{\mathbf{y}}\)

The output layer has two parts, the activations \(\hat{\mathbf{y}}\) and the pre-activations \(\mathbf{a}_L\). The quantity needed later is \(\nabla_{\mathbf{a}_L}\mathcal{L}(\theta)\). As a warm-up, start with \(\hat{\mathbf{y}}\), and with a single entry of it.

The loss \(\mathcal{L}(\theta) = -\log\hat y_\ell\) contains only one entry of \(\hat{\mathbf{y}}\), the one for the true class. Its derivative with respect to entry \(i\) is

\[ \frac{\partial\mathcal{L}(\theta)}{\partial\hat y_i} = \frac{\partial}{\partial\hat y_i}\big(-\log\hat y_\ell\big) = \begin{cases} -\dfrac{1}{\hat y_\ell} & \text{if } i = \ell \\ 0 & \text{otherwise} \end{cases} \]

For example, if \(\ell = 2\) the loss is \(-\log\hat y_2\). It does not involve \(\hat y_1\), so that derivative is 0. With respect to \(\hat y_2\) it is \(-1/\hat y_2\), from \(\frac{d}{dx}\log x = \frac1x\). Every other entry gives 0. An indicator variable \(\mathbb{1}_{\text{condition}}\), which is 1 when the condition holds and 0 otherwise, writes the two cases as one expression 5:07:

\[ \frac{\partial\mathcal{L}(\theta)}{\partial\hat y_i} = -\frac{\mathbb{1}_{i=\ell}}{\hat y_\ell} \]

Now stack the \(k\) entries into the gradient with respect to the vector \(\hat{\mathbf{y}}\). Only the indicator changes from row to row:

\[ \nabla_{\hat{\mathbf{y}}}\mathcal{L}(\theta) = \begin{bmatrix} \frac{\partial\mathcal{L}(\theta)}{\partial\hat y_1}\\ \vdots\\ \frac{\partial\mathcal{L}(\theta)}{\partial\hat y_k} \end{bmatrix} = -\frac{1}{\hat y_\ell}\begin{bmatrix} \mathbb{1}_{\ell=1}\\ \mathbb{1}_{\ell=2}\\ \vdots\\ \mathbb{1}_{\ell=k} \end{bmatrix} = -\frac{1}{\hat y_\ell}\,\mathbf{e}(\ell) \]

\(\mathbf{e}(\ell)\) is the \(k\)-dimensional one-hot vector with 1 in position \(\ell\) and 0 everywhere else (the slides also write it \(e_\ell\)).

The recipe for every gradient in this lecture: first find the partial derivative with respect to one entry of the vector or matrix, then stack the entries and recognize the result as a vector or matrix expression.

Gradient with respect to the output pre-activations \(\mathbf{a}_L\)

Again one entry first. Split the derivative at \(\hat y_\ell\):

\[ \frac{\partial\mathcal{L}(\theta)}{\partial a_{Li}} = \frac{\partial(-\log\hat y_\ell)}{\partial a_{Li}} = \frac{\partial(-\log\hat y_\ell)}{\partial\hat y_\ell}\,\frac{\partial\hat y_\ell}{\partial a_{Li}} \]

Does \(\hat y_\ell\) depend on \(a_{Li}\) when \(i \ne \ell\)? It does, because every entry of \(\mathbf{a}_L\) appears in the softmax denominator:

\[ \hat y_\ell = \frac{\exp(a_{L\ell})}{\sum_{i'}\exp(a_{Li'})} \]

So unlike the previous step, the derivative is not zero for the other entries. (The summation index is written \(i'\) to keep it apart from the \(i\) we differentiate with respect to.) The derivation uses the quotient rule,

\[ \frac{\partial}{\partial x}\,\frac{g(x)}{h(x)} = \frac{\partial g(x)}{\partial x}\,\frac{1}{h(x)} - \frac{g(x)}{h(x)^2}\,\frac{\partial h(x)}{\partial x} \]

and two small facts. The numerator \(\exp(a_{L\ell})\) depends only on entry \(\ell\), so \(\frac{\partial}{\partial a_{Li}}\exp(a_{L\ell}) = \mathbb{1}_{\ell=i}\exp(a_{L\ell})\). In the denominator only one term of the sum contains \(a_{Li}\), so \(\frac{\partial}{\partial a_{Li}}\sum_{i'}\exp(a_{Li'}) = \exp(a_{Li})\). Step by step:

\[ \begin{aligned} \frac{\partial}{\partial a_{Li}}\big(-\log\hat y_\ell\big) &= \frac{-1}{\hat y_\ell}\,\frac{\partial}{\partial a_{Li}}\,\hat y_\ell \;=\; \frac{-1}{\hat y_\ell}\,\frac{\partial}{\partial a_{Li}}\operatorname{softmax}(\mathbf{a}_L)_\ell \\ &= \frac{-1}{\hat y_\ell}\,\frac{\partial}{\partial a_{Li}}\,\frac{\exp(a_{L\ell})}{\sum_{i'}\exp(a_{Li'})} \\ &= \frac{-1}{\hat y_\ell}\left(\frac{\frac{\partial}{\partial a_{Li}}\exp(a_{L\ell})}{\sum_{i'}\exp(a_{Li'})} - \frac{\exp(a_{L\ell})\,\big(\frac{\partial}{\partial a_{Li}}\sum_{i'}\exp(a_{Li'})\big)}{\big(\sum_{i'}\exp(a_{Li'})\big)^2}\right) \\ &= \frac{-1}{\hat y_\ell}\left(\frac{\mathbb{1}_{\ell=i}\exp(a_{L\ell})}{\sum_{i'}\exp(a_{Li'})} - \frac{\exp(a_{L\ell})}{\sum_{i'}\exp(a_{Li'})}\,\frac{\exp(a_{Li})}{\sum_{i'}\exp(a_{Li'})}\right) \\ &= \frac{-1}{\hat y_\ell}\Big(\mathbb{1}_{\ell=i}\operatorname{softmax}(\mathbf{a}_L)_\ell - \operatorname{softmax}(\mathbf{a}_L)_\ell\operatorname{softmax}(\mathbf{a}_L)_i\Big) \\ &= \frac{-1}{\hat y_\ell}\big(\mathbb{1}_{\ell=i}\,\hat y_\ell - \hat y_\ell\,\hat y_i\big) \\ &= -\big(\mathbb{1}_{\ell=i} - \hat y_i\big) \end{aligned} \]

Line 1 is the chain rule for \(-\log u\) with \(u = \hat y_\ell\). Line 3 applies the quotient rule with \(g = \exp(a_{L\ell})\) and \(h = \sum_{i'}\exp(a_{Li'})\). Line 4 uses the two facts and splits the squared denominator into two copies. Line 5 recognizes the softmax entries, line 6 renames them \(\hat y\), and in line 7 the factor \(\hat y_\ell\) cancels.

Stacking the entries for \(i = 1, \dots, k\) gives the vector:

\[ \nabla_{\mathbf{a}_L}\mathcal{L}(\theta) = \begin{bmatrix} \frac{\partial\mathcal{L}(\theta)}{\partial a_{L1}}\\ \vdots\\ \frac{\partial\mathcal{L}(\theta)}{\partial a_{Lk}} \end{bmatrix} = \begin{bmatrix} -(\mathbb{1}_{\ell=1} - \hat y_1)\\ -(\mathbb{1}_{\ell=2} - \hat y_2)\\ \vdots\\ -(\mathbb{1}_{\ell=k} - \hat y_k) \end{bmatrix} = -\big(\mathbf{e}(\ell) - \hat{\mathbf{y}}\big) \]
Gradient with respect to the output layer (softmax with cross-entropy) \[ \nabla_{\mathbf{a}_L}\mathcal{L}(\theta) = -\big(\mathbf{e}(\ell) - \hat{\mathbf{y}}\big) = \hat{\mathbf{y}} - \mathbf{e}(\ell) \]

Entry \(i\) says how much the loss changes when \(a_{Li}\) changes a little. It needs only \(\hat{\mathbf{y}}\) and the label, both known once the forward pass is done.

The result is easy to read. For each wrong class the entry is \(\hat y_i \ge 0\), so a gradient descent step lowers that pre-activation. For the true class it is \(\hat y_\ell - 1 \le 0\), so the step raises it. The entries add up to 0, and the whole vector shrinks to zero as \(\hat{\mathbf{y}}\) approaches \(\mathbf{e}(\ell)\).

2.0 1.0 0.5 −1.0
Loss −log ŷℓ–
ŷℓ–
Sum of gradient entries–
Softmax output and the output-layer gradient. Set the four output pre-activations and the true class. Top: \(\hat{\mathbf{y}} = \operatorname{softmax}(\mathbf{a}_L)\), with the dashed box marking the target \(\mathbf{e}(\ell)\). Bottom: \(\nabla_{\mathbf{a}_L}\mathcal{L} = \hat{\mathbf{y}} - \mathbf{e}(\ell)\). Only the true class gets a negative entry, the entries always add up to zero, and a gradient step on \(\mathbf{a}_L\) raises \(a_{L\ell}\), lowers the rest and reduces the loss.

Not in the lecture: for the regression pair, with an identity output \(\hat{\mathbf{y}} = \mathbf{a}_L\) and the squared error \(\sum_j(\hat y_j - y_j)^2\) of one example, the same steps give \(\nabla_{\mathbf{a}_L}\mathcal{L} = 2(\hat{\mathbf{y}} - \mathbf{y})\). Only this first gradient changes; everything in Modules 4.6 to 4.8 stays the same.

The chain rule along several paths

The hidden layers come next. There may be many of them, so the goal is one formula, derived once, that works for any hidden layer. The output layer was special and got its own formula; the hidden layers should not each need one.

A new feature appears here. In the thin network each quantity fed exactly one later quantity. In a wide network the activation \(h_{22}\) (neuron 2 of hidden layer 2) feeds every neuron of the next layer, \(a_{31}, a_{32}, \dots, a_{3k}\), and the loss depends on all of them. So there are \(k\) paths from \(h_{22}\) to the loss.

The rule for this situation is ordinary calculus. If \(p(z)\) is computed from intermediate results \(q_1(z), \dots, q_m(z)\) that all depend on \(z\), the derivative adds up the chain rule along every path:

\[ \frac{\partial p(z)}{\partial z} = \sum_{m}\frac{\partial p(z)}{\partial q_m}\,\frac{\partial q_m}{\partial z} \]

The lecture's toy example computes \(q_1 = z^2\), \(q_2 = z^3\) and \(q_3 = e^z\) from \(z\), and then \(p\) from the three \(q\)'s. To check the rule on one concrete choice (ours), take \(p = q_1/q_2 + q_3\), which equals \(1/z + e^z\):

\[ \frac{\partial p}{\partial z} = \underbrace{\frac{1}{q_2}\cdot 2z}_{\text{via } q_1} + \underbrace{\Big(-\frac{q_1}{q_2^2}\Big)\cdot 3z^2}_{\text{via } q_2} + \underbrace{1\cdot e^z}_{\text{via } q_3} = \frac{2}{z^2} - \frac{3}{z^2} + e^z = -\frac{1}{z^2} + e^z \]

That is the derivative of \(1/z + e^z\), as it should be.

A toy function with three pathszq₁q₂q₃q₁ = z²q₂ = z³q₃ = eᶻp∂p/∂z = Σₘ (∂p/∂qₘ)(∂qₘ/∂z): one chain-rule product per pathIn a network: h₂₂ reaches the loss k waysh₂₂a₃₁a₃₂a₃ₖ⋮W₃₁₂W₃₂₂W₃ₖ₂L∂L/∂a₃ₘ known(Module 4.5)∂L/∂h₂₂ = Σₘ (∂L/∂a₃ₘ) · W₃ₘ₂
Adding up over paths. Left: \(z\) influences \(p\) through three intermediate results, so \(\partial p/\partial z\) is a sum of three chain-rule products. Right: in a network, \(h_{22}\) reaches the loss through every pre-activation of the next layer, and the derivative along path \(m\) is \(\partial\mathcal{L}/\partial a_{3m}\) times the weight \(W_{3m2}\) on that edge.

In the network, \(p(z)\) is the loss \(\mathcal{L}(\theta)\), \(z = h_{ij}\), and the intermediate results are the pre-activations of the next layer, \(q_m(z) = a_{i+1,m}\) (on the slide, the output layer's \(a_{Lm}\)).

Gradient with respect to a hidden layer's activations \(\mathbf{h}_i\)

One entry first, \(h_{ij}\), neuron \(j\) of layer \(i\). Add up over the paths through the next layer's pre-activations:

\[ \frac{\partial\mathcal{L}(\theta)}{\partial h_{ij}} = \sum_{m=1}^{k}\frac{\partial\mathcal{L}(\theta)}{\partial a_{i+1,m}}\,\frac{\partial a_{i+1,m}}{\partial h_{ij}} = \sum_{m=1}^{k}\frac{\partial\mathcal{L}(\theta)}{\partial a_{i+1,m}}\,W_{i+1,m,j} \]

The sum runs over the neurons of layer \(i+1\): \(k\) of them when \(i+1 = L\), as on the slides, and \(n\) when layer \(i+1\) is hidden. When \(i+1 = L\), the first factor is already known from Module 4.5.

Why \(\partial a_{i+1,m}/\partial h_{ij} = W_{i+1,m,j}\)

Write out \(\mathbf{a}_3 = W_3\mathbf{h}_2 + \mathbf{b}_3\) for two output neurons and three hidden neurons:

\[ \begin{bmatrix} a_{31}\\ a_{32} \end{bmatrix} = \begin{bmatrix} W_{311} & W_{312} & W_{313}\\ W_{321} & W_{322} & W_{323} \end{bmatrix}\begin{bmatrix} h_{21}\\ h_{22}\\ h_{23} \end{bmatrix} + \begin{bmatrix} b_{31}\\ b_{32} \end{bmatrix}, \qquad a_{31} = W_{311}h_{21} + W_{312}h_{22} + W_{313}h_{23} + b_{31} \]

Differentiate \(a_{31}\) with respect to \(h_{22}\). Three of the four terms do not contain \(h_{22}\), so \(\partial a_{31}/\partial h_{22} = W_{312}\). Matching indices, \(i+1 = 3\), \(m = 1\) and \(j = 2\): the derivative is entry \((m, j)\) of \(W_{i+1}\).

From a sum to a dot product

Put the two factors of each term into vectors. The first is the gradient with respect to the next layer's pre-activations. The second is column \(j\) of \(W_{i+1}\), written \(W_{i+1,\cdot,j}\), where the dot means "every row":

\[ \nabla_{\mathbf{a}_{i+1}}\mathcal{L}(\theta) = \begin{bmatrix} \frac{\partial\mathcal{L}(\theta)}{\partial a_{i+1,1}}\\ \vdots\\ \frac{\partial\mathcal{L}(\theta)}{\partial a_{i+1,k}} \end{bmatrix}, \qquad W_{i+1,\cdot,j} = \begin{bmatrix} W_{i+1,1,j}\\ \vdots\\ W_{i+1,k,j} \end{bmatrix} \]

Their dot product multiplies matching entries and adds the products, which is exactly the sum above:

\[ \frac{\partial\mathcal{L}(\theta)}{\partial h_{ij}} = \big(W_{i+1,\cdot,j}\big)^\top\,\nabla_{\mathbf{a}_{i+1}}\mathcal{L}(\theta) \]

The whole vector

Stack the entries \(j = 1, \dots, n\). Row \(j\) is column \(j\) of \(W_{i+1}\) dotted with the same gradient vector. The columns of \(W_{i+1}\) are the rows of \(W_{i+1}^\top\), so the stack is one matrix-vector product:

\[ \nabla_{\mathbf{h}_i}\mathcal{L}(\theta) = \begin{bmatrix} \frac{\partial\mathcal{L}(\theta)}{\partial h_{i1}}\\ \frac{\partial\mathcal{L}(\theta)}{\partial h_{i2}}\\ \vdots\\ \frac{\partial\mathcal{L}(\theta)}{\partial h_{in}} \end{bmatrix} = \begin{bmatrix} (W_{i+1,\cdot,1})^\top\nabla_{\mathbf{a}_{i+1}}\mathcal{L}(\theta)\\ (W_{i+1,\cdot,2})^\top\nabla_{\mathbf{a}_{i+1}}\mathcal{L}(\theta)\\ \vdots\\ (W_{i+1,\cdot,n})^\top\nabla_{\mathbf{a}_{i+1}}\mathcal{L}(\theta) \end{bmatrix} = (W_{i+1})^\top\,\nabla_{\mathbf{a}_{i+1}}\mathcal{L}(\theta) \]
Gradient with respect to a layer's activations \[ \nabla_{\mathbf{h}_i}\mathcal{L}(\theta) = W_{i+1}^\top\,\nabla_{\mathbf{a}_{i+1}}\mathcal{L}(\theta) \]

\(W_{i+1}\) has one row per neuron of layer \(i+1\) and one column per neuron of layer \(i\), so \(W_{i+1}^\top\) carries a gradient on layer \(i+1\) back to a vector the size of layer \(i\).

For \(i = L-1\) both factors are available: the current weights \(W_L\), and \(\nabla_{\mathbf{a}_L}\mathcal{L}\) from Module 4.5. For a lower layer the formula needs \(\nabla_{\mathbf{a}_{i+1}}\mathcal{L}\) for a hidden layer. For example, \(\nabla_{\mathbf{h}_1}\mathcal{L} = W_2^\top\nabla_{\mathbf{a}_2}\mathcal{L}\) needs \(\nabla_{\mathbf{a}_2}\mathcal{L}\), which has not been derived yet.

Gradient with respect to a hidden layer's pre-activations \(\mathbf{a}_i\)

This step is short because there is only one path. Each activation is computed from its own pre-activation, \(h_{ij} = g(a_{ij})\), and \(a_{ij}\) feeds nothing else. So

\[ \frac{\partial\mathcal{L}(\theta)}{\partial a_{ij}} = \frac{\partial\mathcal{L}(\theta)}{\partial h_{ij}}\,\frac{\partial h_{ij}}{\partial a_{ij}} = \frac{\partial\mathcal{L}(\theta)}{\partial h_{ij}}\,g'(a_{ij}) \qquad \big[\because\ h_{ij} = g(a_{ij})\big] \]

Stacking over \(j\):

\[ \nabla_{\mathbf{a}_i}\mathcal{L}(\theta) = \begin{bmatrix} \frac{\partial\mathcal{L}(\theta)}{\partial h_{i1}}\,g'(a_{i1})\\ \vdots\\ \frac{\partial\mathcal{L}(\theta)}{\partial h_{in}}\,g'(a_{in}) \end{bmatrix} = \nabla_{\mathbf{h}_i}\mathcal{L}(\theta)\odot\big[\dots, g'(a_{ij}), \dots\big] \]

\(\odot\) is the Hadamard product: multiply two vectors entry by entry, which gives a vector. It is different from a dot product, which would give a single number. The second vector collects the derivatives of the activations with respect to their pre-activations. For the usual choices of \(g\) these are cheap to compute (Module 4.9). Write \(g'(\mathbf{a}_i)\) for the vector \([\dots, g'(a_{ij}), \dots]\).

Two formulas cover every hidden layer \[ \nabla_{\mathbf{h}_i}\mathcal{L}(\theta) = W_{i+1}^\top\,\nabla_{\mathbf{a}_{i+1}}\mathcal{L}(\theta), \qquad \nabla_{\mathbf{a}_i}\mathcal{L}(\theta) = \nabla_{\mathbf{h}_i}\mathcal{L}(\theta)\odot g'(\mathbf{a}_i) \]

Starting from the output and alternating the two formulas reaches every hidden layer, however deep the network. For \(L = 3\):

StepComputeUses
1\(\nabla_{\mathbf{a}_3}\mathcal{L} = -(\mathbf{e}(\ell) - \hat{\mathbf{y}})\)\(\hat{\mathbf{y}}\) and the label (Module 4.5)
2\(\nabla_{\mathbf{h}_2}\mathcal{L} = W_3^\top\nabla_{\mathbf{a}_3}\mathcal{L}\)\(W_3\), step 1
3\(\nabla_{\mathbf{a}_2}\mathcal{L} = \nabla_{\mathbf{h}_2}\mathcal{L}\odot g'(\mathbf{a}_2)\)step 2, \(\mathbf{a}_2\)
4\(\nabla_{\mathbf{h}_1}\mathcal{L} = W_2^\top\nabla_{\mathbf{a}_2}\mathcal{L}\)\(W_2\), step 3
5\(\nabla_{\mathbf{a}_1}\mathcal{L} = \nabla_{\mathbf{h}_1}\mathcal{L}\odot g'(\mathbf{a}_1)\)step 4, \(\mathbf{a}_1\)

Each step needs only the step before it, the current weights and values from the forward pass. Whenever a quantity is needed, everything it depends on has already been computed.

Last come the parameters. We want \(\nabla_{W_k}\mathcal{L}(\theta)\) for a generic layer \(k\). The derivative of a scalar with respect to a matrix is a matrix of the same shape, with one partial derivative per entry. The recipe is the same as before: one entry first.

The weight \(W_{kij}\) connects neuron \(j\) of layer \(k-1\) to neuron \(i\) of layer \(k\), so it enters exactly one pre-activation, \(a_{ki}\). Recall \(\mathbf{a}_k = \mathbf{b}_k + W_k\mathbf{h}_{k-1}\), entry by entry \(a_{ki} = b_{ki} + \sum_j W_{kij}h_{k-1,j}\). There is one path, so

\[ \frac{\partial\mathcal{L}(\theta)}{\partial W_{kij}} = \frac{\partial\mathcal{L}(\theta)}{\partial a_{ki}}\,\frac{\partial a_{ki}}{\partial W_{kij}} = \frac{\partial\mathcal{L}(\theta)}{\partial a_{ki}}\,h_{k-1,j} \]

The first factor is already known from Modules 4.5 and 4.6. For the second, the lecture takes \(k = 2\) with three neurons per layer and the weight \(W_{222}\). Row 2 of \(W_2\mathbf{h}_1 + \mathbf{b}_2\) is

\[ a_{22} = W_{221}h_{11} + W_{222}h_{12} + W_{223}h_{13} + b_{22} \quad\Longrightarrow\quad \frac{\partial a_{22}}{\partial W_{222}} = h_{12} \]

All the other terms, the bias included, drop out. What is left is the activation at the source end of the weight, in layer \(k-1 = 1\) at neuron \(j = 2\).

The whole matrix is an outer product

Fill in every entry for a \(3\times 3\) matrix \(W_k\):

\[ \nabla_{W_k}\mathcal{L}(\theta) = \begin{bmatrix} \frac{\partial\mathcal{L}(\theta)}{\partial a_{k1}}h_{k-1,1} & \frac{\partial\mathcal{L}(\theta)}{\partial a_{k1}}h_{k-1,2} & \frac{\partial\mathcal{L}(\theta)}{\partial a_{k1}}h_{k-1,3}\\ \frac{\partial\mathcal{L}(\theta)}{\partial a_{k2}}h_{k-1,1} & \frac{\partial\mathcal{L}(\theta)}{\partial a_{k2}}h_{k-1,2} & \frac{\partial\mathcal{L}(\theta)}{\partial a_{k2}}h_{k-1,3}\\ \frac{\partial\mathcal{L}(\theta)}{\partial a_{k3}}h_{k-1,1} & \frac{\partial\mathcal{L}(\theta)}{\partial a_{k3}}h_{k-1,2} & \frac{\partial\mathcal{L}(\theta)}{\partial a_{k3}}h_{k-1,3} \end{bmatrix} \]

Every entry in row \(i\) shares the factor \(\partial\mathcal{L}/\partial a_{ki}\), and every entry in column \(j\) shares \(h_{k-1,j}\). That is the pattern of an outer product. A column vector \(\mathbf{u}\) times a row vector \(\mathbf{v}^\top\) is the matrix with entries \(u_iv_j\):

\[ \begin{bmatrix} u_1\\ u_2\\ u_3 \end{bmatrix}\begin{bmatrix} v_1 & v_2 & v_3 \end{bmatrix} = \begin{bmatrix} u_1v_1 & u_1v_2 & u_1v_3\\ u_2v_1 & u_2v_2 & u_2v_3\\ u_3v_1 & u_3v_2 & u_3v_3 \end{bmatrix} \]

Here \(\mathbf{u} = \nabla_{\mathbf{a}_k}\mathcal{L}(\theta)\) and \(\mathbf{v} = \mathbf{h}_{k-1}\).

u = ∇L w.r.t. aₖ3 × 1u₁u₂u₃×vᵀ = hₖ₋₁ᵀ1 × 3v₁v₂v₃=∇L w.r.t. Wₖ = u vᵀ3 × 3, the shape of Wₖu₁v₁u₁v₂u₁v₃u₂v₁u₂v₂u₂v₃u₃v₁u₃v₂u₃v₃uᵢ = ∂L/∂aₖᵢ comes from the backward pass; vⱼ = hₖ₋₁,ⱼ was stored by the forward pass.Highlighted: entry (2, 3) = ∂L/∂Wₖ₂₃ = u₂v₃, built from u₂ and v₃.
The weight gradient is an outer product. Entry \((i, j)\) of \(\nabla_{W_k}\mathcal{L}\) is \(\frac{\partial\mathcal{L}}{\partial a_{ki}}\,h_{k-1,j}\). Every entry of row \(i\) reuses the backward-pass value \(u_i\), and every entry of column \(j\) reuses the forward-pass value \(v_j\).

Biases M7 · 11:55

The bias \(b_{ki}\) also enters only \(a_{ki}\), with coefficient 1. From \(a_{ki} = b_{ki} + \sum_j W_{kij}h_{k-1,j}\), \(\partial a_{ki}/\partial b_{ki} = 1\), so

\[ \frac{\partial\mathcal{L}(\theta)}{\partial b_{ki}} = \frac{\partial\mathcal{L}(\theta)}{\partial a_{ki}}\,\frac{\partial a_{ki}}{\partial b_{ki}} = \frac{\partial\mathcal{L}(\theta)}{\partial a_{ki}}, \qquad \nabla_{\mathbf{b}_k}\mathcal{L}(\theta) = \begin{bmatrix} \frac{\partial\mathcal{L}(\theta)}{\partial a_{k1}}\\ \frac{\partial\mathcal{L}(\theta)}{\partial a_{k2}}\\ \vdots\\ \frac{\partial\mathcal{L}(\theta)}{\partial a_{kn}} \end{bmatrix} = \nabla_{\mathbf{a}_k}\mathcal{L}(\theta) \]
Gradients for the parameters of layer k \[ \nabla_{W_k}\mathcal{L}(\theta) = \nabla_{\mathbf{a}_k}\mathcal{L}(\theta)\,\mathbf{h}_{k-1}^\top, \qquad \nabla_{\mathbf{b}_k}\mathcal{L}(\theta) = \nabla_{\mathbf{a}_k}\mathcal{L}(\theta) \]

A column with one entry per neuron of layer \(k\), times a row with one entry per neuron of layer \(k-1\), has the shape of \(W_k\).

Two ingredients are needed. \(\nabla_{\mathbf{a}_k}\mathcal{L}\) comes from the backward recursion of Modules 4.5 and 4.6. \(\mathbf{h}_{k-1}\) is computed by the forward pass anyway, with no derivative involved; for the first layer it is \(\mathbf{h}_0 = \mathbf{x}\).

All the pieces are now in place:

  • the gradient with respect to the output layer, \(\nabla_{\mathbf{a}_L}\mathcal{L}(\theta)\);
  • the gradients with respect to the hidden layers, \(\nabla_{\mathbf{h}_k}\mathcal{L}(\theta)\) and \(\nabla_{\mathbf{a}_k}\mathcal{L}(\theta)\) for \(1 \le k < L\);
  • the gradients with respect to the weights and biases, \(\nabla_{W_k}\mathcal{L}(\theta)\) and \(\nabla_{\mathbf{b}_k}\mathcal{L}(\theta)\) for \(1 \le k \le L\).

Plugging them into gradient descent gives the full learning algorithm. Each iteration does one forward pass and one backward pass. As written, it works with one training example \((\mathbf{x}, \mathbf{y})\) at a time. Lecture 5 returns to gradient descent and its variants.

  1. gradient_descent()
  2. \(t \leftarrow 0\); max_iterations \(\leftarrow 1000\)
  3. Initialize \(\theta_0 = [W_1^0, \dots, W_L^0, \mathbf{b}_1^0, \dots, \mathbf{b}_L^0]\)
  4. While \(t\)++ \(<\) max_iterations:
  5. \(\mathbf{a}_1, \mathbf{h}_1,\) \(\mathbf{a}_2, \mathbf{h}_2,\) \(\dots,\) \(\mathbf{a}_{L-1}, \mathbf{h}_{L-1},\) \(\mathbf{a}_L, \hat{\mathbf{y}} = \) forward_propagation\((\theta_t)\)
  6. \(\nabla\theta_t = \) back_propagation\((\mathbf{h}_1, \dots, \mathbf{h}_{L-1},\) \(\mathbf{a}_1, \dots, \mathbf{a}_L,\) \(\mathbf{y}, \hat{\mathbf{y}})\)
  7. \(\theta_{t+1} \leftarrow \theta_t - \eta\,\nabla\theta_t\)
  1. forward_propagation(θ), with \(\mathbf{h}_0 = \mathbf{x}\)
  2. For \(k = 1\) to \(L-1\):
  3. \(\mathbf{a}_k = \mathbf{b}_k + W_k\mathbf{h}_{k-1}\)
  4. \(\mathbf{h}_k = g(\mathbf{a}_k)\)
  5. \(\mathbf{a}_L = \mathbf{b}_L + W_L\mathbf{h}_{L-1}\)
  6. \(\hat{\mathbf{y}} = O(\mathbf{a}_L)\)

The forward pass involves no derivatives, only matrix-vector products and the element-wise \(g\). The output layer is kept out of the loop because it uses \(O\) instead of \(g\). Once \(\hat{\mathbf{y}}\) is known, the label gives the loss.

  1. back_propagation\((\mathbf{h}_1, \dots, \mathbf{h}_{L-1},\) \(\mathbf{a}_1, \dots, \mathbf{a}_L,\) \(\mathbf{y}, \hat{\mathbf{y}})\)
  2. Output gradient: \(\nabla_{\mathbf{a}_L}\mathcal{L}(\theta) = -(\mathbf{e}(y) - \hat{\mathbf{y}})\)
  3. For \(k = L\) down to \(1\):
  4. Parameters: \(\nabla_{W_k}\mathcal{L}(\theta) = \nabla_{\mathbf{a}_k}\mathcal{L}(\theta)\,\mathbf{h}_{k-1}^\top\); \(\nabla_{\mathbf{b}_k}\mathcal{L}(\theta) = \nabla_{\mathbf{a}_k}\mathcal{L}(\theta)\)
  5. Layer below: \(\nabla_{\mathbf{h}_{k-1}}\mathcal{L}(\theta) = W_k^\top\,\nabla_{\mathbf{a}_k}\mathcal{L}(\theta)\)
  6. Layer below, pre-activation: \(\nabla_{\mathbf{a}_{k-1}}\mathcal{L}(\theta) =\) \(\nabla_{\mathbf{h}_{k-1}}\mathcal{L}(\theta)\odot\big[\dots, g'(a_{k-1,j}), \dots\big]\)

\(\mathbf{e}(y)\) is the one-hot vector of this example's true class, which the training data supplies. Every quantity on a right-hand side is available when it is needed: \(\nabla_{\mathbf{a}_k}\mathcal{L}\) from the previous pass through the loop (or the first line), \(W_k\) from the current parameters, and \(\mathbf{h}_{k-1}\) and \(\mathbf{a}_{k-1}\) from the forward pass. That is why the forward pass hands all of its values to the backward pass.

Not in the lecture: at \(k = 1\) the last two lines would compute gradients with respect to the input \(\mathbf{h}_0 = \mathbf{x}\), which nothing uses, so an implementation can stop after the parameter gradients of layer 1.

forward: compute and store every aₖ, hₖx = h₀a₁h₁a₂h₂a₃ŷLggO−log ŷℓW₁, b₁W₂, b₂W₃, b₃∇a₃∇h₂∇a₂∇h₁∇a₁W₃ᵀ⊙ g′W₂ᵀ⊙ g′ŷ − e(ℓ)backward: k = L, …, 1∇W₃ = ∇a₃ h₂ᵀ∇b₃ = ∇a₃∇W₂ = ∇a₂ h₁ᵀ∇b₂ = ∇a₂∇W₁ = ∇a₁ xᵀ∇b₁ = ∇a₁
One iteration for \(L = 3\). The forward pass (top row) computes and stores every pre-activation and activation. The backward pass (bottom row, right to left) starts from \(\hat{\mathbf{y}} - \mathbf{e}(\ell)\) and alternates the two hidden-layer formulas. Each \(\nabla\mathbf{a}_k\) then gives layer \(k\)'s parameter gradients, using the stored \(\mathbf{h}_{k-1}\).

The whole algorithm is a short loop of matrix and vector operations. No formula for an individual weight such as \(W_{312}\) or \(W_{222}\) is ever written out; the same three lines serve every layer, and they translate directly into a few lines of Python.

M8 · 7:35

The last piece: \(g'\) for the logistic and tanh functions

The slides give this its own module, 4.9; the video covers it at the end of M8. The backward pass needs \(g'(a_{k-1,j})\), and for the two activation functions used so far it takes a few lines of calculus.

Logistic

\[ \begin{aligned} g(z) &= \sigma(z) = \frac{1}{1 + e^{-z}} \\ g'(z) &= (-1)\,\frac{1}{(1 + e^{-z})^2}\,\frac{d}{dz}\big(1 + e^{-z}\big) \\ &= (-1)\,\frac{1}{(1 + e^{-z})^2}\,\big(-e^{-z}\big) \\ &= \frac{1}{1 + e^{-z}}\cdot\frac{1 + e^{-z} - 1}{1 + e^{-z}} \\ &= g(z)\big(1 - g(z)\big) \end{aligned} \]

The second line is the chain rule for \(1/u\) with \(u = 1 + e^{-z}\). The fourth writes \(e^{-z}\) as \((1 + e^{-z}) - 1\), so the second fraction is \(1 - \frac{1}{1 + e^{-z}} = 1 - g(z)\).

tanh

\[ \begin{aligned} g(z) &= \tanh(z) = \frac{e^{z} - e^{-z}}{e^{z} + e^{-z}} \\ g'(z) &= \frac{(e^{z} + e^{-z})\,\frac{d}{dz}(e^{z} - e^{-z}) - (e^{z} - e^{-z})\,\frac{d}{dz}(e^{z} + e^{-z})}{(e^{z} + e^{-z})^2} \\ &= \frac{(e^{z} + e^{-z})^2 - (e^{z} - e^{-z})^2}{(e^{z} + e^{-z})^2} \\ &= 1 - \frac{(e^{z} - e^{-z})^2}{(e^{z} + e^{-z})^2} \\ &= 1 - \big(g(z)\big)^2 \end{aligned} \]

The second line is the quotient rule; the third uses \(\frac{d}{dz}(e^{z} - e^{-z}) = e^{z} + e^{-z}\) and \(\frac{d}{dz}(e^{z} + e^{-z}) = e^{z} - e^{-z}\).

Both derivatives are expressions in \(g(z)\) itself, and \(g(\mathbf{a}_k) = \mathbf{h}_k\) is already stored by the forward pass. So \(g'(\mathbf{a}_k) = \mathbf{h}_k\odot(1 - \mathbf{h}_k)\) for the logistic function and \(1 - \mathbf{h}_k\odot\mathbf{h}_k\) for tanh, at almost no extra cost.

Logistic σ(z)00.251−6−3036σ(z)σ′ = σ(1 − σ)ztanh(z)−101−3−1.501.53tanh z1 − tanh²zz
The two activation functions and their derivatives, drawn to scale. The logistic derivative peaks at 0.25 at \(z = 0\) and the tanh derivative at 1. Both fall toward 0 as \(|z|\) grows, so a neuron whose pre-activation is far from 0 multiplies the gradient passing through it by a small number.
Slides

The whole algorithm on a 2-2-2 network

A network small enough to check by hand: two inputs, one hidden layer of two logistic neurons and two softmax outputs, so \(L = 2\) with 12 parameters. The pseudocode becomes

\[ \mathbf{a}_1 = \mathbf{b}_1 + W_1\mathbf{x}, \quad \mathbf{h}_1 = \sigma(\mathbf{a}_1), \quad \mathbf{a}_2 = \mathbf{b}_2 + W_2\mathbf{h}_1, \quad \hat{\mathbf{y}} = \operatorname{softmax}(\mathbf{a}_2), \quad \mathcal{L} = -\log\hat y_\ell \]
\[ \begin{aligned} \nabla_{\mathbf{a}_2}\mathcal{L} &= \hat{\mathbf{y}} - \mathbf{e}(\ell), & \nabla_{W_2}\mathcal{L} &= \nabla_{\mathbf{a}_2}\mathcal{L}\,\mathbf{h}_1^\top, & \nabla_{\mathbf{b}_2}\mathcal{L} &= \nabla_{\mathbf{a}_2}\mathcal{L}, \\ \nabla_{\mathbf{h}_1}\mathcal{L} &= W_2^\top\nabla_{\mathbf{a}_2}\mathcal{L}, & \nabla_{\mathbf{a}_1}\mathcal{L} &= \nabla_{\mathbf{h}_1}\mathcal{L}\odot\mathbf{h}_1\odot(1 - \mathbf{h}_1), & \nabla_{W_1}\mathcal{L} &= \nabla_{\mathbf{a}_1}\mathcal{L}\,\mathbf{x}^\top, \quad \nabla_{\mathbf{b}_1}\mathcal{L} = \nabla_{\mathbf{a}_1}\mathcal{L} \end{aligned} \]

The figure starts from \(\mathbf{x} = [1, -0.5]\), true class 1, \(W_1 = \begin{bmatrix} 0.5 & -0.4\\ 0.3 & 0.8 \end{bmatrix}\), \(\mathbf{b}_1 = [0.1, -0.1]\), \(W_2 = \begin{bmatrix} 1.2 & -0.7\\ -0.6 & 0.9 \end{bmatrix}\) and \(\mathbf{b}_2 = [0, 0.1]\), all chosen for these notes. One forward and one backward pass by hand:

PassQuantityValue
Forward\(\mathbf{a}_1 = \mathbf{b}_1 + W_1\mathbf{x}\)\([0.80,\ -0.20]\)
\(\mathbf{h}_1 = \sigma(\mathbf{a}_1)\)\([0.690,\ 0.450]\)
\(\mathbf{a}_2 = \mathbf{b}_2 + W_2\mathbf{h}_1\)\([0.513,\ 0.091]\)
\(\hat{\mathbf{y}} = \operatorname{softmax}(\mathbf{a}_2)\)\([0.604,\ 0.396]\)
\(\mathcal{L} = -\log\hat y_1\)\(0.504\)
Backward\(\nabla_{\mathbf{a}_2}\mathcal{L} = \hat{\mathbf{y}} - \mathbf{e}(1)\)\([-0.396,\ 0.396]\)
\(\nabla_{\mathbf{h}_1}\mathcal{L} = W_2^\top\nabla_{\mathbf{a}_2}\mathcal{L}\)\([-0.713,\ 0.634]\)
\(\nabla_{\mathbf{a}_1}\mathcal{L} = \nabla_{\mathbf{h}_1}\mathcal{L}\odot\mathbf{h}_1\odot(1 - \mathbf{h}_1)\)\([-0.713 \times 0.214,\ 0.634 \times 0.248] = [-0.153,\ 0.157]\)
\(\partial\mathcal{L}/\partial W_{112} = (\partial\mathcal{L}/\partial a_{11})\,x_2\)\((-0.153)(-0.5) = 0.076\)

The last row is the weight \(W_{112}\) that Module 4.4 started from.

Gradient check (not in the lecture). A standard way to test a backpropagation implementation is to compare each derivative with a finite-difference estimate, \(\big(\mathcal{L}(\theta + \epsilon) - \mathcal{L}(\theta - \epsilon)\big)/2\epsilon\), where one parameter is nudged by \(\epsilon = 10^{-4}\) and everything else is held fixed. The figure does this for whichever parameter you select.

1.00 −0.50
−0.40
Loss −log ŷℓ–
∂L/∂θ by backprop–
Finite difference–
Difference–
Forward values and backpropagated gradients on a 2-2-2 network. Inside each unit: its pre-activation \(a\) and its activation (\(h\), or \(\hat y\) at the output). Below every unit: \(\partial\mathcal{L}/\partial a\), which is also the gradient of that unit's bias; above each hidden unit: \(\partial\mathcal{L}/\partial h\). Each edge shows its weight \(w\) and \(\partial\mathcal{L}/\partial w\), the product of \(\partial\mathcal{L}/\partial a\) at its target and \(h\) at its source; edge thickness follows \(|w|\). The shaded output is the true class. Pick a parameter to highlight it, change its value, and compare the backprop derivative with the finite-difference estimate. "Gradient step" updates all 12 parameters at once, \(\theta \leftarrow \theta - 0.5\,\nabla\theta\).

Key terms

Feedforward neural network
Layers of neurons in which each layer feeds only the next: input layer 0, hidden layers 1 to \(L-1\), output layer \(L\).
Pre-activation \(\mathbf{a}_k\)
A layer's weighted sum plus bias, \(\mathbf{a}_k = \mathbf{b}_k + W_k\mathbf{h}_{k-1}\).
Activation \(\mathbf{h}_k\)
The pre-activation passed through the element-wise activation function, \(\mathbf{h}_k = g(\mathbf{a}_k)\), with \(\mathbf{h}_0 = \mathbf{x}\).
Output function \(O\)
The function at the output layer, \(\hat{\mathbf{y}} = O(\mathbf{a}_L)\): linear for real-valued targets, softmax for class probabilities.
Softmax
\(\hat y_j = e^{a_{L,j}}/\sum_i e^{a_{L,i}}\), which turns any real vector into a probability distribution.
Squared-error loss
\(\frac1N\sum_i\sum_j(\hat y_{ij} - y_{ij})^2\), used with a linear output for regression.
Cross-entropy loss
\(-\sum_c y_c\log\hat y_c\); for a one-hot label it equals \(-\log\hat y_\ell\).
Negative log-likelihood
\(-\log\hat y_\ell\), minus the log of the predicted probability of the true class.
One-hot vector \(\mathbf{e}(\ell)\)
A vector with 1 in position \(\ell\) and 0 everywhere else.
Indicator \(\mathbb{1}_{\text{cond}}\)
1 if the condition holds and 0 otherwise.
Chain rule over several paths
\(\partial p/\partial z = \sum_m(\partial p/\partial q_m)(\partial q_m/\partial z)\), adding one product for every intermediate result that depends on \(z\).
Hadamard product \(\odot\)
The entry-by-entry product of two vectors of the same size; the result is a vector.
Outer product
\(\mathbf{u}\mathbf{v}^\top\), the matrix with entries \(u_iv_j\); it gives \(\nabla_{W_k}\mathcal{L} = \nabla_{\mathbf{a}_k}\mathcal{L}\,\mathbf{h}_{k-1}^\top\).
Backpropagation
Computing every gradient by the chain rule from the output layer down, reusing each layer's result for the layer below, after a forward pass that stores every \(\mathbf{a}_k\) and \(\mathbf{h}_k\).

Test yourself

Q1A network takes \(\mathbf{x} \in \mathbb{R}^4\), has hidden layers of 5 and 3 neurons, and 2 outputs. Give the shape of every \(W_k\) and \(\mathbf{b}_k\) and the total number of parameters.

\(W_k\) has one row per neuron of layer \(k\) and one column per neuron of layer \(k-1\). So \(W_1 \in \mathbb{R}^{5\times 4}\), \(\mathbf{b}_1 \in \mathbb{R}^5\); \(W_2 \in \mathbb{R}^{3\times 5}\), \(\mathbf{b}_2 \in \mathbb{R}^3\); \(W_3 \in \mathbb{R}^{2\times 3}\), \(\mathbf{b}_3 \in \mathbb{R}^2\). Total \(20 + 5 + 15 + 3 + 6 + 2 = 51\).

Q2Why is the logistic function a poor output function for predicting movie ratings, and what keeps a linear output's predictions in a sensible range?

The logistic function only produces values in \((0, 1)\), so no choice of weights can output a rating like 7.5. A linear output is unbounded, but predictions far outside the targets' range make the squared error huge, and gradient descent moves the parameters away from such settings.

Q3For \(\mathbf{a}_L = [1, 2, 3]\) and true class \(\ell = 3\), compute \(\hat{\mathbf{y}}\), the cross-entropy loss and \(\nabla_{\mathbf{a}_L}\mathcal{L}\).

\(\hat{\mathbf{y}} = [e^1, e^2, e^3]/(e^1 + e^2 + e^3)\), which is about \([0.090, 0.245, 0.665]\). Loss \(-\log 0.665 \approx 0.408\). Gradient \(\hat{\mathbf{y}} - \mathbf{e}(3) \approx [0.090, 0.245, -0.335]\); its entries add up to 0.

Q4\(\partial\mathcal{L}/\partial\hat y_i = 0\) for every \(i \ne \ell\), yet \(\partial\mathcal{L}/\partial a_{Li} \ne 0\) for those same \(i\). Why?

\(\mathcal{L} = -\log\hat y_\ell\) contains only \(\hat y_\ell\), so the other outputs do not appear in it. But \(\hat y_\ell\) itself depends on every \(a_{Li}\) through the softmax denominator \(\sum_{i'}\exp(a_{Li'})\), so changing any \(a_{Li}\) changes the loss.

Q5Why is \(\partial\mathcal{L}/\partial h_{ij}\) a sum over \(m\), while \(\partial\mathcal{L}/\partial a_{ij}\) has a single term?

\(h_{ij}\) feeds every pre-activation \(a_{i+1,m}\) of the next layer, so there is one path to the loss per neuron of layer \(i+1\), and the multi-path chain rule adds them up. \(a_{ij}\) feeds only \(h_{ij} = g(a_{ij})\), so there is one path and one factor, \(g'(a_{ij})\).

Q6A logistic hidden layer has \(\mathbf{h}_i = [0.5, 0.9]\) and \(\nabla_{\mathbf{h}_i}\mathcal{L} = [0.2, -0.4]\). Find \(\nabla_{\mathbf{a}_i}\mathcal{L}\).

\(g'(\mathbf{a}_i) = \mathbf{h}_i\odot(1 - \mathbf{h}_i) = [0.25, 0.09]\). Then \(\nabla_{\mathbf{a}_i}\mathcal{L} = [0.2 \times 0.25,\ -0.4 \times 0.09]\) \(= [0.05, -0.036]\).

Q7Explain why \(\nabla_{W_k}\mathcal{L} = \nabla_{\mathbf{a}_k}\mathcal{L}\,\mathbf{h}_{k-1}^\top\) has the shape of \(W_k\), and say where each factor comes from.

\(\nabla_{\mathbf{a}_k}\mathcal{L}\) is a column with one entry per neuron of layer \(k\), and \(\mathbf{h}_{k-1}^\top\) is a row with one entry per neuron of layer \(k-1\); their outer product has the shape of \(W_k\). The first factor comes from the backward pass, the second was stored by the forward pass.

Q8In back_propagation, which values from the forward pass are used, and why can the loop start at \(k = L\)?

\(\hat{\mathbf{y}}\) gives \(\nabla_{\mathbf{a}_L}\mathcal{L} = \hat{\mathbf{y}} - \mathbf{e}(y)\); each \(\mathbf{h}_{k-1}\) gives \(\nabla_{W_k}\mathcal{L}\); each \(\mathbf{a}_{k-1}\) (or \(\mathbf{h}_{k-1}\)) gives \(g'(\mathbf{a}_{k-1})\). The loop can start at \(L\) because the output gradient needs nothing but \(\hat{\mathbf{y}}\) and the label, and every later step uses only the step before it.