Backpropagation

EE 541 - Unit 6

Dr. Brandon Franzke

Fall 2026

Outline

Deriving the Backward Pass

Gradients for Hidden Layers and The Chain Rule

  • Parameters with no target
  • Products along paths, Jacobian transposes

Scalar Networks

  • \(\delta\) and the recursion
  • Branching, one gradient step

Vectorizing Backpropagation and Mini-Batches

  • Softmax at the output
  • Outer product, \(\mathbf{W}^T \boldsymbol{\delta}\), Hadamard product
  • Samples as rows

Running It

Algorithm and Cost

  • Gradients, not errors
  • Time, memory, slopes

Computational Graphs

  • Local rules, reverse mode
  • Recorded graphs and frameworks

Implementation and Gradient Checking and Gradients Through Depth

  • NumPy lines and finite differences
  • LMS and online adaptation
  • Sigmoid against ReLU

Appendix: Row Gradients

Gradients for Hidden Layers

Logistic Gradient: Product of Three Derivatives

Logistic neuron

  • \(z = \mathbf{w}^T\mathbf{x} + b\), \(\;\pi = \sigma(z)\)
  • Cross-entropy against the target \(y\): \(\;\ell = -y \log \pi - (1 - y) \log(1 - \pi)\)

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}}\]

  • Loss to output: \(\dfrac{\partial \ell}{\partial \pi} = \dfrac{\pi - y}{\pi(1 - \pi)}\), from comparing \(\pi\) with \(y\)
  • Output to score: \(\dfrac{d\pi}{dz} = \sigma'(z) = \pi(1 - \pi)\)
  • Score to weights: \(\dfrac{\partial z}{\partial \mathbf{w}} = \mathbf{x}\)
  • Product: \((\pi - y)\,\mathbf{x}\), the gradient of logistic regression

All three factors are functions of \(\pi\), \(y\), and \(\mathbf{x}\): available after one forward pass

Hidden Units Compute Features Fit From Data

One neuron: one hyperplane in \(\mathbf{x}\)

  • XOR needs a product feature such as \(x_1 x_2\), chosen by hand

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)})\]

  • \(\mathbf{a}^{(1)}\): features fit from data
  • \(\mathbf{W}^{(1)}\) and \(\mathbf{W}^{(2)}\): fit together, by gradient descent on \(\ell(\hat{y}, y)\)

Every unit is a weighted sum, a bias, and a sigmoid

Superscript \((l)\) Indexes the Layer

Layer index in parentheses, not a power

  • \(\mathbf{W}^{(1)} \in \mathbb{R}^{m \times d}\), \(\;\mathbf{b}^{(1)} \in \mathbb{R}^{m}\)
  • \(\mathbf{W}^{(2)} \in \mathbb{R}^{1 \times m}\), \(\;\mathbf{b}^{(2)} \in \mathbb{R}\)
  • \(\mathbf{a}^{(l)}\): the output of layer \(l\), with \(\mathbf{a}^{(0)} = \mathbf{x}\)

Both weight sets are matrices

  • One row per unit, one column per input to the layer
  • The output layer has one unit, so one row
  • One index per layer, so the notation extends to any depth

Renaming only: the network is unchanged (figure)

Loss Depends on Hidden Units Only Through the Output

Loss \(\ell(\hat{y}, y)\): a function of the output and the target

  • A training pair \((\mathbf{x}, y)\): \(y\) is the target for \(\hat{y}\)
  • \(\mathbf{a}^{(1)}\) has no target

Output weights \(\mathbf{W}^{(2)}\): \(\ell\) depends on them directly, through \(\hat{y}\)

  • One neuron with input \(\mathbf{a}^{(1)}\): gradient \((\hat{y} - y)\,\mathbf{a}^{(1)}\)

Hidden weights \(\mathbf{W}^{(1)}\): \(\ell\) depends on them only through \(\mathbf{a}^{(1)}\), and on \(\mathbf{a}^{(1)}\) only through \(\hat{y}\)

  • No term of \(\ell\) involves \(\mathbf{a}^{(1)}\) except 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}\)
  • Gradient descent needs \(\partial \ell / \partial \mathbf{W}^{(1)}\) for all 78,500

All 78,500 hidden gradients have to come from \(\ell\) through \(\hat{y}\), and at some cost

\(\partial \ell / \partial \mathbf{W}^{(1)}\) Expands Through the Output Unit

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)}}\]

  • Second factor: the input \(\mathbf{x}\), as before
  • First factor: the loss derivative at the hidden pre-activation, where one neuron had \(\pi - y\)

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)})}\]

  • Loss to output: \(\partial \ell / \partial \hat{y}\), from comparing \(\hat{y}\) with \(y\)
  • Output unit’s slope: \(\sigma'(s^{(2)})\)
  • Output unit’s weights: \(\mathbf{W}^{(2)}\)
  • Hidden units’ slopes: \(\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)}\)

Forward Pass: Every Layer Computes \(\mathbf{s} = \mathbf{W}\mathbf{a} + \mathbf{b}\), Then \(\mathbf{a} = \sigma(\mathbf{s})\)

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)})\]

  • \(\mathbf{W}^{(l)} \in \mathbb{R}^{n_l \times n_{l-1}}\), \(\;\mathbf{b}^{(l)} \in \mathbb{R}^{n_l}\), with \(n_0 = d\)
  • \(\mathbf{a}^{(0)} = \mathbf{x}\), \(\;\mathbf{a}^{(L)} = \hat{\mathbf{y}}\), loss \(\ell(\hat{\mathbf{y}}, y)\)

Pre-activation and activation

  • \(\mathbf{s}^{(l)}\): linear in \(\mathbf{W}^{(l)}\)
  • \(\mathbf{a}^{(l)}\): the next layer’s input

Activation \(\sigma^{(l)}\) can differ by layer

  • Hidden layers: sigmoid, or ReLU, \(\sigma(s) = \max(s, 0)\)
  • Output layer: sigmoid or softmax

Two-layer network: \(L = 2\), \(n_2 = 1\)

Backward Pass: Two Factors per Layer, Its Slope and Its Weights

Three derivatives per layer

  • Activation: \(\partial \mathbf{a}^{(l)} / \partial \mathbf{s}^{(l)} = \sigma'(\mathbf{s}^{(l)})\)
  • Previous layer’s output: \(\partial \mathbf{s}^{(l)} / \partial \mathbf{a}^{(l-1)} = \mathbf{W}^{(l)}\)
  • Own weights: \(\partial \mathbf{s}^{(l)} / \partial \mathbf{W}^{(l)} = \mathbf{a}^{(l-1)}\)

From the loss to the input, given \(\partial \ell / \partial \mathbf{a}^{(l)}\)

  • Times \(\sigma'(\mathbf{s}^{(l)})\) and \(\mathbf{W}^{(l)}\): \(\partial \ell / \partial \mathbf{a}^{(l-1)}\), the previous layer’s output gradient
  • Times \(\sigma'(\mathbf{s}^{(l)})\) and \(\mathbf{a}^{(l-1)}\): \(\partial \ell / \partial \mathbf{W}^{(l)}\), this layer’s weight gradient

Target appears once, in \(\partial \ell / \partial \hat{\mathbf{y}}\), and every other factor is a slope, a weight matrix, or an input

One Forward and One Backward Pass Produce All \(n\) Gradients

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}\]

  • No derivative formula, only loss evaluations
  • Two evaluations per weight, \(2n\) in all, and approximate

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
  • \(n = 235{,}146\): \(\;470{,}292\) evaluations for one gradient

One weight’s gradient: a product of derivatives from \(\ell\) to that weight

  • Every weight into layer \(l\) has the same factors from \(\ell\) to \(\mathbf{s}^{(l)}\)
  • Only the final factor differs by weight: its input \(a^{(l-1)}_j\)

Shared factors computed once per layer, from the loss to the input

  • Each weight’s gradient is then one multiplication
  • One forward pass and one backward pass: all \(n\) gradients

Cost: two to three forward passes

  • \(470{,}292 / 3 \approx 157{,}000\) times fewer evaluations for this network, a ratio that grows with \(n\)

Finite differences remain the check on a backward-pass implementation

The Chain Rule

Derivative of a Composition Is a Product

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)\]

  • One factor per function
  • \(f_k'\) evaluated at its own input \(u_{k-1}\)

Example: \(u_1 = 3x + 1\), \(\;u_2 = \sigma(u_1)\), \(\;y = u_2^2\), at \(x = 0.5\)

  • \(u_1 = 2.5\), \(\;u_2 = 0.924\), \(\;y = 0.854\)
  • \(f_1' = 3\), \(\;f_2' = \sigma'(2.5) = 0.0701\), \(\;f_3' = 2u_2 = 1.848\)
  • \(dy/dx = 1.848 \times 0.0701 \times 3 = 0.389\)

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\)

Contributions From Different Paths Add

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)\]

  • One product per path
  • Products add

Example: \(u = 2x\), \(\;v = x^2\), \(\;z = uv\), at \(x = 1.5\)

  • \(u = 3\), \(\;v = 2.25\), \(\;z = 6.75\)
  • Path through \(u\): \(v \cdot 2 = 4.5\)
  • Path through \(v\): \(u \cdot 2x = 9\)
  • Sum: \(13.5\)
  • Check: \(z = 2x^3\), \(\;6x^2 = 13.5\)

Hidden unit with \(K\) units after it: \(K\) paths to the loss

  • \(K\) products, summed

\(\nabla_{\mathbf{x}} f\) Has the Shape of \(\mathbf{x}\)

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}\]

  • Update: \(\mathbf{x} \leftarrow \mathbf{x} - \eta\, \nabla_{\mathbf{x}} f\), no transpose
  • First-order change: \(df = \nabla_{\mathbf{x}} f^T\, d\mathbf{x}\)

Three derivatives used throughout

  • Inner product \(f = \mathbf{a}^T \mathbf{x}\): \(\;\nabla_{\mathbf{x}} f = \mathbf{a}\)
  • Linear map \(\mathbf{y} = \mathbf{W}\mathbf{x}\): \(\;\partial \mathbf{y} / \partial \mathbf{x} = \mathbf{W}\)
  • Quadratic form \(f = \mathbf{x}^T \mathbf{A} \mathbf{x}\): \(\;\nabla_{\mathbf{x}} f = (\mathbf{A} + \mathbf{A}^T)\, \mathbf{x}\), \(\;2\mathbf{A}\mathbf{x}\) for symmetric \(\mathbf{A}\)

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}\]

  • Row \(j\): \((\nabla_{\mathbf{x}} y_j)^T\)

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\)

  • \((\mathbf{A} + \mathbf{A}^T)\,\mathbf{x} = \begin{bmatrix} 4 & 1 \\ 1 & 6 \end{bmatrix} \begin{bmatrix} 1 \\ 2 \end{bmatrix} = \begin{bmatrix} 6 \\ 13 \end{bmatrix}\)
  • Check, \(f = 2x_1^2 + x_1 x_2 + 3x_2^2\): \(\;\partial f / \partial x_1 = 4x_1 + x_2 = 6\), \(\;\partial f / \partial x_2 = x_1 + 6x_2 = 13\)

Row gradients: the other consistent layout, in the appendix

Vector Steps Contribute Their Jacobian Transposes

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}\]

  • Check: \(\partial f / \partial x_1 = 2(1.1)(0.5) + 2(0.6)(-0.2) = 0.86\)

Row \(i\) of \(\mathbf{J}^T\): every \(y_j\) that depends on \(x_i\), the sum over paths as one matrix row

Backward Pass: One Running Product

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\)

  • \(P_L = 1\)
  • \(P_k = P_{k+1} \cdot f_{k+1}'(u_k)\): one more factor per step inward

Cost for all \(L\) gradients

  • Extending one product: \(L - 1\) multiplications
  • Each product on its own: \(L(L-1)/2\)

Network: \(\theta_k\) is layer \(k\)’s weights

  • \(P\) at layer \(l\): the derivatives from \(\ell\) to \(\mathbf{s}^{(l)}\), shared by every weight into the layer
  • Weight gradient: \(P\) times the weight’s input \(a^{(l-1)}_j\)

Backward pass: \(P\) extended from the loss to the input, one layer at a time

  • Each \(u_k\) it is evaluated at comes from the forward pass

Scalar Networks

Forward Pass Produces Every Value the Backward Pass Needs

One unit per layer: every weight, pre-activation, and activation is a single number

  • Every chain-rule factor is a plain multiplication
  • The vector case repeats the same steps with matrices

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\]

Output Weight: Logistic Regression’s Gradient With Input \(a^{(1)}\)

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

  • Loss slope: \(\partial \ell / \partial \hat{y} = -1/\hat{y} = -2.260\)
  • Activation slope: \(\sigma'(s^{(2)}) = \hat{y}(1 - \hat{y}) = 0.247\)
  • Input to the weight: \(a^{(1)} = 0.731\)

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

Hidden Weight: The Product Expands Through the Output Unit

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

  • \(w^{(2)} = -1\): the output unit’s weight on the hidden activation
  • \(\sigma'(s^{(1)}) = a^{(1)}(1 - a^{(1)}) = 0.197\): the hidden unit’s slope
  • \(x = 2\): the input to the weight

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\]

\(\delta\): Gradient at the Pre-Activation

Definition \[\delta^{(l)} = \frac{\partial \ell}{\partial s^{(l)}}\]

Already in both gradients, as the bracketed product before the input

  • \(\partial \ell / \partial w^{(2)} = \big[\partial \ell / \partial \hat{y} \cdot \sigma'(s^{(2)})\big] \cdot a^{(1)} = \delta^{(2)} a^{(1)}\), with \(\delta^{(2)} = -0.558\)
  • \(\partial \ell / \partial w^{(1)} = \big[\delta^{(2)} \cdot w^{(2)} \cdot \sigma'(s^{(1)})\big] \cdot x = \delta^{(1)} x\), with \(\delta^{(1)} = 0.110\)

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

  • Weight: \(\partial \ell / \partial w^{(l)} = \delta^{(l)}\, a^{(l-1)}\)
  • Bias: \(\partial \ell / \partial b^{(l)} = \delta^{(l)}\)

\(\delta^{(l)} = \delta^{(l+1)}\, w^{(l+1)}\, \sigma'(s^{(l)})\)

Two chain-rule steps from \(\delta^{(2)}\) to \(\delta^{(1)}\)

  • Through the weight, \(s^{(2)} = w^{(2)} a^{(1)} + b^{(2)}\): \[\frac{\partial \ell}{\partial a^{(1)}} = \frac{\partial \ell}{\partial s^{(2)}} \cdot \frac{\partial s^{(2)}}{\partial a^{(1)}} = \delta^{(2)}\, w^{(2)} = (-0.558)(-1) = 0.558\]
  • Through the activation, \(a^{(1)} = \sigma(s^{(1)})\): \[\delta^{(1)} = \frac{\partial \ell}{\partial a^{(1)}} \cdot \sigma'(s^{(1)}) = 0.558 \cdot 0.197 = 0.110\]

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

  • \(\delta^{(l+1)}\), from the layer after it
  • \(w^{(l+1)}\), the weight between them
  • \(s^{(l)}\), its own stored pre-activation

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

Backward Pass: Recursion From Output to Input

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)}\)

Branching: Each Branch Adds a Term to \(\partial \ell / \partial a^{(1)}\)

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

  • \(\delta_1 = \hat{y}_1 - y_1 = -0.558\)
  • \(\delta_2 = \hat{y}_2 - y_2 = 0.675\)

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

  • \(\delta^{(1)} = 1.233 \times \sigma'(s^{(1)}) = 0.242\)
  • \(\partial \ell / \partial w^{(1)} = 0.242 \times 2 = 0.485\)

\(K\) branches: \(K\) terms, which the vector form writes as \(\mathbf{W}^T \boldsymbol{\delta}\)

Gradient Descent: One Step = Two Passes and an Update

Step: \(\theta \leftarrow \theta - \eta\, \partial \ell / \partial \theta\) for every parameter, with \(\eta = 1\)

  • \(w^{(2)} \leftarrow -1 - (-0.408) = -0.592\)
  • \(b^{(2)} \leftarrow 0.5 - (-0.558) = 1.058\)
  • \(w^{(1)} \leftarrow 0.5 - 0.219 = 0.281\)
  • \(b^{(1)} \leftarrow 0 - 0.110 = -0.110\)

Loss after the step, by a new forward pass

  • \(s^{(1)} = 0.281 \cdot 2 - 0.110 = 0.452\), \(\;a^{(1)} = 0.611\)
  • \(s^{(2)} = -0.592 \cdot 0.611 + 1.058 = 0.695\), \(\;\hat{y} = 0.667\)
  • \(\ell = -\log 0.667 = 0.405\), from \(0.815\)

Each step of the loop

  • Forward pass: the values, stored
  • Backward pass: \(\delta^{(L)}\) to \(\delta^{(1)}\)
  • Read off: one multiplication per parameter
  • Update: every parameter at once

Loop is gradient descent as fit logistic regression: backpropagation replaces the one-neuron gradient \((\pi - y)\,\mathbf{x}\) and nothing else changes

Vectorizing Backpropagation

Forward Pass: One Matrix Product per Layer

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

  • \(\mathbf{s}^{(1)} = \mathbf{W}^{(1)} \mathbf{x} + \mathbf{b}^{(1)} = [0.35,\ 0.05,\ -0.15,\ 0.70]^T\)
  • \(\mathbf{a}^{(1)} = \sigma(\mathbf{s}^{(1)}) = [0.587,\ 0.512,\ 0.463,\ 0.668]^T\)
  • \(\mathbf{s}^{(2)} = \mathbf{W}^{(2)} \mathbf{a}^{(1)} + \mathbf{b}^{(2)} = [0.299,\ 0.342]^T\)
  • \(\hat{\mathbf{y}} = \mathrm{softmax}(\mathbf{s}^{(2)}) = [0.489,\ 0.511]^T\), \(\;\ell = -\log 0.511 = 0.672\) for target \(\mathbf{y} = [0,\ 1]^T\)

Softmax Depends Only on the Differences Between Scores

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

  • \(K = 2\): \(\;\hat{y}_2 = \sigma(s_2 - s_1)\), the logistic neuron with one score
  • Subtracting \(\max_j s_j\) before exponentiating leaves \(\hat{\mathbf{y}}\) unchanged and \(e^{s}\) finite (\(e^{710}\) overflows in double precision)

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\)

  • The output layer’s weights and biases set this scale

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}\]

  • Finite for every \(\mathbf{s}\), since no \(\hat{y}_c\) is zero
  • For a wrong output with a large margin, \(\ell \approx \max_j s_j - s_c\), linear in the scores

Other maps from scores to probabilities, on the same \(\mathbf{s}\)

  • Squares, \(s_i^2 / \sum_j s_j^2\): \(\;[0.762,\ 0.048,\ 0.190]^T\), the sign of a score lost
  • Shift to zero, \((s_i - s_{\min}) / \sum_j (s_j - s_{\min})\): \(\;[0.667,\ 0.333,\ 0]^T\), a probability of exactly zero, where \(\log\) is undefined
  • Sigmoid per class, \(\sigma(s_i)\): \(\;[0.881,\ 0.622,\ 0.269]^T\), sum \(1.77\), \(K\) separate binary decisions

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\)

Cross-Entropy at the Output: \(\boldsymbol{\delta}^{(L)} = \hat{\mathbf{y}} - \mathbf{y}\)

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\]

  • The scalar network’s output step, unit by unit

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\)

  • Entries sum to zero: the probabilities sum to one for every \(\mathbf{s}\)

For both output types: the prediction error, the one-neuron result of logistic regression for every output unit at once

Each Weight Affects One Pre-Activation

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\)

  • \(\delta^{(2)}_2 = -0.489\), \(\;a^{(1)}_3 = 0.463\)
  • \(\partial \ell / \partial w^{(2)}_{23} = (-0.489)(0.463) = -0.226\)

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

Weight Gradient: One Outer Product for the Whole Layer

One weight: \(\;\partial \ell / \partial w_{ij} = \delta_i\, a_j\)

  • \(\delta_i\): the change in \(\ell\) per unit change in \(s_i\), the pre-activation that contains the weight
  • \(a_j\): the change in \(s_i\) per unit change in \(w_{ij}\), the input the weight multiplies
  • The same product as for the scalar network, \(\delta^{(l)} a^{(l-1)}\), with each weight’s own unit and own input

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}\]

  • Row \(i\): \(\delta_i\, \mathbf{a}^T\), the whole input scaled by unit \(i\)’s \(\delta\)
  • \(n_l + n_{l-1}\) numbers, no loop over the \(n_l n_{l-1}\) weights

Update of unit \(i\)’s weight vector: \(\;\mathbf{w}_i \leftarrow \mathbf{w}_i - \eta\, \delta_i\, \mathbf{a}\)

  • Along the input \(\mathbf{a}\): toward it when \(\delta_i < 0\), away from it when \(\delta_i > 0\)
  • The step of logistic regression, \((\pi - y)\,\mathbf{x}\), for every unit at once with \(\delta_i\) in place of \(\pi - y\)

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}\]

  • Row 2, the target class: toward \(\mathbf{a}^{(1)}\), raising \(s_2\)
  • Row 1: away from \(\mathbf{a}^{(1)}\) by the same amount, lowering \(s_1\)

\(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\))

Each Activation Affects Every Pre-Activation of the Next Layer

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

Hidden Gradient: \(\partial \ell / \partial \mathbf{a} = \mathbf{W}^T \boldsymbol{\delta}\)

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}\]

  • Entry \((i, j)\) is \(w_{ij}\): the Jacobian of \(\mathbf{s} = \mathbf{W}\mathbf{a} + \mathbf{b}\) is \(\mathbf{W}\) itself

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}]\]

  • Row \(j\) of \(\mathbf{W}^T\) is column \(j\) of \(\mathbf{W}\): the sum \(\sum_i \delta_i w_{ij}\) as one row of the product

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

Element-Wise Activations Have Diagonal Jacobians

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\)

\(\mathbf{v} \odot \mathbf{u} = \mathrm{diag}(\mathbf{v})\, \mathbf{u}\)

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}\]

  • \(n\) multiplications in place of \(n^2\): the diagonal matrix is never formed
  • Two diagonal matrices: \(\mathrm{diag}(\mathbf{v})\, \mathrm{diag}(\mathbf{u}) = \mathrm{diag}(\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

  • Sigmoid: \(\sigma'(\mathbf{s}) = \mathbf{a} \odot (\mathbf{1} - \mathbf{a})\)
  • ReLU: \(\sigma'(\mathbf{s}) = \mathbb{1}[\mathbf{s} > 0]\), so \(\odot\) leaves an entry or sets it to zero

\(\boldsymbol{\delta}^{(l)} = \big(\mathbf{W}^{(l+1)T} \boldsymbol{\delta}^{(l+1)}\big) \odot \sigma'(\mathbf{s}^{(l)})\)

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}\]

Backward Pass: Three Matrix Operations per Layer

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

  • Transpose product \(\mathbf{W}^{(l+1)T} \boldsymbol{\delta}^{(l+1)}\): \(\;n_l n_{l+1}\)
  • Element-wise product: \(\;n_l\)
  • Outer product \(\boldsymbol{\delta}^{(l)} \mathbf{a}^{(l-1)T}\): \(\;n_l n_{l-1}\)

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

Gradient Descent: One Step on the 3-4-2 Network

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)}\]

  • \(w^{(2)}_{11} \leftarrow 0.5 - 0.287 = 0.213\)
  • \(\mathbf{b}^{(2)} \leftarrow [0,\ 0.1]^T - [0.489,\ -0.489]^T = [-0.489,\ 0.589]^T\)
  • \(w^{(1)}_{11} \leftarrow 0.1 - 0.083 = 0.017\)

Loss after the step, by a new forward pass

  • \(\hat{\mathbf{y}} = [0.088,\ 0.912]^T\), from \([0.489,\ 0.511]^T\)
  • \(\ell = -\log 0.912 = 0.092\), from \(0.672\)

Scalar network’s loop, with matrices in place of scalars

  • Multiply by the weight: a transpose product
  • Multiply by the slope: an element-wise product
  • Multiply by the input: an outer product

Mini-Batches

Samples as Rows: One Forward Pass for the Whole Batch

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}\]

  • Row 1: the single-sample forward pass, entry by entry

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

Mini-Batch Forward Pass: One Matrix Product per Layer

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\]

  • A mean rather than a sum, so \(\eta\) means the same at every \(m\) Samples as rows: \(\mathbf{X} \in \mathbb{R}^{m \times d}\), row \(i\) equal to \(\mathbf{x}_i^T\), \(\;\mathbf{A}^{(0)} = \mathbf{X}\)

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)})\]

  • Shapes: \([m \times n_{l-1}][n_{l-1} \times n_l] = [m \times n_l]\)
  • Row \(i\) of \(\mathbf{S}^{(l)}\): sample \(i\)’s \(\mathbf{s}^{(l)T}\), unchanged by the other rows

Row \(i\) of every matrix: sample \(i\), start to finish

\(\boldsymbol{\Delta}^{(l)} = \big(\boldsymbol{\Delta}^{(l+1)} \mathbf{W}^{(l+1)}\big) \odot \sigma'(\mathbf{S}^{(l)})\)

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]\]

  • Row \(i\) of \(\boldsymbol{\Delta}^{(l+1)} \mathbf{W}^{(l+1)}\): \(\;\boldsymbol{\delta}_i^{(l+1)T} \mathbf{W}^{(l+1)} = \big(\mathbf{W}^{(l+1)T} \boldsymbol{\delta}_i^{(l+1)}\big)^T\)
  • \(\mathbf{W}\) on the right without a transpose: each row is a transposed column gradient

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}\]

  • Row 1 of \(\boldsymbol{\Delta}^{(1)}\): \(\;[0.083,\ -0.086,\ 0.036,\ -0.022]\), the single-sample \(\boldsymbol{\delta}^{(1)T}\)
  • Row 2 of \(\boldsymbol{\Delta}^{(1)}\): \(\;[-0.091,\ 0.090,\ -0.039,\ 0.026]\)

Same operations as for one sample: \(m\) rows through each product in one call, no loop over samples

Batch Gradient: \(\frac{1}{m}\, \boldsymbol{\Delta}^T \mathbf{A}\) Averages the Outer Products

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\)

  • Entry \((1, 1)\): \(\;\big(0.489 (0.587) - 0.522 (0.520) + 0.512 (0.540)\big) / 3 = 0.097\)
  • \(\partial \mathcal{L} / \partial \mathbf{W}^{(2)}\) row 1: \(\;[0.097,\ 0.055,\ 0.082,\ 0.111]\), row 2 its negative
  • \(\partial \mathcal{L} / \partial \mathbf{b}^{(2)} = [0.160,\ -0.160]^T\)

Same shape as \(\mathbf{W}^{(l)}\): \(n_l \times n_{l-1}\), whatever \(m\) is

Memory for Activations Grows With \(m\)

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}\]

  • The weight count does not depend on \(m\)

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

  • \(m = 32\): \(\;37{,}696\) activations, \(16\%\) of the weight count

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

Algorithm and Cost

One Backward Pass Produces Every Gradient of an \(L\)-Layer Network

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

Backpropagation Propagates Gradients, Not Errors

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)})\)
  • 3-4-2 network: \(\boldsymbol{\delta}^{(2)} = [0.489,\ -0.489]^T\), \(\;\boldsymbol{\delta}^{(1)} = [0.083,\ -0.086,\ 0.036,\ -0.022]^T\)

Output pairings with \(\boldsymbol{\delta}^{(L)} = \hat{\mathbf{y}} - \mathbf{y}\)

  • Sigmoid with cross-entropy
  • Softmax with cross-entropy
  • Linear output with squared loss

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

  • The propagated quantity there: \(\partial \ell / \partial \mathbf{s}^{(l)}\), called the error signal

Every parameter’s gradient is one chain rule: \(\partial \ell / \partial w_{ij} = \delta_i\, a_j\)

One Training Step Costs Two to Three Forward Passes

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\)
  • Element-wise products: \(256 + 128 = 384\), small beside the matrix products

Backward pass: \(269{,}184\) multiplications, \(1.15\) forward passes

  • With \(\partial \ell / \partial \mathbf{x}\), as when these layers follow other layers: \(469{,}888\), two 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
  • The count is in multiplications only
  • The time includes the exponentials, the element-wise products, and the memory traffic of every array, so the measured ratio differs from \(1.15\)
  • At a larger batch, a larger share of the time is in the matrix products, where the count applies

Only the Slope \(\sigma'\) of an Activation Appears in the Backward Pass

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\)

Units With Zero Slope Are Dead

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

  • \(\mathbf{s}^{(1)} = [0.35,\ 0.05,\ -0.15,\ 0.70]^T\), so \(\mathbf{a}^{(1)} = [0.35,\ 0.05,\ 0,\ 0.70]^T\) and \(\sigma'(\mathbf{s}^{(1)}) = [1,\ 1,\ 0,\ 1]^T\)
  • \(\hat{\mathbf{y}} = [0.493,\ 0.507]^T\), \(\;\boldsymbol{\delta}^{(2)} = [0.493,\ -0.493]^T\)

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

  • ReLU: \(s_i < 0\), exactly zero
  • Sigmoid: \(|s_i| = 5\), slope \(0.0066\), a change \(38\) times smaller than at \(s_i = 0\)

\(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

Softmax Has a Full Jacobian

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

  • The loop’s \(\odot\, \sigma'\) line becomes \(\mathbf{J}^T(\cdot)\) with that map’s Jacobian
  • The Jacobian rebuilt from the stored values

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

\(\boldsymbol{\delta}^{(1)}\) Unrolled: One Slope Matrix and One Weight Matrix per Layer

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)\]

  • \(L - 1\) diagonal slope matrices and \(L - 1\) transposed weight matrices, alternating
  • Every factor evaluated at the forward pass’s values

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}\]

  • \(L = 10\): \(\;(1/4)^9 = 3.8 \times 10^{-6}\)
  • \(L = 20\): \(\;(1/4)^{19} = 3.6 \times 10^{-12}\)

Weight gradients have the same factor: \(\partial \ell / \partial \mathbf{W}^{(1)} = \boldsymbol{\delta}^{(1)} \mathbf{a}^{(0)T}\)

In the same product

  • \(\|\mathbf{W}^{(l)}\|\): weights larger than one can offset the slopes
  • The slope bound: \(1\) for tanh, \(1\) for ReLU, \(1/4\) for the sigmoid

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

L2 Penalty: \(\lambda \mathbf{W}^{(l)}\) Added to Each Weight Gradient

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)}\]

  • Every weight shrinks by the factor \(1 - \eta\lambda\), then takes the gradient step

3-4-2 network, output layer, \(\lambda = 0.1\)

  • Penalty: \(\tfrac{\lambda}{2} \|\mathbf{W}^{(2)}\|_F^2 = 0.1 \times 0.345 = 0.0345\)
  • Entry \((1, 1)\): \(\;0.287 + 0.1 \times 0.5 = 0.337\)

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

Memory per Training Step Is Linear in the Batch Size

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\)

  • \(m = 32\): \(2.03\) MB
  • \(m = 1024\): \(6.71\) MB

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 Graphs

Networks Are Directed Acyclic Graphs of Operations

Computational graph: nodes are operations, edges the arrays between them

  • Directed: an edge runs from the operation that produces an array to each operation that uses it
  • Acyclic: no array depends on itself, so the nodes have a topological order, every node after the nodes it uses

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

  • A layer is three nodes: multiply, add, activate

Cycle would leave no order: a recurrent connection is unrolled in time into a DAG before either pass runs

Local Gradients: Each Node’s Backward Call Uses Only Its Own Values

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
  • \(\partial \ell / \partial \text{input}\) from \(\partial \ell / \partial \text{output}\) and the kept values, nothing else
  • \(\ell\), the other nodes, and the node’s position in the graph never appear

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 each
  • At the end, g[W⁽ˡ⁾] and g[b⁽ˡ⁾] are the parameter gradients

Chain rule is in the driver, not in the nodes: composition is the reverse loop, summation over paths is the +=

Each Node Implements a Forward Call and a Backward Call

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

  • Multiply by \(\mathbf{W}^{(2)}\): \(\partial \ell / \partial \mathbf{a}^{(1)}\) from \(\boldsymbol{\delta}^{(2)}\)
  • Then \(\sigma\): \(\boldsymbol{\delta}^{(1)}\) from \(\partial \ell / \partial \mathbf{a}^{(1)}\)
  • The layer recursion is these two calls

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

  • To \(\mathbf{a}^{(1)}\): \(\;\mathbf{W}^{(2)T} \boldsymbol{\delta}^{(2)} = [0.342,\ -0.342,\ 0.147,\ -0.098]^T\)
  • To \(\mathbf{W}^{(2)}\): \(\;\boldsymbol{\delta}^{(2)} \mathbf{a}^{(1)T}\), first row \([0.287,\ 0.251,\ 0.226,\ 0.327]\)

No derivation per network: a new graph of the same nodes has its backward pass from the same calls, in its own reverse order

Every Node Rule Is a Jacobian Transpose Times \(\mathbf{g}\)

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: \(\mathbf{W}\)
  • Activation: diagonal
  • Softmax: full
  • Add: the identity

Multiply node, wherever it appears: the same two products

  • In the first layer, in the output layer, in any other graph that contains it

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

Stored Values, Broadcast Sums, and One Gradient per Input

Store what the Jacobian needs, in the smallest form

  • Sigmoid: \(\mathbf{a}\), since \(\sigma' = a(1 - a)\), and \(\mathbf{s}\) is not kept
  • ReLU: the mask \(\mathbb{1}[\mathbf{s} > 0]\), one bit per unit
  • Multiply: both inputs, since the gradient to each needs the other
  • The stored values of all nodes are the activation memory, \(1{,}178\) per sample for layers of 784, 256, 128, and 10 units

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

  • \(\mathbf{b}\) added to all \(m\) rows of \(\mathbf{S}\): \(\;\partial \mathcal{L} / \partial \mathbf{b} = \boldsymbol{\Delta}^T \mathbf{1}\), the sum over rows
  • A scalar multiplied into every entry: its gradient is the sum over every entry

One gradient per input

  • Multiply node: \(\mathbf{W}^T \mathbf{g}\) to \(\mathbf{a}\) and \(\mathbf{g}\, \mathbf{a}^T\) to \(\mathbf{W}\)
  • Add node: \(\mathbf{g}\) to each input
  • Loss node: a gradient to \(\hat{\mathbf{y}}\), none to the target

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

Gradients Add Where a Value Is Reused

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}\]

  • Hidden activation into \(n_{l+1}\) units: \(n_{l+1}\) terms, which is \(\mathbf{W}^{(l+1)T} \boldsymbol{\delta}^{(l+1)}\)
  • Bias added to every row of a batch: \(m\) terms, which is \(\boldsymbol{\Delta}^{(l)T} \mathbf{1}\)
  • Shortcut \(\mathbf{y} = \mathbf{x} + f(\mathbf{x})\): \(\;\partial \ell / \partial \mathbf{x} = \partial \ell / \partial \mathbf{y} + \mathbf{J}_f^T\, \partial \ell / \partial \mathbf{y}\)
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

Reverse Mode: One Backward Pass for Every Input of a Scalar Output

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}\)

  • One pass gives \(\partial \ell / \partial \boldsymbol{\theta}\) along \(\mathbf{d}\): one number
  • All \(n\) partial derivatives: \(n\) passes, one per coordinate direction

Reverse mode: one vector \(\mathbf{u}\) multiplied through the transposes in reverse order, \(\mathbf{J}_1^T \cdots \mathbf{J}_L^T \mathbf{u}\)

  • One pass gives \(\partial \ell / \partial \boldsymbol{\theta}\) for every \(\theta\) at once, since the output is one number and \(\mathbf{u} = 1\)
  • All \(n\) partial derivatives: one pass
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

  • Many inputs, one output, as for every loss: reverse mode
  • One input, many outputs: forward mode

Automatic differentiation: the node rules run by a program on a recorded graph

  • Forward mode: Jacobian-vector products
  • Reverse mode: vector-Jacobian products

Backpropagation is reverse-mode automatic differentiation on a layered graph

Running the Forward Pass Records the Graph

Record: one entry per operation as it executes, with its inputs and its stored values

  • Each result points to the entry that produced it
  • The backward pass runs through the record in reverse, calling each entry’s backward

Consequences

  • The forward code is ordinary code: branches, loops, and calls, and whatever ran is what is differentiated
  • Two inputs can produce two different graphs, as when a loop’s length depends on the data
  • Every stored value is kept until its backward call, which is the activation memory

What has an entry: operations on arrays marked as needing a gradient

  • The parameters and everything computed from them
  • Not the input data

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

  • The forward loop writes it
  • The backward and read-off loops are its entries’ backward calls

Frameworks Derive the Backward Pass From the Forward Pass

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

  • Each entry’s backward rule called in turn
  • Each parameter’s gradient left in its .grad

Printed gradients: the hand-computed values, to six decimals

  • \(\partial \ell / \partial \mathbf{W}^{(2)}\): first row \([0.287,\ 0.251,\ 0.226,\ 0.327]\)
  • \(\partial \ell / \partial \mathbf{W}^{(1)}\): first row \([0.083,\ 0.042,\ {-0.042}]\)

Nothing in the record is specific to layers

  • Any forward computation from differentiable nodes: the same walk, the same gradients

Implementation and Gradient Checking

Forward Pass and Loss: One NumPy Line per Equation

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}\]

  • \(\mathbf{W}^T\) on the right because the samples are rows
  • \(+\ \mathbf{b}\) broadcasts over the rows: the \(\mathbf{1}\mathbf{b}^T\) of the equation
  • Loss from the log-softmax, \(\log \hat{Y}_{ik}\) computed as the shifted score minus the log of the sum
  • No probability formed, so none rounds to zero before its log

Three-sample batch of the 3-4-2 network: \(\mathcal{L} = 0.709\)

S1 = X @ W1.T + b1                       # m × n1
A1 = 1 / (1 + np.exp(-S1))               # m × n1
S2 = A1 @ W2.T + b2                      # m × K
Z = S2 - S2.max(axis=1, keepdims=True)   # shift
logZ = np.log(np.exp(Z).sum(axis=1, keepdims=True))
logP = Z - logZ                          # log-softmax
loss = -(Y * logP).sum(axis=1).mean()
print(f"loss {loss:.3f}")
loss 0.709

Stored for the backward pass: X, A1, and logP, the three arrays it uses

Backward Pass and Update: One NumPy Line per Equation

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}\]

  • In the code, D2 is \(\boldsymbol{\Delta}^{(2)} / m\): the \(1/m\) applied once, so every later product is already a mean
  • sum(axis=0): the sum over the rows the bias was broadcast to
  • \(\sigma'\) from the stored A1, no S1 kept

Printed: \(\partial \mathcal{L} / \partial \mathbf{W}^{(2)}\), first row \([0.097,\ 0.055,\ 0.082,\ 0.111]\), the hand-computed value

D2 = (np.exp(logP) - Y) / m              # Δ2 / m, m × K
gW2 = D2.T @ A1                          # K × n1
gb2 = D2.sum(axis=0)                     # K
D1 = (D2 @ W2) * A1 * (1 - A1)           # m × n1
gW1 = D1.T @ X                           # n1 × d
gb1 = D1.sum(axis=0)                     # n1
W2 -= eta * gW2;  b2 -= eta * gb2
W1 -= eta * gW1;  b1 -= eta * gb1
print(gW2.round(3))
[[ 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\)
  • A transpose in the wrong place fails to multiply

Finite Differences Check the Backward Pass Against the Loss Itself

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

  • \(1.4 \times 10^{-4}\) at \(\epsilon = 10^{-1}\), \(\;1.4 \times 10^{-6}\) at \(10^{-2}\)

Roundoff, two nearly equal losses each exact to \(u = 1.1 \times 10^{-16}\), their difference divided by \(2\epsilon\)

  • Grows as \(1/\epsilon\): about \(10^{-6}\) at \(10^{-9}\), \(10^{-3}\) at \(10^{-12}\)

Floor: about \(10^{-10}\) at \(\epsilon = 10^{-5}\) on the 3-4-2 batch, in double precision

Cost: two forward passes per parameter checked

  • A small network, or a sample of entries

Agreement: an error near the floor, \(10^{-10}\) to \(10^{-8}\) at \(\epsilon = 10^{-5}\) in double precision

  • A wrong backward pass is off by orders of magnitude, so the band need not be exact

Missing Factors Show as Errors of Order One

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)}\)

  • Centered difference: \([{-0.014},\ 0.044,\ {-0.020}]\)
  • Correct backward pass: the same, relative error about \(10^{-10}\)
  • Slope missing: \([{-0.055},\ 0.177,\ {-0.082}]\), relative error \(0.60\)

Detected: any wrong line of the backward pass

  • The difference quotient uses the forward pass and the loss only

Not detected: a wrong forward pass, a wrong loss

  • Both sides share them

ReLU at the kink

  • A parameter with some \(s_i\) within \(\epsilon\) of zero: \(w + \epsilon\) and \(w - \epsilon\) on different sides of the kink
  • Difference quotient wrong at that entry
  • Check with a sigmoid, or exclude such entries

Forward, Backward, Update on Real Data: Three Species From Two Measurements

Example: Palmer penguins

  • 342 birds, bill length and flipper length
  • Three species as the classes
  • 240 training, 102 test, features standardized

Network: \(2\)-\(8\)-\(3\), sigmoid hidden layer, softmax output

  • Fit by the forward, backward, and update lines from random weights of spread \(0.5\)
  • Full training batch, \(\eta = 0.5\), \(2{,}000\) steps

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

  • The hidden layer bends the regions and changes no decision
  • The species separate along nearly straight lines in these two features

Data: Palmer Penguins, A. M. Horst, A. P. Hill, K. B. Gorman, 2020 (CC0); collected by K. Gorman and Palmer Station LTER

Gradients Through Depth

Backpropagation Generalizes LMS

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

  • Computed from the weights and slopes between the layer and the loss
  • Then the same step as every other case

LMS is backpropagation on one linear layer with squared loss: \(\delta^{(1)} = -e\), and the recursion has nothing to propagate

Online Adaptation Is the Same Loop With One Sample per Update

Batch update, \(m\) samples per step \[\boldsymbol{\theta} \leftarrow \boldsymbol{\theta} - \eta\, \frac{1}{m} \sum_{i=1}^{m} \nabla \ell_i\]

  • The mean gradient of one fixed \(P(Y \mid \mathbf{x})\)
  • Averaging over \(m\) samples lowers the variance

Online update, \(m = 1\), as in LMS \[\boldsymbol{\theta}_{t+1} = \boldsymbol{\theta}_t - \eta\, \nabla \ell_t\]

  • Tracks a \(P_t(Y \mid \mathbf{x})\) that changes with \(t\)
  • \(\eta\) sets how far back the samples still count: a larger \(\eta\) tracks a change sooner and averages fewer samples
  • Same forward pass, backward pass, and read off, every matrix one row

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}\]

  • LMS or logistic regression on the hidden layers’ features
  • Backward pass ends at \(\boldsymbol{\delta}^{(L)}\): no recursion, no stored hidden values
  • \(n_L (n_{L-1} + 1)\) parameters adapt

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

Sigmoid Layers Shrink the Gradient

Network: \(2\)-\(16\)-\(\cdots\)-\(16\)-\(3\), ten hidden layers of \(16\) units, the \(240\) training penguins

  • Weights \(\mathcal{N}(0, 1/n_{\text{in}})\) so that each \(s_i\) starts with variance near one, biases zero, at initialization

Measured: \(\|\partial \mathcal{L} / \partial \mathbf{W}^{(l)}\|_F\) by layer, one backward pass

  • Sigmoid: \(7.0 \times 10^{-1}\) at the output layer, \(8.1 \times 10^{-7}\) at the first
  • Ratio first to last: \(1.2 \times 10^{-6}\), a factor \(0.26\) per layer
  • Tanh: ratio \(3.0\)
  • ReLU: ratio \(0.49\)

Mean slope per hidden layer, sigmoid: \(0.21\) to \(0.24\)

  • Product over the ten layers: \(4.5 \times 10^{-7}\)
  • The weight matrices, largest singular values \(1.3\) to \(2.0\), multiply in the same product

Tanh and ReLU slopes are \(1\) where the unit is active: the gradient retains its size through every layer

ReLU Layers Preserve the Gradient

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\)
  • Sigmoid at five and ten layers: the loss stays near \(\log 3 = 1.099\), chance for three classes, and the first layer does not move
  • ReLU at every depth: the first layer moves and the loss falls

Open problem: gradients of one size at every depth

  • Activation: the slope, \(1/4\) at most for the sigmoid, \(1\) for ReLU
  • Weight scale: the \(\|\mathbf{W}^{(l)}\|\) in the same product
  • Step size: the five-layer ReLU network at \(\eta = 0.5\) diverges within the \(2{,}000\) steps

Backward pass exact at every depth: what shrinks is the gradient itself, and the remedies change the network, not the algorithm

Appendix: Row Gradients

Two Conventions for the Vector Derivative \(\partial \mathbf{y} / \partial \mathbf{x}\)

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\)

  • As an \(m \times n\) matrix, rows indexed by the outputs \(y_j\): the Jacobian of calculus
  • As an \(n \times m\) matrix, rows indexed by the inputs \(x_i\): its transpose

Names: in \(\partial \mathbf{y} / \partial \mathbf{x}\), which of \(\mathbf{y}\) and \(\mathbf{x}\) sets the row index

  • Numerator convention: rows follow \(\mathbf{y}\), the matrix is \(m \times n\)
  • Denominator convention: rows follow \(\mathbf{x}\), the matrix is \(n \times m\)

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

  • Analysis: the Jacobian as the matrix of the linear map \(d\mathbf{y} = \mathbf{J}\, d\mathbf{x}\), rows indexed by outputs
  • Optimization and statistics: the gradient as the direction of steepest ascent, a vector the shape of the variable it updates

Same \(mn\) numbers either way: one arrangement is the transpose of the other

What a convention fixes

  • The shape of a gradient, row or column
  • The side of the Jacobian in the chain rule
  • Whether the transpose is in the chain rule or in the update

Numerator and Denominator Jacobians Are Transposes

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}\]

EE 541 Convention: Numerator Jacobian, Column Gradient

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: Row Gradient, Chain Rule in Forward Order

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]\]

  • Vector-valued steps compose the same way: \(\partial f / \partial \mathbf{x} = \partial f / \partial \mathbf{z} \cdot \partial \mathbf{z} / \partial \mathbf{y} \cdot \partial \mathbf{y} / \partial \mathbf{x}\), every factor a Jacobian

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

Both Conventions Give the Same Numbers

Identity: for any matrix \(\mathbf{J}\) and vector \(\mathbf{u}\) \[\big(\mathbf{J}^T \mathbf{u}\big)^T = \mathbf{u}^T \mathbf{J}\]

  • Column form on the left, row form on the right, the same \(n\) numbers
  • Example: \(\mathbf{W}^T [2.4,\ 0.8]^T\) and \([2.4,\ 0.8]\, \mathbf{W}\) are both \(1.04\), \(1.04\), \(-0.08\)

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\)
  • \(\mathbf{D} = \mathrm{diag}(\sigma'(\mathbf{s}^{(l)}))\), the same diagonal in both
  • 3-4-2 network: \([0.342,\ -0.342,\ 0.147,\ -0.098]\) as a column from \(\mathbf{W}^{(2)T} \boldsymbol{\delta}^{(2)}\), as a row from \(\boldsymbol{\delta}^{(2)T} \mathbf{W}^{(2)}\)

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