x = jnp.arange(4.0)
xDive into Deep Learning · §1.5
From the chain rule to backpropagation
the engine that differentiates a whole network for you.
Motivation
Hand-deriving gradients for a million-parameter network is hopeless. Instead the framework records each operation as you run the forward pass, then replays it in reverse, applying the chain rule of the calculus section mechanically, to get the gradient w.r.t. every input at once.
Every training step in this book is one forward pass and one backward pass over this graph.
01
The mechanics
record forward · sweep backward
Mechanics
Mechanics
JAX is functional: there is nothing to attach. You write the function, and grad transforms it into its derivative. The forward pass is an ordinary call:
Array(28., dtype=float32)
Mechanics
One call sweeps the graph in reverse, and the result equals the promised 4\mathbf{x}, at every coordinate:
Array([ 0., 4., 8., 12.], dtype=float32)
Array([ True, True, True, True], dtype=bool)
That reverse sweep is the calculus section’s chain rule, run from output to input.
02
Working with gradients
accumulation · non-scalar outputs · detaching · inference
Gradients
Recording a new computation overwrites the previous gradient; there is no buffer to reset:
Array([1., 1., 1., 1.], dtype=float32)
Gradients
Gradients are defined for a scalar loss. For a vector y, the engine differentiates the sum of its components (a vector–Jacobian product), exactly what a per-example batch loss needs:
Array([0., 2., 4., 6.], dtype=float32)
Gradients
Sometimes a value should count as a constant: gradients must not flow through it. detach (or stop_gradient) severs the graph above it, so z = u \cdot x differentiates to u, not to 3x^2:
Array([ True, True, True, True], dtype=bool)
Gradients
When we only need the value (prediction, evaluation, manual updates), we turn recording off and pay nothing for it. This is the default mode for inference throughout the book:
Array(28., dtype=float32)
03
Dynamic graphs
the graph is whatever actually ran
Dynamic graphs
Dynamic graphs
Each call realizes a concrete graph that backward can walk. Whichever branch ran, f scaled its input by some constant, so f(a) = k\,a and the gradient must equal f(a)/a. It does:
Array(True, dtype=bool)
Beyond · payoff
A counting argument settles which way to sweep. With n inputs and m outputs, the full derivative matrix costs m reverse sweeps or n forward sweeps, each sweep priced at roughly one function evaluation.
A training loss has m = 1 and n in the millions: one reverse sweep delivers every parameter’s gradient, for the cost of about one extra forward pass. Forward mode wins the opposite regime (few inputs, many outputs) and Hessian–vector products.
Beyond
The gradient is itself a function on the graph, so we can differentiate it. For f(x) = x^3 at x = 2: f'(2) = 3x^2 = 12 and f''(2) = 6x = 12: the same number, by coincidence, and autograd nails both:
(Array(12., dtype=float32, weak_type=True),
Array(12., dtype=float32, weak_type=True))
Wrap-up
detach / no-grad keep values out of the graph.Backpropagation through real networks gets its full treatment in the backpropagation section; forward vs. reverse mode is derived in the matrix-calculus-and-automatic-differentiation section.