x = jnp.arange(4.0)
xDive into Deep Learning · §1.5
From the chain rule to backpropagation
automatic differentiation for a complete network.
Motivation
Deriving a million-parameter network’s gradient by hand is impractical. Instead, the framework records each operation during the forward pass, then replays it in reverse, systematically applying the chain rule from the calculus section to obtain the gradient with respect to every input at once.
Training steps in this book generally include a forward pass and a 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 avoid its bookkeeping cost. 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
In eager execution, autograd records the operations executed by if and while statements. This function’s loop count and branch both depend on its input (compiled transformations may impose additional constraints):
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
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 values coincide at this point, and autograd computes 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.