Backpropagation
How does Backpropagation work?
Backpropagation (backprop) is an efficient algorithm for computing the gradient of the loss $\mathcal{L}$ with respect to all network parameters $\theta$ by applying the chain rule in reverse order through the computational graph. It runs in $O(p)$ time (proportional to the number of parameters), the same cost as a single forward pass.
The Chain Rule
For a composition $f(g(x))$:
For a multi-variable composition $\ell = \mathcal{L}(z)$, $z = Wh + b$:
Backpropagation applies this chain rule systematically across all layers.
Forward Pass
Compute and cache intermediate activations ${\mathbf{z}^{(l)}, \mathbf{h}^{(l)}}$ for each layer $l = 1, \ldots, L$:
Output: $\hat{\mathbf{y}} = \mathbf{h}^{(L)}$, then compute $\mathcal{L}(\hat{\mathbf{y}}, \mathbf{y})$.
These cached values are reused during the backward pass.
Backward Pass
Define the error signal (upstream gradient) at layer $l$:
Output layer ($l = L$):
For softmax + cross-entropy: $\boldsymbol{\delta}^{(L)} = \hat{\mathbf{y}} - \mathbf{y}$ (clean closed form).
Propagation (layer $l$ from $l+1$):
Parameter gradients:
Algorithm Summary
Forward pass:
for l = 1 to L:
z[l] = W[l] @ h[l-1] + b[l]
h[l] = σ(z[l])
compute L(h[L], y)
Backward pass:
δ[L] = ∂L/∂z[L]
for l = L-1 downto 1:
δ[l] = (W[l+1].T @ δ[l+1]) * σ'(z[l])
for l = 1 to L:
∂L/∂W[l] = δ[l] @ h[l-1].T
∂L/∂b[l] = δ[l]
Computational Cost
- Forward pass: $O(\sum_l n_l \cdot n_{l-1})$ multiplications.
- Backward pass: same asymptotic cost as forward pass ($\approx 2\times$ wall-clock time).
- Memory: $O(\sum_l n_l)$ to store activations. Memory grows with depth and batch size.
Automatic Differentiation (Autograd)
Modern frameworks implement backprop via automatic differentiation, not symbolic or numerical differentiation.
Forward mode AD: accumulates derivatives alongside the primal computation. Efficient when input dimension is small; $O(p)$ passes for $p$ parameters.
Reverse mode AD (backprop): one backward pass computes all $p$ partial derivatives simultaneously. $O(1)$ passes regardless of $p$. Ideal for neural networks where $p \gg$ number of outputs.
Tape-based (eager): PyTorch records operations dynamically; backward traverses the recorded tape.
Symbolic/compiled: TensorFlow (v1), JAX’s jit: builds static computation graph; enables optimization passes (operation fusion, XLA compilation).
Vanishing and Exploding Gradients
The gradient at layer $l$ involves a product of Jacobians:
Each Jacobian factor is $W^{(k)T} \cdot \text{diag}(\sigma’(\mathbf{z}^{(k)}))$.
Vanishing: if $|W^{(k)T}| \cdot |\sigma’(\mathbf{z}^{(k)})| < 1$ on average, the product shrinks exponentially with depth. Early layers learn extremely slowly.
Exploding: if the product grows exponentially, gradients blow up. Weights diverge.
Solutions:
| Problem | Solution |
|---|---|
| Vanishing | ReLU activation, residual connections, careful init |
| Exploding | Gradient clipping, careful init, weight norm |
| Both | Batch normalization, layer normalization |
Jacobians and the General Case
For vector-to-vector functions $\mathbf{f}: \mathbb{R}^m \to \mathbb{R}^n$, the Jacobian is:
Backprop computes vector-Jacobian products (VJPs) $\mathbf{v}^T J_f$, where $\mathbf{v}$ is the upstream gradient. This is cheaper than materializing the full Jacobian.
Numerical Gradient Checking
Finite difference approximation to verify backprop implementation:
Use $\epsilon = 10^{-5}$. Compare against analytical gradient; relative error should be $< 10^{-5}$. Only used for debugging; too expensive for training.