Type at least 2 letters. Enter opens the first result, Esc closes.

    Calculus for ML · Lesson 6 · Chain rule, automatic differentiation and backpropagation

    Backpropagation by hand: MLPs, losses and layer normalization

    Intermediate 90 min 5 interactive figures 4 videos Colab notebook

    Before this lesson: lesson 03 (Jacobians, Hessians, Taylor expansion), lesson 04 (chain rule, VJPs of WxWx and of elementwise maps, fan-out), lesson 05 (reverse mode). Outer products from linear-algebra lesson 01. The softmax Jacobian is derived in linear-algebra lesson 16. Time: about 90 minutes, plus the notebook. This is the heaviest lesson of the track; take a break after section 3.3.


    Why this matters #

    Every training step of every neural network runs a backward pass. Frameworks do it for you, but you need the formulas when you write a custom layer or a fused GPU kernel, when a loss turns into nan, when gradients vanish in a deep model, or when an interviewer asks you to "derive backprop for an MLP" or "the gradient of softmax cross-entropy" on a whiteboard. These are among the most common technical interview questions in ML.

    The losses of this lesson are most of the loss code in practice: least squares in regression, logistic loss in binary classification, softmax cross-entropy in every classifier and every large language model (LLM). Layer normalization (LayerNorm) sits in every transformer block. In ML for science, weighted least squares is the data-misfit term of data assimilation and seismic inversion, and LayerNorm is inside transformer-based weather models.

    1. Predict first #

    2. The picture #

    Backprop is reverse mode on a chain of layers. An MLP computes x→z1→a1→z2→⋯→zL→ℓx \to z^1 \to a^1 \to z^2 \to \cdots \to z^L \to \ell. Lesson 05 showed that reverse mode runs the chain once forward (and stores the values), then once backward with the seed ∂ℓ/∂ℓ=1\partial\ell/\partial\ell = 1. On the way back we carry the error signal of each layer,

    δl=∂ℓ∂zl(a vector with the shape of zl).\delta^l = \frac{\partial \ell}{\partial z^l} \qquad (\text{a vector with the shape of } z^l).

    Each layer does two jobs on the backward sweep:

    1. Pass the error down: δl\delta^l is the VJP of the layer above. Multiply δl+1\delta^{l+1} by (Wl+1)⊤(W^{l+1})^\top, then multiply entrywise by the slope φ′(zl)\varphi'(z^l) of the activation.
    2. Pay the weights: the gradient of WlW^l is the outer product δl(al−1)⊤\delta^l (a^{l-1})^\top of the error signal and the input of the layer.
    Interactive figure: loading…

    Step through one training step. Press Next step. The forward pass fills the nodes with values (blue); the backward pass fills them with error signals δ\delta (orange). A grey hidden unit is "off" (ReLU with z≤0z \le 0): its slope is 0, so no error passes through it, and its row of ∂ℓ/∂W1\partial\ell/\partial W^1 is zero. Move xx with the sliders to switch units on and off, and switch to the softmax loss to see δ2=p−ey\delta^2 = p - e_y.

    Prediction minus target. Let zz be the score (or the vector of scores) that a model outputs, and yy the target. The three common losses have the same gradient form:

    Loss Formula in the score zz Prediction ∂ℓ/∂z\partial\ell/\partial z
    squared error 12(z−y)2\tfrac12 (z - y)^2 zz z−yz - y
    logistic, y∈{0,1}y \in \{0, 1\} log⁡(1+ez)−yz\log(1 + e^z) - yz σ(z)=1/(1+e−z)\sigma(z) = 1/(1 + e^{-z}) σ(z)−y\sigma(z) - y
    softmax cross-entropy, CC classes log⁡∑kezk−zy\log\sum_k e^{z_k} - z_y p=softmax⁡(z)p = \operatorname{softmax}(z) p−eyp - e_y

    Here eye_y is the one-hot vector of the true class yy: 1 in entry yy, 0 elsewhere. If the prediction equals the target, the gradient is zero. For the logistic and softmax losses, a model that is sure and wrong gets gradient entries close to ±1\pm 1, the largest possible size, so the next step corrects it strongly.

    Why the same form? (generalized linear models)

    Each loss has the form ℓ(z)=A(z)−y⊤z\ell(z) = A(z) - y^\top z (plus a constant), with A(z)=12z2A(z) = \tfrac12 z^2, A(z)=log⁡(1+ez)A(z) = \log(1 + e^z) and A(z)=log⁡∑kezkA(z) = \log\sum_k e^{z_k} (in the last case the target is the one-hot vector eye_y). These functions AA are the log-partition functions of the Gaussian (unit variance), Bernoulli and categorical distributions. A standard fact about exponential families: ∇A(z)\nabla A(z) is the mean of the distribution (the prediction) and ∇2A(z)\nabla^2 A(z) is its covariance: 11, σ(1−σ)\sigma(1 - \sigma) and diag⁡(p)−pp⊤\operatorname{diag}(p) - pp^\top. A covariance matrix is positive semidefinite (PSD), so every one of these losses is convex in the scores. These are the canonical-link generalized linear models of the ML fundamentals track (coming later).

    Interactive figure: loading…

    Move the logits and watch the gradient. The bars show the probabilities pp (blue) and the gradient p−eyp - e_y (orange). The gradient entries always sum to 0 and stay in [−1,1][-1, 1]. Then move the shift slider: adding the same number to every logit changes nothing in pp, but the naive formula (magenta) breaks once a logit passes about 710, while log-sum-exp stays correct.

    Quick check

    A 3-class classifier outputs p=softmax⁡(z)=(0.7,0.2,0.1)p = \operatorname{softmax}(z) = (0.7, 0.2, 0.1), and the true class is y=1y = 1. What is ∂ℓ/∂z\partial\ell/\partial z for ℓ=−log⁡py\ell = -\log p_y?

    3. Definitions and main results #

    3.1 The MLP and its notation #

    We use column vectors for one sample (the same notation as lessons 03–05).

    • Input a0=x∈Rn0a^0 = x \in \R^{n_0}.
    • Hidden layers l=1,…,L−1l = 1, \dots, L-1: the pre-activation zl=Wlal−1+blz^l = W^l a^{l-1} + b^l and the activation al=φ(zl)a^l = \varphi(z^l), with Wl∈Rnl×nl−1W^l \in \R^{n_l \times n_{l-1}}, bl∈Rnlb^l \in \R^{n_l} and an elementwise function φ\varphi (ReLU, tanh, ...).
    • Output layer: the logits zL=WLaL−1+bL∈RCz^L = W^L a^{L-1} + b^L \in \R^C, one score per class.
    • Loss: softmax cross-entropy ℓ=−log⁡softmax⁡(zL)y\ell = -\log \operatorname{softmax}(z^L)_y for the true class y∈{1,…,C}y \in \{1, \dots, C\}.
    • Error signal δl=∂ℓ/∂zl∈Rnl\delta^l = \partial\ell/\partial z^l \in \R^{n_l}.

    ⊙\odot is the entrywise product. Two facts from lesson 04: the VJP of z=Waz = Wa is u↦W⊤uu \mapsto W^\top u, and the VJP of an elementwise map a=φ(z)a = \varphi(z) is u↦φ′(z)⊙uu \mapsto \varphi'(z) \odot u.

    3.2 Theorem 1 (backpropagation for an LL-layer MLP) #

    With the notation of 3.1:

    • (a) δL=p−ey\delta^L = p - e_y, where p=softmax⁡(zL)p = \operatorname{softmax}(z^L);
    • (b) δl=φ′(zl)⊙(Wl+1)⊤δl+1\delta^l = \varphi'(z^l) \odot (W^{l+1})^\top \delta^{l+1} for l=L−1,…,1l = L-1, \dots, 1;
    • (c) ∂ℓ∂Wl=δl(al−1)⊤\dfrac{\partial\ell}{\partial W^l} = \delta^l (a^{l-1})^\top (an outer product, nl×nl−1n_l \times n_{l-1}) and ∂ℓ∂bl=δl\dfrac{\partial\ell}{\partial b^l} = \delta^l;
    • (d) ∂ℓ∂x=(W1)⊤δ1\dfrac{\partial\ell}{\partial x} = (W^1)^\top \delta^1.

    Proof.

    1. Output. ℓ=−zyL+log⁡∑kezkL\ell = -z^L_y + \log\sum_k e^{z^L_k}. So ∂ℓ/∂zkL=−[k=y]+ezkL/∑jezjL\partial\ell/\partial z^L_k = -[k = y] + e^{z^L_k}/\sum_j e^{z^L_j}, which is pk−[k=y]p_k - [k = y]. Here [k=y][k = y] is 1 if k=yk = y and 0 otherwise. This is (a). (Another loss changes only this step: for ℓ=12∥zL−t∥2\ell = \tfrac12\|z^L - t\|^2 we get δL=zL−t\delta^L = z^L - t.)
    2. Layer Jacobian. ℓ\ell depends on zlz^l only through zl+1=Wl+1φ(zl)+bl+1z^{l+1} = W^{l+1}\varphi(z^l) + b^{l+1}. By the chain rule, ∂zl+1/∂zl=Wl+1diag⁡(φ′(zl))\partial z^{l+1}/\partial z^l = W^{l+1}\operatorname{diag}(\varphi'(z^l)).
    3. Recursion. The gradient through a map is the VJP with its Jacobian. So δl=(Wl+1diag⁡(φ′(zl)))⊤δl+1\delta^l = \big(W^{l+1}\operatorname{diag}(\varphi'(z^l))\big)^\top \delta^{l+1}. A diagonal matrix times a vector is an entrywise product, so this is φ′(zl)⊙(Wl+1)⊤δl+1\varphi'(z^l) \odot (W^{l+1})^\top \delta^{l+1}: rule (b).
    4. Weights. ℓ\ell depends on WlW^l only through zlz^l, and zkl=∑jWkjlajl−1+bklz^l_k = \sum_j W^l_{kj} a^{l-1}_j + b^l_k. So ∂zkl/∂Wijl=[k=i] ajl−1\partial z^l_k/\partial W^l_{ij} = [k = i]\, a^{l-1}_j. In the sum ∂ℓ/∂Wijl=∑kδkl[k=i] ajl−1\partial\ell/\partial W^l_{ij} = \sum_k \delta^l_k [k = i]\, a^{l-1}_j only k=ik = i survives, so ∂ℓ/∂Wijl=δilajl−1\partial\ell/\partial W^l_{ij} = \delta^l_i a^{l-1}_j. In matrix form this is δl(al−1)⊤\delta^l (a^{l-1})^\top. In the same way, ∂zkl/∂bil=[k=i]\partial z^l_k/\partial b^l_i = [k = i] gives ∂ℓ/∂bl=δl\partial\ell/\partial b^l = \delta^l. This is (c).
    5. Input. ℓ\ell depends on xx only through z1=W1x+b1z^1 = W^1 x + b^1, so ∂ℓ/∂x=(W1)⊤δ1\partial\ell/\partial x = (W^1)^\top \delta^1. This is (d).
    6. Order. Step 1 gives δL\delta^L. Step 3 gives each δl\delta^l from δl+1\delta^{l+1}, for l=L−1l = L-1 down to 1. Steps 4 and 5 then give all parameter and input gradients. ■\blacksquare

    Read it. The backward pass is reverse accumulation on the chain x→z1→⋯→zL→ℓx \to z^1 \to \cdots \to z^L \to \ell. Unrolled, it is a product of transposed layer Jacobians:

    δ1=diag⁡(φ′(z1)) (W2)⊤ diag⁡(φ′(z2)) (W3)⊤⋯diag⁡(φ′(zL−1)) (WL)⊤ δL.\delta^1 = \operatorname{diag}(\varphi'(z^1))\,(W^2)^\top\,\operatorname{diag}(\varphi'(z^2))\,(W^3)^\top \cdots \operatorname{diag}(\varphi'(z^{L-1}))\,(W^L)^\top\,\delta^L .

    Step 1 differentiates the softmax and the loss together. So the C×CC \times C softmax Jacobian diag⁡(p)−pp⊤\operatorname{diag}(p) - pp^\top is never formed; the backward pass costs O(C)O(C) at the output.

    What must be stored. Rule (b) needs φ′(zl)\varphi'(z^l) and rule (c) needs al−1a^{l-1}. So the forward pass keeps every activation until the backward pass reaches its layer. For tanh, φ′(z)=1−tanh⁡2z=1−a2\varphi'(z) = 1 - \tanh^2 z = 1 - a^2, and for ReLU, 1[z>0]=1[a>0]\mathbf 1[z > 0] = \mathbf 1[a > 0], so storing ala^l is enough.

    Answer to question 1 of section 1. ∂ℓ/∂W=δa⊤\partial\ell/\partial W = \delta a^\top has the shape of WW, 512×256512 \times 256. Its rank is at most 1, because it is an outer product: every column is a multiple of δ\delta. It is exactly 1 when δ≠0\delta \ne 0 and a≠0a \ne 0.

    Quick check

    One sample goes through z=Waz = Wa with W∈R512×256W \in \R^{512 \times 256}, a≠0a \ne 0 and δ=∂ℓ/∂z≠0\delta = \partial\ell/\partial z \ne 0. What are the shape and the rank of ∂ℓ/∂W\partial\ell/\partial W?

    3.3 Batch form #

    Now take a batch of BB samples and the mean loss ℓ=1B∑i=1Bℓi\ell = \frac1B\sum_{i=1}^B \ell_i. Put one sample per row: A0=X∈RB×n0A^0 = X \in \R^{B \times n_0} and

    Zl=Al−1(Wl)⊤+1(bl)⊤,Al=φ(Zl),Z^l = A^{l-1}(W^l)^\top + \mathbf 1 (b^l)^\top, \qquad A^l = \varphi(Z^l),

    where 1∈RB\mathbf 1 \in \R^B is the vector of ones. Row ii of ZlZ^l is (zil)⊤(z^l_i)^\top, the pre-activation of sample ii. This is the layout of PyTorch: nn.Linear stores WlW^l with shape (out, in) and computes A @ W.T + b. Let Δl=∂ℓ/∂Zl∈RB×nl\Delta^l = \partial\ell/\partial Z^l \in \R^{B \times n_l}. Its row ii is (δil)⊤/B(\delta^l_i)^\top / B, because ℓi\ell_i enters the mean with weight 1/B1/B.

    1. ΔL=(P−Y)/B\Delta^L = (P - Y)/B, with PP the row-wise softmax of ZLZ^L and YY the one-hot labels as rows.
    2. Δl=(Δl+1Wl+1)⊙φ′(Zl)\Delta^l = (\Delta^{l+1} W^{l+1}) \odot \varphi'(Z^l): rule (b), transposed and stacked as rows.
    3. ∂ℓ/∂Wl=(Δl)⊤Al−1\partial\ell/\partial W^l = (\Delta^l)^\top A^{l-1}. By the outer-product view of the matrix product (linear-algebra lesson 01), this is ∑i(row i of Δl)⊤(row i of Al−1)\sum_i (\text{row } i \text{ of } \Delta^l)^\top (\text{row } i \text{ of } A^{l-1}): the sum of the per-sample outer products.
    4. ∂ℓ/∂bl=(Δl)⊤1\partial\ell/\partial b^l = (\Delta^l)^\top \mathbf 1: the column sums over the batch.

    The factor 1/B1/B enters once, in ΔL\Delta^L. After that every step is a plain product or sum.

    Shapes are your test. ∂ℓ/∂Wl\partial\ell/\partial W^l must have the shape of WlW^l (nl×nl−1n_l \times n_{l-1}). Some code stores the transposed matrix W=(Wl)⊤W = (W^l)^\top with shape (in, out) and computes Z=AW+1b⊤Z = AW + \mathbf 1 b^\top. Then step 3 reads A⊤ΔA^\top\Delta and step 2 multiplies by W⊤W^\top. Derive in one layout, then check the shapes in the other.

    Cost and memory. Per layer, the forward pass does one matrix product. The backward pass does two of the same size: ΔlWl\Delta^l W^l (the error for the layer below) and (Δl)⊤Al−1(\Delta^l)^\top A^{l-1} (the weight gradient). So the backward pass costs about twice the forward pass, and a full training step about three forward passes. This is the "small constant" of reverse mode in lesson 05. The price is memory: all Al−1A^{l-1} and φ′(Zl)\varphi'(Z^l) stay stored until the backward pass uses them. Activation memory grows with depth and batch size; checkpointing (lesson 05) trades it for recomputation.

    Quick check

    In the batch formulas above (ℓ\ell is the mean loss and Δl=∂ℓ/∂Zl\Delta^l = \partial\ell/\partial Z^l), where does the factor 1/B1/B appear?

    3.4 Theorem 2 (least squares) #

    Now three losses on a linear model. Notation: nn samples with dd features; X∈Rn×dX \in \R^{n \times d} has one sample xi⊤x_i^\top per row; w∈Rdw \in \R^d are the weights; the scores are z=Xwz = Xw; the loss is a mean over the samples. (Here the batch is the whole data set; for a mini-batch replace nn by BB.) We use one fact from the optimization track: a twice continuously differentiable function on Rd\R^d is convex if and only if its Hessian is PSD at every point.

    Theorem 2. L(w)=12n∥Xw−y∥2L(w) = \frac{1}{2n}\|Xw - y\|^2 has

    ∇L(w)=1nX⊤(Xw−y),∇2L=1nX⊤X⪰0.\nabla L(w) = \tfrac1n X^\top (Xw - y), \qquad \nabla^2 L = \tfrac1n X^\top X \succeq 0 .

    So LL is convex. It is strictly convex if and only if XX has full column rank.

    Proof.

    1. Let r=Xw−yr = Xw - y (the residual). For a step h∈Rdh \in \R^d, expand the square in L(w+h)=12n∥r+Xh∥2L(w + h) = \frac{1}{2n}\|r + Xh\|^2. This gives L(w+h)=L(w)+1nr⊤Xh+12nh⊤X⊤XhL(w + h) = L(w) + \tfrac1n r^\top X h + \tfrac{1}{2n} h^\top X^\top X h.
    2. LL is quadratic, so this Taylor expansion (lesson 03) is exact. The linear term is ∇L⊤h\nabla L^\top h, so ∇L=1nX⊤r\nabla L = \frac1n X^\top r. The quadratic term is 12h⊤∇2L h\frac12 h^\top \nabla^2 L\, h, so ∇2L=1nX⊤X\nabla^2 L = \frac1n X^\top X.
    3. h⊤X⊤Xh=∥Xh∥2≥0h^\top X^\top X h = \|Xh\|^2 \ge 0, so the Hessian is PSD and LL is convex.
    4. ∥Xh∥2=0\|Xh\|^2 = 0 exactly for hh in the null space of XX. So the Hessian is positive definite if and only if that null space is {0}\{0\}, that is, XX has full column rank. ■\blacksquare

    ∇L=0\nabla L = 0 gives the normal equations X⊤Xw=X⊤yX^\top X w = X^\top y. Solve them with QR or lstsq, not with (X⊤X)−1(X^\top X)^{-1} (linear-algebra lesson 12). A quadratic equals its own second-order Taylor model, so one Newton step w−(∇2L)−1∇Lw - (\nabla^2 L)^{-1}\nabla L lands exactly on the minimizer.

    3.5 Theorem 3 (logistic regression) #

    Labels yi∈{0,1}y_i \in \{0, 1\} and scores zi=xi⊤wz_i = x_i^\top w. The model says P(y=1)=σ(z)P(y = 1) = \sigma(z). The negative log-likelihood of one sample is ℓ(z)=−ylog⁡σ(z)−(1−y)log⁡(1−σ(z))\ell(z) = -y\log\sigma(z) - (1 - y)\log(1 - \sigma(z)). It simplifies to log⁡(1+ez)−yz\log(1 + e^z) - yz, because log⁡σ(z)=z−log⁡(1+ez)\log\sigma(z) = z - \log(1 + e^z) and log⁡(1−σ(z))=−log⁡(1+ez)\log(1 - \sigma(z)) = -\log(1 + e^z). So

    L(w)=1n∑i=1n[log⁡(1+ezi)−yizi],∇L=1nX⊤(σ(Xw)−y),∇2L=1nX⊤SX,L(w) = \frac1n \sum_{i=1}^n \big[\log(1 + e^{z_i}) - y_i z_i\big],\qquad \nabla L = \frac1n X^\top\big(\sigma(Xw) - y\big),\qquad \nabla^2 L = \frac1n X^\top S X,

    with S=diag⁡(si)S = \operatorname{diag}(s_i) and si=σ(zi)(1−σ(zi))s_i = \sigma(z_i)(1 - \sigma(z_i)). LL is convex, and strictly convex if XX has full column rank.

    Proof.

    1. ddzlog⁡(1+ez)=ez/(1+ez)=σ(z)\frac{d}{dz}\log(1 + e^z) = e^z/(1 + e^z) = \sigma(z). So dℓdz=σ(z)−y\frac{d\ell}{dz} = \sigma(z) - y.
    2. σ′(z)=σ(z)(1−σ(z))\sigma'(z) = \sigma(z)(1 - \sigma(z)) (lesson 01). So d2ℓdz2=s>0\frac{d^2\ell}{dz^2} = s > 0.
    3. Chain rule through z=Xwz = Xw (Jacobian XX): ∇L=1nX⊤(σ(z)−y)\nabla L = \frac1n X^\top(\sigma(z) - y).
    4. The gradient is 1n∑i(σ(zi)−yi) xi\frac1n \sum_i (\sigma(z_i) - y_i)\, x_i. Its Jacobian in ww is 1n∑isi xixi⊤=1nX⊤SX\frac1n \sum_i s_i\, x_i x_i^\top = \frac1n X^\top S X.
    5. For any vv: v⊤∇2L v=1n∑isi(xi⊤v)2≥0v^\top \nabla^2 L\, v = \frac1n \sum_i s_i (x_i^\top v)^2 \ge 0, since every si>0s_i > 0. If XX has full column rank and v≠0v \ne 0, some xi⊤v≠0x_i^\top v \ne 0, so the sum is >0> 0. ■\blacksquare

    Two remarks on the curvature. First, si≤14s_i \le \tfrac14, so ∇2L⪯14nX⊤X⪯λmax⁡(X⊤X)4nI\nabla^2 L \preceq \frac{1}{4n} X^\top X \preceq \frac{\lambda_{\max}(X^\top X)}{4n} I: the loss is smooth, and gradient descent with the fixed step η=4n/λmax⁡(X⊤X)\eta = 4n/\lambda_{\max}(X^\top X) decreases it at every step (the optimization track proves this). Second, si→0s_i \to 0 when ∣zi∣→∞|z_i| \to \infty: the curvature vanishes far from the origin. This matters on separable data.

    Separable data: no minimizer. The data are linearly separable if some wsepw_{\text{sep}} has xi⊤wsep>0x_i^\top w_{\text{sep}} > 0 for every yi=1y_i = 1 and xi⊤wsep<0x_i^\top w_{\text{sep}} < 0 for every yi=0y_i = 0. Along the ray t wsept\,w_{\text{sep}}, each sample loss is log⁡(1+e−t∣xi⊤wsep∣)\log(1 + e^{-t|x_i^\top w_{\text{sep}}|}). It decreases to 0 as t→∞t \to \infty but never reaches 0. So no finite minimizer exists, and gradient descent lets ∥w∥\|w\| grow without bound. Strict convexity says only "at most one minimizer", not "a minimizer exists". An L2 penalty λ2∥w∥2\tfrac\lambda2\|w\|^2 adds λI\lambda I to the Hessian and gives a unique minimizer. (Newton's method on this loss is called IRLS, iteratively reweighted least squares.)

    Interactive figure: loading…

    Drag the weight vector ww or press Run gradient descent. The aqua lines are level sets of the logistic loss L(w)L(w) in the plane of weights; the blue arrow is one gradient-descent step −η∇L(w)-\eta\nabla L(w) with the step η=4n/λmax⁡(X⊤X)\eta = 4n/\lambda_{\max}(X^\top X) of 3.5. The inset shows the data and the decision line w⊤x=0w^\top x = 0. On the data that are not separable, the level sets are closed curves around one minimizer w∗w^*. Switch to separable: the level sets open up, gradient descent runs off along a ray, the loss decreases slowly towards 0, and the Hessian eigenvalues (readout) shrink towards 0.

    Stable computation. log⁡(1+e800)\log(1 + e^{800}) overflows to inf in float64: eze^z overflows above z≈709.8z \approx 709.8 (in float32 above z≈88.7z \approx 88.7). Use the softplus form log⁡(1+ez)=max⁡(z,0)+log⁡(1+e−∣z∣)\log(1 + e^z) = \max(z, 0) + \log(1 + e^{-|z|}) (np.logaddexp(0, z)): the exponent is never positive. Compute σ(z)\sigma(z) as 1/(1+e−z)1/(1 + e^{-z}) for z≥0z \ge 0 and as ez/(1+ez)e^z/(1 + e^z) for z<0z < 0.

    Quick check

    Why is the logistic-regression Hessian 1nX⊤SX\frac1n X^\top S X positive semidefinite?

    3.6 Theorem 4 (softmax cross-entropy) #

    One sample: logits z∈RCz \in \R^C, true class yy, pk=ezk/∑jezjp_k = e^{z_k}/\sum_j e^{z_j}. Write lse⁡(z)=log⁡∑kezk\operatorname{lse}(z) = \log\sum_k e^{z_k} (the log-sum-exp function). Then

    ℓ(z)=−log⁡py=lse⁡(z)−zy,∇zℓ=p−ey,∇z2ℓ=diag⁡(p)−pp⊤⪰0.\ell(z) = -\log p_y = \operatorname{lse}(z) - z_y, \qquad \nabla_z \ell = p - e_y, \qquad \nabla_z^2 \ell = \operatorname{diag}(p) - pp^\top \succeq 0 .

    For a batch with logits Z∈RB×CZ \in \R^{B \times C} (one row zbz_b per sample), labels yby_b and the mean loss L=1B∑bℓ(zb)L = \frac1B \sum_b \ell(z_b):

    ∂L∂Z=1B(P−Y),\frac{\partial L}{\partial Z} = \frac{1}{B}(P - Y),

    where PP is the row-wise softmax of ZZ and YY holds the one-hot labels as rows.

    Proof.

    1. ∂lse⁡/∂zj=ezj/∑kezk=pj\partial \operatorname{lse}/\partial z_j = e^{z_j}/\sum_k e^{z_k} = p_j: the gradient of log-sum-exp is the softmax.
    2. ∂(−zy)/∂zj=−1\partial(-z_y)/\partial z_j = -1 if j=yj = y and 0 otherwise. So ∇zℓ=p−ey\nabla_z \ell = p - e_y.
    3. Row bb of ZZ enters only the term ℓ(zb)\ell(z_b), with the factor 1/B1/B. So row bb of ∂L/∂Z\partial L/\partial Z is (pb−eyb)⊤/B(p_b - e_{y_b})^\top/B. This is the matrix formula.
    4. The Hessian is the Jacobian of pp: ∂pi/∂zj=pi([i=j]−pj)\partial p_i/\partial z_j = p_i([i = j] - p_j), that is, diag⁡(p)−pp⊤\operatorname{diag}(p) - pp^\top (linear-algebra lesson 16).
    5. For any vv: v⊤(diag⁡(p)−pp⊤)vv^\top(\operatorname{diag}(p) - pp^\top)v equals ∑kpkvk2−(∑kpkvk)2\sum_k p_k v_k^2 - \big(\sum_k p_k v_k\big)^2. This is the variance of vkv_k when the index kk is drawn with probabilities pp. So it is ≥0\ge 0, and ℓ\ell is convex in zz. It is 0 for v=1v = \mathbf 1: adding a constant to all logits changes nothing. ■\blacksquare

    Properties you will use. The entries of p−eyp - e_y sum to 0 (shift invariance). Each entry lies in [−1,1][-1, 1], however large the logits are. For softmax regression, Z=XWZ = XW with W∈Rd×CW \in \R^{d \times C}, the VJP of a linear map gives ∂L/∂W=X⊤(P−Y)/B\partial L/\partial W = X^\top (P - Y)/B.

    Stable computation. Let m=max⁡kzkm = \max_k z_k. Since ∑kezk=em∑kezk−m\sum_k e^{z_k} = e^m \sum_k e^{z_k - m},

    lse⁡(z)=m+log⁡∑kezk−m.\operatorname{lse}(z) = m + \log \sum_k e^{z_k - m} .

    Every exponent is ≤0\le 0, so nothing overflows. The largest term is e0=1e^0 = 1, so the sum is ≥1\ge 1 and its log is never log⁡0\log 0. Then compute ℓ=lse⁡(z)−zy\ell = \operatorname{lse}(z) - z_y (this is −-log_softmax). Do not compute −log⁡py-\log p_y from the probabilities: even a stable softmax gives py=0p_y = 0 when zyz_y is more than about 745 below the largest logit (e−746e^{-746} rounds to 0 in float64), and then −log⁡0=∞-\log 0 = \infty. This is why torch.nn.functional.cross_entropy takes logits, not probabilities. (See linear-algebra lesson 13 for overflow and underflow.)

    Answer to question 2 of section 1. For y=1y = 1: e1000e^{1000} overflows to inf, and inf/inf is nan, so the loss is nan. For y=2y = 2: p2=1/∞=0p_2 = 1/\infty = 0 and −log⁡0=∞-\log 0 = \infty. The true losses are log⁡(1+e−1000)≈0\log(1 + e^{-1000}) \approx 0 and 1000+log⁡(1+e−1000)≈10001000 + \log(1 + e^{-1000}) \approx 1000. The log-sum-exp form gives both.

    3.7 Theorem 5 (layer normalization backward) #

    Layer normalization acts on one row x∈RDx \in \R^D (the DD features of one token). In this section σ\sigma is a standard deviation, not the sigmoid:

    μ=1D∑ixi,v=1D∑i(xi−μ)2,σ=v+ε,x^=(x−μ1)/σ,y=γ⊙x^+β.\mu = \tfrac1D \textstyle\sum_i x_i,\quad v = \tfrac1D \sum_i (x_i - \mu)^2,\quad \sigma = \sqrt{v + \varepsilon},\quad \hat x = (x - \mu\mathbf 1)/\sigma,\quad y = \gamma \odot \hat x + \beta .

    vv is the biased variance (divide by DD), as in PyTorch's layer_norm. ε≥0\varepsilon \ge 0 is a small constant (PyTorch default 10−510^{-5}); we need v+ε>0v + \varepsilon > 0. γ,β∈RD\gamma, \beta \in \R^D are learned.

    The picture: two invariances. Adding a constant cc to every entry of xx does not change x^\hat x. So a step along 1\mathbf 1 does not change the loss, and the gradient has no component along 1\mathbf 1: its entries sum to 0. With ε=0\varepsilon = 0, multiplying xx by a positive number does not change x^\hat x either, so the gradient also has no component along x−μ1x - \mu\mathbf 1 (that is, along x^\hat x). The backward pass removes these two components from the incoming gradient.

    Theorem 5. Let g=∂L/∂yg = \partial L/\partial y be the upstream gradient and g^=γ⊙g\hat g = \gamma \odot g. Then

    ∂L∂β=∑rowsg,∂L∂γ=∑rowsg⊙x^,∂L∂x=1σ(g^−mean⁡(g^) 1−x^ mean⁡(g^⊙x^)),\frac{\partial L}{\partial \beta} = \sum_{\text{rows}} g, \qquad \frac{\partial L}{\partial \gamma} = \sum_{\text{rows}} g \odot \hat x, \qquad \frac{\partial L}{\partial x} = \frac1\sigma\Big(\hat g - \operatorname{mean}(\hat g)\,\mathbf 1 - \hat x\, \operatorname{mean}(\hat g \odot \hat x)\Big),

    where the last formula holds for each row, and mean averages over the DD features.

    Proof of Theorem 5 (two simple maps and the chain rule)
    1. Affine part. yj=γjx^j+βjy_j = \gamma_j \hat x_j + \beta_j. So for one row ∂L/∂βj=gj\partial L/\partial \beta_j = g_j, ∂L/∂γj=gjx^j\partial L/\partial \gamma_j = g_j \hat x_j and ∂L/∂x^=γ⊙g=g^\partial L/\partial \hat x = \gamma \odot g = \hat g. The same γ,β\gamma, \beta act on every row, so their gradients are summed over the rows (fan-out, lesson 04).
    2. Split the rest. x^=N(Πx)\hat x = N(\Pi x), with the centering matrix Π=I−1D11⊤\Pi = I - \tfrac1D \mathbf 1 \mathbf 1^\top (so Πx=x−μ1\Pi x = x - \mu \mathbf 1) and N(c)=c/ρ(c)N(c) = c/\rho(c), ρ(c)=∥c∥2/D+ε\rho(c) = \sqrt{\|c\|^2/D + \varepsilon}. At c=Πxc = \Pi x we have ∥c∥2/D=v\|c\|^2/D = v, so ρ=σ\rho = \sigma and c/σ=x^c/\sigma = \hat x.
    3. Jacobian of NN. ∂ρ/∂cj=cj/(Dρ)\partial \rho/\partial c_j = c_j/(D\rho). So by the quotient rule ∂Ni/∂cj=[i=j]/ρ−cicj/(Dρ3)\partial N_i/\partial c_j = [i = j]/\rho - c_i c_j/(D\rho^3). In matrix form, JN=1σ(I−1Dx^x^⊤)J_N = \tfrac1\sigma\big(I - \tfrac1D \hat x \hat x^\top\big), a symmetric matrix.
    4. VJP through NN. u=JN⊤g^=1σ(g^−x^ (x^⊤g^)/D)u = J_N^\top \hat g = \tfrac1\sigma\big(\hat g - \hat x\,(\hat x^\top \hat g)/D\big). Since (x^⊤g^)/D=mean⁡(g^⊙x^)(\hat x^\top \hat g)/D = \operatorname{mean}(\hat g \odot \hat x), this is u=1σ(g^−x^ mean⁡(g^⊙x^))u = \tfrac1\sigma\big(\hat g - \hat x\, \operatorname{mean}(\hat g \odot \hat x)\big).
    5. VJP through Π\Pi. Π\Pi is symmetric and Πu=u−mean⁡(u)1\Pi u = u - \operatorname{mean}(u)\mathbf 1. Since mean⁡(x^)=0\operatorname{mean}(\hat x) = 0, we get mean⁡(u)=mean⁡(g^)/σ\operatorname{mean}(u) = \operatorname{mean}(\hat g)/\sigma. So ∂L/∂x=Πu\partial L/\partial x = \Pi u is the formula. ■\blacksquare

    Checks.

    • Row sums: sum the formula over jj. The first two terms give ∑jg^j−Dmean⁡(g^)=0\sum_j \hat g_j - D\operatorname{mean}(\hat g) = 0. The third gives mean⁡(g^⊙x^)∑jx^j=0\operatorname{mean}(\hat g \odot \hat x)\sum_j \hat x_j = 0, because ∑jx^j=0\sum_j \hat x_j = 0. So ∑j∂L/∂xj=0\sum_j \partial L/\partial x_j = 0, for every ε\varepsilon.
    • Orthogonality to x^\hat x: using ∥x^∥2=Dv/(v+ε)\|\hat x\|^2 = Dv/(v + \varepsilon), one finds that x^⊤∂L/∂x\hat x^\top \partial L/\partial x equals 1σ(x^⊤g^)\tfrac1\sigma (\hat x^\top \hat g) times ε/(v+ε)\varepsilon/(v + \varepsilon). This is exactly 0 for ε=0\varepsilon = 0 (scale invariance) and small when v≫εv \gg \varepsilon. When the row variance vv is close to ε\varepsilon, it is not small.

    Cost. O(D)O(D) per row. Cache x^\hat x and 1/σ1/\sigma from the forward pass. Never build the D×DD \times D Jacobian.

    Answer to question 3 of section 1. ∑j∂L/∂xj=0\sum_j \partial L/\partial x_j = 0. Layer norm of x+c1x + c\mathbf 1 equals layer norm of xx, so 0=ddcL(x+c1)∣c=0=1⊤∇xL0 = \frac{d}{dc} L(x + c\mathbf 1)\big|_{c=0} = \mathbf 1^\top \nabla_x L.

    Interactive figure: loading…

    Compare the full backward pass with a naive one. The grey bars are g^=γ⊙g\hat g = \gamma \odot g, which is what you get (times 1/σ1/\sigma) if you treat μ\mu and σ\sigma as constants. The orange bars are the true gradient, times σ\sigma. The true one always sums to 0, and it is orthogonal to x^\hat x while the row variance is much larger than ε=10−5\varepsilon = 10^{-5}. Move the scale slider down until the variance is close to ε\varepsilon and watch the cosine readout.

    Quick check

    Layer normalization (with ε=10−5\varepsilon = 10^{-5}) gets a row x∈R5x \in \R^5, and the backward pass returns dx=∂L/∂xdx = \partial L/\partial x. What is dx1+dx2+⋯+dx5dx_1 + dx_2 + \dots + dx_5?

    3.8 Vanishing and exploding gradients #

    The unrolled form of Theorem 1 shows that δ1\delta^1 is δL\delta^L multiplied by L−1L - 1 factors Jl⊤=diag⁡(φ′(zl)) (Wl+1)⊤J_l^\top = \operatorname{diag}(\varphi'(z^l))\,(W^{l+1})^\top. Norms of products are at most products of norms, so

    ∥δ1∥≤(∏l=1L−1∥diag⁡(φ′(zl))∥ ∥Wl+1∥) ∥δL∥.\|\delta^1\| \le \Big(\prod_{l=1}^{L-1} \|\operatorname{diag}(\varphi'(z^l))\|\,\|W^{l+1}\|\Big)\,\|\delta^L\| .

    If every factor has norm at most q<1q < 1, then ∥δ1∥≤qL−1∥δL∥\|\delta^1\| \le q^{L-1}\|\delta^L\|: the gradient of the first layers shrinks exponentially with depth (vanishing gradients). The sigmoid has σ′≤1/4\sigma' \le 1/4, so a deep sigmoid network vanishes unless the weights are large. When the factors stretch vectors on average by more than 1, the norm typically grows exponentially (exploding gradients). Careful initialization (variance 1/n1/n for tanh, 2/n2/n for ReLU), residual connections (y=x+F(x)y = x + F(x) adds an identity path, so its Jacobian is I+DFI + DF) and normalization layers keep this product near norm 1. The deep learning track treats these methods in detail.

    Interactive figure: loading…

    Change the depth, the activation and the weight scale. The orange line is the norm of the error signal ∥δl∥\|\delta^l\| at each layer of a random MLP of width 64, on a log scale; the backward pass starts at the right with ∥δL∥=1\|\delta^L\| = 1. With tanh and scale 1 the norm decreases slowly; with scale 0.5 it vanishes fast. With ReLU, scale 2≈1.41\sqrt 2 \approx 1.41 (He initialization) keeps it nearly flat, and scale 2 explodes. The sigmoid vanishes even at scale 1.

    4. Worked example #

    (a) One sample through a small MLP. Two inputs, three hidden ReLU units, one output, squared loss:

    x=(1−1),W1=(21130−1),b1=0,W2=(1−12),b2=0.5,t=1.5,x = \begin{pmatrix}1\\-1\end{pmatrix},\quad W^1 = \begin{pmatrix}2&1\\1&3\\0&-1\end{pmatrix},\quad b^1 = 0,\quad W^2 = \begin{pmatrix}1&-1&2\end{pmatrix},\quad b^2 = 0.5,\quad t = 1.5,

    with ℓ=12(z2−t)2\ell = \tfrac12(z^2 - t)^2 and φ=ReLU⁡\varphi = \operatorname{ReLU}, so φ′(z)=1[z>0]\varphi'(z) = \mathbf 1[z > 0].

    Forward. Row by row, z1=W1xz^1 = W^1 x is

    z1=(2−1, 1−3, 0+1)=(1,−2,1),a1=(1,0,1).z^1 = (2 - 1,\ 1 - 3,\ 0 + 1) = (1, -2, 1), \qquad a^1 = (1, 0, 1).

    Unit 2 is off. Then z2=W2a1+b2=1−0+2+0.5=3.5z^2 = W^2 a^1 + b^2 = 1 - 0 + 2 + 0.5 = 3.5 and ℓ=12(3.5−1.5)2=2\ell = \tfrac12(3.5 - 1.5)^2 = 2.

    Backward.

    1. Output error: δ2=z2−t=2\delta^2 = z^2 - t = 2.
    2. Gradient of W2W^2: δ2(a1)⊤=2 (1,0,1)=(2,0,2)\delta^2 (a^1)^\top = 2\,(1, 0, 1) = (2, 0, 2). Also ∂ℓ/∂b2=2\partial\ell/\partial b^2 = 2.
    3. Pass the error down: (W2)⊤δ2=(2,−2,4)(W^2)^\top\delta^2 = (2, -2, 4). The mask φ′(z1)=(1,0,1)\varphi'(z^1) = (1, 0, 1) gives δ1=(2,0,4)\delta^1 = (2, 0, 4).
    4. Gradient of W1W^1 (an outer product), and ∂ℓ/∂b1=δ1=(2,0,4)\partial\ell/\partial b^1 = \delta^1 = (2, 0, 4):
    ∂ℓ∂W1=δ1x⊤=(204)(1−1)=(2−2004−4).\frac{\partial\ell}{\partial W^1} = \delta^1 x^\top = \begin{pmatrix}2\\0\\4\end{pmatrix}\begin{pmatrix}1 & -1\end{pmatrix} = \begin{pmatrix}2&-2\\0&0\\4&-4\end{pmatrix}.
    1. Gradient of the input:
    ∂ℓ∂x=(W1)⊤δ1=(2⋅2+1⋅0+0⋅41⋅2+3⋅0−1⋅4)=(4−2).\frac{\partial\ell}{\partial x} = (W^1)^\top\delta^1 = \begin{pmatrix} 2\cdot 2 + 1\cdot 0 + 0\cdot 4 \\ 1\cdot 2 + 3\cdot 0 - 1\cdot 4 \end{pmatrix} = \begin{pmatrix} 4 \\ -2 \end{pmatrix}.

    The row of W1W^1 for the dead unit gets a zero gradient. Each weight gradient has rank 1.

    The same result as a Jacobian product. The row vector ∇xℓ⊤\nabla_x\ell^\top is the product of the local Jacobians, from the loss back to the input:

    ∇xℓ⊤=2⏟δ2 (1,−1,2)⏟W2 diag⁡(1,0,1)⏟ReLU W1⏟3×2=2 (1,0,2) W1=2 (2,−1)=(4,−2).\nabla_x\ell^\top = \underbrace{2}_{\delta^2}\ \underbrace{(1, -1, 2)}_{W^2}\ \underbrace{\operatorname{diag}(1, 0, 1)}_{\text{ReLU}}\ \underbrace{W^1}_{3\times 2} = 2\,(1, 0, 2)\,W^1 = 2\,(2, -1) = (4, -2).

    (b) A batch of two. Add x′=(0,1)x' = (0, 1) with target t′=−0.5t' = -0.5.

    Forward for sample 2. z1′=(1,3,−1)z^{1\prime} = (1, 3, -1), a1′=(1,3,0)a^{1\prime} = (1, 3, 0), z2′=1−3+0+0.5=−1.5z^{2\prime} = 1 - 3 + 0 + 0.5 = -1.5 and ℓ′=12(−1)2=0.5\ell' = \tfrac12(-1)^2 = 0.5. The mean loss is (2+0.5)/2=1.25(2 + 0.5)/2 = 1.25.

    Backward for the batch (one row per sample):

    Δ2=12(3.5−1.5−1.5+0.5)=(1−0.5),∂ℓ∂W2=(Δ2)⊤A1=1⋅(1,0,1)−0.5⋅(1,3,0)=(0.5,−1.5,1),\Delta^2 = \frac12 \begin{pmatrix} 3.5 - 1.5 \\ -1.5 + 0.5 \end{pmatrix} = \begin{pmatrix} 1 \\ -0.5 \end{pmatrix}, \qquad \frac{\partial\ell}{\partial W^2} = (\Delta^2)^\top A^1 = 1\cdot(1, 0, 1) - 0.5\cdot(1, 3, 0) = (0.5, -1.5, 1),

    and ∂ℓ/∂b2=1−0.5=0.5\partial\ell/\partial b^2 = 1 - 0.5 = 0.5. Then

    Δ1=(Δ2W2)⊙1[Z1>0]=(1−12−0.50.5−1)⊙(101110)=(102−0.50.50),\Delta^1 = (\Delta^2 W^2) \odot \mathbf 1[Z^1 > 0] = \begin{pmatrix}1&-1&2\\-0.5&0.5&-1\end{pmatrix} \odot \begin{pmatrix}1&0&1\\1&1&0\end{pmatrix} = \begin{pmatrix}1&0&2\\-0.5&0.5&0\end{pmatrix},
    ∂ℓ∂W1=(Δ1)⊤X=(1−0.500.520)(1−101)=(1−1.500.52−2),\frac{\partial\ell}{\partial W^1} = (\Delta^1)^\top X = \begin{pmatrix}1&-0.5\\0&0.5\\2&0\end{pmatrix}\begin{pmatrix}1&-1\\0&1\end{pmatrix} = \begin{pmatrix}1&-1.5\\0&0.5\\2&-2\end{pmatrix},

    and ∂ℓ/∂b1=(0.5,0.5,2)\partial\ell/\partial b^1 = (0.5, 0.5, 2) (the column sums of Δ1\Delta^1).

    Check. Sample 2 alone gives δ1′=(−1,1,0)\delta^{1\prime} = (-1, 1, 0). The mean of its outer product δ1′x′⊤\delta^{1\prime}x'^\top and the sample-1 gradient from (a) is the same matrix:

    12((2−2004−4)+(0−10100))=(1−1.500.52−2).\frac12\left(\begin{pmatrix}2&-2\\0&0\\4&-4\end{pmatrix} + \begin{pmatrix}0&-1\\0&1\\0&0\end{pmatrix}\right) = \begin{pmatrix}1&-1.5\\0&0.5\\2&-2\end{pmatrix}.

    (c) Softmax cross-entropy for a batch, B=2B = 2, C=3C = 3.

    • Row 1: z1=(ln⁡4,ln⁡2,ln⁡2)z_1 = (\ln 4, \ln 2, \ln 2) and y1=2y_1 = 2. Then ez1=(4,2,2)e^{z_1} = (4, 2, 2), p1=(1/2,1/4,1/4)p_1 = (1/2, 1/4, 1/4) and ℓ1=−log⁡14=1.3863\ell_1 = -\log\tfrac14 = 1.3863.
    • Row 2: z2=(0,0,0)z_2 = (0, 0, 0) and y2=3y_2 = 3. Then p2=(1/3,1/3,1/3)p_2 = (1/3, 1/3, 1/3) and ℓ2=log⁡3=1.0986\ell_2 = \log 3 = 1.0986.
    • The mean loss is L=(1.3863+1.0986)/2=1.2425L = (1.3863 + 1.0986)/2 = 1.2425, and
    ∂L∂Z=12(P−Y)=12(0.5−0.750.250.33330.3333−0.6667)=(0.25−0.3750.1250.16670.1667−0.3333).\frac{\partial L}{\partial Z} = \frac12 (P - Y) = \frac12\begin{pmatrix} 0.5 & -0.75 & 0.25 \\ 0.3333 & 0.3333 & -0.6667 \end{pmatrix} = \begin{pmatrix} 0.25 & -0.375 & 0.125 \\ 0.1667 & 0.1667 & -0.3333 \end{pmatrix}.

    Each row sums to 0. The Hessian of ℓ1\ell_1 is

    diag⁡(p1)−p1p1⊤=(0.25−0.125−0.125−0.1250.1875−0.0625−0.125−0.06250.1875),\operatorname{diag}(p_1) - p_1p_1^\top = \begin{pmatrix} 0.25 & -0.125 & -0.125 \\ -0.125 & 0.1875 & -0.0625 \\ -0.125 & -0.0625 & 0.1875 \end{pmatrix},

    with eigenvalues 00, 0.250.25 and 0.3750.375. The zero eigenvalue has the eigenvector 1\mathbf 1.

    (d) Layer norm, D=4D = 4, ε=0\varepsilon = 0 (to keep the numbers exact), x=(1,1,5,5)x = (1, 1, 5, 5), γ=(1,1,2,1)\gamma = (1, 1, 2, 1), β=0\beta = 0, upstream g=(1,2,1,0)g = (1, 2, 1, 0).

    • Forward: μ=3\mu = 3, v=(4+4+4+4)/4=4v = (4 + 4 + 4 + 4)/4 = 4, σ=2\sigma = 2, x^=(−1,−1,1,1)\hat x = (-1, -1, 1, 1).
    • ∂L/∂β=g=(1,2,1,0)\partial L/\partial \beta = g = (1, 2, 1, 0).
    • ∂L/∂γ=g⊙x^=(−1,−2,1,0)\partial L/\partial \gamma = g \odot \hat x = (-1, -2, 1, 0).
    • g^=γ⊙g=(1,2,2,0)\hat g = \gamma \odot g = (1, 2, 2, 0), so mean⁡(g^)=5/4\operatorname{mean}(\hat g) = 5/4 and mean⁡(g^⊙x^)=(−1−2+2+0)/4=−1/4\operatorname{mean}(\hat g \odot \hat x) = (-1 - 2 + 2 + 0)/4 = -1/4.
    • The bracket of Theorem 5 and the result:
    g^−541+14x^=(−0.25,0.75,0.75,−1.25)+(−0.25,−0.25,0.25,0.25)=(−0.5,0.5,1,−1),\hat g - \tfrac54 \mathbf 1 + \tfrac14 \hat x = (-0.25, 0.75, 0.75, -1.25) + (-0.25, -0.25, 0.25, 0.25) = (-0.5, 0.5, 1, -1),
    ∂L∂x=(−0.5,0.5,1,−1)2=(−0.25,0.25,0.5,−0.5).\frac{\partial L}{\partial x} = \frac{(-0.5, 0.5, 1, -1)}{2} = (-0.25, 0.25, 0.5, -0.5).

    Checks: the entries sum to 0, and x^⋅∂L/∂x=0.25−0.25+0.5−0.5=0\hat x \cdot \partial L/\partial x = 0.25 - 0.25 + 0.5 - 0.5 = 0.

    5. In code #

    NumPy checks examples (b), (c) and (d) with hand-written backward passes. The comments show the printed output.

    import numpy as np
    from scipy.special import logsumexp
    
    # (a) + (b): the 2-3-1 ReLU network of section 4, batch form (one sample per row)
    W1 = np.array([[2., 1.], [1., 3.], [0., -1.]]); b1 = np.zeros(3)
    W2 = np.array([[1., -1., 2.]]);                 b2 = np.array([0.5])
    X = np.array([[1., -1.], [0., 1.]]);  T = np.array([[1.5], [-0.5]])
    B = len(X)
    
    Z1 = X @ W1.T + b1;  A1 = np.maximum(Z1, 0)        # forward, store Z1 and A1
    Z2 = A1 @ W2.T + b2; L = 0.5 * np.mean(np.sum((Z2 - T) ** 2, axis=1))
    D2 = (Z2 - T) / B                                    # Delta^2: the only 1/B
    dW2, db2 = D2.T @ A1, D2.sum(0)
    D1 = (D2 @ W2) * (Z1 > 0)                            # Delta^1
    dW1, db1 = D1.T @ X, D1.sum(0)
    print(L, dW2, db2)        # 1.25 [[ 0.5 -1.5  1. ]] [0.5]
    print(dW1.tolist())       # [[1.0, -1.5], [0.0, 0.5], [2.0, -2.0]]
    
    # (c) softmax cross-entropy: (P - Y) / B, computed with log-sum-exp
    Z = np.array([[np.log(4), np.log(2), np.log(2)], [0., 0., 0.]])
    y = np.array([1, 2])                                  # classes 2 and 3 (code counts from 0)
    lse = logsumexp(Z, axis=1)
    loss = np.mean(lse - Z[np.arange(2), y])
    P = np.exp(Z - lse[:, None]); Y = np.eye(3)[y]
    print(round(loss, 4), ((P - Y) / 2).round(4).tolist())
    # 1.2425 [[0.25, -0.375, 0.125], [0.1667, 0.1667, -0.3333]]
    
    # naive vs stable at z = (1000, 0)
    z = np.array([1000., 0.])
    with np.errstate(over="ignore", invalid="ignore", divide="ignore"):
        print(-np.log(np.exp(z) / np.exp(z).sum()))       # [nan inf]
    print(logsumexp(z) - z)                               # [   0. 1000.]
    
    # (d) layer norm backward (eps = 0), and its two checks
    def layernorm_backward(x, gamma, g, eps=0.0):
        mu, var = x.mean(), x.var()                       # biased variance, as in PyTorch
        sigma = np.sqrt(var + eps); xh = (x - mu) / sigma
        gh = gamma * g
        return (gh - gh.mean() - xh * np.mean(gh * xh)) / sigma, xh
    
    x = np.array([1., 1., 5., 5.])
    gamma = np.array([1., 1., 2., 1.])
    g = np.array([1., 2., 1., 0.])                        # upstream gradient dL/dy
    dx, xh = layernorm_backward(x, gamma, g)
    print(dx, dx.sum(), xh @ dx)                          # [-0.25  0.25  0.5  -0.5 ] 0.0 0.0
    

    In PyTorch, autograd gives the same numbers. Note the weight layout of nn.Linear (out, in) and that cross_entropy takes logits:

    import torch
    import torch.nn.functional as F
    torch.set_default_dtype(torch.float64)
    
    # The same 2-3-1 network as nn.Linear layers: weight shape (out, in), Z = A W^T + b.
    net = torch.nn.Sequential(torch.nn.Linear(2, 3), torch.nn.ReLU(), torch.nn.Linear(3, 1))
    with torch.no_grad():
        net[0].weight.copy_(torch.tensor([[2., 1.], [1., 3.], [0., -1.]])); net[0].bias.zero_()
        net[2].weight.copy_(torch.tensor([[1., -1., 2.]]));               net[2].bias.fill_(0.5)
    X = torch.tensor([[1., -1.], [0., 1.]]); T = torch.tensor([[1.5], [-0.5]])
    loss = 0.5 * F.mse_loss(net(X), T)       # mse_loss is the mean of squares
    loss.backward()
    print(loss.item(), net[0].weight.grad.tolist())
    # 1.25 [[1.0, -1.5], [0.0, 0.5], [2.0, -2.0]]
    
    # Softmax cross-entropy takes LOGITS; its gradient is (P - Y) / B.
    Z = torch.tensor([[1000., 0., 0.], [0., 0., 0.]], requires_grad=True)
    ce = F.cross_entropy(Z, torch.tensor([1, 2]))       # finite: log-sum-exp inside
    ce.backward()
    print(round(ce.item(), 4), Z.grad.numpy().round(4).tolist())
    # 500.5493 [[0.5, -0.5, 0.0], [0.1667, 0.1667, -0.3333]]
    

    The loss of the first row is lse⁡(1000,0,0)−0≈1000\operatorname{lse}(1000, 0, 0) - 0 \approx 1000, finite, and its gradient row is (p−e2)/2≈(1,−1,0)/2(p - e_2)/2 \approx (1, -1, 0)/2.

    6. Where it shows up #

    • Every autodiff framework. Each operation in PyTorch or JAX has a VJP rule like the ones in this lesson, and backward() runs them in reverse order. A custom torch.autograd.Function asks you to write the VJP by hand. Fused kernels (for example for LayerNorm or cross-entropy) use the closed forms of Theorems 4 and 5 to save memory and time.
    • Classifiers and language models. The logit gradient of every classifier and LLM is (P−Y)/B(P - Y)/B. With a vocabulary of 10510^5 tokens, PP is the largest tensor of the step, so memory-efficient kernels compute lse⁡\operatorname{lse} and p−eyp - e_y in chunks of the vocabulary.
    • Soft targets. For any target distribution qq (∑kqk=1\sum_k q_k = 1), the loss −∑kqklog⁡pk=lse⁡(z)−q⊤z-\sum_k q_k \log p_k = \operatorname{lse}(z) - q^\top z has the gradient p−qp - q. This covers label smoothing and knowledge distillation. With a temperature TT (logits z/Tz/T) the gradient is (softmax⁡(z/T)−q)/T(\operatorname{softmax}(z/T) - q)/T. When qq is the teacher's softmax at the same temperature, this scales like 1/T21/T^2 for large TT, so distillation losses are often multiplied by T2T^2.
    • Outer-product structure. A per-sample weight gradient δa⊤\delta a^\top has rank 1 and Frobenius norm ∥δ∥ ∥a∥\|\delta\|\,\|a\|. So per-sample gradient norms (needed for clipping in differentially private SGD) cost almost nothing. A batch gradient has rank at most BB. K-FAC approximates the curvature of a layer with the Kronecker factors E[aa⊤]\mathbb E[aa^\top] and E[δδ⊤]\mathbb E[\delta\delta^\top].
    • Newton, Fisher and Gauss–Newton. The logistic Hessian 1nX⊤SX\frac1n X^\top S X does not depend on yy, so it equals the Fisher information. For a nonlinear model with these losses, the Gauss–Newton matrix J⊤∇z2ℓ JJ^\top \nabla^2_z \ell\, J uses exactly the score Hessians of this lesson: II, SS or diag⁡(p)−pp⊤\operatorname{diag}(p) - pp^\top (lesson 08).
    • ML for science. Variational data assimilation and seismic inversion minimize a weighted least squares loss J(u)=12(Gu−d)⊤Σo−1(Gu−d)J(u) = \tfrac12 (Gu - d)^\top \Sigma_o^{-1}(Gu - d) + 12(u−ub)⊤Σb−1(u−ub)+\ \tfrac12 (u - u_b)^\top \Sigma_b^{-1}(u - u_b) (observation operator GG, data dd, background state ubu_b, covariances Σo\Sigma_o and Σb\Sigma_b). The proof of Theorem 2 gives ∇J=G⊤Σo−1(Gu−d)+Σb−1(u−ub)\nabla J = G^\top \Sigma_o^{-1}(Gu - d) + \Sigma_b^{-1}(u - u_b). For a nonlinear GG, the transposed Jacobian DG(u)⊤DG(u)^\top is the adjoint model: one backward run gives the whole gradient. LayerNorm (or its variant RMSNorm) is in every block of transformer-based weather and Earth-system models.

    7. Common mistakes #

    • Missing transposes. The recursion uses (Wl+1)⊤δl+1(W^{l+1})^\top\delta^{l+1}, not Wl+1δl+1W^{l+1}\delta^{l+1}, and ∂ℓ/∂Wl=δl(al−1)⊤\partial\ell/\partial W^l = \delta^l(a^{l-1})^\top has the shape of WlW^l. Shapes catch both mistakes, unless the layer is square.
    • The mask of the wrong layer. δl\delta^l uses φ′(zl)\varphi'(z^l), the pre-activation of layer ll itself, not φ′(zl+1)\varphi'(z^{l+1}).
    • Mixing the two layouts. z=Waz = Wa (one column per sample) and Z=AW⊤Z = AW^\top or Z=AWZ = AW (one row per sample) give transposed formulas. Derive in one layout, then check shapes in the other.
    • 1/B1/B twice, or not at all. It enters once, in ΔL\Delta^L. Then ∂ℓ/∂b\partial\ell/\partial b is a sum over the batch, not another mean. A sum instead of a mean makes the gradient BB times larger.
    • Naive logs and exps. log⁡(1+ez)\log(1 + e^z) and log⁡∑kezk\log\sum_k e^{z_k} overflow when an exponent is above about 710 in float64 (about 89 in float32). Use logaddexp, softplus, log_sigmoid, logsumexp.
    • −log⁡-\log of a probability. Even a stable softmax underflows for unlikely classes. Use lse⁡(z)−zy\operatorname{lse}(z) - z_y; this is why cross_entropy takes logits.
    • Sigmoid plus squared error for classification. 12(σ(z)−y)2\tfrac12(\sigma(z) - y)^2 has the gradient (σ−y) σ(1−σ)(\sigma - y)\,\sigma(1 - \sigma), which vanishes when the model is confidently wrong. At z=−5z = -5 and y=1y = 1 it is −0.0066-0.0066; cross-entropy gives σ−y=−0.9933\sigma - y = -0.9933, about 150 times larger.
    • Layer norm with μ\mu and σ\sigma treated as constants. That gives dx=g^/σdx = \hat g/\sigma and misses two terms. Test: the entries of dxdx must sum to 0.
    • Biased vs unbiased variance. Layer norm uses the biased variance. np.var is biased by default, but torch.var is unbiased by default (correction=1).
    • Forgetting to sum dγd\gamma and dβd\beta over all rows (all tokens of all sequences in the batch).
    • "Strictly convex, so a minimizer exists." Not on separable data: strict convexity gives at most one minimizer, not existence.

    8. Check yourself #

    1. ℓ=12∥σ(Wx+b)−t∥2\ell = \tfrac12\|\sigma(Wx + b) - t\|^2 with the elementwise sigmoid σ\sigma. Give δ=∂ℓ/∂z\delta = \partial\ell/\partial z for z=Wx+bz = Wx + b, and ∂ℓ/∂W\partial\ell/\partial W, ∂ℓ/∂b\partial\ell/\partial b, ∂ℓ/∂x\partial\ell/\partial x.
      Answerδ=(σ(z)−t)⊙σ(z)⊙(1−σ(z))\delta = (\sigma(z) - t)\odot\sigma(z)\odot(1 - \sigma(z)), using σ′=σ(1−σ)\sigma' = \sigma(1 - \sigma). Then ∂ℓ/∂W=δx⊤\partial\ell/\partial W = \delta x^\top, ∂ℓ/∂b=δ\partial\ell/\partial b = \delta and ∂ℓ/∂x=W⊤δ\partial\ell/\partial x = W^\top\delta.
    2. A batch of B=8B = 8 samples passes through a layer with Wl∈R512×256W^l \in \R^{512\times256}. What is the largest possible rank of ∂ℓ/∂Wl\partial\ell/\partial W^l?
      Answer8. (Δl)⊤Al−1(\Delta^l)^\top A^{l-1} has inner dimension BB: it is a sum of 8 outer products, so its rank is at most min⁡(8,512,256)=8\min(8, 512, 256) = 8.
    3. A residual block computes y=x+F(x)y = x + F(x), and the loss depends on xx only through yy. With yˉ=∂ℓ/∂y\bar y = \partial\ell/\partial y, give ∂ℓ/∂x\partial\ell/\partial x. Why does this help deep networks?
      Answer∂ℓ/∂x=yˉ+DF(x)⊤yˉ=(I+DF(x))⊤yˉ\partial\ell/\partial x = \bar y + DF(x)^\top\bar y = (I + DF(x))^\top\bar y: two paths, and at a fan-out the contributions add (lesson 04). The identity path passes yˉ\bar y back unchanged, so the gradient does not have to go through a long product of layer Jacobians that can shrink to zero.
    4. Label smoothing with α=0.3\alpha = 0.3 uses the target q=(1−α)ey+αC1q = (1 - \alpha)e_y + \tfrac{\alpha}{C}\mathbf 1. For z=(ln⁡4,ln⁡2,ln⁡2)z = (\ln 4, \ln 2, \ln 2) and y=2y = 2, what is ∇zℓ\nabla_z \ell?
      Answerq=(0.1,0.8,0.1)q = (0.1, 0.8, 0.1) and p=(0.5,0.25,0.25)p = (0.5, 0.25, 0.25), so ∇zℓ=p−q=(0.4,−0.55,0.15)\nabla_z \ell = p - q = (0.4, -0.55, 0.15). The entries still sum to 0.
    5. Compute ℓ\ell for z=(1000,1000,998)z = (1000, 1000, 998) and y=3y = 3 without overflow.
      Answerm=1000m = 1000. Then lse⁡(z)=1000+log⁡(e0+e0+e−2)\operatorname{lse}(z) = 1000 + \log(e^0 + e^0 + e^{-2}), which is 1000+log⁡2.1353=1000.75861000 + \log 2.1353 = 1000.7586. So ℓ=1000.7586−998=2.7586\ell = 1000.7586 - 998 = 2.7586.
    6. In the logistic example of 3.5, change the labels to y=(1,1,0)y = (1, 1, 0). Is the Hessian at w=(0,ln⁡3)w = (0, \ln 3) still positive definite? Does LL have a minimizer?
      AnswerThe Hessian 1nX⊤SX\tfrac1n X^\top S X does not depend on yy, so it is the same positive definite matrix. But no minimizer exists: wsep=(0.5,1)w_{\text{sep}} = (0.5, 1) gives the scores (0.5,1.5,−0.5)(0.5, 1.5, -0.5), positive exactly for the label-1 samples. So L(t wsep)→0L(t\,w_{\text{sep}}) \to 0 as t→∞t \to \infty without reaching 0 (for t=1,10,30t = 1, 10, 30: 0.380.38, 4.5⋅10−34.5\cdot10^{-3}, 2.0⋅10−72.0\cdot10^{-7}).
    Recall cardsAnswer in your head first, then reveal. Add the cards to your review deck to see them again just before you would forget.
    1. What is δL=∂ℓ/∂zL\delta^L = \partial\ell/\partial z^L for softmax cross-entropy?

    2. Write the backprop recursion for the error signal δl\delta^l of a hidden layer.

    3. What is ∂ℓ/∂Wl\partial\ell/\partial W^l for one sample, and what is its rank?

    4. Batch form (one sample per row, mean loss): what are ΔL\Delta^L and ∂ℓ/∂Wl\partial\ell/\partial W^l?

    5. Gradient and Hessian of L(w)=12n∥Xw−y∥2L(w) = \frac{1}{2n}\|Xw - y\|^2?

    6. Gradient and Hessian of the mean logistic loss with scores z=Xwz = Xw?

    7. Why has logistic regression no minimizer on linearly separable data?

    8. Write the stable log-sum-exp formula and say why it does not overflow.

    9. Why do the entries of the layer-norm input gradient sum to 0?

    10. Why do gradients vanish in a deep sigmoid network?

    Open the review →

    9. Interview angle #

    • "Derive backprop for an MLP." — Define δl=∂ℓ/∂zl\delta^l = \partial\ell/\partial z^l. Then δL=p−ey\delta^L = p - e_y, δl=φ′(zl)⊙(Wl+1)⊤δl+1\delta^l = \varphi'(z^l)\odot(W^{l+1})^\top\delta^{l+1}, ∂ℓ/∂Wl=δl(al−1)⊤\partial\ell/\partial W^l = \delta^l(a^{l-1})^\top, ∂ℓ/∂bl=δl\partial\ell/\partial b^l = \delta^l. Check every shape. Then give the batch form with ΔL=(P−Y)/B\Delta^L = (P - Y)/B.
    • "Derive the gradient of softmax cross-entropy." — Write ℓ=lse⁡(z)−zy\ell = \operatorname{lse}(z) - z_y. The gradient of lse⁡\operatorname{lse} is the softmax, so ∇ℓ=p−ey\nabla\ell = p - e_y; for a batch mean, (P−Y)/B(P - Y)/B.
    • "Why subtract the max? Why does cross_entropy take logits?" — eze^z overflows, and −log⁡py-\log p_y is ∞\infty when pyp_y underflows. The shifted lse⁡(z)−zy\operatorname{lse}(z) - z_y avoids both; the shift is exact because the softmax is shift invariant.
    • "Is logistic regression convex? Is a neural network?" — Logistic: yes, the Hessian 1nX⊤SX\frac1n X^\top S X is PSD. A network: no. Permuting the hidden units of a minimizer gives another minimizer with the same loss; for a convex loss the midpoint of the two would also be a minimizer, but the averaged weights are in general worse.
    • "Why cross-entropy and not squared error for classification?" — It is the maximum-likelihood loss of the categorical model, it is convex in the logits, and its gradient p−eyp - e_y stays large when the model is confidently wrong (squared error on a sigmoid output vanishes there).
    • "What is the backward pass of layer norm?" — The formula of Theorem 5; it removes the mean and the x^\hat x component of γ⊙g\gamma\odot g, then divides by σ\sigma.
    • "Why do gradients vanish or explode?" — The gradient at the input side is a product of L−1L - 1 layer Jacobians; initialization, residual connections and normalization keep it near norm 1.

    10. Practice #

    The notebook for this lesson runs in Colab. Each exercise has a check cell (it compares your gradient with a central-difference gradient) and a folded solution:

    1. softmax_cross_entropy(Z, y): the stable mean loss and its gradient (P−Y)/B(P - Y)/B, finite at logits ±1000\pm 1000.
    2. logistic_regression(X, y, w): loss, gradient and Hessian; then Newton's method on the breast cancer data set of scikit-learn.
    3. layernorm_forward and layernorm_backward: check the gradient and its two invariances.
    4. mlp_forward and mlp_backward for a 2-layer MLP on the scikit-learn digits; gradient check, then train a few epochs.
    5. Measure the gradient norm per layer of a deep tanh MLP at two weight scales.

    11. Watch and read #

    The chain rule through one neuron per layer, then the indices for many neurons: the same recursion as Theorem 1.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    The picture before the formulas: how each weight's gradient depends on the activation that feeds it.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    A full backward pass by hand, tensor by tensor, including cross-entropy and batch normalization; compare each step with sections 3.3 and 3.6.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    Computational graphs, local gradients and the vectorized backward pass of a linear layer.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play