Appendix C — The Mathematics
The chapters state some results without proof, so that the ideas come first. This appendix gives the proofs. It assumes first-semester calculus: derivatives, the chain rule, and the derivatives of \(e^x\) and \(\log x\). Throughout, \(\log\) is the natural logarithm.
Each cell here runs on its own; no cell needs another to have run first. The first run takes fifteen to thirty seconds while Python downloads.
C.1 The gradient behind gradient descent
Section 4.1.3 stated that for logistic regression with the cross-entropy loss, the slope of the loss with respect to a weight is
\[ \frac{\partial L}{\partial w_j} = (\hat{y} - y)\, x_j , \]
and then used it to update the weights. This section derives that formula with the chain rule, explains the update with differentials, and repeats both for more than two classes.
C.1.1 The calculus we need
Four facts from Calculus I, plus one way of extending them to functions of several variables.
- Derivatives of the exponential and the logarithm: \(\dfrac{d}{dx}e^{x} = e^{x}\) and \(\dfrac{d}{dx}\log x = \dfrac{1}{x}\).
- The chain rule. If \(y\) depends on \(u\) and \(u\) depends on \(x\), then \(\dfrac{dy}{dx} = \dfrac{dy}{du}\,\dfrac{du}{dx}\). A long chain multiplies one derivative per link.
- The quotient rule: \(\left(\dfrac{f}{g}\right)' = \dfrac{f'g - fg'}{g^{2}}\).
- The differential. If \(y = f(x)\), then \(dy = f'(x)\,dx\): a small change \(dx\) in the input changes the output by approximately \(f'(x)\,dx\). This is the tangent-line approximation, and it is accurate only while \(dx\) is small.
- Partial derivatives. The loss depends on many weights at once. The partial derivative \(\partial L/\partial w_j\) is the ordinary derivative with respect to \(w_j\), with every other weight held fixed.
C.1.2 Two classes: the sigmoid
C.1.2.1 The loss as a composition
For one report with feature values \(x_1, \ldots, x_n\) and true label \(y\) (0 or 1), the loss is built in three stages, each feeding the next:
\[ \begin{aligned} z &= w_1x_1 + w_2x_2 + \cdots + w_nx_n + b, \\ \hat{y} &= \sigma(z) = \frac{1}{1 + e^{-z}}, \\ L &= -\Big[\, y\log \hat{y} + (1-y)\log(1-\hat{y}) \,\Big]. \end{aligned} \]
So \(L\) is a function of \(\hat{y}\), which is a function of \(z\), which is a function of the weights \(w_1, w_2, \ldots, w_n\) and the bias \(b\):
\[ L = L\big(\hat{y}(z(w_1, w_2, \ldots, w_n, b))\big). \]
A weight \(w_j\) reaches the loss only through this chain, \(w_j \to z \to \hat{y} \to L\), so the chain rule gives the slope as a product of three derivatives, one per link:
\[ \frac{\partial L}{\partial w_j} = \frac{dL}{d\hat{y}} \cdot \frac{d\hat{y}}{dz} \cdot \frac{\partial z}{\partial w_j}. \]
Here are the three links, one at a time.
C.1.2.2 Link 1: the loss with respect to the prediction
\(y\) is a fixed number (the true label), so only \(\hat{y}\) varies. Using \(\frac{d}{d\hat{y}}\log\hat{y} = \frac{1}{\hat{y}}\) and, by the chain rule, \(\frac{d}{d\hat{y}}\log(1-\hat{y}) = \frac{-1}{1-\hat{y}}\):
\[ \frac{dL}{d\hat{y}} = -\left[\frac{y}{\hat{y}} - \frac{1-y}{1-\hat{y}}\right] = \frac{-y(1-\hat{y}) + (1-y)\hat{y}}{\hat{y}(1-\hat{y})} = \frac{\hat{y} - y}{\hat{y}(1-\hat{y})} . \]
In the numerator the terms \(+y\hat{y}\) and \(-y\hat{y}\) cancel, leaving \(\hat{y} - y\).
C.1.2.3 Link 2: the sigmoid with respect to the logit
Write \(\sigma(z) = (1 + e^{-z})^{-1}\) and use the chain rule twice:
\[ \frac{d\hat{y}}{dz} = -(1 + e^{-z})^{-2}\cdot(-e^{-z}) = \frac{e^{-z}}{(1+e^{-z})^{2}} = \frac{1}{1+e^{-z}} \cdot \frac{e^{-z}}{1+e^{-z}} . \]
The first factor is \(\sigma(z) = \hat{y}\). The second is \(1 - \hat{y}\), because \(1 - \frac{1}{1+e^{-z}} = \frac{e^{-z}}{1+e^{-z}}\). So
\[ \frac{d\hat{y}}{dz} = \hat{y}\,(1-\hat{y}) . \]
C.1.2.4 Link 3: the logit with respect to a weight
\(z = w_1x_1 + \cdots + w_jx_j + \cdots + w_nx_n + b\). Holding every other weight fixed, only the term \(w_jx_j\) changes, so
\[ \frac{\partial z}{\partial w_j} = x_j , \qquad \frac{\partial z}{\partial b} = 1 . \]
C.1.2.5 Putting the links together
\[ \frac{\partial L}{\partial w_j} = \frac{\hat{y} - y}{\hat{y}(1-\hat{y})} \cdot \hat{y}(1-\hat{y}) \cdot x_j = (\hat{y} - y)\, x_j , \qquad \frac{\partial L}{\partial b} = \hat{y} - y . \]
The factor \(\hat{y}(1-\hat{y})\) from link 1 cancels the one from link 2. That cancellation is why the formula in Section 4.1.3 is so simple, and it is one reason the cross-entropy loss is paired with the sigmoid.
Training minimizes the average loss over many reports. The derivative of a sum is the sum of the derivatives, so the slope of the average loss is the average of \((\hat{y} - y)\,x_j\) over the reports. Stochastic gradient descent uses one report at a time, which is the formula above.
C.1.3 From slope to update: differentials
The gradient gives a slope. Here is why the update in Section 4.1.3,
\[ w_j \leftarrow w_j - \eta\,(\hat{y} - y)\,x_j , \]
is the right way to use it.
C.1.3.1 One weight
Change one weight \(w_j\) by a small amount \(dw_j\), holding everything else fixed. By the differential formula \(dy = f'(x)\,dx\), the loss changes by approximately
\[ dL = \frac{\partial L}{\partial w_j}\, dw_j . \]
We want \(dL\) to be negative: the loss should go down. That happens when \(dw_j\) has the opposite sign to \(\partial L/\partial w_j\). The simplest choice is a small multiple of the slope with its sign flipped:
\[ dw_j = -\eta\, \frac{\partial L}{\partial w_j} , \qquad\text{which gives}\qquad dL = -\eta \left(\frac{\partial L}{\partial w_j}\right)^{2} \le 0 . \]
A square is never negative, so with \(\eta > 0\) the loss can only go down (to this approximation), and it goes down fastest along the weights with the steepest slopes. The new weight is the old one plus the change, \(w_j + dw_j\), which is
\[ w_j \leftarrow w_j - \eta\,\frac{\partial L}{\partial w_j} = w_j - \eta\,(\hat{y} - y)\,x_j . \]
That is step 4 of Section 4.1.3.
C.1.3.2 All the weights at once
Gradient descent changes every weight in the same step. For a function of several variables the differential adds up one term per variable:
\[ dL = \frac{\partial L}{\partial w_1}\,dw_1 + \cdots + \frac{\partial L}{\partial w_n}\,dw_n + \frac{\partial L}{\partial b}\,db . \]
Making the same choice for every weight and the bias, \(dw_j = -\eta\,\partial L/\partial w_j\) and \(db = -\eta\,\partial L/\partial b\), gives
\[ dL = -\eta \left[ \left(\frac{\partial L}{\partial w_1}\right)^{2} + \cdots + \left(\frac{\partial L}{\partial w_n}\right)^{2} + \left(\frac{\partial L}{\partial b}\right)^{2} \right] \le 0 . \]
Every term is a square, so again the loss goes down.
C.1.3.3 Why the step must be small
The differential is the tangent-line approximation, so it is trustworthy only for small changes. This is the learning rate’s job: a small \(\eta\) keeps every \(dw_j\) small. The cell below takes review 1 of Chapter 4 with the hand-chosen weights, makes one gradient-descent step with several learning rates, and compares the change in loss the differential predicts with the change that actually happens:
How the code works:
predictedis the differential, \(\sum_j \frac{\partial L}{\partial w_j}dw_j + \frac{\partial L}{\partial b}db\).new_L - Lis the true change, computed by actually moving the weights and recomputing the loss.- At \(\eta = 0.01\) and \(0.1\) the two agree closely, and the loss goes down as promised.
- At \(\eta = 5\) the differential predicts a drop of more than 3, which is impossible: the loss is only 0.34 to begin with, and cross-entropy is never negative. The step left the region where the tangent line is a good guide. The loss did still fall here, but in general a step that large can overshoot and make the loss worse. That is the overshooting described in Section 4.1.3.
C.1.4 More than two classes: the softmax
C.1.4.1 The loss as a composition
With \(K\) classes, class \(c\) has its own weights \(w_{c,1}, \ldots, w_{c,n}\) and bias \(b_c\) (Section 4.2). For one report the stages are
\[ \begin{aligned} z_c &= w_{c,1}x_1 + \cdots + w_{c,n}x_n + b_c , \\ p_c &= \frac{e^{z_c}}{\sum_{k=1}^{K} e^{z_k}} , \\ L &= -\log p_t , \end{aligned} \]
where \(t\) is the true class. It helps to write the label as \(K\) numbers \(y_1, \ldots, y_K\), with \(y_t = 1\) for the true class and \(y_c = 0\) for the others. Then the loss is \(L = -\sum_k y_k \log p_k\); all terms but one are zero.
The chain is \(w_{c,j} \to z_c \to (p_1, \ldots, p_K) \to L\). There is one new feature: a single logit \(z_c\) affects every probability, because every \(p_k\) has \(e^{z_c}\) in its denominator. When one variable reaches the result along several paths, the chain rule adds the paths:
\[ \frac{\partial L}{\partial z_c} = \sum_{k=1}^{K} \frac{\partial L}{\partial p_k}\, \frac{\partial p_k}{\partial z_c} . \]
C.1.4.2 Link 1: the loss with respect to the probabilities
\(L = -\sum_k y_k \log p_k\), so
\[ \frac{\partial L}{\partial p_k} = -\frac{y_k}{p_k} . \]
This is zero except for the true class, where it is \(-1/p_t\).
C.1.4.3 Link 2: the softmax with respect to the logits
Write \(S = \sum_k e^{z_k}\), so \(p_k = e^{z_k}/S\), and note that \(\partial S/\partial z_c = e^{z_c}\). Use the quotient rule. There are two cases.
If \(k = c\), the numerator \(e^{z_c}\) also depends on \(z_c\):
\[ \frac{\partial p_c}{\partial z_c} = \frac{e^{z_c}S - e^{z_c}e^{z_c}}{S^{2}} = \frac{e^{z_c}}{S}\left(1 - \frac{e^{z_c}}{S}\right) = p_c\,(1 - p_c) . \]
If \(k \ne c\), only the denominator depends on \(z_c\):
\[ \frac{\partial p_k}{\partial z_c} = \frac{0\cdot S - e^{z_k}e^{z_c}}{S^{2}} = -\,p_k\,p_c . \]
Raising one logit raises its own probability and lowers every other one, as it must, since the probabilities add to 1. Notice that the first case has the same form as the sigmoid’s \(\hat{y}(1-\hat{y})\).
C.1.4.4 Links 1 and 2 together: the loss with respect to a logit
Add the paths. Only \(k = t\) contributes, because \(\partial L/\partial p_k = 0\) for every other \(k\):
\[ \frac{\partial L}{\partial z_c} = -\frac{1}{p_t}\cdot\frac{\partial p_t}{\partial z_c} . \]
If \(c = t\), this is \(-\frac{1}{p_t}\cdot p_t(1-p_t) = p_t - 1\). If \(c \ne t\), it is \(-\frac{1}{p_t}\cdot(-p_t\,p_c) = p_c\). Both cases are one formula, because \(y_t = 1\) and \(y_c = 0\) for \(c \ne t\):
\[ \frac{\partial L}{\partial z_c} = p_c - y_c . \]
Once again \(1/p_t\) cancels against a factor of \(p_t\), just as \(\hat{y}(1-\hat{y})\) cancelled in the two-class case.
C.1.4.5 Link 3, and the result
Weight \(w_{c,j}\) appears only in \(z_c\), with \(\partial z_c/\partial w_{c,j} = x_j\) and \(\partial z_c/\partial b_c = 1\). So
\[ \frac{\partial L}{\partial w_{c,j}} = (p_c - y_c)\, x_j , \qquad \frac{\partial L}{\partial b_c} = p_c - y_c . \]
This is the two-class formula with \(\hat{y}\) replaced by \(p_c\) and \(y\) by \(y_c\), once for each class.
C.1.4.6 The update
The differential argument of Section C.1.3 does not care how many weights there are. Choosing \(dw_{c,j} = -\eta\,\partial L/\partial w_{c,j}\) for every class and feature makes \(dL\) minus \(\eta\) times a sum of squares, so the loss goes down, and the update is
\[ w_{c,j} \leftarrow w_{c,j} - \eta\,(p_c - y_c)\,x_j . \]
Read it class by class. For the true class, \(p_t - 1\) is negative, so its weights for the features present in the report go up, by \(\eta(1 - p_t)x_j\). For every other class, \(p_c\) is positive, so its weights go down, by \(\eta\,p_c\,x_j\): the more probability a wrong class received, the harder it is pushed down.
C.1.4.7 Two classes again
With \(K = 2\), fix the logit of class 0 at \(z_0 = 0\) and call the other logit \(z\), as in Section 4.2. Then \(p_1 = \sigma(z) = \hat{y}\) and \(y_1 = y\), and the formula for class 1 becomes \((\hat{y} - y)\,x_j\): the softmax derivation contains the sigmoid one as a special case.
C.1.4.8 A numerical check
A derivative can be checked without any calculus: nudge one logit by a tiny \(h\) and divide the change in loss by \(h\). The cell compares that with \(p_c - y_c\) for the logits of the chart in Section 4.2, \(z = [2.0,\ 1.0,\ 0.1]\), with the first class (Breast) as the true class:
How the code works:
losscomputes \(-\log p_t\) straight from the logits, with no derivative anywhere.- Dividing the change in loss by the size of the nudge is the differential read backwards: \(dL = \frac{\partial L}{\partial z_c}\,dz_c\), so \(\frac{\partial L}{\partial z_c} \approx dL/h\) when \(dz_c = h\) is tiny.
- The two columns agree to five decimal places. The true class gets \(-0.341\), so gradient descent raises its logit; the two wrong classes get \(+0.242\) and \(+0.099\), so it lowers theirs.