
EE 541 - Unit 6
Fall 2026
Gradients for Hidden Layers and The Chain Rule
Vectorizing Backpropagation and Mini-Batches
Implementation and Gradient Checking and Gradients Through Depth
Logistic neuron
Gradient descent: \(\mathbf{w} \leftarrow \mathbf{w} - \eta\, \partial \ell / \partial \mathbf{w}\)
Gradient as a product \[\frac{\partial \ell}{\partial \mathbf{w}} = \frac{\partial \ell}{\partial \pi} \cdot \frac{d\pi}{dz} \cdot \frac{\partial z}{\partial \mathbf{w}}\]
All three factors are functions of \(\pi\), \(y\), and \(\mathbf{x}\): available after one forward pass

One neuron: one hyperplane in \(\mathbf{x}\)
Hidden layer: \(m\) logistic units on \(\mathbf{x}\), then one logistic unit on their outputs \[\mathbf{a}^{(1)} = \sigma(\mathbf{W}^{(1)}\mathbf{x} + \mathbf{b}^{(1)}), \qquad \hat{y} = \sigma(\mathbf{W}^{(2)}\mathbf{a}^{(1)} + \mathbf{b}^{(2)})\]
Every unit is a weighted sum, a bias, and a sigmoid

Layer index in parentheses, not a power
Both weight sets are matrices
Renaming only: the network is unchanged (figure)

Loss \(\ell(\hat{y}, y)\): a function of the output and the target
Output weights \(\mathbf{W}^{(2)}\): \(\ell\) depends on them directly, through \(\hat{y}\)
Hidden weights \(\mathbf{W}^{(1)}\): \(\ell\) depends on them only through \(\mathbf{a}^{(1)}\), and on \(\mathbf{a}^{(1)}\) only through \(\hat{y}\)
Example: \(d = 784\) inputs, \(m = 100\) hidden units
| Parameters | Count | Enter \(\ell\) |
|---|---|---|
| \(\mathbf{W}^{(1)}\), \(\mathbf{b}^{(1)}\) | \(100 \times 785 = 78{,}500\) | through \(\mathbf{a}^{(1)}\), then \(\hat{y}\) |
| \(\mathbf{W}^{(2)}\), \(\mathbf{b}^{(2)}\) | \(101\) | through \(\hat{y}\) |
All 78,500 hidden gradients have to come from \(\ell\) through \(\hat{y}\), and at some cost
Same form as one neuron \[\frac{\partial \ell}{\partial \mathbf{W}^{(1)}} = \frac{\partial \ell}{\partial \mathbf{s}^{(1)}} \cdot \frac{\partial \mathbf{s}^{(1)}}{\partial \mathbf{W}^{(1)}}\]
First factor expands through the output unit \[\frac{\partial \ell}{\partial \mathbf{s}^{(1)}} = \underbrace{\frac{\partial \ell}{\partial \hat{y}}}_{\text{loss slope}} \cdot \underbrace{\frac{d\hat{y}}{ds^{(2)}}}_{\sigma'(s^{(2)})} \cdot \underbrace{\frac{\partial s^{(2)}}{\partial \mathbf{a}^{(1)}}}_{\mathbf{W}^{(2)}} \cdot \underbrace{\frac{\partial \mathbf{a}^{(1)}}{\partial \mathbf{s}^{(1)}}}_{\sigma'(\mathbf{s}^{(1)})}\]
First two factors are also in \(\partial \ell / \partial \mathbf{W}^{(2)}\): \(\;\partial \ell / \partial \hat{y} \cdot \sigma'(s^{(2)}) \cdot \mathbf{a}^{(1)}\)


Layer \(l = 1, \ldots, L\) \[\mathbf{s}^{(l)} = \mathbf{W}^{(l)}\mathbf{a}^{(l-1)} + \mathbf{b}^{(l)}, \qquad \mathbf{a}^{(l)} = \sigma^{(l)}(\mathbf{s}^{(l)})\]
Pre-activation and activation
Activation \(\sigma^{(l)}\) can differ by layer
Two-layer network: \(L = 2\), \(n_2 = 1\)
Three derivatives per layer
From the loss to the input, given \(\partial \ell / \partial \mathbf{a}^{(l)}\)
Target appears once, in \(\partial \ell / \partial \hat{\mathbf{y}}\), and every other factor is a slope, a weight matrix, or an input

One weight at a time, numerically: perturb and re-evaluate \[\frac{\partial \ell}{\partial w} \approx \frac{\ell(w + \epsilon) - \ell(w - \epsilon)}{2\epsilon}\]
Example: layers of 784, 256, 128, and 10 units (a \(28 \times 28\) image, 10 classes)
| Layer | Weights | Biases |
|---|---|---|
| 1 | \(256 \times 784 = 200{,}704\) | 256 |
| 2 | \(128 \times 256 = 32{,}768\) | 128 |
| 3 | \(10 \times 128 = 1{,}280\) | 10 |
One weight’s gradient: a product of derivatives from \(\ell\) to that weight
Shared factors computed once per layer, from the loss to the input
Cost: two to three forward passes
Finite differences remain the check on a backward-pass implementation
Composition of \(L\) functions, \(u_k = f_k(u_{k-1})\), \(u_0 = x\) \[y = f_L(\cdots f_2(f_1(x))), \qquad \frac{dy}{dx} = f_L'(u_{L-1}) \cdots f_2'(u_1)\, f_1'(u_0)\]
Example: \(u_1 = 3x + 1\), \(\;u_2 = \sigma(u_1)\), \(\;y = u_2^2\), at \(x = 0.5\)
Logistic neuron, \(L = 3\): \(\;f_1 = \mathbf{w}^T\mathbf{x} + b\), \(\;f_2 = \sigma\), \(\;f_3 = \ell\)
Forward values appear in the derivative: \(\sigma'\) at \(u_1 = 2.5\), not at \(x\)

Two paths from \(x\) to \(z\): \(\;z = f(u, v)\), \(\;u = g(x)\), \(\;v = h(x)\) \[\frac{dz}{dx} = \frac{\partial f}{\partial u}\, g'(x) + \frac{\partial f}{\partial v}\, h'(x)\]
Example: \(u = 2x\), \(\;v = x^2\), \(\;z = uv\), at \(x = 1.5\)
Hidden unit with \(K\) units after it: \(K\) paths to the loss

Gradient of a scalar \(f(\mathbf{x})\), \(\mathbf{x} \in \mathbb{R}^n\): a column \[\nabla_{\mathbf{x}} f = \begin{bmatrix} \partial f / \partial x_1 \\ \vdots \\ \partial f / \partial x_n \end{bmatrix} \in \mathbb{R}^{n}\]
Three derivatives used throughout
Scalar by matrix: \(\partial \ell / \partial \mathbf{W}\) has the shape of \(\mathbf{W}\), one entry per weight
Jacobian of a vector function \(\mathbf{y} = g(\mathbf{x})\), \(\mathbf{y} \in \mathbb{R}^m\): rows indexed by the outputs \[\mathbf{J} = \frac{\partial \mathbf{y}}{\partial \mathbf{x}} \in \mathbb{R}^{m \times n}, \qquad J_{ji} = \frac{\partial y_j}{\partial x_i}\]
Chain rule: pre-multiply by the transpose \[\nabla_{\mathbf{x}} f = \mathbf{J}^T\, \nabla_{\mathbf{y}} f\]
Example: \(\mathbf{A} = \begin{bmatrix} 2 & 1 \\ 0 & 3 \end{bmatrix}\), \(\;\mathbf{x} = \begin{bmatrix} 1 \\ 2 \end{bmatrix}\), \(\;f = \mathbf{x}^T \mathbf{A} \mathbf{x} = 16\)
Row gradients: the other consistent layout, in the appendix
Vector step inside a composition: \(\;\mathbf{y} = g(\mathbf{x})\), then a scalar \(f(\mathbf{y})\), the chain rule with column gradients applied
One path per component \(y_j\) \[\frac{\partial f}{\partial x_i} = \sum_{j=1}^{m} \frac{\partial f}{\partial y_j}\,\frac{\partial y_j}{\partial x_i}\]
As a matrix, with \(J_{ji} = \partial y_j / \partial x_i\), rows indexed by the outputs \[\nabla_{\mathbf{x}} f = \mathbf{J}^T \nabla_{\mathbf{y}} f\]
Linear, \(\mathbf{y} = \mathbf{W}\mathbf{x}\): \(\;\mathbf{J} = \mathbf{W}\), \(\;\nabla_{\mathbf{x}} f = \mathbf{W}^T \nabla_{\mathbf{y}} f\)
Element-wise, \(y_j = \sigma(x_j)\): \(\;\mathbf{J} = \mathrm{diag}(\sigma'(\mathbf{x}))\), \(\;\nabla_{\mathbf{x}} f = \sigma'(\mathbf{x}) \odot \nabla_{\mathbf{y}} f\), with \(\odot\) the entry-by-entry product
Example: \(\mathbf{W} = \begin{bmatrix} 0.5 & 0.3 \\ -0.2 & 0.4 \end{bmatrix}\), \(\;\mathbf{x} = \begin{bmatrix} 1 \\ 2 \end{bmatrix}\), \(\;f(\mathbf{y}) = \|\mathbf{y}\|^2\)
\[\mathbf{y} = \begin{bmatrix} 1.1 \\ 0.6 \end{bmatrix}, \qquad \nabla_{\mathbf{y}} f = 2\mathbf{y} = \begin{bmatrix} 2.2 \\ 1.2 \end{bmatrix}\]
\[\nabla_{\mathbf{x}} f = \mathbf{W}^T \nabla_{\mathbf{y}} f = \begin{bmatrix} 0.5 & -0.2 \\ 0.3 & 0.4 \end{bmatrix} \begin{bmatrix} 2.2 \\ 1.2 \end{bmatrix} = \begin{bmatrix} 0.86 \\ 1.14 \end{bmatrix}\]
Row \(i\) of \(\mathbf{J}^T\): every \(y_j\) that depends on \(x_i\), the sum over paths as one matrix row
Parameters at every depth: \(\;u_k = f_k(u_{k-1}; \theta_k)\), \(\;y = u_L\) \[\frac{\partial y}{\partial \theta_k} = \underbrace{f_L'(u_{L-1}) \cdots f_{k+1}'(u_k)}_{P_k} \cdot \frac{\partial f_k}{\partial \theta_k}\]
Running product \(P_k\), from the output to \(u_k\)
Cost for all \(L\) gradients
Network: \(\theta_k\) is layer \(k\)’s weights
Backward pass: \(P\) extended from the loss to the input, one layer at a time

One unit per layer: every weight, pre-activation, and activation is a single number
Parameters: \(w^{(1)} = 0.5\), \(\;b^{(1)} = 0\), \(\;w^{(2)} = -1\), \(\;b^{(2)} = 0.5\)
Input and target: \(x = 2\), \(\;y = 1\)
Kept for the backward pass: \(a^{(1)}\) (the output weight’s input), \(s^{(1)}\), \(s^{(2)}\)
Forward pass, one line per operation \[s^{(1)} = w^{(1)} x + b^{(1)} = 0.5 \cdot 2 + 0 = 1\] \[a^{(1)} = \sigma(s^{(1)}) = \sigma(1) = 0.731\] \[s^{(2)} = w^{(2)} a^{(1)} + b^{(2)} = -0.731 + 0.5 = -0.231\] \[\hat{y} = \sigma(s^{(2)}) = 0.442, \qquad \ell = -\log \hat{y} = 0.815\]
Chain rule on \(w^{(2)}\): \(\ell\) depends on \(w^{(2)}\) through \(s^{(2)}\), then \(\hat{y}\) \[\frac{\partial \ell}{\partial w^{(2)}} = \frac{\partial \ell}{\partial \hat{y}} \cdot \frac{d\hat{y}}{ds^{(2)}} \cdot \frac{\partial s^{(2)}}{\partial w^{(2)}}\]
Each factor from the forward values
Substituted \[\frac{\partial \ell}{\partial w^{(2)}} = (-2.260)(0.247)(0.731) = -0.408\]
Bias, the same two slopes with input \(1\) \[\frac{\partial \ell}{\partial b^{(2)}} = (-2.260)(0.247) = -0.558\]

Two slopes multiply to \(\hat{y} - y = -0.558\): the prediction error, as for one logistic neuron
Chain rule on \(w^{(1)}\): \(\ell\) depends on \(w^{(1)}\) through \(s^{(1)}\), \(a^{(1)}\), \(s^{(2)}\), and \(\hat{y}\) \[\frac{\partial \ell}{\partial w^{(1)}} = \underbrace{\frac{\partial \ell}{\partial \hat{y}} \cdot \sigma'(s^{(2)})}_{\text{as for } w^{(2)}} \cdot \underbrace{w^{(2)}}_{\partial s^{(2)} / \partial a^{(1)}} \cdot \underbrace{\sigma'(s^{(1)})}_{\partial a^{(1)} / \partial s^{(1)}} \cdot \underbrace{x}_{\partial s^{(1)} / \partial w^{(1)}}\]
Shared with the output weight: the first two factors, \(-0.558\)
New in this product, both from stored values
Substituted \[\frac{\partial \ell}{\partial w^{(1)}} = (-0.558)(-1)(0.197)(2) = 0.219\] \[\frac{\partial \ell}{\partial b^{(1)}} = (-0.558)(-1)(0.197) = 0.110\]

Definition \[\delta^{(l)} = \frac{\partial \ell}{\partial s^{(l)}}\]
Already in both gradients, as the bracketed product before the input
At the output: \(\delta^{(2)} = \hat{y} - y\), the prediction error
At the hidden layer: \(\delta^{(1)}\) is a derivative with no target behind it

One number per layer: each parameter’s gradient is \(\delta^{(l)}\) times that parameter’s input
Two chain-rule steps from \(\delta^{(2)}\) to \(\delta^{(1)}\)
One weight and one slope per layer: the weight connecting layer \(l\) to layer \(l + 1\), and layer \(l\)’s own slope at its stored \(s^{(l)}\)
For \(L\) layers, the same two steps from the output layer to the first, \(l = L - 1, \ldots, 1\) \[\delta^{(L)} = \frac{\partial \ell}{\partial \hat{y}} \cdot \sigma'(s^{(L)})\] \[\delta^{(l)} = \delta^{(l+1)}\, w^{(l+1)}\, \sigma'(s^{(l)})\]
What layer \(l\) uses
Target appears once: in \(\partial \ell / \partial \hat{y}\), at the output, and every layer’s \(\delta\) contains that factor
Cost per layer: two multiplications for \(\delta^{(l)}\), then one per parameter

Initialize at the output: \(\delta^{(L)} = \partial \ell / \partial \hat{y} \cdot \sigma'(s^{(L)})\)
Propagate one layer at a time: \(\delta^{(l)} = \delta^{(l+1)}\, w^{(l+1)}\, \sigma'(s^{(l)})\)
Read off at every layer: \(\partial \ell / \partial w^{(l)} = \delta^{(l)} a^{(l-1)}\), \(\;\partial \ell / \partial b^{(l)} = \delta^{(l)}\)
Hidden unit’s output branches to two output units, loss \(\ell = \ell_1 + \ell_2\), subscripts \(1\) and \(2\) indexing the output units
\(w^{(3)} = -1\), \(\;b^{(3)} = 0.5\), \(\;w^{(4)} = 1\), \(\;b^{(4)} = 0\)
\(\hat{y}_1 = \sigma(w^{(3)} a^{(1)} + b^{(3)}) = 0.442\), target \(y_1 = 1\)
\(\hat{y}_2 = \sigma(w^{(4)} a^{(1)} + b^{(4)}) = 0.675\), target \(y_2 = 0\)
At each output, as for one neuron
At the branch point, each branch contributes a term \[\frac{\partial \ell}{\partial a^{(1)}} = \delta_1 w^{(3)} + \delta_2 w^{(4)} = (-0.558)(-1) + (0.675)(1) = 1.233\]
From \(a^{(1)}\) to \(w^{(1)}\), the single chain as before

\(K\) branches: \(K\) terms, which the vector form writes as \(\mathbf{W}^T \boldsymbol{\delta}\)
Step: \(\theta \leftarrow \theta - \eta\, \partial \ell / \partial \theta\) for every parameter, with \(\eta = 1\)
Loss after the step, by a new forward pass
Each step of the loop
Loop is gradient descent as fit logistic regression: backpropagation replaces the one-neuron gradient \((\pi - y)\,\mathbf{x}\) and nothing else changes
3-4-2 network: 3 inputs, 4 sigmoid hidden units, 2 softmax outputs, cross-entropy
Layer as a matrix: row \(i\) of \(\mathbf{W}^{(l)}\) is unit \(i\)’s weight vector \[\mathbf{s}^{(l)} = \mathbf{W}^{(l)} \mathbf{a}^{(l-1)} + \mathbf{b}^{(l)}, \qquad \mathbf{W}^{(l)} \in \mathbb{R}^{n_l \times n_{l-1}}\]
First layer \[\mathbf{W}^{(1)} = \begin{bmatrix} 0.1 & 0.2 & -0.1 \\ 0.3 & -0.2 & 0.1 \\ -0.1 & 0.1 & 0.2 \\ 0.2 & 0.3 & -0.3 \end{bmatrix}\] \[\mathbf{b}^{(1)} = \begin{bmatrix} 0.1 \\ -0.1 \\ 0 \\ 0.2 \end{bmatrix}, \qquad \mathbf{x} = \begin{bmatrix} 1 \\ 0.5 \\ -0.5 \end{bmatrix}\] \[\mathbf{W}^{(2)} = \begin{bmatrix} 0.5 & -0.3 & 0.2 & 0.1 \\ -0.2 & 0.4 & -0.1 & 0.3 \end{bmatrix}, \qquad \mathbf{b}^{(2)} = \begin{bmatrix} 0 \\ 0.1 \end{bmatrix}\]
Unit 1, row 1 against \(\mathbf{x}\): \(\;s^{(1)}_1 = 0.1 (1) + 0.2 (0.5) + (-0.1)(-0.5) + 0.1 = 0.35\)
Forward pass, one product per layer

Output layer with \(K = n_L\) classes: scores \(\mathbf{s} \in \mathbb{R}^K\) of any sign, probabilities \(\hat{\mathbf{y}}\) nonnegative and summing to one \[\hat{y}_i = \frac{e^{s_i}}{\sum_{j=1}^{K} e^{s_j}}\] \[\mathbf{s} = [2,\ 0.5,\ -1]^T \;\text{ gives }\; \hat{\mathbf{y}} = [0.786,\ 0.175,\ 0.039]^T\]
Invariant to a shift: \(\mathbf{s} + c\mathbf{1}\) gives the same \(\hat{\mathbf{y}}\), so one of the \(K\) scores is redundant
Scale of the differences sets the confidence: \(2\mathbf{s}\) gives \([0.950,\ 0.047,\ 0.002]^T\), \(\;\mathbf{s}/2\) gives \([0.590,\ 0.279,\ 0.132]^T\)
Order kept, no exact zeros: \(e^{s}\) is positive and increasing, so the largest score has the largest probability and every class has a nonzero one
With cross-entropy, target class \(c\) \[\ell = -\log \hat{y}_c = -s_c + \log \sum_{j} e^{s_j}\]
Other maps from scores to probabilities, on the same \(\mathbf{s}\)
Shift invariance fixes a property of the Jacobian: adding \(c\) to every score leaves \(\hat{y}_i\) unchanged, so \(\sum_j \partial \hat{y}_i / \partial s_j = 0\) for every \(i\)
Sigmoid outputs, cross-entropy per unit: \(\hat{y}_k\) depends on \(s_k\) alone \[\delta^{(L)}_k = \frac{\partial \ell}{\partial \hat{y}_k}\, \sigma'(s_k) = \frac{\hat{y}_k - y_k}{\hat{y}_k (1 - \hat{y}_k)} \cdot \hat{y}_k (1 - \hat{y}_k) = \hat{y}_k - y_k\]
Softmax outputs: every \(\hat{y}_i\) depends on every \(s_j\) \[\frac{\partial \hat{y}_i}{\partial s_j} = \hat{y}_i\,(\mathbb{1}[i = j] - \hat{y}_j)\] \[\frac{\partial \hat{\mathbf{y}}}{\partial \mathbf{s}} = \begin{bmatrix} 0.250 & -0.250 \\ -0.250 & 0.250 \end{bmatrix} \text{ at } \hat{\mathbf{y}} = \begin{bmatrix} 0.489 \\ 0.511 \end{bmatrix}\]
Chain rule: the Jacobian transpose times the loss gradient, \(\nabla_{\hat{\mathbf{y}}} \ell = -\mathbf{y} / \hat{\mathbf{y}}\) \[\boldsymbol{\delta}^{(L)} = \Big(\frac{\partial \hat{\mathbf{y}}}{\partial \mathbf{s}}\Big)^T \nabla_{\hat{\mathbf{y}}} \ell = \begin{bmatrix} 0.250 & -0.250 \\ -0.250 & 0.250 \end{bmatrix} \begin{bmatrix} 0 \\ -1.958 \end{bmatrix} = \begin{bmatrix} 0.489 \\ -0.489 \end{bmatrix}\]
Directly from the scores, target class \(c\) \[\ell = -s_c + \log \sum_j e^{s_j}, \qquad \frac{\partial \ell}{\partial s_j} = -\mathbb{1}[j = c] + \frac{e^{s_j}}{\sum_k e^{s_k}} = \hat{y}_j - y_j\]
Both routes: \(\;\boldsymbol{\delta}^{(2)} = \hat{\mathbf{y}} - \mathbf{y} = [0.489,\ -0.489]^T\)
For both output types: the prediction error, the one-neuron result of logistic regression for every output unit at once
Layer written out: \(w_{ij}\) appears in row \(i\) and in no other row \[s_i = \sum_{j} w_{ij}\, a_j + b_i, \qquad \frac{\partial s_k}{\partial w_{ij}} = \begin{cases} a_j & k = i \\ 0 & k \neq i \end{cases}\]
Chain rule through the pre-activations: \(n_l\) terms, one nonzero \[\frac{\partial \ell}{\partial w_{ij}} = \sum_{k=1}^{n_l} \frac{\partial \ell}{\partial s_k}\, \frac{\partial s_k}{\partial w_{ij}} = \delta_1 \cdot 0 + \cdots + \delta_i\, a_j + \cdots + \delta_{n_l} \cdot 0 = \delta_i\, a_j\]
Example: \(w^{(2)}_{23}\), unit 2’s weight on \(a^{(1)}_3\)
Bias: \(\partial s_i / \partial b_i = 1\), so \(\partial \ell / \partial b_i = \delta_i\)

Same form as the scalar network: \(\delta^{(l)} a^{(l-1)}\), with the \(\delta\) of the unit the weight belongs to and the input it multiplies
One weight: \(\;\partial \ell / \partial w_{ij} = \delta_i\, a_j\)
All weights of the layer: the \(n_l \times n_{l-1}\) table of these products is one outer product \[\frac{\partial \ell}{\partial \mathbf{W}} = \begin{bmatrix} \delta_1 a_1 & \delta_1 a_2 & \cdots & \delta_1 a_{n_{l-1}} \\ \vdots & & & \vdots \\ \delta_{n_l} a_1 & \delta_{n_l} a_2 & \cdots & \delta_{n_l} a_{n_{l-1}} \end{bmatrix} = \boldsymbol{\delta}\, \mathbf{a}^T, \qquad \frac{\partial \ell}{\partial \mathbf{b}} = \boldsymbol{\delta}\]
Update of unit \(i\)’s weight vector: \(\;\mathbf{w}_i \leftarrow \mathbf{w}_i - \eta\, \delta_i\, \mathbf{a}\)
Output layer of the example: \(\boldsymbol{\delta}^{(2)} = [0.489,\ -0.489]^T\), target class 2 \[\begin{aligned} \frac{\partial \ell}{\partial \mathbf{W}^{(2)}} &= \begin{bmatrix} 0.489 \\ -0.489 \end{bmatrix} \begin{bmatrix} 0.587 & 0.512 & 0.463 & 0.668 \end{bmatrix} \\ &= \begin{bmatrix} 0.287 & 0.251 & 0.226 & 0.327 \\ -0.287 & -0.251 & -0.226 & -0.327 \end{bmatrix} \end{aligned}\]
\(w_{ij}\) does not change when \(a_j = 0\) or \(\delta_i = 0\): an input at zero, or a unit with zero slope (a saturated sigmoid, a ReLU with \(s_i < 0\))
Layer written out: \(a_j\) multiplies one weight in every row, column \(j\) of \(\mathbf{W}\) \[s_i = \sum_{j} w_{ij}\, a_j + b_i, \qquad \frac{\partial s_i}{\partial a_j} = w_{ij} \;\text{ for every } i\]
Chain rule through the pre-activations: \(n_l\) terms, all nonzero, one per path to the loss \[\frac{\partial \ell}{\partial a_j} = \sum_{i=1}^{n_l} \frac{\partial \ell}{\partial s_i}\, \frac{\partial s_i}{\partial a_j} = \sum_{i=1}^{n_l} \delta_i\, w_{ij}\]
Example: \(a^{(1)}_1\) is an input to both output units, through \(w^{(2)}_{11} = 0.5\) and \(w^{(2)}_{21} = -0.2\) \[\frac{\partial \ell}{\partial a^{(1)}_1} = (0.489)(0.5) + (-0.489)(-0.2) = 0.342\]
Weights in the sum are column \(j\) of \(\mathbf{W}\): one input’s weights into every unit, where a row is one unit’s weights from every input

Branching rule of the scalar network: one product per branch, summed, with \(n_l\) branches
Jacobian of the linear step: entry \((i, j)\) is \(\partial s_i / \partial a_j\), row \(i\) the gradient of \(s_i\) \[\begin{aligned} \frac{\partial \mathbf{s}^{(2)}}{\partial \mathbf{a}^{(1)}} &= \begin{bmatrix} \partial s_1 / \partial a_1 & \cdots & \partial s_1 / \partial a_4 \\ \partial s_2 / \partial a_1 & \cdots & \partial s_2 / \partial a_4 \end{bmatrix} \\ &= \begin{bmatrix} 0.5 & -0.3 & 0.2 & 0.1 \\ -0.2 & 0.4 & -0.1 & 0.3 \end{bmatrix} = \mathbf{W}^{(2)} \end{aligned}\]
Chain rule with column gradients: pre-multiply by the Jacobian transpose \[\frac{\partial \ell}{\partial \mathbf{a}^{(l-1)}} = \Big(\frac{\partial \mathbf{s}^{(l)}}{\partial \mathbf{a}^{(l-1)}}\Big)^T \boldsymbol{\delta}^{(l)} = \mathbf{W}^{(l)T} \boldsymbol{\delta}^{(l)}\] \[[n_{l-1} \times n_l][n_l] = [n_{l-1}]\]

Example \[\frac{\partial \ell}{\partial \mathbf{a}^{(1)}} = \mathbf{W}^{(2)T} \boldsymbol{\delta}^{(2)} = [0.342,\ -0.342,\ 0.147,\ -0.098]^T\]
Cost: \(n_{l-1} n_l\) multiplications, the same as the layer’s forward product
Activation written out: \(a_i = \sigma(s_i)\), with no other pre-activation in the expression \[\frac{\partial a_i}{\partial s_j} = \begin{cases} \sigma'(s_i) & j = i \\ 0 & j \neq i \end{cases}\]
Jacobian: the slopes on the diagonal, zeros elsewhere \[\frac{\partial \mathbf{a}^{(1)}}{\partial \mathbf{s}^{(1)}} = \mathrm{diag}\big(\sigma'(\mathbf{s}^{(1)})\big) = \begin{bmatrix} 0.242 & 0 & 0 & 0 \\ 0 & 0.250 & 0 & 0 \\ 0 & 0 & 0.249 & 0 \\ 0 & 0 & 0 & 0.222 \end{bmatrix}\]
Chain rule: pre-multiply by the transpose, which for a diagonal matrix is the matrix itself \[\boldsymbol{\delta}^{(l)} = \Big(\frac{\partial \mathbf{a}^{(l)}}{\partial \mathbf{s}^{(l)}}\Big)^T \frac{\partial \ell}{\partial \mathbf{a}^{(l)}} = \mathrm{diag}\big(\sigma'(\mathbf{s}^{(l)})\big)\, \frac{\partial \ell}{\partial \mathbf{a}^{(l)}}\]
Example, entry 1 of the product: \(\;0.242 (0.342) + 0 (-0.342) + 0 (0.147) + 0 (-0.098) = 0.083\)

Sixteen products, twelve of them zero: each \(\delta_i\) is its own slope times its own \(\partial \ell / \partial a_i\)
Hadamard product of two vectors of the same length: entry by entry \[\mathbf{v} \odot \mathbf{u} = \begin{bmatrix} v_1 u_1 \\ \vdots \\ v_n u_n \end{bmatrix}, \qquad \mathbf{v} \odot \mathbf{u} = \mathbf{u} \odot \mathbf{v}\]
Equal to a diagonal matrix times a vector \[\mathrm{diag}(\mathbf{v})\, \mathbf{u} = \begin{bmatrix} v_1 & & \\ & \ddots & \\ & & v_n \end{bmatrix} \begin{bmatrix} u_1 \\ \vdots \\ u_n \end{bmatrix} = \begin{bmatrix} v_1 u_1 \\ \vdots \\ v_n u_n \end{bmatrix} = \mathbf{v} \odot \mathbf{u}\]
Activation step in Hadamard form \[\boldsymbol{\delta}^{(l)} = \mathrm{diag}\big(\sigma'(\mathbf{s}^{(l)})\big)\, \frac{\partial \ell}{\partial \mathbf{a}^{(l)}} = \frac{\partial \ell}{\partial \mathbf{a}^{(l)}} \odot \sigma'(\mathbf{s}^{(l)})\]
Hidden layer of the example, entry 1: \(\;0.342 \times 0.242 = 0.083\), with no other entry involved
Slopes from stored values
Two Jacobian transposes per layer, from \(\boldsymbol{\delta}^{(l+1)}\) to \(\boldsymbol{\delta}^{(l)}\) \[\boldsymbol{\delta}^{(l)} = \Big(\frac{\partial \mathbf{a}^{(l)}}{\partial \mathbf{s}^{(l)}}\Big)^T \Big(\frac{\partial \mathbf{s}^{(l+1)}}{\partial \mathbf{a}^{(l)}}\Big)^T \boldsymbol{\delta}^{(l+1)} = \mathrm{diag}\big(\sigma'(\mathbf{s}^{(l)})\big)\, \mathbf{W}^{(l+1)T} \boldsymbol{\delta}^{(l+1)}\] \[= \big(\mathbf{W}^{(l+1)T} \boldsymbol{\delta}^{(l+1)}\big) \odot \sigma'(\mathbf{s}^{(l)})\] \[[n_l \times n_{l+1}][n_{l+1}] \odot [n_l] = [n_l]\]
Initialize at the output: \(\;\boldsymbol{\delta}^{(L)} = \hat{\mathbf{y}} - \mathbf{y}\) with cross-entropy
Propagate: \(l = L - 1, \ldots, 1\), the weight step then the slope step
Scalar case, \(n_l = 1\) at every layer: \(\;\delta^{(l)} = \delta^{(l+1)}\, w^{(l+1)}\, \sigma'(s^{(l)})\), the scalar network’s recursion
What layer \(l\) uses: \(\boldsymbol{\delta}^{(l+1)}\), the weight matrix after it, and its own stored \(\mathbf{s}^{(l)}\)
Example, output to hidden layer \[\boldsymbol{\delta}^{(2)} = \hat{\mathbf{y}} - \mathbf{y} = \begin{bmatrix} 0.489 \\ -0.489 \end{bmatrix}\] \[\mathbf{W}^{(2)T} \boldsymbol{\delta}^{(2)} = \begin{bmatrix} 0.5 & -0.2 \\ -0.3 & 0.4 \\ 0.2 & -0.1 \\ 0.1 & 0.3 \end{bmatrix} \begin{bmatrix} 0.489 \\ -0.489 \end{bmatrix} = \begin{bmatrix} 0.342 \\ -0.342 \\ 0.147 \\ -0.098 \end{bmatrix}\] \[\boldsymbol{\delta}^{(1)} = \begin{bmatrix} 0.342 \\ -0.342 \\ 0.147 \\ -0.098 \end{bmatrix} \odot \begin{bmatrix} 0.242 \\ 0.250 \\ 0.249 \\ 0.222 \end{bmatrix} = \begin{bmatrix} 0.083 \\ -0.086 \\ 0.036 \\ -0.022 \end{bmatrix}\]
Initialize: \(\;\boldsymbol{\delta}^{(L)} = \hat{\mathbf{y}} - \mathbf{y}\)
Propagate, \(l = L - 1, \ldots, 1\) \[\boldsymbol{\delta}^{(l)} = \big(\mathbf{W}^{(l+1)T} \boldsymbol{\delta}^{(l+1)}\big) \odot \sigma'(\mathbf{s}^{(l)})\]
Read off, \(l = L, \ldots, 1\) \[\frac{\partial \ell}{\partial \mathbf{W}^{(l)}} = \boldsymbol{\delta}^{(l)}\, \mathbf{a}^{(l-1)T}, \qquad \frac{\partial \ell}{\partial \mathbf{b}^{(l)}} = \boldsymbol{\delta}^{(l)}\]
Multiplications per layer, against \(n_l n_{l-1}\) for its forward product
3-4-2 network: hidden layer \(4 \cdot 2 + 4 + 4 \cdot 3 = 24\), output layer \(2 \cdot 4 = 8\), against \(20\) for the forward pass
Stored from the forward pass: \(\mathbf{a}^{(0)}, \ldots, \mathbf{a}^{(L-1)}\) for the outer products, \(\mathbf{s}^{(l)}\) or \(\mathbf{a}^{(l)}\) for the slopes

Read off at the hidden layer, from \(\boldsymbol{\delta}^{(1)}\) and \(\mathbf{x} = [1,\ 0.5,\ -0.5]^T\) \[\frac{\partial \ell}{\partial \mathbf{W}^{(1)}} = \boldsymbol{\delta}^{(1)} \mathbf{x}^T = \begin{bmatrix} 0.083 & 0.042 & -0.042 \\ -0.086 & -0.043 & 0.043 \\ 0.036 & 0.018 & -0.018 \\ -0.022 & -0.011 & 0.011 \end{bmatrix}\] \[\frac{\partial \ell}{\partial \mathbf{b}^{(1)}} = \boldsymbol{\delta}^{(1)}\]
Update, \(\eta = 1\), every parameter at once \[\mathbf{W}^{(l)} \leftarrow \mathbf{W}^{(l)} - \eta\, \boldsymbol{\delta}^{(l)} \mathbf{a}^{(l-1)T}, \qquad \mathbf{b}^{(l)} \leftarrow \mathbf{b}^{(l)} - \eta\, \boldsymbol{\delta}^{(l)}\]
Loss after the step, by a new forward pass
Scalar network’s loop, with matrices in place of scalars
Inputs and targets: one sample per row, targets classes 2, 1, 2 \[\mathbf{X} = \begin{bmatrix} 1 & 0.5 & -0.5 \\ 0.8 & -0.3 & 0.4 \\ -0.6 & 0.7 & 0.2 \end{bmatrix}\]
First layer, \(\mathbf{S}^{(1)} = \mathbf{X} \mathbf{W}^{(1)T} + \mathbf{1}\, \mathbf{b}^{(1)T}\), \(\;3 \times 4\) \[\mathbf{S}^{(1)} = \begin{bmatrix} 0.35 & 0.05 & -0.15 & 0.70 \\ 0.08 & 0.24 & -0.03 & 0.15 \\ 0.16 & -0.40 & 0.17 & 0.23 \end{bmatrix}\]
Output layer, \(\mathbf{S}^{(2)} = \mathbf{A}^{(1)} \mathbf{W}^{(2)T} + \mathbf{1}\, \mathbf{b}^{(2)T}\), softmax by row \[\hat{\mathbf{Y}} = \begin{bmatrix} 0.489 & 0.511 \\ 0.478 & 0.522 \\ 0.512 & 0.488 \end{bmatrix}, \qquad \mathbf{Y} = \begin{bmatrix} 0 & 1 \\ 1 & 0 \\ 0 & 1 \end{bmatrix}\]
Losses by row: \(0.672\), \(0.738\), \(0.718\), mean \(\mathcal{L} = 0.709\)
One matrix product per layer for all \(m\) samples: \(m\) matrix-vector products become one matrix-matrix product
Objective over \(m\) samples: the mean loss, the empirical risk \[\mathcal{L} = \frac{1}{m} \sum_{i=1}^{m} \ell_i, \qquad \nabla \mathcal{L} = \frac{1}{m} \sum_{i=1}^{m} \nabla \ell_i\]
Forward pass for the batch: \(\mathbf{W}^T\) on the right, the bias added to every row \[\mathbf{S}^{(l)} = \mathbf{A}^{(l-1)} \mathbf{W}^{(l)T} + \mathbf{1}\, \mathbf{b}^{(l)T}, \qquad \mathbf{A}^{(l)} = \sigma(\mathbf{S}^{(l)})\]
Row \(i\) of every matrix: sample \(i\), start to finish

Per sample: \(\boldsymbol{\delta}_i^{(l)}\) from the single-sample recursion, row \(i\) of \(\boldsymbol{\Delta}^{(l)} \in \mathbb{R}^{m \times n_l}\)
Initialize, one row per sample \[\boldsymbol{\Delta}^{(L)} = \hat{\mathbf{Y}} - \mathbf{Y}\]
Propagate, all samples at once \[\boldsymbol{\Delta}^{(l)} = \big(\boldsymbol{\Delta}^{(l+1)} \mathbf{W}^{(l+1)}\big) \odot \sigma'(\mathbf{S}^{(l)})\] \[[m \times n_{l+1}][n_{l+1} \times n_l] = [m \times n_l]\]
Three samples \[\boldsymbol{\Delta}^{(2)} = \hat{\mathbf{Y}} - \mathbf{Y} = \begin{bmatrix} 0.489 & -0.489 \\ -0.522 & 0.522 \\ 0.512 & -0.512 \end{bmatrix}\]
Same operations as for one sample: \(m\) rows through each product in one call, no loop over samples
Per sample: \(\boldsymbol{\delta}_i\, \mathbf{a}_i^T\), one outer product
Mean over the batch: one matrix product, the sum over \(i\) as its inner dimension \[\frac{\partial \mathcal{L}}{\partial \mathbf{W}^{(l)}} = \frac{1}{m} \sum_{i=1}^{m} \boldsymbol{\delta}_i^{(l)} \mathbf{a}_i^{(l-1)T} = \frac{1}{m}\, \boldsymbol{\Delta}^{(l)T} \mathbf{A}^{(l-1)}\] \[[n_l \times m][m \times n_{l-1}] = [n_l \times n_{l-1}]\]
Bias: the mean of the rows of \(\boldsymbol{\Delta}^{(l)}\) \[\frac{\partial \mathcal{L}}{\partial \mathbf{b}^{(l)}} = \frac{1}{m}\, \boldsymbol{\Delta}^{(l)T} \mathbf{1}\]
Three samples, output layer: column 1 of \(\boldsymbol{\Delta}^{(2)}\) against column 1 of \(\mathbf{A}^{(1)}\), \([0.587,\ 0.520,\ 0.540]^T\)

Same shape as \(\mathbf{W}^{(l)}\): \(n_l \times n_{l-1}\), whatever \(m\) is
Kept from the forward pass: every \(\mathbf{A}^{(l)}\), \(l = 0, \ldots, L\), as the inputs of the outer products and the source of the slopes
Stored numbers per batch \[\text{activations: } m \sum_{l=0}^{L} n_l, \qquad \text{weights: } \sum_{l=1}^{L} n_l\, n_{l-1}\]
3-4-2 network, \(m = 3\): \(\;3 \times 9 = 27\) activations against \(20\) weights
784-256-128-10 network: \(1{,}178\) activations per sample against \(234{,}752\) weights
Update per batch \[\mathbf{W}^{(l)} \leftarrow \mathbf{W}^{(l)} - \eta\, \frac{1}{m}\, \boldsymbol{\Delta}^{(l)T} \mathbf{A}^{(l-1)}, \qquad \mathbf{b}^{(l)} \leftarrow \mathbf{b}^{(l)} - \eta\, \frac{1}{m}\, \boldsymbol{\Delta}^{(l)T} \mathbf{1}\]
One update per \(m\) samples: \(m\) gradients averaged, at \(m\) times one sample’s memory and time
Forward pass, with the values the backward pass uses stored
a⁽⁰⁾ ← x
for l = 1, …, L:
s⁽ˡ⁾ ← W⁽ˡ⁾ a⁽ˡ⁻¹⁾ + b⁽ˡ⁾
a⁽ˡ⁾ ← σ⁽ˡ⁾(s⁽ˡ⁾) store a⁽ˡ⁾
ℓ ← ℓ(a⁽ᴸ⁾, y)
Backward pass, one \(\boldsymbol{\delta}\) per layer
δ⁽ᴸ⁾ ← ∂ℓ/∂s⁽ᴸ⁾ ŷ − y under cross-entropy
for l = L − 1, …, 1:
δ⁽ˡ⁾ ← (W⁽ˡ⁺¹⁾ᵀ δ⁽ˡ⁺¹⁾) ⊙ σ′(s⁽ˡ⁾)
Read off and update, every parameter at once
for l = 1, …, L:
∂ℓ/∂W⁽ˡ⁾ ← δ⁽ˡ⁾ a⁽ˡ⁻¹⁾ᵀ
∂ℓ/∂b⁽ˡ⁾ ← δ⁽ˡ⁾
W⁽ˡ⁾ ← W⁽ˡ⁾ − η ∂ℓ/∂W⁽ˡ⁾
b⁽ˡ⁾ ← b⁽ˡ⁾ − η ∂ℓ/∂b⁽ˡ⁾
Stored by the forward loop: every \(\mathbf{a}^{(l)}\), the input of the next layer’s outer product and the source of its own slope \(\sigma'(\mathbf{s}^{(l)})\)
Backward loop ends at \(\boldsymbol{\delta}^{(1)}\): \(\mathbf{x}\) has no parameters
Propagated: \(\boldsymbol{\delta}^{(l)} = \partial \ell / \partial \mathbf{s}^{(l)}\), the gradient of the loss with respect to layer \(l\)’s pre-activations
| Layer | Target | \(\boldsymbol{\delta}\) |
|---|---|---|
| Output | \(\mathbf{y}\) | \(\hat{\mathbf{y}} - \mathbf{y}\) under cross-entropy |
| Hidden | none | \(\big(\mathbf{W}^{(l+1)T} \boldsymbol{\delta}^{(l+1)}\big) \odot \sigma'(\mathbf{s}^{(l)})\) |
Output pairings with \(\boldsymbol{\delta}^{(L)} = \hat{\mathbf{y}} - \mathbf{y}\)
Sigmoid with squared loss: \(\;\delta^{(L)} = (\hat{y} - y)\, \hat{y}(1 - \hat{y})\), the error times the slope
Hidden \(\boldsymbol{\delta}\): either sign, any size, no target for \(\mathbf{a}^{(l)}\)

“Learning representations by back-propagating errors”, Rumelhart, Hinton, and Williams, 1986
Every parameter’s gradient is one chain rule: \(\partial \ell / \partial w_{ij} = \delta_i\, a_j\)
Multiplications per layer, counted on layers of 784, 256, 128, and 10 units
| Layer | Forward \(n_l n_{l-1}\) | Transpose product \(n_{l-1} n_l\) | Outer product \(n_l n_{l-1}\) |
|---|---|---|---|
| 1 | \(200{,}704\) | \(200{,}704\), only for \(\partial \ell / \partial \mathbf{x}\) | \(200{,}704\) |
| 2 | \(32{,}768\) | \(32{,}768\) | \(32{,}768\) |
| 3 | \(1{,}280\) | \(1{,}280\) | \(1{,}280\) |
| Total | \(234{,}752\) | \(34{,}048\) or \(234{,}752\) | \(234{,}752\) |
Backward pass: \(269{,}184\) multiplications, \(1.15\) forward passes
One training step: \(2.15\) forward passes, or \(3.00\) with the input gradient
Finite differences for the same gradient: \(470{,}292\) forward passes
Measured at render, NumPy float32 on the rendering machine, median of 30 runs
batch forward (ms) backward (ms) backward / forward
32 0.06 0.08 1.38
256 0.30 0.17 0.57
In the loops: \(\sigma^{(l)}\) once in the forward pass, \(\sigma'(\mathbf{s}^{(l)})\) once in the backward pass, nowhere else
| Activation | \(\sigma(s)\) | \(\sigma'(s)\) | From \(a\) | Max slope |
|---|---|---|---|---|
| Sigmoid | \(1 / (1 + e^{-s})\) | \(\sigma(s)(1 - \sigma(s))\) | \(a(1 - a)\) | \(1/4\), at \(s = 0\) |
| Tanh | \(\tanh s\) | \(1 - \tanh^2 s\) | \(1 - a^2\) | \(1\), at \(s = 0\) |
| ReLU | \(\max(s, 0)\) | \(\mathbb{1}[s > 0]\) | \(\mathbb{1}[a > 0]\) | \(1\), for every \(s > 0\) |
Stored per layer: \(\mathbf{a}^{(l)}\) alone
Changing the activation: new numbers in \(\sigma'(\mathbf{s}^{(l)})\), the same three loops

Sigmoid slope: \(0.045\) at \(s = 3\) and \(0.0066\) at \(s = 5\)
Unit with \(\sigma'(s_i) = 0\) contributes nothing to the backward pass: \(\delta_i = 0\), so its weights do not change and it contributes no term to the layer before
3-4-2 network with a ReLU hidden layer, same weights and input
Backward pass through unit 3 \[\boldsymbol{\delta}^{(1)} = \mathbf{W}^{(2)T} \boldsymbol{\delta}^{(2)} \odot \sigma'(\mathbf{s}^{(1)}) = \begin{bmatrix} 0.345 \\ -0.345 \\ 0.148 \\ -0.099 \end{bmatrix} \odot \begin{bmatrix} 1 \\ 1 \\ 0 \\ 1 \end{bmatrix} = \begin{bmatrix} 0.345 \\ -0.345 \\ 0 \\ -0.099 \end{bmatrix}\]
\[\frac{\partial \ell}{\partial \mathbf{W}^{(1)}} = \boldsymbol{\delta}^{(1)} \mathbf{x}^T = \begin{bmatrix} 0.345 & 0.172 & -0.172 \\ -0.345 & -0.172 & 0.172 \\ 0 & 0 & 0 \\ -0.099 & -0.049 & 0.049 \end{bmatrix}\]
Row 3 of \(\mathbf{W}^{(1)}\) and \(b^{(1)}_3\): gradient zero, no change on this input
Term of unit 3 in \(\partial \ell / \partial \mathbf{a}^{(0)}\): \(w^{(1)}_{3j}\, \delta^{(1)}_3 = 0\) for every \(j\)
Where slopes are zero or small
\(s_i\) is set by the weights: \(s_i = \mathbf{w}_i^T \mathbf{a}^{(l-1)} + b_i\), and with \(\delta_i = 0\) those weights have zero gradient, so \(s_i\) is unchanged on this input
Element-wise step: assumes \(a_i = \sigma(s_i)\), a diagonal Jacobian, and the product \(\odot\, \sigma'(\mathbf{s})\)
Softmax: \(\hat{y}_i = e^{s_i} / \sum_j e^{s_j}\), every output a function of every pre-activation \[\frac{\partial \hat{y}_i}{\partial s_j} = \hat{y}_i\,(\mathbb{1}[i = j] - \hat{y}_j), \qquad \frac{\partial \hat{\mathbf{y}}}{\partial \mathbf{s}} = \mathrm{diag}(\hat{\mathbf{y}}) - \hat{\mathbf{y}}\, \hat{\mathbf{y}}^T\]
Chain rule step for a map that mixes the units: a full matrix-vector product in place of \(\odot\) \[\boldsymbol{\delta} = \Big(\frac{\partial \hat{\mathbf{y}}}{\partial \mathbf{s}}\Big)^T \frac{\partial \ell}{\partial \hat{\mathbf{y}}}, \qquad n^2 \text{ multiplications in place of } n\]
3-4-2 network, at \(\hat{\mathbf{y}} = [0.489,\ 0.511]^T\): every entry of the \(2 \times 2\) Jacobian is \(\pm 0.250\), and the product with \(\nabla_{\hat{\mathbf{y}}} \ell\) is \([0.489,\ -0.489]^T\)
At the output with cross-entropy: the product collapses to \(\hat{\mathbf{y}} - \mathbf{y}\), so the loop writes \(\boldsymbol{\delta}^{(L)}\) directly and never computes the Jacobian
As a hidden layer, or any map that mixes a layer’s units
One such map in these networks: the softmax output; any later layer that normalizes or mixes its units has the same \(\mathbf{J}^T\) step
Recursion written out, from \(\boldsymbol{\delta}^{(L)}\) to \(\boldsymbol{\delta}^{(1)}\) \[\boldsymbol{\delta}^{(1)} = \mathbf{D}^{(1)} \mathbf{W}^{(2)T} \mathbf{D}^{(2)} \mathbf{W}^{(3)T} \cdots \mathbf{D}^{(L-1)} \mathbf{W}^{(L)T} \boldsymbol{\delta}^{(L)}\] \[\mathbf{D}^{(l)} = \mathrm{diag}\big(\sigma'(\mathbf{s}^{(l)})\big)\]
Sigmoid slopes: every diagonal entry of \(\mathbf{D}^{(l)}\) at most \(1/4\) \[\|\boldsymbol{\delta}^{(1)}\| \le \Big(\frac{1}{4}\Big)^{L-1} \prod_{l=2}^{L} \|\mathbf{W}^{(l)}\|\; \|\boldsymbol{\delta}^{(L)}\|, \qquad \|\mathbf{W}\| \text{ the largest singular value}\]
Weight gradients have the same factor: \(\partial \ell / \partial \mathbf{W}^{(1)} = \boldsymbol{\delta}^{(1)} \mathbf{a}^{(0)T}\)
In the same product
In the flat regions: slopes far below \(1/4\), so the product is smaller than the bound
Deep sigmoid network: \(\boldsymbol{\delta}^{(1)}\) is a product of many factors less than one, and the layers nearest the input have the smallest gradients
Penalized objective, the convention of ridge regression: on the averaged loss, biases unpenalized \[\mathcal{L}_\lambda = \frac{1}{m} \sum_{i=1}^{m} \ell_i + \frac{\lambda}{2} \sum_{l=1}^{L} \|\mathbf{W}^{(l)}\|_F^2, \qquad \|\mathbf{W}\|_F^2 = \sum_{i, j} w_{ij}^2\]
Gradient of the penalty: \(\partial / \partial w_{ij}\) of \(\tfrac{\lambda}{2} \sum w_{ij}^2\) is \(\lambda w_{ij}\) \[\frac{\partial \mathcal{L}_\lambda}{\partial \mathbf{W}^{(l)}} = \frac{1}{m}\, \boldsymbol{\Delta}^{(l)T} \mathbf{A}^{(l-1)} + \lambda \mathbf{W}^{(l)}, \qquad \frac{\partial \mathcal{L}_\lambda}{\partial \mathbf{b}^{(l)}} = \frac{1}{m}\, \boldsymbol{\Delta}^{(l)T} \mathbf{1}\]
Unchanged: the forward pass, the recursion for \(\boldsymbol{\Delta}^{(l)}\), and the bias gradients, since the penalty is a function of the weights only
Update \[\mathbf{W}^{(l)} \leftarrow (1 - \eta \lambda)\, \mathbf{W}^{(l)} - \eta\, \frac{1}{m}\, \boldsymbol{\Delta}^{(l)T} \mathbf{A}^{(l-1)}\]
3-4-2 network, output layer, \(\lambda = 0.1\)
Same penalty as regularized logistic regression: \(\lambda = 1 / (n \tau^2)\) for \(n\) training samples under the Gaussian prior \(w_j \sim \mathcal{N}(0, \tau^2)\), on the weights of every layer
Held through one step, float32, layers of 784, 256, 128, and 10 units
| Array | Values | Size | Held until |
|---|---|---|---|
| Parameters | \(235{,}146\) | \(0.94\) MB | always |
| Gradients | \(235{,}146\) | \(0.94\) MB | the update |
| \(\mathbf{a}^{(0)}, \ldots, \mathbf{a}^{(L)}\) | \(1{,}178\) per sample | \(4.7\) KB per sample | the backward pass |
Per step: \(\;1.88 \text{ MB} + 4.7 \text{ KB} \times m\)
Largest batch in a budget: \(m \le (\text{budget} - 1.88 \text{ MB}) / 4.7 \text{ KB}\), \(\;1{,}298\) in \(8\) MB
Inference stores no gradients and one layer’s activations at a time: the batch size is a training cost

Computational graph: nodes are operations, edges the arrays between them
Forward pass: the nodes in a topological order, each from the outputs of the nodes before it
Backward pass: the same nodes in the reverse order, each from the gradient of the node after it
3-4-2 network: a chain of seven nodes with \(26\) parameters at two of them
Cycle would leave no order: a recurrent connection is unrolled in time into a DAG before either pass runs

Node contract: two calls and a store
forward(inputs):
keep what the Jacobian is evaluated at
return output
backward(g_out): g_out = ∂ℓ/∂output
for each input i:
Jᵢ = ∂output/∂inputᵢ from the kept values
g_in[i] = Jᵢᵀ g_out
return g_in
Graph driver: the same two loops for any DAG
forward pass:
for node in topological order:
node.output = node.forward(its inputs)
backward pass:
g[ℓ] = 1
g[every other array] = 0
for node in reverse order:
g_in = node.backward(g[node.output])
for each input i:
g[inputᵢ] += g_in[i]
+=: an array used by several nodes has the sum of one term from eachg[W⁽ˡ⁾] and g[b⁽ˡ⁾] are the parameter gradientsChain rule is in the driver, not in the nodes: composition is the reverse loop, summation over paths is the +=
Forward call: inputs in, output out, and the values its Jacobian is evaluated at stored
Backward call: the gradient at its output in, one gradient per input out \[\frac{\partial \ell}{\partial \mathbf{u}} = \Big(\frac{\partial \mathbf{v}}{\partial \mathbf{u}}\Big)^T \frac{\partial \ell}{\partial \mathbf{v}}\]
Multiply node, \(\mathbf{s} = \mathbf{W}\mathbf{a}\)
forward(W, a):
keep W, a
return W a
backward(g):
return g aᵀ to W
Wᵀ g to a
Chain rule as composition: the backward calls in reverse order, each from the previous one’s result

Output layer’s multiply node, backward call with \(\boldsymbol{\delta}^{(2)} = [0.489,\ -0.489]^T\) in and \(\mathbf{a}^{(1)} = [0.587,\ 0.512,\ 0.463,\ 0.668]^T\) stored
No derivation per network: a new graph of the same nodes has its backward pass from the same calls, in its own reverse order
3-4-2 network’s nodes, \(\mathbf{g}\) the gradient at the node’s output
| Node | Stores | Gradient to its input | Gradient to its parameter |
|---|---|---|---|
| \(\mathbf{s} = \mathbf{W}\mathbf{a}\) | \(\mathbf{W}\), \(\mathbf{a}\) | \(\mathbf{W}^T \mathbf{g}\) | \(\mathbf{g}\, \mathbf{a}^T\) |
| \(\mathbf{s} + \mathbf{b}\) | nothing | \(\mathbf{g}\) | \(\mathbf{g}\) |
| \(\sigma(\mathbf{s})\), element-wise | \(\mathbf{a}\) | \(\mathbf{g} \odot \sigma'(\mathbf{s})\) | |
| softmax | \(\hat{\mathbf{y}}\) | \(\big(\mathrm{diag}(\hat{\mathbf{y}}) - \hat{\mathbf{y}}\hat{\mathbf{y}}^T\big)\, \mathbf{g}\) | |
| \(-\log \hat{y}_c\) | \(\hat{\mathbf{y}}\) | \(-\mathbf{y} / \hat{\mathbf{y}}\) |
Jacobian of each
Multiply node, wherever it appears: the same two products
Add node: the identity Jacobian, so both inputs have gradient \(\mathbf{g}\)
Softmax with the loss node after it: two rules in turn, which collapse to \(\hat{\mathbf{y}} - \mathbf{y}\) with a one-hot target
Store what the Jacobian needs, in the smallest form
Sigmoid node
forward(s): a = σ(s)
keep a
return a
backward(g): return g ⊙ a ⊙ (1 − a)
ReLU node
forward(s): m = [s > 0]
keep m
return s ⊙ m
backward(g): return g ⊙ m
Sum over whatever the forward pass broadcast: a gradient has the shape of its variable
One gradient per input
Shape check on every return: \(\partial \ell / \partial \mathbf{u}\) has the shape of \(\mathbf{u}\), so a mismatch is the first sign of a wrong backward call
Stored values are freed by the backward call that uses them: the memory of a training step peaks at the end of the forward pass
Fork: a value \(\mathbf{a}\) used by \(k\) nodes has \(k\) gradients, one per path, summed \[\frac{\partial \ell}{\partial \mathbf{a}} = \sum_{j=1}^{k} \mathbf{J}_j^T\, \frac{\partial \ell}{\partial \mathbf{v}_j}\]
g[a] = 0
for each node j that uses a:
g[a] += Jⱼᵀ g[vⱼ]
Join: a node with several inputs gives each its own gradient, as the multiply node gives \(\mathbf{W}\) and \(\mathbf{a}\) theirs
Two-path chain rule, for any graph: one product per path, products added

Shortcut’s first term has no slope and no weight in it: \(\partial \ell / \partial \mathbf{y}\) is added to \(\partial \ell / \partial \mathbf{x}\) unchanged, whatever \(f\) does to the second term
Chain of Jacobians, \(\ell = f_L(\cdots f_1(\boldsymbol{\theta}))\) with \(n\) parameters in and one number out \[\frac{\partial \ell}{\partial \boldsymbol{\theta}} = \mathbf{J}_1^T \mathbf{J}_2^T \cdots \mathbf{J}_L^T \cdot 1\]
Forward mode: one direction \(\mathbf{d}\) multiplied through the Jacobians in forward order, \(\mathbf{J}_L \cdots \mathbf{J}_1 \mathbf{d}\)
Reverse mode: one vector \(\mathbf{u}\) multiplied through the transposes in reverse order, \(\mathbf{J}_1^T \cdots \mathbf{J}_L^T \mathbf{u}\)
forward mode: one direction d
v = d
for k = 1, …, L:
v = Jₖ v
reverse mode: every input at once
u = 1
for k = L, …, 1:
u = Jₖᵀ u
Each pass costs about one forward evaluation: one Jacobian product per node
Layers of 784, 256, 128, and 10 units, \(n = 235{,}146\) parameters
| Passes for the full gradient | |
|---|---|
| Forward mode | \(235{,}146\) |
| Reverse mode | \(1\) |
| Finite differences | \(470{,}292\) |
What sets the choice: the ratio of inputs to outputs
Automatic differentiation: the node rules run by a program on a recorded graph
Backpropagation is reverse-mode automatic differentiation on a layered graph
Record: one entry per operation as it executes, with its inputs and its stored values
Consequences
What has an entry: operations on arrays marked as needing a gradient
Record of the 3-4-2 forward pass
# op inputs stores
1 W⁽¹⁾· W⁽¹⁾, x W⁽¹⁾, x
2 + b⁽¹⁾ 1, b⁽¹⁾ nothing
3 σ 2 a⁽¹⁾
4 W⁽²⁾· W⁽²⁾, 3 W⁽²⁾, a⁽¹⁾
5 + b⁽²⁾ 4, b⁽²⁾ nothing
6 softmax 5 ŷ
7 −log ŷ_c 6, y ŷ
backward: 7, 6, 5, 4, 3, 2, 1
Two loops of the algorithm, one record
PyTorch on the 3-4-2 network, the forward pass written as arithmetic on tensors
import torch
tW1 = torch.tensor(W1, requires_grad=True)
tb1 = torch.tensor(b1, requires_grad=True)
tW2 = torch.tensor(W2, requires_grad=True)
tb2 = torch.tensor(b2, requires_grad=True)
tx = torch.tensor(x)
s1_t = tW1 @ tx + tb1 # graph recorded
a1_t = torch.sigmoid(s1_t)
s2_t = tW2 @ a1_t + tb2
loss_t = -torch.log_softmax(s2_t, 0)[1]
loss_t.backward() # every .grad filled
print(tW2.grad.numpy().round(3))
print(tW1.grad.numpy().round(3))[[ 0.287 0.251 0.226 0.327]
[-0.287 -0.251 -0.226 -0.327]]
[[ 0.083 0.042 -0.042]
[-0.086 -0.043 0.043]
[ 0.036 0.018 -0.018]
[-0.022 -0.011 0.011]]
requires_grad: marks the tensors that have record entries, the four parameters
backward(): a reverse run through the record from loss_t
.gradPrinted gradients: the hand-computed values, to six decimals
Nothing in the record is specific to layers
Batch forms, samples as rows, \(\mathbf{X}\) of shape \(m \times d\)
\[\mathbf{S}^{(1)} = \mathbf{X}\mathbf{W}^{(1)T} + \mathbf{1}\mathbf{b}^{(1)T}, \qquad \mathbf{A}^{(1)} = \sigma(\mathbf{S}^{(1)})\] \[\mathbf{S}^{(2)} = \mathbf{A}^{(1)}\mathbf{W}^{(2)T} + \mathbf{1}\mathbf{b}^{(2)T}\] \[\log \hat{Y}_{ik} = S^{(2)}_{ik} - \max_j S^{(2)}_{ij} - \log \sum_j e^{S^{(2)}_{ij} - \max_j S^{(2)}_{ij}}\] \[\mathcal{L} = -\frac{1}{m} \sum_{i} \sum_{k} Y_{ik} \log \hat{Y}_{ik}\]
Three-sample batch of the 3-4-2 network: \(\mathcal{L} = 0.709\)
loss 0.709
Stored for the backward pass: X, A1, and logP, the three arrays it uses
Batch forms, \(\boldsymbol{\Delta}^{(l)}\) with one row per sample
\[\boldsymbol{\Delta}^{(2)} = \hat{\mathbf{Y}} - \mathbf{Y}, \qquad \frac{\partial \mathcal{L}}{\partial \mathbf{W}^{(2)}} = \frac{1}{m}\, \boldsymbol{\Delta}^{(2)T} \mathbf{A}^{(1)}, \qquad \frac{\partial \mathcal{L}}{\partial \mathbf{b}^{(2)}} = \frac{1}{m}\, \boldsymbol{\Delta}^{(2)T} \mathbf{1}\] \[\boldsymbol{\Delta}^{(1)} = \big(\boldsymbol{\Delta}^{(2)} \mathbf{W}^{(2)}\big) \odot \mathbf{A}^{(1)} \odot (\mathbf{1} - \mathbf{A}^{(1)})\] \[\frac{\partial \mathcal{L}}{\partial \mathbf{W}^{(1)}} = \frac{1}{m}\, \boldsymbol{\Delta}^{(1)T} \mathbf{X}, \qquad \frac{\partial \mathcal{L}}{\partial \mathbf{b}^{(1)}} = \frac{1}{m}\, \boldsymbol{\Delta}^{(1)T} \mathbf{1}\]
D2 is \(\boldsymbol{\Delta}^{(2)} / m\): the \(1/m\) applied once, so every later product is already a meansum(axis=0): the sum over the rows the bias was broadcast toA1, no S1 keptPrinted: \(\partial \mathcal{L} / \partial \mathbf{W}^{(2)}\), first row \([0.097,\ 0.055,\ 0.082,\ 0.111]\), the hand-computed value
[[ 0.097 0.055 0.082 0.111]
[-0.097 -0.055 -0.082 -0.111]]
Every line has a shape to check
D2: \(m \times K\), \(\;\)gW2: \(K \times n_1\)D1: \(m \times n_1\), \(\;\)gW1: \(n_1 \times d\)Centered difference, one parameter at a time, from the forward pass alone \[\frac{\partial \mathcal{L}}{\partial w} \approx \frac{\mathcal{L}(w + \epsilon) - \mathcal{L}(w - \epsilon)}{2\epsilon}\]
Relative error between the backward pass’s gradient \(\mathbf{g}\) and the difference gradient \(\mathbf{g}_\epsilon\) \[\frac{\|\mathbf{g} - \mathbf{g}_\epsilon\|}{\|\mathbf{g}\| + \|\mathbf{g}_\epsilon\|}\]
Truncation, the \(\epsilon^2\) terms dropped from the Taylor expansion
Roundoff, two nearly equal losses each exact to \(u = 1.1 \times 10^{-16}\), their difference divided by \(2\epsilon\)
Floor: about \(10^{-10}\) at \(\epsilon = 10^{-5}\) on the 3-4-2 batch, in double precision
Cost: two forward passes per parameter checked

Agreement: an error near the floor, \(10^{-10}\) to \(10^{-8}\) at \(\epsilon = 10^{-5}\) in double precision
One line wrong: the sigmoid slope left out of \(\boldsymbol{\Delta}^{(1)}\)
D1 = D2 @ W2 slope missing
D1 = (D2 @ W2) * A1 * (1 - A1) correct
On the 3-4-2 batch, first row of \(\partial \mathcal{L} / \partial \mathbf{W}^{(1)}\)
Detected: any wrong line of the backward pass
Not detected: a wrong forward pass, a wrong loss

ReLU at the kink
Example: Palmer penguins
Network: \(2\)-\(8\)-\(3\), sigmoid hidden layer, softmax output
Training loss: \(1.103\) to \(0.122\)
| Model | Training birds correct | Test birds correct |
|---|---|---|
| Two-layer network | \(229\) of \(240\) | \(99\) of \(102\) |
| Multinomial logistic regression | \(229\) of \(240\) | \(99\) of \(102\) |
Same count as multinomial logistic regression on the same split

Data: Palmer Penguins, A. M. Horst, A. P. Hill, K. B. Gorman, 2020 (CC0); collected by K. Gorman and Palmer Station LTER
LMS, one linear unit with squared loss: \(\hat{y} = \mathbf{w}^T \mathbf{x}\), \(\;e = y - \hat{y}\) \[\mathbf{w} \leftarrow \mathbf{w} + \eta\, e\, \mathbf{x}\]
Logistic neuron with cross-entropy: \(\pi = \sigma(\mathbf{w}^T \mathbf{x})\) \[\mathbf{w} \leftarrow \mathbf{w} - \eta\, (\pi - y)\, \mathbf{x}\]
Output layer, softmax with cross-entropy: \(\boldsymbol{\delta}^{(L)} = \hat{\mathbf{y}} - \mathbf{y}\) \[\mathbf{W}^{(L)} \leftarrow \mathbf{W}^{(L)} - \eta\, \boldsymbol{\delta}^{(L)} \mathbf{a}^{(L-1)T}\]
Hidden layer: \(\boldsymbol{\delta}^{(l)} = \big(\mathbf{W}^{(l+1)T} \boldsymbol{\delta}^{(l+1)}\big) \odot \sigma'(\mathbf{s}^{(l)})\) \[\mathbf{W}^{(l)} \leftarrow \mathbf{W}^{(l)} - \eta\, \boldsymbol{\delta}^{(l)} \mathbf{a}^{(l-1)T}\]
One step in every case: an error signal times the input, scaled by \(\eta\)
| Model | Error signal | Source |
|---|---|---|
| LMS | \(e = y - \hat{y}\) | observed |
| Logistic neuron | \(\pi - y\) | observed |
| Output layer | \(\hat{\mathbf{y}} - \mathbf{y}\) | observed |
| Hidden layer | \(\boldsymbol{\delta}^{(l)}\) | computed |
What backpropagation adds: the error signal of a layer with no target
LMS is backpropagation on one linear layer with squared loss: \(\delta^{(1)} = -e\), and the recursion has nothing to propagate
Batch update, \(m\) samples per step \[\boldsymbol{\theta} \leftarrow \boldsymbol{\theta} - \eta\, \frac{1}{m} \sum_{i=1}^{m} \nabla \ell_i\]
Online update, \(m = 1\), as in LMS \[\boldsymbol{\theta}_{t+1} = \boldsymbol{\theta}_t - \eta\, \nabla \ell_t\]
Last layer alone: hidden layers fixed, \(\mathbf{a}^{(L-1)}\) a fixed feature map \[\mathbf{W}^{(L)} \leftarrow \mathbf{W}^{(L)} - \eta\, \boldsymbol{\delta}^{(L)} \mathbf{a}^{(L-1)T}\]
One loop, two settings: \(m\), and which layers the update changes
Trained network in a changing environment: fit by batches on past data, adapted online as new data arrive
Network: \(2\)-\(16\)-\(\cdots\)-\(16\)-\(3\), ten hidden layers of \(16\) units, the \(240\) training penguins
Measured: \(\|\partial \mathcal{L} / \partial \mathbf{W}^{(l)}\|_F\) by layer, one backward pass
Mean slope per hidden layer, sigmoid: \(0.21\) to \(0.24\)

Tanh and ReLU slopes are \(1\) where the unit is active: the gradient retains its size through every layer
Trained: by the forward, backward, and update lines, \(\eta = 0.1\), \(2{,}000\) full-batch steps, hidden layers of \(16\) units
| Activation | Layers | Loss | Test, of \(102\) | \(\|\mathbf{W}^{(1)} - \mathbf{W}^{(1)}_0\|_F\) |
|---|---|---|---|---|
| Sigmoid | \(1\) | \(0.132\) | \(99\) | \(3.09\) |
| Sigmoid | \(5\) | \(1.034\) | \(43\) | \(0.10\) |
| Sigmoid | \(10\) | \(1.035\) | \(43\) | \(0.000\) |
| ReLU | \(1\) | \(0.115\) | \(100\) | \(1.44\) |
| ReLU | \(5\) | \(0.086\) | \(96\) | \(0.78\) |
| ReLU | \(10\) | \(0.056\) | \(100\) | \(0.72\) |

Open problem: gradients of one size at every depth
Backward pass exact at every depth: what shrinks is the gradient itself, and the remedies change the network, not the algorithm
Derivative of \(\mathbf{y} \in \mathbb{R}^m\) by \(\mathbf{x} \in \mathbb{R}^n\): the \(mn\) partial derivatives \(\partial y_j / \partial x_i\)
Names: in \(\partial \mathbf{y} / \partial \mathbf{x}\), which of \(\mathbf{y}\) and \(\mathbf{x}\) sets the row index
Scalar \(f\) by \(\mathbf{x}\), \(m = 1\): a \(1 \times n\) row in the numerator convention, an \(n \times 1\) column in the denominator convention
Origins
Same \(mn\) numbers either way: one arrangement is the transpose of the other
What a convention fixes
Numerator convention: \(m \times n\), row \(j\) is output \(y_j\), row \(1\) marked \[\frac{\partial \mathbf{y}}{\partial \mathbf{x}} = \begin{bmatrix} \color{#C62828}{\dfrac{\partial y_1}{\partial x_1}} & \color{#C62828}{\dfrac{\partial y_1}{\partial x_2}} & \color{#C62828}{\cdots} & \color{#C62828}{\dfrac{\partial y_1}{\partial x_n}} \\[1.2ex] \dfrac{\partial y_2}{\partial x_1} & \dfrac{\partial y_2}{\partial x_2} & \cdots & \dfrac{\partial y_2}{\partial x_n} \\[0.6ex] \vdots & \vdots & \ddots & \vdots \\[0.6ex] \dfrac{\partial y_m}{\partial x_1} & \dfrac{\partial y_m}{\partial x_2} & \cdots & \dfrac{\partial y_m}{\partial x_n} \end{bmatrix}\]
Scalar \(f\), \(m = 1\): one row \[\frac{\partial f}{\partial \mathbf{x}} = \begin{bmatrix} \dfrac{\partial f}{\partial x_1} & \dfrac{\partial f}{\partial x_2} & \cdots & \dfrac{\partial f}{\partial x_n} \end{bmatrix}, \qquad 1 \times n\]
Denominator convention: \(n \times m\), row \(i\) is input \(x_i\), the same entries now column \(1\) \[\frac{\partial \mathbf{y}}{\partial \mathbf{x}} = \begin{bmatrix} \color{#C62828}{\dfrac{\partial y_1}{\partial x_1}} & \dfrac{\partial y_2}{\partial x_1} & \cdots & \dfrac{\partial y_m}{\partial x_1} \\[1.2ex] \color{#C62828}{\dfrac{\partial y_1}{\partial x_2}} & \dfrac{\partial y_2}{\partial x_2} & \cdots & \dfrac{\partial y_m}{\partial x_2} \\[0.6ex] \color{#C62828}{\vdots} & \vdots & \ddots & \vdots \\[0.6ex] \color{#C62828}{\dfrac{\partial y_1}{\partial x_n}} & \dfrac{\partial y_2}{\partial x_n} & \cdots & \dfrac{\partial y_m}{\partial x_n} \end{bmatrix}\]
Scalar \(f\), \(m = 1\): one column \[\frac{\partial f}{\partial \mathbf{x}} = \begin{bmatrix} \dfrac{\partial f}{\partial x_1} \\[0.6ex] \vdots \\[0.6ex] \dfrac{\partial f}{\partial x_n} \end{bmatrix} = \nabla_{\mathbf{x}} f, \qquad n \times 1\]
\[\Big(\frac{\partial \mathbf{y}}{\partial \mathbf{x}}\Big)_{\text{denominator}} = \Big(\frac{\partial \mathbf{y}}{\partial \mathbf{x}}\Big)_{\text{numerator}}^{T}\]
Jacobian of \(\mathbf{y} = g(\mathbf{x})\): numerator convention, \(m \times n\) \[\mathbf{J} = \frac{\partial \mathbf{y}}{\partial \mathbf{x}}, \qquad J_{ji} = \frac{\partial y_j}{\partial x_i}\]
Gradient of a scalar: denominator convention, a column the shape of \(\mathbf{x}\) \[\nabla_{\mathbf{x}} f \in \mathbb{R}^{n}, \qquad \mathbf{x} \leftarrow \mathbf{x} - \eta\, \nabla_{\mathbf{x}} f\]
Chain rule: pre-multiply by the Jacobian transpose \[\nabla_{\mathbf{x}} f = \mathbf{J}^T\, \nabla_{\mathbf{y}} f, \qquad [n \times m][m \times 1] = [n \times 1]\]
Example: \(\mathbf{y} = \mathbf{W}\mathbf{x}\), \(\mathbf{W}\) of shape \(2 \times 3\), \(f = \|\mathbf{y}\|^2\) \[\mathbf{W} = \begin{bmatrix} 0.5 & 0.3 & -0.1 \\ -0.2 & 0.4 & 0.2 \end{bmatrix}, \qquad \mathbf{x} = \begin{bmatrix} 1 \\ 2 \\ -1 \end{bmatrix}\] \[\mathbf{y} = \begin{bmatrix} 1.2 \\ 0.4 \end{bmatrix}, \qquad \nabla_{\mathbf{y}} f = 2\mathbf{y} = \begin{bmatrix} 2.4 \\ 0.8 \end{bmatrix}\]

Entry 1: \(\;0.5 (2.4) + (-0.2)(0.8) = 1.04\), the first column of \(\mathbf{W}\) against \(\nabla_{\mathbf{y}} f\)
Mixed convention: numerator Jacobian, denominator gradient, the common choice in machine learning texts
Numerator convention throughout: the gradient of a scalar is its \(1 \times n\) Jacobian, a row \[\frac{\partial f}{\partial \mathbf{x}} = (\nabla_{\mathbf{x}} f)^T \in \mathbb{R}^{1 \times n}\]
Chain rule: post-multiply, the factors in the order of the forward pass \[\frac{\partial f}{\partial \mathbf{x}} = \frac{\partial f}{\partial \mathbf{y}}\, \frac{\partial \mathbf{y}}{\partial \mathbf{x}}, \qquad [1 \times n] = [1 \times m][m \times n]\]
Update: the transpose is in the update instead \[\mathbf{x} \leftarrow \mathbf{x} - \eta\, \Big(\frac{\partial f}{\partial \mathbf{x}}\Big)^T\]
Same example: \(\partial f / \partial \mathbf{y} = [2.4,\ 0.8]\), a row

Entry 1: \(\;2.4 (0.5) + 0.8 (-0.2) = 1.04\), \(\partial f / \partial \mathbf{y}\) against the first column of \(\mathbf{W}\)
One object for every derivative: a scalar’s gradient and a vector function’s Jacobian are both Jacobians, and the chain rule is one rule
Identity: for any matrix \(\mathbf{J}\) and vector \(\mathbf{u}\) \[\big(\mathbf{J}^T \mathbf{u}\big)^T = \mathbf{u}^T \mathbf{J}\]
Backward pass of a network, both forms
| Column form, EE 541 | Row form | |
|---|---|---|
| Hidden gradient | \(\mathbf{W}^T \boldsymbol{\delta}\) | \(\boldsymbol{\delta}^T \mathbf{W}\) |
| Recursion | \(\mathbf{D}\, \mathbf{W}^{(l+1)T} \boldsymbol{\delta}^{(l+1)}\) | \(\boldsymbol{\delta}^{(l+1)T} \mathbf{W}^{(l+1)} \mathbf{D}\) |
| Weight gradient | \(\boldsymbol{\delta}\, \mathbf{a}^T\) | \(\boldsymbol{\delta}\, \mathbf{a}^T\) |
Mini-batch form is the row form: \(\boldsymbol{\Delta}^{(l)} = \big(\boldsymbol{\Delta}^{(l+1)} \mathbf{W}^{(l+1)}\big) \odot \sigma'(\mathbf{S}^{(l)})\), one row gradient per sample
Where the transpose is
| Chain rule | Update | |
|---|---|---|
| Column form | \(\mathbf{J}^T\) | none |
| Row form | none | \((\partial f / \partial \mathbf{x})^T\) |
Reading another source: its convention is the shape of its gradient of a scalar, a column or a row, and of its Jacobian, \(m \times n\) or \(n \times m\)
Mixing conventions: one transpose too many or too few, and a shape that fails to multiply