Forward-Mode and Reverse-Mode Autodiff
Backpropagation is one of two ways to apply the chain rule mechanically, and the choice between them is decided by a single number, the ratio of inputs to outputs, which is why training uses reverse mode and Hessian-vector products use both.
A network with 8 billion parameters and one scalar loss needs 8 billion partial derivatives. Computing them one at a time by perturbing each parameter would take 8 billion forward passes. Backpropagation gets all of them in roughly the cost of two. That is not a property of neural networks; it is a property of the direction in which the chain rule is applied, and the same machinery run the other way would be catastrophically slow here and exactly right somewhere else.
Two ways to multiply the same Jacobians
A program computing \(f: \mathbb{R}^n \to \mathbb{R}^m\) decomposes into elementary steps, and the chain rule makes its Jacobian a product of per-step Jacobians:
Matrix multiplication is associative, so the product can be evaluated from either end. Evaluating right to left is forward mode: propagate a tangent vector \(v\) alongside the computation to get \(Jv\), a Jacobian-vector product. Evaluating left to right is reverse mode: run the program, then propagate a cotangent \(u\) backwards to get \(u^\top J\), a vector-Jacobian product (Baydin et al., 2018, Automatic Differentiation in Machine Learning: a Survey, JMLR 18(153), arXiv:1502.05767).
Each JVP costs about one forward pass and yields one column of \(J\). Each VJP costs about one forward pass plus one backward sweep and yields one row. To fill the whole Jacobian you need \(n\) forward-mode passes or \(m\) reverse-mode passes.
| Forward mode | Reverse mode | |
|---|---|---|
| Primitive | \(Jv\) (JVP) | \(u^\top J\) (VJP) |
| Passes for full Jacobian | \(n\) (inputs) | \(m\) (outputs) |
| Extra memory | \(O(1)\), tangents travel with values | \(O(\text{steps})\), must store the tape |
| Wins when | \(n \ll m\) | \(m \ll n\) |
Training has \(n \approx 10^{10}\) and \(m = 1\). The choice is not close.
Memory is the price of reversal
Forward mode carries a tangent next to every value and discards both when the value dies, so peak memory looks like the forward pass. Reverse mode cannot start until the forward pass finishes, and each backward step needs intermediates from its forward counterpart, so those intermediates stay alive. This is the entire reason activation memory dominates training and the entire reason gradient checkpointing exists: recompute intermediates in the backward pass rather than store them.
It also explains why forward mode never disappeared. It has no tape, so it works inside code that reverse mode cannot handle, including loops with data-dependent trip counts and calls into libraries that do not record a graph.
Where forward mode is genuinely the right tool
Curvature. The Hessian-vector product \(Hv\) can be written as a JVP applied to the gradient function, so a single forward-over-reverse composition gives \(Hv\) at roughly the cost of a couple of gradient evaluations and without ever forming the \(n \times n\) Hessian (Pearlmutter, 1994, Fast Exact Multiplication by the Hessian, Neural Computation 6(1)). Every matrix-free second-order method, conjugate gradient on the Gauss-Newton system, Lanczos spectrum estimation, K-FAC diagnostics, and the sharpness probes used in loss-landscape work, is built on this. In JAX it is jax.jvp(jax.grad(f), ...); in PyTorch it is torch.func.jvp over torch.func.grad.
When it breaks
The cost model assumes you want the full gradient. Reverse mode's advantage evaporates when \(m\) is large: a Jacobian for a 1,000-dimensional output takes 1,000 backward passes, and people reach for reverse mode anyway out of habit and are surprised by the wall clock.
Reverse mode also assumes the forward pass is replayable and its intermediates are storable. Neither holds universally. Long sequences make the tape the memory bottleneck; in-place mutation invalidates saved values and raises the errors that make PyTorch autograd feel adversarial; control flow that depends on values the tape did not record silently differentiates the wrong branch.
Forward mode has an efficiency trap of its own. Running \(n\) JVPs to build a full Jacobian is embarrassingly parallel in principle but \(n\) times the work in practice, and vectorising it with vmap multiplies memory by the batch of tangents. It is the right tool for one direction, or a few, not for filling a matrix.
Neither mode fixes a numerically bad program. Autodiff differentiates what you wrote, including the catastrophic cancellation and the saturating exp you did not notice, which is why the stable formulations of softmax, log-sum-exp, and cross-entropy are written as fused primitives with hand-derived gradients rather than left to the graph.
12 flashcards for this concept
Click a card to reveal the answer.