backward() closure that performs exactly this multiplication and accumulates into the input's gradient.
Training updates weights: for every weight W we need ∂L/∂W, then W ← W − η·∂L/∂W. The input x is fixed; we never update it. The derivation below shows, step by step, why activation gradients (the δ values) control every weight gradient. Two calculus facts are all that is needed: the chain rule and "derivative of a sum = sum of derivatives".
Setup. x is one training sequence — a batch of T token positions processed together. Each position's embedding is a vector in ℝ^d_model, so x has shape T × d_model. The loss is averaged over all T positions. h_l is the activation (hidden state) coming out of layer l; in a transformer, this is the residual stream at that depth. W_l are the weight matrices of layer l. f_l is the computation layer l performs (attention + FFN in a transformer block):
Two calculus facts. In (2) and (3) below, u and v stand for any two intermediate quantities in the computation — for example, u could be a weight entry W[i][j] and v could be an activation vector h_l. The chain rule says: to find how a change in u affects the loss, multiply how u affects v by how v affects the loss.
One-variable chain rule, and its several-variable form — if u affects the loss through multiple intermediate quantities v[j], each route contributes one term and the routes add:
The messenger δ. Define, for every layer boundary, the row vector of partials of the loss with respect to that activation:
δ_l answers: "how much does the loss change if I nudge h_l[j] slightly?" It is not used to update h_l — activations are never updated. It is the information that travels backward so that weight gradients can be computed at each layer. The term "activation gradient" means: gradient of the loss with respect to an activation, not gradient of the activation itself.
What one matmul contributes to one weight. Take a linear step h_out = h_in·W, written per entry so the partial is a one-liner:
The weight gradient formula. W[i][j] reaches the loss only through h_out[j] (one route), so (3) has a single term:
Every weight gradient is a product of two factors: the activation h_in stored during the forward pass, and the activation gradient δ_out arriving from the loss. The first factor is local and always available. The second factor is what depth puts at risk.
Propagating the messenger. Collect all partials of one step into its Jacobian:
h_{l−1}[i] reaches the loss through all entries of h_l — (3) with routes h_{l−1}[i] → h_l[j] → Loss:
Combining (6) and (9) — the key result:
We never use ∂Loss/∂x to update anything — but by (10) the Jacobian product is a shared factor in every weight gradient at layer l. If each Jacobian shrinks vectors by at most a factor s (its largest singular value; this is |slope| in the 1-dim case):
With the residual stream. A pre-norm block writes into the stream instead of replacing it:
This demo shows what happens when gradient signal travels backward through a network with no residual connections, compared to one that has them. From equation (9), the gradient at each layer is the gradient from the layer above multiplied by that layer's local slope. Without a residual connection, that multiplication happens at every layer. If the slope is less than 1, the gradient shrinks at every step — a geometric decay that leaves early layers with almost no signal. At slope 0.5 and 12 layers, the first layer receives 0.5¹² ≈ 1/4096 of the loss gradient, and every weight update in that layer is scaled by that same factor.
Why normalizing the gradient does not fix this. A natural response is to keep only the gradient's direction and discard its magnitude, similar to what LayerNorm does for activations in the forward pass. This does not work for two reasons. First, the gradient's magnitude carries information: a near-zero gradient means "this weight barely affects the loss right now — leave it alone." Normalizing it to unit length causes a large, unwarranted update. Second, normalizing at one layer does not help layers further down. The attenuated signal still passes through every remaining Jacobian before reaching those layers, shrinking at each one. You would have to normalize after every single layer, and each normalization discards the information that earlier layers need about how much to change.
The residual connection does something different: it adds 1 to every Jacobian (equation 12). The backward pass then includes one path through which the loss signal travels unchanged, skipping every layer's weights (the identity path). This path cannot vanish no matter how small the weights become — there are no weights in it.
Drag the slope slider to see gradient magnitude change across layers (top diagram) and how gradient mass shifts across path lengths (bottom diagram). Both diagrams respond to the same slider. Gradient mass is the share of the total gradient signal flowing through paths of a given length — where length counts how many layer branches the signal passes through rather than skipping.
Red row: plain network. Each box multiplies the gradient by slope a. Green row: residual network. Each box multiplies by 1+a. The colored bar below each box is the gradient magnitude at that point, on a log scale — taller bars mean more signal. The dashed line marks gradient = 1. By equation (10), every weight gradient at a given depth carries the same factor as the messenger bar shown there.
The bar at k=0 (leftmost) is the pure identity path — the gradient arrives unchanged, bypassing all layer weights. With slope less than 1, most gradient mass concentrates at short path lengths. Veit et al. 2016 measured this in a 110-layer ResNet: effective paths are only 10–34 layers deep, even in a 110-layer network.
Multiply out the residual chain's backward factor (algebra, no calculus):
The leading 1 is the all-skip path: it delivers δ_L to the bottom unchanged. This is equation (5) of He et al. 2016 [1603.05027], quoted verbatim in §4: the gradient splits into ∂E/∂x_L · (1 + ∂/∂x_l ΣF) — our equation (13) — and therefore "the gradient of a layer does not vanish even when the weights are arbitrarily small." In a plain chain, making weights small kills every path; in the residual chain the identity path contains no weights, so no weight can kill it.