Quantitative Finance — the Mathematics of Markets · chapter 24
24Classification, Imbalance & PCA
Chapter 23 closed on a door it did not walk through. The boosting loop never cared what the loss was. It took a derivative, fitted a small tree to the negative gradient, and stepped. So hand it a loss that scores a probability and the identical machine trains a classifier. That is the door, and this chapter walks through it. But it opens onto a question the last chapter could not answer, because once a model says fraud or not fraud you have to score it without lying to yourself. Everything hard on this page is one quantity being squeezed through something. Squeeze a probability through a linear model and it will not fit, so you move it to a coordinate where it does. Squeeze it through a threshold and you have stopped modelling and started deciding. Squeeze it through two hundred correlated features and the weights stop meaning anything at all. Three squeezes, one instinct underneath them: stop working in the coordinates you were handed.
Look at what this page stands on, because almost all of it is already yours. Chapter 20 gave us least squares and the fact that a fitted value estimates the conditional meanE[y|x]. It also gave us multicollinearity, where near-duplicate columns make the coefficients wild while the predictions sit still. Chapter 12 gave us the Bernoulli variable and the small sentence this chapter turns on, E[y] = p. Chapter 5 gave us the logarithm, including the move we lean on constantly, which is that ln turns a product into a sum. Chapter 10 gave us conditional probability and Bayes. Chapter 11 gave us expectation, Chapter 17 gave us Type I and Type II errors, and Chapter 8 gave us gradient descent and Lagrange multipliers. Chapter 7 gave us eigenvectors, and Chapter 18 gave us the covariance matrix. Every one of those is spent somewhere below.
The boosting loop from Ch 23, with one switch on it: what is the loss? Flip it and watch which parts move.
Squared error → a raw number.
What you’re looking at — one machine, two losses, and the door into this chapter
boxes 1 and 3, and the raw score F — identical in both modes
the target the little tree is fitted to — the only thing the loss changes
p = σ(F), the raw score squashed into (0, 1) — now readable as a probability
the two questions that flip opens — the two halves of Ch 24
Read the subtraction, not the words: y − F becomes y − p. It is the same “truth minus what you predicted”; the loss only changed what predicted means. That is the whole bridge from Ch 23 — boosting never cared what the loss was. And here is the price: F could be 1.40 or −7, so it was never a probability, while p is pinned inside (0, 1) and every target now sits between −1 and +1. You have bought an honest probability, and lost the easy question “how far off is the number?” What replaces it — how you score a classifier when fraud is one case in a thousand — is the rest of this chapter.
Fig. 1. Three customers, and a boosting model part-way through its run. y is the truth (1 = it really was fraud), F is the raw score the trees have added up so far, and the gold column is the target the next little tree gets fitted to. Under squared error that target is y − F and the output is a bare number — 1.40 is not a probability, and no amount of tree-fitting will make it one. Flip the switch to a loss that scores a probability and the machine does not move: boxes 1 and 3 are untouched, the subtraction is untouched, and the only change is that F is squashed by the sigmoid into p = σ(F), so the target becomes y − p and the output lands inside (0, 1). That is the entire bridge from Ch 23 — boosting fits whatever the loss points at — and it immediately opens the two red questions this chapter exists to answer: which loss does that, and how do you honestly score a classifier once it starts handing you probabilities?
So the seam from last chapter is short. Boosting's target is whatever the loss says it is, so a loss built for probabilities turns that loop into a classifier without changing a line of it. But that raises the question the previous chapter left standing. We now need a loss that scores a probability, and a way to judge the result when one class almost never happens.
01A yes/no column, and the tool you already own
Start with the smallest honest thing we can do, which is to use what we have. Here are twelve borrowers. Each one has a single feature, debt-to-income, and a column y that is 1 if they defaulted and 0 if they did not. Nothing about that column is special, because it is just twelve numbers, and Chapter 20 fits a line to numbers.
So fit it, and least squares does not complain — it hands back ŷ = −0.3382 + 2.3949·DTI. The line slopes upward, which is what you would expect, because higher debt-to-income does go with more default in this book. Then read a prediction off the right-hand end.
Ch 20’s least-squares line, pointed at a column that is only ever 0 or 1. Drag the ringed borrower right.
Predict first: as that one row slides right, does the cyan boundary go left or right? Then drag.
12 rows, one line, two problems
What you’re looking at — the right target, an output range that cannot hold the answer
twelve loans. y is the answer column: filled dot = defaulted (y = 1), hollow = repaid (0).
the least-squares line and its two fitted values ŷ (“y-hat”, the number the line predicts).
everything above 1 and below 0: no probability lives there. The red square is one row’s miss, squared.
where the line crosses 0.5 — the actual yes/no boundary every other borrower is judged by.
Fig. 2. Ch 20’s machinery works perfectly here — and that is the problem. Fit the least-squares line to a column that is only ever 0 or 1 and it returns −0.0987 for the borrower at DTI 0.10 and 1.2424 for the one at 0.66. Both readouts sit in the red: a probability cannot be negative and cannot exceed 1, so the model has the right target and an output range that cannot hold the answer. Now the second crack, in the same picture. Squared error squares the distance, so the row that sits furthest from the line has by far the loudest vote. Drag the ringed defaulter out to DTI 1.60 and the line swings to chase it: the slope collapses from 2.3949 to 0.8260, and the 0.5 crossing — the actual yes/no boundary — slides from 0.3500 to 0.4567. Nobody else’s row changed, yet two other borrowers are now judged the other way. That is why a classifier cannot just be a regression, and it is exactly what the sigmoid and log-loss are about to fix.
At a debt-to-income of 0.66 the model says 1.2424, and at 0.10 it says −0.0987. Say those out loud as answers to the question we asked. A 124% chance of default is nothing at all, and a negative chance of default is worse than nothing.
Now the diagnostic question, and it is the one that turns a broken toy into the foundation of the chapter. Is the idea wrong, or is the range wrong? Put two sentences you already own next to each other. Chapter 20 said a fitted value estimates the conditional mean E[y|x], and Chapter 12 said that for a Bernoulli variable, E[y] = p.
A 0/1 column is a Bernoulli variable, so its conditional mean is a conditional probability. That means least squares on a yes/no target was estimating P(y=1|x) the whole time, without anybody saying so. It was never solving the wrong problem. It was solving the right problem with an output range that cannot hold the answer.
200 loans, each one a 0 or a 1. Drag the bin width, click a column — then read the box height twice.
Predict before you press: can one straight line touch every box top?
Click a column to average it
What you're looking at — one column of 0s and 1s, averaged, wearing two different names
a dot on the top rail is one loan with y = 1 — it defaulted. DTI = debt-to-income, the borrower's monthly debt as a % of income.
a dot on the floor is y = 0 — repaid. Every row is one of these two values and nothing else.
the box height is the plain arithmetic mean of the 0s and 1s in that bin — adding a 0/1 column and dividing IS counting the 1s, so it IS a probability.
Ch 20's straight-line fit, laid over the tops. It was estimating E[y | x] the whole time — and here E[y | x] = P(y = 1 | x).
Fig. 3. Two hundred loans, one column of answers: 1 if the loan defaulted, 0 if it repaid — that is the entire y column, sitting on two rails. Click any column and the widget does the only thing it can do with a stack of 0s and 1s: it adds them up and divides. Seven ones over nineteen rows is 0.368, and the box grows to that height. Now read the two lines in the gold panel. Mean of the 0/1 column = 0.368. P(default | this bin) = 0.368. Not close, not approximately — the same arithmetic, twice, because adding a column of zeros and ones just counts the ones, and counting the ones over the rows is what “probability” has meant since Ch 12. Squeeze the bins to 2 % and the estimates go jumpy: ten rows is thin evidence, and one extra default swings the box. Widen them to 20 % and they steady out, but each box now smears twenty percentage points of DTI into a single number. Then press Overlay the OLS line. Ch 20's straight-line fit — ordinary least squares, run on this same 0/1 column without a single change — walks straight through the box tops. That is the whole bridge from regression to classification: the fit was estimating E[y | x] all along, and when y is 0/1, E[y | x] is P(y = 1 | x). Which is why the honest output of a classifier is a probability, never a label — the label is something you manufacture afterwards by picking a threshold, and that choice is yours to defend.
This is worth pausing on, because it settles what a classifier is actually for. Ask what "will this loan default" means for one loan. The loan defaults once, or it does not, and there is no repeated experiment hiding behind it. So the only answer with any content is how likely. The honest deliverable of a classifier is a probability, not a label, and everything later in this chapter depends on that sentence being installed now.
There is a second crack in the same picture, and it is quieter. Take one defaulting borrower and drag them far to the right, out to a debt-to-income of 1.60. The whole line tilts. The point where it crosses 0.5 slides from 0.3500 to 0.4567, so the boundary moved because of a single row. Squared error takes distant points extremely seriously, since it squares the distance.
That gives us a specification, and it is worth writing down because the next three sections are just filling it in. We want the same linear combination of features, and we want an output trapped inside (0,1). And we want a loss that cares how confident you were rather than how far away you sat. That list is not something I handed you — it came out of watching the tool you already had break.
02A coordinate a probability can live in
Here is the move, and it is the reverse of the obvious one. Do not go hunting for a function that squashes the real line into (0,1). Instead, take the probability and push it outward until it covers the whole line. Building a map forwards from a bounded quantity is elementary algebra, and we can run any map backwards later.
Half-step one, and you have been reading this number your whole life. Instead of the probability, take the odds, p/(1−p). A 75% chance gives 0.75/0.25 = 3, which is what a bookmaker calls three to one on. A 50% chance gives exactly 1, even money, and a 10% chance gives 1/9, about 0.1111.
One warning before it costs us later. In ordinary speech people say "the odds are 30%", and they mean the probability. In this chapter odds means the ratio and nothing else. Probability 0.75 and odds 3 are the same belief written as two different numbers, and they are never interchangeable here.
Now watch the range. As p runs from 0 to 1, the odds run from 0 to +∞. The ceiling is gone, which is real progress. But the floor is still there, since odds can never drop below zero, and a linear model living in odds-space could still predict something impossible.
One belief, three rulers. Drag p — then take the two half-steps and watch which ruler runs out of room.
BELIEF p 0.750
◀ drag p toward 1 ▶
p is caged: 0 and 1 are walls
Drag p and try to leave (0,1). You can't — and a linear formula, which happily outputs 7 or −3, cannot live in that cage. Press ②.
What you're looking at — the same belief written three ways, and each rewrite is forced by what the last one failed to fix
p — the probability. Trapped between two hard walls at 0 and 1, so no linear formula can output it.
odds = p/(1−p) — the ceiling is gone (it really runs to ∞; squeezed here to fit), but a floor at 0 survives.
ln(odds) — ln's stated job is to map (0,∞) onto all of ℝ. Floor gone. 0 = even money; flip p and it flips sign.
one belief, one 1% window — the gold box is the same 1% of p on both rulers. Near certainty it barely moves p and tears across log-odds.
Fig. 4. One belief, three rulers, driven by a single slider. On the blue probability ruler p is caged between two hard walls, which is why no linear formula can output it. Dividing by what's left, odds = p/(1−p), kills the ceiling but leaves a floor at zero — the violet ruler can never go left of 0. Taking ln removes that floor, because mapping (0,∞) onto the whole real line is precisely what the logarithm does. Push p toward 1 and watch the gold marker crawl into a corner up top while it sprints off the edge below; press mirror and the log-odds simply changes sign.
So we need a function that takes (0, ∞) and spreads it across all of ℝ, and Chapter 5 handed us exactly one. That is the whole stated job of the natural logarithm. Take z = ln(p/(1−p)) and the floor disappears too.
That number is the log-odds, also called the logit, and it is worth reading rather than just computing. At p = 0.5 the odds are 1 and ln 1 = 0, so even money sits exactly at the origin. Positive z means more likely than not, negative means less likely than not, so the sign carries which side you are betting on. And the map is antisymmetric: p = 0.9 gives ln 9 = 2.1972 while p = 0.1 gives −2.1972 exactly. Default and no-default are mirror images, and neither one is privileged.
The strategy now has a name worth having. We are not going to model p directly, but a transform of p instead, and that transform is called the link function. The linear model is not being abandoned — it is being moved into a room where it fits.
03Run the map backwards
Put the linear model in the new room, and declare that the log-odds is linear in the features: ln(p/(1−p)) = w·x + b.
That is the entire model statement, and notice what we did not do. We did not invent a squashing function. We asserted linearity in the one coordinate where linearity is harmless, which is the whole reason we built that coordinate. Everything left is bookkeeping, because we want a probability back and the map runs both ways.
Before the algebra, try it yourself. Invent a function that takes any real number and returns something in (0,1). Most people sketch something S-shaped. Some propose a scaled arctangent, or a straight line clipped at both ends, and every one of those genuinely works. That is exactly the problem with starting from this end. There are infinitely many squashers and no reason on earth to prefer one.
Nobody ever chose the S-curve. Demand an unbounded coordinate, solve z = ln(p/(1−p)) for p in three lines of Chapter 5 algebra, and watch the log-odds curve rotate into the sigmoid.
You need a number in (0,1). arctan does it. A clipped line does it. A hand-drawn S does it. Tap one — nothing yet says which.
panel
three squashers, none forced
What you're looking at — a curve nobody chose, falling out of a curve you already built
Candidates (panel 1): arctan, a clipped line, a hand-drawn S. All three squash every real number into (0,1). Not one of them is forced — that is the whole complaint.
The log-odds map z = ln(p/(1−p)): the honest coordinate you demanded, stretching a probability in (0,1) out across the entire real line.
The sigmoid is that same curve rotated 180° about the dashed mirror — inverting a function is reflecting it in the diagonal. The green dot is the flip's one fixed point: σ(0) = 0.5.
Fig. 5. The sigmoid, assembled by inversion — and the point is that no one assembled it. Panel 1 lays out three honest candidates for squashing a real number into a probability: arctan, a line simply clipped at 0 and 1, and an S drawn freehand. Tap any of them; each one works, which is exactly the trouble. If the curve is chosen, someone else may choose differently and there is no argument to settle it. Panel 2 stops choosing and starts demanding. A probability lives in the cramped interval (0,1); the linear part of a model — a weighted sum of features — roams over the whole real line. So insist on the coordinate that reconciles them: the log-odds, z = ln(p/(1−p)), which stretches p = 0.001 down to −6.9 and p = 0.999 up to +6.9, symmetrically about p = 0.5. That map is the blue curve. Undo the logarithm (Chapter 5) and ez = p/(1−p): the odds. Panel 3 multiplies by (1−p) and gathers terms, ez = p(1 + ez). Panel 4 divides, then divides again by ez to clear the top: p = 1/(1 + e−z). Three lines of school algebra, no modelling decisions taken. Meanwhile the plot has been turning. Solving z = f(p) for p is inverting f, and inverting a function is geometrically nothing more than reflecting its graph in the diagonal — so the picture rotates 180° about that dashed mirror, and the gold S-curve you land on in panel 5 is the blue log-odds curve seen from the other side. Drag the flip slider yourself and watch it go back and forth; the curve never changes, only which axis you call the input. Now every feature of the sigmoid has a reason. Its tails are flat because the log-odds axis runs to infinity — pushing z from 6 to 12 barely moves p, since 0.9975 has little room left. Its steepest point is at the origin because the log-odds are symmetric about even odds. And the one point the whole rotation leaves standing, the green dot at the centre, is z = 0 ⇄ p = 0.5: odds of 1:1, a log-odds of zero, σ(0) = 0.5. Forced, not picked — which is also why a logistic coefficient stays readable as "how much this feature moves the log-odds", and why credit-risk desks still reach for it.
So we do not choose. Write z = w·x + b, then solve the equation we already built. Exponentiate both sides of z = ln(p/(1−p)) to get e^z = p/(1−p). Multiply out to get p = e^z − p·e^z, collect to get p(1 + e^z) = e^z, and divide. That gives p = e^z/(1 + e^z), and dividing top and bottom by e^z gives the form everyone writes, p = σ(z) = 1/(1 + e^(−z)).
That is the logistic function, the sigmoid. And here is the sentence that should change how this model feels: we did not choose it. Its S-shape, its flat tails, its exact steepness at the origin, all of it is a consequence of demanding an unbounded symmetric coordinate and then coming home. The sigmoid is the log-odds map read backwards. Nothing about it is a matter of taste.
Two readings arrive free, and both matter more than they look. The first is about the boundary. Wherever σ(z) = 0.5 we have z = 0, and z = 0 means w·x + b = 0. That is a straight line in x, a flat plane in higher dimensions. So logistic regression is a linear classifier, despite the curve on your screen. The curvature lives entirely in how confidence grows as you walk away from the boundary, not in the shape of the boundary itself.
One coefficient, two readings. Tilt the line, drag a borrower, then hit +1 UNIT OF x and watch the same bump mean two different things.
STEP 1 OF 4 · THE BOUNDARY
TILT & SHIFT ← drag the sliders
boundary shape—
curvature lives in—
odds multiplier—
tilt it · shift it · still straight
What you're looking at — one model, read twice. z = w·x + b is the plain straight-line score; σ(z) = 1/(1+e−z) squashes it into a probability p̂; and odds = p̂/(1−p̂) is that same probability written as "how many times more likely than not".
blue side, p̂ near 0 — the model says repays. The shading is the probability itself, drawn continuously, and it curves.
red side, p̂ near 1 — the model says defaults. Walk further out and confidence climbs, but nothing about the edge changes.
the gold edge is p̂ = 0.5, which means z = 0, which is w·x + b = 0 — a straight line, always. Logistic regression is a linear classifier; the curvature is in the confidence, never in the line.
the two borrowers take the same +1 unit of x. A moves 0.0987, B moves 0.0033 — 30× apart. Yet both odds are multiplied by e0.4 = 1.4918. That is the aha: a coefficient is an additive shift in log-odds, so it is a multiplier on odds and a moving target in probability.
Fig. 6. Two readings of the same line. Because σ(z) = 0.5 happens exactly at z = 0, and z = w·x + b = 0 is the equation of a straight line, the decision boundary of a logistic model is straight no matter how you tilt or shift it — step 1 lets you try, and step 2 lets you walk a borrower away from it and watch the confidence climb toward 1 while the edge stays dead straight. The curvature lives in the confidence, not in the line. Steps 3 and 4 follow the same two borrowers through one event, described twice. A coefficient of w = 0.4 and one extra unit of x adds 0.4 to the log-odds of both; in probability that is +0.0987 for the borrower sitting at 0.50 and only +0.0033 for the one already at 0.99 — thirty times smaller, for the identical change. In odds it is ×e0.4 = 1.4918 for both, exactly. That is why a logistic coefficient is reported as an odds ratio and never as an effect on probability: reading w as "how much p goes up" is not merely sloppy, it is dimensionally impossible once you are anywhere above p = 0.6, because there is barely any probability left to move.
The second reading is about the coefficients, and this is where nearly everyone gets hurt. Because the model is linear in log-odds, adding one unit of x_j adds w_j to the log-odds, which multiplies the odds by e^(w_j). That number is the odds ratio, and it is what credit-risk teams actually report.
Watch what that does to the naive reading. Take w = 0.4 and apply the identical one-unit bump to two borrowers. Start one at p = 0.50 and it moves to 0.5987, a jump of about 0.099. Start the other at p = 0.99 and it moves to 0.9933, a jump of 0.0033. Same coefficient, same change in x, and the probability moves thirty times further in one place than the other. Meanwhile the odds were multiplied by e^0.4 = 1.4918 in both cases, identically.
So "a one-unit increase raises the default probability by w_j" is not merely imprecise. It is dimensionally impossible for any borrower already above p = 0.6. The coefficient is linear in log-odds and emphatically non-linear in probability, which is why e^(w_j) is the honest thing to quote.
04What probability did you assign to what happened?
We have a model and no way to choose w and b. The obvious answer, reuse squared error, is wrong in a way that tells us the right answer. So take the smallest example that forces it.
Two candidate models look at the same loan, and that loan defaulted. Model A said p̂ = 0.6. Model B said p̂ = 0.99. Any rule that only asks "did you get the label right" scores them identically, which already tells us accuracy is useless as a training signal. Squared error can separate them, but it has no story about why, and it treats a confidently wrong answer as merely somewhat bad.
So stop picking a loss and ask a question about the data instead. What probability did my model assign to what actually happened? For one row that is easy, because the outcome is Bernoulli. The model said p̂ for a 1 and (1 − p̂) for a 0, and both cases fit in one expression, p̂^y · (1 − p̂)^(1−y).
That looks exotic and it is nothing but a switch. Set y = 1 and the second factor has exponent 0, so it collapses to 1, leaving p̂. Set y = 0 and the first factor collapses to 1, leaving (1 − p̂). Anything to the power zero is one, and that is the entire trick.
Across n independent rows, probabilities of independent things multiply, so the likelihood of the whole dataset is the product. Three rows that all defaulted, predicted at 0.9, 0.8 and 0.7, give 0.9 × 0.8 × 0.7 = 0.504. Now your Chapter 5 reflex should fire, because a product of hundreds of numbers below one is unusable. In IEEE double precision, 0.5^1000 is 9.33e−302 and still a number, while 0.5^1100 is exactly 0.0.
Take the logarithm and the product becomes a sum, which is the same move Chapter 5 used on log returns. Because ln is strictly increasing, it does not move where the maximum sits. Negate it so we are minimising, divide by n so the number does not depend on dataset size, and read off what fell out: L = −(1/n) Σ [ y·ln p̂ + (1−y)·ln(1−p̂) ].
That is log-loss, also called binary cross-entropy. We derived it, and nobody decreed it. On those same three rows it gives 0.2284, and e^(−0.2284) = 0.504, which is the likelihood we started from.
CodeRun — ask the model one question: what probability did you give to what actually happened? Then find the row that costs infinity.
1 · YOUR CALLpick one
A model swears one loan is safe: p_hat = 0.000. It defaults. What should that single row cost?
2 · RUN ITtap
3 · ROW 6 p_hattap
pick A, B or C — then RUN
What you’re looking at — log-loss is not a rule someone decreed. It is one question, asked of every row, multiplied, then logged.
P(what happened) — if it defaulted take p̂, if it repaid take 1−p̂. Multiply across rows, then take Ch 5’s log to turn × into +.
log-loss = mean of −ln(·) — the gold line. Same accuracy, wildly different cost: confidence is the thing accuracy cannot see.
row 6 — dial its p̂ down and the bar runs off the chart. −ln(0) = ∞: confident wrongness is unbounded, and that is what forces p̂ to be honest.
Fig. 7. Nobody handed down log-loss on a tablet; you can derive it in five lines, and this run does. Start with the trap: one loan defaulted, model A muttered 0.60 and model B swore 0.99, and accuracy scores them exactly the same because it rounds a probability to a yes-or-no and throws the confidence in the bin. Now ask the only honest question — what probability did you give to what actually happened? — and the two part company: 0.51083 against 0.01005. Step 2 unpacks the formula that frightens people, p̂ᵧ(1−p̂)¹⁻ᵧ: since anything to the power zero is 1, one factor always switches itself off, so it is not algebra at all — it is an if. Step 3 multiplies three independent rows, 0.9×0.8×0.7 = 0.504, takes Ch 5's log to turn the product into a sum, negates and divides by n; then prints exp(−total) = 0.504000, identical to the digit, so you can see the log changed nothing but the arithmetic. Step 4 says why we bother: 0.5**1100 = 0.0, the likelihood underflowing to literal nothing while its log sits at a comfortable 762.46. And step 5 is the point. Six rows, each with its cost, and then the dial: drop row 6's p̂ from 0.01 to 0.001 and the price climbs 4.6052 → 6.9078; drop it to 0.0 and the bar runs off the top of the chart, because −ln(0) = ∞. That single unbounded penalty is the whole reason a logistic model's output deserves the word probability: the sigmoid only guarantees a number in (0, 1), but the loss makes claiming certainty you do not have infinitely expensive — so the model learns to say 0.7 when it means 0.7. That is calibration, and it is a consequence, not a decoration.
Now look at what this loss does, because the rest of the chapter leans on it. If the truth is y = 1 and you said p̂ = 0.01, your contribution is −ln(0.01) = 4.6052. If you said p̂ = 0, it is −ln(0) = ∞. Confident wrongness costs infinity, without limit and without appeal.
That is the mechanism, and it is the thing almost nobody says out loud. The sigmoid gives you a number inside (0,1), which is not the same as a probability. The loss is what makes it one. The only way to minimise log-loss is to output numbers you would genuinely bet at, so p̂ = 0.03 has to correspond to things that really do happen about three times in a hundred. That property is called calibration, and it is the licence that lets us read the output as a probability at all.
Then the mechanics, and one correction to an expectation you may have picked up. Setting the gradient to zero here gives equations with p̂ tangled inside a sigmoid, and no algebraic solution exists. There is no closed form. Chapter 20's normal equations were the exception in this course, not the rule.
CodeRun — the closed form stalls. Press RUN and watch it cost you nothing.
1 · PREDICTpick one
There is no formula for w. So we walk downhill from three different random starts. What are the three final answers?
2 · EXECUTEtap
3 · READ THE OUTPUTtap
4 · STEP SIZE ηbreak it
predict, then press RUN
What you’re looking at — the missing formula is a non-event. The gradient here is X′(p̂ − y): Chapter 20’s X′(ŷ − y) with one letter changed.
STALLED — set the derivative to zero and you get X′y = X′σ(Xw). In Ch20 w sat outside and you inverted X′X; here it is inside σ, which has no inverse you can pull out of a sum. No closed form exists.
three random starts — blue, violet and cyan begin at three different corners of the (w0, w1) plane and take three different routes down.
one floor — all three land on w0 = −10.2958, w1 = +30.2871, log-loss 0.1999. Log-loss is convex: one bowl, one bottom. Set η = 2.00 and the trace lurches upward for a few steps — and still lands there, because there is nowhere else to land.
Fig. 8. Set the gradient to zero and w stays trapped inside the sigmoid — no closed form. It does not matter: log-loss is convex, so three random starts walk down to the same minimum, using the same residual gradient X′(p̂ − y) you have run since Chapter 20.
It does not matter, because the loss is convex in w. Chapter 4 told us a convex function has one minimum and nowhere else to be, so Chapter 8's gradient descent walks straight to it. And the gradient is worth staring at, because it is ∇L = (1/n)·Xᵀ(p̂ − y). That is the same shape as the OLS gradient, with the residual sitting in the middle. Same machine, same descent, different link.
05The model's job ends at p̂
The model is finished, so hand it a borrower and it returns 0.031, which is a complete and honest answer. Notice what has not happened, because nothing has been classified.
To get a decision you have to compare p̂ against a cutoff t and flag everything above it. The moment you do that you have left statistics and entered economics, because t is not estimated from data. It is priced.
Everybody defaults to t = 0.5 and hardly anybody says why, so let us find out when 0.5 is right. You are screening loan applications. If you approve a loan that defaults you lose the principal, call it $50,000. If you refuse a loan that would have been fine you lose the interest, call it $500. Both are mistakes and they are not the same size of mistake.
Before the arithmetic, commit yourself and write down the cutoff you would actually use. Most people write something between 0.3 and 0.5, and a few write 0.2 and feel daring about it.
Now compute, using nothing but Chapter 11's expectation. Approving costs $50,000 with probability p̂, so its expected cost is p̂ × 50,000. Refusing costs $500 with probability (1 − p̂), so its expected cost is (1 − p̂) × 500. Refusing is the better action exactly when p̂ × 50,000 > (1 − p̂) × 500, and solving that gives p̂ > 500/50,500.
The cost-optimal threshold on this problem is 0.0099, which is about one percent and not fifty.
Commit your cut-off before you see the money — then drag, and let the two price tags tell you where the line belonged.
pick a cut-off, then lock it in
What you're looking at — 187 loan applications, laid out by the model's score p̂
a borrower who did default — left of the line it slipped through: a miss, price CFN
a borrower who repaid — right of the line you refused them: a false alarm, price CFP
the cut-off t, and below it the total bill for the whole book at every t
the line you committed to before any of this was priced
Fig. 9. The model's job ends at p̂, the estimated chance this borrower defaults. A decision needs a cut-off, and the cut-off is not estimated — it is priced: t* = CFP/(CFP+CFN). When a missed default costs $50,000 and a wrongly-refused good borrower costs $500, the cheapest line sits at 0.0099 — you refuse four applicants in five. Set both mistakes to $500 and the same formula returns exactly 0.500. That is the only thing 0.5 has ever meant: my two mistakes cost the same. Accepting it silently is a claim about your business, not a neutral default. (The score axis is stretched near zero so the crowded low-risk end stays readable.)
In general t* = C_FP/(C_FP + C_FN), so setting the two costs equal returns exactly 0.5. So the universal default is not neutral at all — it is a specific claim that your two mistakes cost the same, and in finance that is almost never true.
The gut resists this, and it is worth naming why. Flagging a hundred good borrowers to catch one bad one feels like generating false alarms on purpose. It is simply correct when the bad one costs a hundred times more, and $50,000 divided by $500 is exactly one hundred.
This gives us the separation the whole second half of the chapter needs. The score is what the model produces, and it is the model's business. The decision is what you do with the score, and it is the desk's business. They are computed by different people from different information. A great many broken production classifiers are the two of them welded together at 0.5 by nobody in particular.
06Four boxes, and nowhere else to go
Fix a threshold and every row now carries two bits: what was actually true, and what you said. Two bits give four combinations, and there is nowhere else for anything to go.
Do it by hand before any vocabulary shows up. Take the twelve loans, four of which really defaulted, flag everything scoring above 0.30, and sort them into four piles. Really defaulted and I flagged it. Really defaulted and I missed it. Was fine and I flagged it anyway. Was fine and I left it alone. The piles come out at 3, 1, 2 and 6, and they add to twelve, because they have to.
Arrange those four counts in a grid with the truth down the side and your call across the top. That grid is the confusion matrix, and it is the complete accounting of a thresholded classifier. Nothing is thrown away and nothing is added.
Twelve loans, one threshold, four boxes — and every box has three names. Sort the pile and read them off.
1 · BUILD ITtap
2 · JUMP4 steps
3 · SELF-TESTtap a box
“The loan was fine and you flagged it.” Which of the four boxes is that?
THE NAMING RULE 2nd word = what you SAID 1st word = were you RIGHT
12 loans, one stack. Tap APPLY.
What you’re looking at — truth has 2 values, your call has 2 values, so there are exactly 2×2 = 4 outcomes. Not three, not five. Four.
what was TRUE (down the side) — the blue dot on a chip means that loan really defaulted. Fixed by the world; nothing you do changes it.
what you SAID (across the top) — flagged or cleared, decided entirely by the threshold t.
“true…” = you were right — true positive (a hit), true negative (a correct rejection).
“false…” = you were wrong — false positive = Ch17’s Type I error = a false alarm; false negative = Type II = a miss. “False” never describes the loan — it describes your claim about the loan. That one line is the whole of the confusion matrix, in all three vocabularies.
Fig. 10. Cross what was true with what you said and there are exactly four outcomes — no more, no fewer. The naming rule ends the confusion in one line: the second word is what you said, the first word is whether you were right. So a false positive is a loan that was fine which you flagged — and it is also Chapter 17’s Type I error, and also a false alarm: one idea, three vocabularies. Then move t from 0.30 to 0.15 on the same model and watch all four numbers change — the confusion matrix belongs to the threshold, not to the classifier.
Now the names, with a rule that makes them permanent. The second word is what you said, and the first word says whether you were right. A false positive means you said positive and you were wrong. A false negative means you said negative and you were wrong. True positive means you said positive and you were right. There is no fifth case and no ambiguity, and you never have to look this up again.
Test it on the case that trips everyone. The loan was fine and you flagged it, so which box? You said positive, so the second word is positive, and you were wrong, so the first word is false. False positive. The reason this is hard without the rule is that "false" feels like it should describe the truth, when it actually describes your claim.
Then weld this to something you already own, because it is the same idea in different clothes. Chapter 17 gave us Type I error, the false alarm, rejecting a null that was true. That is the false positive. It gave us Type II error, the miss, failing to detect a real effect. That is the false negative. One concept, two vocabularies, taught in two different rooms by two different traditions. Statistics says Type I, machine learning says false positive, medicine says false alarm, and they are the same box in the same table.
One last observation, and it drives the next two sections. Every number in this table depends on t. Drop the cutoff to 0.15 and the same model on the same rows gives 4, 0, 4 and 4 instead. The table is not a property of the model — it is a property of the model and a decision.
07★★ Two directions through the same four numbers
Before anything else, a prediction, and please write it down. Your fraud detector has been evaluated on a million transactions, a hundred of which were fraud. The model caught ninety of the hundred, so its recall is 90%. It is also stingy, and flags only 1% of the perfectly good transactions.
Of everything this model flags, what fraction is actually fraud?
Most people write something between 80% and 95%. Ninety percent caught and one percent false alarms sounds like a strong model from both sides.
Now count, rather than reaching for a formula, because counting cannot be argued with. There were 999,900 good transactions, and 1% of those is 9,999 false alarms. Set that against 90 real catches, and of 10,089 flags exactly 90 are fraud.
A million transactions · 100 are fraud · it catches 90 (recall 90%) and flags 1% of the good ones. Before you read on: what fraction of its flags is real?
Of every 100 things it flags, how many are really fraud? Commit, then we count.
%
Your number gets pinned here, beside what the counting says.
recall — never twitches—
precision — slides—
flags raised—
write a number, then lock it in
What you're looking at — one table, two directions, and the base rate as the exchange rate
along the row — P(flag | bad) = recall. Denominator: the truly bad. It never moves.
down the column — P(bad | flag) = precision. Denominator: everything you flagged.
the 90 — the one cell both directions share. Same number, two stories.
1% of a huge crowd is 9,999 — it swamps 90 real catches. Relabel it and it is Ch 10's disease test.
Fig. 11. Commit first: with recall 90% and a 1% false-alarm rate on a million transactions, what fraction of this model's flags is real? Lock in your guess, then watch the counting — 999,900 good transactions, 1% of them is 9,999 false alarms, against 90 real catches. Then sweep the same four numbers twice: along the truth row you get P(flag | bad) = recall 90%, a triumph; down the prediction column you get P(bad | flag) = precision 0.89%, noise. Nothing changed but the direction you read them in. Drag the base rate and precision slides from 98.9% to 0.89% while recall never twitches — the base rate is the exchange rate between the two conditionals. Relabel the table and it is Ch 10's disease test, the same object with different words on it — and you got that one right, because somebody told you to look at the denominator.
That is 0.89%, which means this model is wrong about ninety-nine times out of a hundred every single time it opens its mouth. Its accuracy, meanwhile, is 989,991 out of a million, or 99.0%. And a model consisting of the single word "no" scores 99.99% and beats it.
Nothing went wrong, and there is no error anywhere in that table. What changed is the direction you read it in.
Read along a row of the truth, taking the transactions that really were fraud, all hundred of them, and asking what fraction you flagged. That is TP/(TP+FN) = 90/100, and the denominator is a truth row. So the question is "given that it was bad, did I catch it". It has three names because three fields invented it separately: recall, sensitivity, and the true positive rate. Written honestly it is P(flag | bad).
Now read down a column of your own predictions, taking everything you flagged, all 10,089, and asking what fraction really was bad. That is TP/(TP+FP) = 90/10,089, and the denominator is a prediction column. The question is "given that I raised the alarm, was I right". It is called precision, and honestly written it is P(bad | flag).
Stare at those two, P(flag | bad) and P(bad | flag): the same two events, conditioned in opposite directions. These are Bayes-inverses, and Chapter 10 already told you exactly what joins them: P(bad | flag) = P(flag | bad) · P(bad) / P(flag).
The base rateP(bad) is the exchange rate between the two directions. That is why they can come apart so violently. Hold recall at 0.90 and the false-alarm rate at 1%, then slide the base rate down. Precision reads 0.9890 when half the population is bad, 0.4762 at a base rate of 1%, and 0.0089 at one in ten thousand. Recall never moves at all, because its denominator never contained the base rate, and precision's denominator is made of almost nothing else.
You have solved this before, and you got it right. Chapter 10's disease test was this table with different words on it, and the reason you got it right there is that somebody told you to look at the denominator.
Now look at accuracy, (TP+TN) over everything, and notice what is missing. It has no conditioning direction at all, because it is a raw tally over the whole population, which means it is owned by whichever class is bigger. On a rare-event problem, accuracy is a measurement of the base rate wearing a model's clothes. The accuracy paradox is not a warning to remember — it is Chapter 10's base-rate neglect with a job title.
Two more definitions fall out of the same reading, so take them while the table is in front of you. Specificity is TN/(TN+FP) = P(no flag | good), which is the other truth row. And the false positive rate is FP/(FP+TN), which is that same row read the other way, so FPR = 1 − specificity.
Finally F1, the harmonic mean 2PR/(P+R), and there is an honest reason for the harmonic mean rather than the ordinary one. Take a model with precision 0.01 and recall 1.00, which is a machine that flags literally everything. The arithmetic mean says 0.505, a respectable-looking number for something that has learned nothing. The harmonic mean says 0.0198.
One pair of numbers, three different means — and only one of them refuses to be rescued by the healthy half.
all three agree — nothing hidden
What you're looking at — one pair (P, R), scored three ways, on a square of every pair there is
The dot is your model. P = of everything you flagged, how much was real; R = of everything real, how much you caught.
Arithmetic level sets are straight lines — a 1.00 pays for a 0.01 at face value.
Geometric √(P·R) bends a little.
Harmonic level sets bend into the corners, so the bar stays glued to the smaller number. That, and nothing else, is why F1 uses it.
Fig. 12. The same two numbers, averaged three ways. Press the model that flags everything: precision 0.01, recall 1.00, and the arithmetic mean hands it a comfortable 0.5050 while F1 says 0.0198. The underlay shows why — straight level sets let the healthy number buy off the sick one; curved ones never do.
That is not a convention, because the harmonic mean cannot be rescued by the healthy member of a pair, and it always sits near the smaller one. That single property is the entire reason to use it.
08Stop choosing a threshold — sweep it
Everything in the last section was computed at one threshold, and we already know the threshold was a choice. So stop choosing, and instead sweep t from 1 down to 0 and watch the table evolve.
At t = 1 you flag nothing, so recall is 0 and there are no false alarms. At t = 0 you flag everything, so recall is 1 and precision has collapsed to the base rate. Every cutoff in between is one point, and the points trace a curve. Plot FPR against TPR and you have the ROC curve. Plot recall against precision and you have the precision–recall curve.
One knob, two curves — and one of them is lying to you. Before you drag: when fraud gets ten times rarer, which curve slumps?
PREDICT · THEN drag t ←
THRESHOLD t0.55
BASE RATE1 in 10
DENOMINATOR INSPECTOR
RARER FRAUD — WHICH SLUMPS?
prec 0.504 · recall 0.834
What you're looking at — 100,000 transactions, one score per transaction, and one dial. A transaction is flagged when its score clears the threshold t, so every count in the table, and therefore every metric, is a function of t alone.
ROC plots (FPR, TPR). TPR = TP/(TP+FN) divides by the fraud row; FPR = FP/(FP+TN) divides by the legit row. Rows are set by the truth, so the base rate π multiplies top and bottom alike and cancels — the curve is structurally blind to rarity.
PR plots (recall, precision). Precision = TP/(TP+FP) divides by the FLAG column — a column you built. Make fraud rarer and that column fills with false alarms, so precision slumps. The dashed line is π: both the coin-flip baseline and exactly where the curve must land at recall = 1.
the travelling dot is the one t you have chosen; the bright trail is every threshold above it. Watch accuracy too — at 1 in 1000 the do-nothing model scores 0.9990, which is the whole reason these curves exist.
Fig. 13. One hundred thousand transactions, one score each, and a single knob. Slide t left and the flagged set grows: every count in the table moves, and with it every metric — which is the first thing to accept, that a classifier has no single “accuracy”, only a curve. Sweep from t = 1 (flag nothing) down to t = 0 (flag everything) and the two curves paint themselves in behind the gold dot. Now the part that matters. Start at 1 in 10 with t = 0.55: TPR 0.834, FPR 0.091, precision 0.504. Predict which curve moves when fraud becomes ten times rarer — then drag BASE RATE to 1 in 100. The ROC dot does not move at all, AUC stays 0.950, and precision collapses to 0.083; carry on to 1 in 1000 and precision is 0.009 — ninety-nine flags in a hundred are innocent — while the ROC panel reports exactly the same two numbers it reported before. Press PREC in the inspector to see why. TPR and FPR each divide by a row of the table, and a row is fixed by the truth, so π appears in the numerator and the denominator alike and cancels: π·TPR / π·1. Precision divides by the FLAG column, and a column mixes both rows — πTPR / (πTPR + (1−π)FPR) — so as π shrinks the (1−π)FPR term swamps it. That is the whole of the folklore: ROC is not merely optimistic under imbalance, it is structurally incapable of noticing that your flags are drowning in false alarms, because prevalence was divided out before the plot was drawn. Note the dashed violet line too — it is the base rate, and it is simultaneously the random-scorer baseline and the exact value the PR curve must fall to at recall = 1, which is why a PR curve at 1 in 1000 lies on the floor. And glance at the accuracy readout while you work: at 1 in 1000 the model that never flags anything scores 0.9990, better than anything you can reach here. That is the trap this whole chapter exists to disarm.
Now the part that turns folklore into mechanism, and it is one sentence long. Look at the denominators of the ROC's two axes. TPR = TP/(TP+FN) is a truth row, and FPR = FP/(FP+TN) is the other truth row. Both coordinates are conditioned on the truth, so the count of positives and the count of negatives each divide out of their own axis.
That means the base rate cancels, and the ROC curve barely notices when a problem becomes a hundred times rarer. This is not a defect being hidden. It is genuinely what you want when you need a prevalence-independent view of a scorer. But it is exactly why ROC flatters a model on a rare-event problem, because it is structurally incapable of seeing that your flags are now drowning in false alarms.
Precision's denominator is TP+FP, a prediction column, and prediction columns fill up with false alarms as the problem gets rarer. So the PR curve collapses and reports the damage. That is the whole of "use PR for imbalanced problems", derived instead of remembered.
Then the areas. AUC-ROC has an exact interpretation, and it is worth reading twice: it is the probability that a randomly chosen positive receives a higher score than a randomly chosen negative.
AUC is not a curve fact — it is a pair count. Draw the sixteen pairs by hand and watch the shaded area assemble itself.
Every positive is compared with every negative — 4 × 4 = 16 pairs. Draw one, or click any empty cell in the ROC square.
AUC (area)0.875log-loss0.480
16 pairs · none drawn yet
What you’re looking at — one pair, one cell, and the area is just the tally
positives (the four frauds) and negatives, stacked by rank. A pair is concordant when the positive sits higher: green, else red. AUC = P(score of a random positive > score of a random negative).
the ROC staircase cuts the unit square into exactly those 16 cells — each is 1/4 × 1/4 of the area. Green cells land under the curve, red ones above. So 14/16 = 0.875 twice over. Dashed = random, 0.50; on a PR curve the random baseline is instead the base rate, here 4/8 = 0.50.
the honesty panel. Model B keeps the identical order with every score squashed into 0.01–0.03, so AUC never moves — while calibration falls off the diagonal and log-loss quadruples. Ranking and calibration are two different virtues, and only the second one can be spent on a threshold.
Fig. 14. AUC, taken apart into the only thing it has ever been: a count of pairs. Four positives and four negatives stand in two ranked columns, and every positive must be compared against every negative — 4 × 4 = 16 pairs, no more and no fewer. Draw one and a link is struck between the two chips: green when the positive outranked the negative (a concordant pair), red when it did not. That is the entire definition — AUC = P(a random positive scores above a random negative) — and the running tally is your own estimate of it. Now look right. The ROC unit square has been cut into exactly those sixteen cells, each one quarter by one quarter, because four positives means the curve can only ever step up in quarters and four negatives means it can only step right in quarters. Every green cell falls under the gold staircase and every red one above it, so when the sixteenth pair lands, the shaded area and the pair count print the same number: 14/16 = 0.875. The area was never a separate fact. Then flip to model B, whose scores are squashed into 0.01–0.03 in the identical order. Nothing on the left moves — not a chip, not a link, not a cell — and so AUC cannot move either. But the calibration points collapse onto the left wall, log-loss goes from 0.480 to 1.913, and the banner turns red: 0.02 is not a 2% chance of fraud, so the cost-optimal threshold of the previous figure — which spends the probability, not the ranking — has nothing honest to spend. That is how a 0.98 AUC can be worth nothing you can act on. (Baselines: a coin-flip model traces the dashed diagonal, AUC = 0.50; on a precision–recall curve the coin-flip baseline is instead the base rate, here 4/8 = 0.50, and under real imbalance that floor drops to 0.001 while ROC's stays flatteringly at 0.50.)
So AUC measures ranking, and nothing else. A model whose probabilities all sit squashed between 0.01 and 0.03, but in perfect order, scores an AUC of 1.0 and is useless for the threshold arithmetic we just built. A perfectly calibrated model that ranks badly scores poorly on AUC while its numbers are entirely honest. Ranking quality and calibration quality are two different virtues, and the phrase "a good model" hides which one you meant.
Last, the baselines, without which no AUC number can be read at all. A coin-flip model traces the diagonal and scores 0.5 on ROC. That same coin-flip model scores an AUC-PR equal to the base rate. So AUC-PR of 0.10 on a problem with a 0.01% base rate is a thousand times better than random and might be excellent, while AUC-ROC of 0.75 on the same problem may be worth nothing. Always quote the baseline beside the number.
09Imbalance, in order of honesty
Now we can say something useful about imbalance, and the order matters more than the list. Most readers meet a rare-event problem and reach straight for SMOTE, because resampling is what tutorials teach and because "the data is imbalanced" sounds like a data defect. Usually it is not.
Remedy zero, which solves a large share of real cases: change nothing about the data and fix the scoring instead. Stop reporting accuracy. Report precision and recall at a stated threshold, plus AUC-PR against its base-rate baseline. Set the threshold from costs rather than from habit. A great deal of "the model can't handle imbalance" turns out to mean "we scored it with accuracy and cut it at 0.5", and both of those are now fixed.
Remedy one is class weights. Multiply the minority terms in the log-loss by a factor, so the loss becomes −(1/n) Σ [ w₁·y·ln p̂ + w₀·(1−y)·ln(1−p̂) ], and a missed positive hurts more during training. Two things are worth noticing. It is not a new idea, because it is the cost asymmetry from the threshold section moved out of the decision and into the objective. And it is mechanically the same as duplicating each minority row w₁ times, which demystifies both at once.
Remedy two, and last, is resampling. Undersample the majority, which is cheap and throws information away, or oversample the minority, which invites overfitting to the exact rows you duplicated. Or use SMOTE, which manufactures synthetic minority rows by interpolating between a minority point and its near neighbours. All three change the class ratio the model sees.
Now the two warnings, and they are the actual content of this section, because the remedies are in every tutorial and the warnings are in almost none.
First: reweighting and resampling break calibration, and they break it by design. You deliberately changed the prior the model was trained under, so p̂ no longer means what it says. A model trained on a rebalanced 50/50 sample reports default probabilities near 0.5 for a book whose true default rate is 1%.
CodeRun — this really ran. One book, 1% defaults, the model fitted three ways. Predict the last column, then RUN.
Rebalancing to 50/50 does catch far more defaults. But p — the probability the model reports for a loan — is what actually prices the book. Guess the mean p it now returns. Truth on this book: 0.0104.
read the run
drag your guess, then RUN
What you're looking at — the same fitted model read three ways, and only the last column notices the damage
moving the cut to 0.0098 buys recall 0.697 for free: identical AUC, mean p untouched at 0.0103.
refitting 50/50 buys LESS recall (0.670), and mean p jumps to 0.3757 — 36× the truth.
the one-line check: print mean p beside the observed base rate. Recall, precision, F1 and AUC are all blind to it.
Fig. 15. Thirty thousand synthetic loans, 1.04% of them defaulting, and the same logistic model read three ways on the same 10,500 held-out rows. Row A is the plain fit at the textbook cut of 0.50, and it is the useless classifier from the top of this chapter, discovered by accident: it flags nobody, recall 0.000, precision undefined because you cannot ask "of those I flagged, how many were real" when you flagged none — and it is 98.96% accurate. Row B is the identical fitted model with nothing changed but the cut, moved from 0.50 down to 0.0098: recall 0.697, and the AUC is bit-for-bit the same 0.782 because moving a threshold cannot reorder anything. Row C is the fix everyone reaches for — refit on a 50/50 rebalanced sample — and here is the sting: it catches fewer defaults than B (0.670 against 0.697), its AUC is slightly worse (0.777), and its mean predicted probability is 0.3757 against an observed base rate of 0.0104. Thirty-six times too high. Every metric you were taught to check is either threshold-based (recall, precision, F1) or rank-based (AUC, PR), so every one of them is structurally blind to a uniform shift in the probabilities themselves; you can be wrong about the level by a factor of fifty and pass every dashboard in the building. The reason is one line of Chapter 10: fitting on a 50/50 sample changes the prior, so the model's output is no longer P(default | features) on your book but on an imaginary book where half the loans default. Part 2 turns that into money — the same 10,500 loans, $25,000 exposure each, 45% loss given default: the plain model prices the book at $1,213,884 against $1,226,250 that actually burned, and the rebalanced model prices it at $44,375,611. Part 3 is the second trap. Run SMOTE across the whole dataset and then split, and validation AUC reads 0.977; run the same SMOTE inside the fold on training rows only and it reads 0.571. The synthetic minority points are interpolations between real minority rows, so when the split comes afterwards a point's parents end up in training while the child sits in validation — you are testing on smeared copies of what you trained on, which is Chapter 21's leakage in a new costume. So the order of honesty runs: fix the metric first, then the threshold (free, and it kept the probabilities intact), then class weights or resampling only if you still need them — and if you use either, recalibrate, or at minimum print mean p beside the observed base rate before anyone ships.
If your deliverable is a ranking, that may be fine. If the deliverable is the probability itself, you have just destroyed the product to improve a metric. In credit risk the probability is the product, because PD feeds straight into expected loss, and Chapter 32 will spend that number. The fix is to not resample, or to recalibrate afterwards by correcting for the prior shift.
What makes this so dangerous is that it is invisible. Every metric you were taught to check is either threshold-based or rank-based, so precision, recall, F1 and AUC are all completely blind to a global shift in the probabilities. The one-line check is to print the mean predicted probability and compare it to the observed base rate.
Second: resampling must happen inside the cross-validation fold, after the split, never before. Oversample first and copies of the same row land in both training and validation. Run SMOTE first and a synthetic point interpolated from two training rows sits in your validation set. Your score goes up and it is a lie. That is Chapter 21's data leakage in a new costume, and it is one of the most common ways a rare-event model ships broken.
So the correct pipeline is an ordered list, and it is worth copying down. Split first. Resample the training part only. Fit. Then score the untouched validation part.
10Two hundred features that are secretly three
The first half of this chapter fixed what the model outputs, and this half fixes what goes in.
Hand our logistic model a realistic feature set: twenty points along a government yield curve, say, or two hundred variables from a credit bureau. Something breaks that has nothing to do with probabilities or thresholds.
Fit the model on the first half of the data and read the coefficients, then fit it on the second half and read them again. The two-year weight is strongly positive in one fit and strongly negative in the other. The three-year weight moves the opposite way by almost exactly as much. And the fitted probabilities barely change at all.
That last part is the tell, and Chapter 20 already named the cause. This is multicollinearity. When two features are near-duplicates the model cannot tell which one is doing the work, because only their sum is determined by the data, and any split of that sum fits equally well. Chapter 6 said a near-singular matrix collapses space, so undoing it amplifies noise. Chapter 16 said a statistic is itself a random variable, and this one has an enormous spread.
The result is a model that is simultaneously predictive and uninterpretable, which is fatal anywhere you must justify a decision. Add Chapter 21's p ≫ n on top, two hundred features against three hundred observations, and there is not enough data to pin anything down at all.
Twenty tenors into one logistic model — the coefficients tell two opposite stories while the prediction never budges.
One dataset, split in two, twenty tenors each. Predict: how far apart are the two fits' probabilities?
your prediction
sign flips—
widest coef gap—
max |Δp|—
predict, then watch the curves
What you're looking at — a model that predicts beautifully and explains nothing
fit on the first half of the data — and, in band 2, the daily curve moves
fit on the second half: same model, same kind of data, a different story
tenors whose coefficient changed sign — the explanation reversed
the number to watch: the fitted probabilities move by 0.011 at most
Fig. 16. Twenty tenors go into one logistic model, and Chapter 20's multicollinearity walks straight back in. Band 1 — the damage. One labelled dataset, split down the middle; the same model fitted on each half. The blue bars are the twenty coefficients from the first half, the violet bars the twenty from the second, each bar measured left or right of its own zero line. They disagree wildly: the widest single coefficient moves by 1.57, and six tenors marked in red do not merely shrink — they change sign, so a tenor that pushed the predicted default probability up in one half pushes it down in the other. Predict what that does to the predictions, then look: the two fitted-probability curves over the same 44 borrowers lie on top of each other, and the largest gap anywhere is 0.011. Two opposite explanations, one identical forecast. That is the disease in one picture — the model is predictive and its explanation is worthless, which is fatal the moment you must justify a decision to a regulator, a credit committee, or a rejected applicant. Band 2 — the cause, shown rather than asserted. Five days of real-shaped yield-curve moves, each a twenty-point line. Peel off "the whole curve moved up or down" and 88 per cent of the wiggle is already gone; peel off "the short end moved against the long end" and you are down to about one per cent; peel off "the belly bulged" and the lines lie flat on zero. Those twenty columns were never twenty independent facts — they mostly move in three ways, and we are naming these level, slope and curvature purely as things observed in the data, because nothing here has been derived yet. Band 3 — the quantitative shadow. The 20×20 correlation block of those daily moves is nearly solid: 123 of the 190 distinct pairs sit above 0.90, and the least-related pair in the entire matrix — the 3-month against the 40-year — still correlates 0.60. Hover any cell to read its pair. Least-squares and maximum likelihood both need the columns to carry independent information; when they carry three directions' worth wearing twenty names, the fitted coefficients are free to slide along a flat ridge, and which point on that ridge you land on is decided by noise. Which sets up the exact question the next section answers: what are those three directions — found from the data itself, not from a story about bonds?
Chapter 22 already gave us one answer, which is to shrink: ridge stops the coefficients running to ±∞ in opposite directions, and lasso zeroes most of them outright. Both are real answers and neither is wrong.
But there is a completely different answer available, and it starts by noticing something about the data rather than about the model. Look at how a yield curve actually moves from one day to the next. Overwhelmingly, the whole curve shifts up or down together, and every tenor moves the same way. Sometimes the short end goes one way while the long end goes the other, so the curve steepens or flattens. Occasionally the belly bulges relative to both ends.
Those three motions have names on every rates desk: level, slope and curvature. Between them they account for almost everything that ever happens to the curve. So the twenty numbers you were handed were never twenty independent facts. They are about three real degrees of freedom wearing twenty costumes, and the correlation matrix is the shadow that redundancy casts.
Which gives us the question the rest of the chapter answers. If the data really only moves in three ways, what are those three directions, and how would we find them from the data itself rather than from a story about bond markets?
11The direction that carries the movement
"Find the directions the data actually moves in" is a wish, not a definition. So make it precise, and watch it turn into something we already own.
A direction is a unit vectoru. Project every centred observation onto it and you get one number per row, u·x, which is how much of that direction is present in that row. Now ask how much those numbers vary. Chapter 18's covariance matrix gives the answer in one expression, Var(u·x) = uᵀΣu.
A matrix sandwiched between two copies of a vector is a genuine shock the first time, so ground it before we use it. Take u = (1,0), which is the first feature's own axis. Then uᵀΣu picks out Σ₁₁, the variance of feature one. So this is not a new object at all. It is the variance in a direction, and the direction happens to be one you can point at.
Now "the direction that carries the most movement" has an exact meaning. Maximise uᵀΣu, but not without a constraint, and the reason for the constraint matters. Double the length of u and uᵀΣu quadruples, so without a constraint you would just make u longer forever and learn nothing about direction. Fix the length: uᵀu = 1.
Turn the line until the shadows spread widest — then look at what number you just maximised, and what it turned out to be.
predict: where does spread peak?
What you're looking at — asking for the widest spread, and getting an eigenvector handed back
48 readings of two features x and y, already centred on the origin
u, the direction you turn — the dashed circle is the constraint uᵀu = 1
each point's shadow on the line, and the rose = their variance at every angle you've visited
the peak: the trace touches the outer ring, and uᵀΣu = λ₁
Fig. 17. Every direction u gives a number, uᵀΣu — the variance of the shadows the data casts on that line. Ask for the unit direction that makes it biggest and the Lagrange condition hands you Σu = λu: the eigenvector was never chosen, and λ is that variance in your data's own units.
That is a constrained optimisation, which is Chapter 8's Lagrange multipliers, and it is four lines. Form the Lagrangian uᵀΣu − λ(uᵀu − 1). Differentiate with respect to u to get 2Σu − 2λu = 0, then rearrange, and out comes Σu = λu.
Stop and look at what just happened. We asked a purely statistical question, which direction has the biggest spread, and out fell the eigenvector equation from Chapter 7. We met that as pure linear algebra with no statistics anywhere near it. Nobody chose eigenvectors here — they were forced by asking for maximum variance at unit length.
There is a second gift in the same four lines. Substitute the solution back into the objective: uᵀΣu = uᵀ(λu) = λ(uᵀu) = λ. The value of the thing we were maximising, at the optimum, is exactly the eigenvalue.
So λ is not an abstract stretch factor here. The eigenvalue IS the variance of the data along its own eigenvector, measured in the units of the data. That one identity is why every sentence ever written about PCA mentions eigenvalues, and why "this component explains 62% of the variance" is a literal statement rather than a figure of speech. That direction has a name: the principal component.
12The rotation that makes the covariance diagonal
One direction is not enough, so keep going. The second principal component is the max-variance direction among those orthogonal to the first, the third is orthogonal to both, and so on down.
That sounds like a fresh constrained problem every time, and Chapter 7 already spared us the work. Σ is symmetric, so its eigenvectors are automatically orthogonal, which means the eigenvectors sorted by descending eigenvalue are that sequence. And because Σ is positive-semidefinite, every eigenvalue is at least zero, which is the only sane outcome given each one is a variance.
So here is the whole algorithm, in seven moves. Centre the data, then standardize it, form Σ, and eigendecompose. Sort λ₁ ≥ λ₂ ≥ …, keep the top k, and project.
Six steps of PCA on one small cloud — watch the covariance grid beside it change
Each column loses its own mean.
Σ won't change — only the cross moves.
PREDICT: 2Y in basis points (×100). PC1 then…
Standardized, so Σ is the correlation.
Both tenors move together: 0.94.
Eigenvectors of Σ = max-variance axes.
λ = the variance along that axis.
Rewrite every day in the new basis.
Watch the off-diagonal fall to 0.00.
Two things share the word "component".
p = 20 tenors. n = 40 days.
step 1 — press: subtract mean
What you're looking at — PCA is one rotation, and the grid on the right is the proof
blue = the data (12 days of 2Y and 10Y yield moves) and the two variances on Σ's diagonal
gold = the number to watch: the off-diagonal covariance, 0.94 before the rotation and 0.00 after
violet = PC1, the first new axis — its length is the spread √λ₁, its label prints the variance λ₁
cyan = PC2, exactly perpendicular because Σ is symmetric — that is Ch7's theorem doing the work
Fig. 18. The whole algorithm, one step per button. Centre (means print 0.00, and note Σ doesn't budge — centring only moves where the axes cross). Scale: predict what happens to PC1 if the 2Y column is written in basis points instead of percent, then press ×100 — the loading bar swings to 1.00 / 0.01 and PC1 becomes "whichever column has the biggest units", until standardize puts it back to 0.71 / 0.71. Form Σ: off-diagonal 0.94, the two tenors moving together. Eigendecompose: λ₁=1.94, λ₂=0.06, and they sum to the trace, 2.00 — so PC1 is literally 97% of the total variance. Project: the cloud rotates onto those axes and the off-diagonal counts down 0.94 → 0.00. That zero is the point: because Σ is symmetric its eigenvectors are perpendicular, so in the new basis the covariance matrix is diagonal and the components are uncorrelated by construction. PCA is nothing more, and nothing less, than the rotation that diagonalises Σ. Step 6 separates the two things people both call a "component": the loading (one weight per tenor — a recipe, length p) and the score (one number per day — an amount, length n).
Three parts of that deserve more than a mention, and the first is a word that names two different objects. A loading is a direction: an eigenvector, a fixed recipe of length p over the original features. A score is a number per observation: the projection of one row onto that direction, of which there are n. People call both of them "the component", which is why nobody can say whether PC1 is a vector of length p or of length n. It is both, in two different senses, and keeping the words apart fixes it.
The second is standardization, which is not optional. PCA maximises variance, and variance has units. A feature recorded in basis points has a variance ten thousand times larger than the same feature in percent, purely because of the scale. Skip Chapter 22's z-scoring and PC1 is simply whichever column happened to be written in the smallest units. Nothing errors, the scree plot looks fine, and the answer is wrong.
The third is the payoff, and it is the real reason to do any of this. In the new coordinates the covariance matrix is diagonal. Projections onto different eigenvectors have zero covariance with each other, by construction, because Σ diagonalises in its own eigenbasis. The components are uncorrelated, which cures exactly the disease that sent us here. One line describes the whole method: PCA is the rotation that diagonalizes the covariance matrix.
That also settles how much to keep. Total variance is Σλᵢ, which equals trace(Σ), which equals the sum of the individual feature variances. So λ_k/Σλ is a genuine fraction of the total variability rather than a metaphor. Plot the sorted eigenvalues and you get the scree plot, and on real yield-curve data the first three bars typically account for something like 99% of daily variation. Level, slope and curvature reappear as a number the data produced by itself.
One practical note from Chapter 7 that matters more than it looks. In production you never form Σ at all. You take the SVD of the centred, standardized data matrix, whose right singular vectors are exactly these eigenvectors and whose singular values squared over (n−1) are exactly these eigenvalues. Forming Σ squares the data, which squares the condition number, and Chapter 7 told us what that does to accuracy.
13What PCA never looked at
Now the honesty, because PCA has one blind spot and it is enormous. It never looked at y.
Go back through the derivation and check. Every line used Σ, the covariance of the features. The labels were not in the room at any point, which is what it means to say PCA is unsupervised. "The direction with the most variance" and "the direction that separates the classes" are two different requests, and they coincide often enough that people stop distinguishing them.
Here is the smallest picture that makes the failure permanent. Two features, two classes. The cloud is enormously spread along one axis, with both classes fully mixed along it. Along the other axis, the narrow one carrying a few percent of the variance, the two classes sit in clean separated bands.
Keep the direction that explains 96% of the spread — and watch the entire signal disappear.
Ninety-six per cent of this cloud’s spread lies along PC1. Predict: keeping only PC1, how well can any classifier possibly do?
your prediction
variance kept—
best accuracy—
coin flip50.0%
predict, then press a button
What you’re looking at — a compression that keeps the loud direction and deletes the useful one
class 0 — the loans that were repaid
class 1 — the loans that defaulted
PC1, the direction of greatest variance — the classes are perfectly interleaved along it
PC2, the short direction — 4% of the spread, and it carries the entire separation
Fig. 19. The cloud on the left is eighty-eight borrowers plotted on two measured features, coloured by what actually happened: blue repaid, violet defaulted. Principal component analysis is run on it properly — the covariance matrix of Chapter 18, the eigenvectors of Chapter 7 — and the two axes it returns are drawn straight onto the data. PC1, in red, is the long one: it carries 96 per cent of the total variance, which is the number every tutorial teaches you to look at. PC2, in green, is the stubby one across it, worth a mere 4 per cent. Now look at where the colours change. They do not change along the long axis at all — blue and violet alternate the whole way down it — they change across the short one. Predict what happens when you do the standard thing and keep the top component, then press. On the right is every point reduced to its single PC1 number, and the two colours are shuffled together beyond repair; the best threshold any classifier could ever draw on that number scores about 51 per cent, which is a coin flip, printed directly beneath a proud "explained variance kept: 96.0%". One click on keep PC2 flips the projection and the strip falls apart into two clean blocks at 4 per cent explained variance — that single click is the entire argument. The reason is not subtle once you have seen it: PCA never received y. It is unsupervised, it optimises spread and nothing else, and it has no way of knowing that your signal happens to live in a quiet direction. So "reducing dimensions improves the model" is not a rule you can apply by habit — explained variance is the features talking among themselves, and a high number is a statement about redundancy, not about usefulness. Panel 2 turns that into the choice you actually make at the desk: three questions, lasso and PCA facing each other, and the answer falling out of the question rather than out of the algorithm. Panel 3 is the recurring sin, wearing the same diagram as the resampling trap earlier in the chapter: the rotation is a fitted parameter, so if you learn the axes from the full dataset before splitting, the test fold has already leaked into them, and your reported 0.94 is really 0.78. Fit the rotation on the training fold, then project the test fold through it. Same rule, third time.
PCA sorts by variance, so PC1 is the useless long axis and PC2 carries the entire signal. Keep "the first component, explaining 96% of the variance" and you have thrown away everything you needed. Your explained-variance number looks excellent while your classifier goes to chance.
So "reducing dimensions improves the model" is not a rule. It is sometimes true and sometimes catastrophic, and the deciding question is whether the signal happens to live in the high-variance directions. Nothing in the algorithm checks that for you.
Three more limits, stated plainly. The components are dense blends of every original feature, so interpretability is gone. You can no longer say "debt-to-income was the driver", only "component 2 was the driver", and component 2 is a recipe with two hundred non-zero ingredients. That is the direct trade against Chapter 22's lasso, which shrinks worse but hands you names. Choose lasso when you must justify a decision to a regulator. Choose PCA when the features are a redundant basis like a curve and what you need is stability.
Next, PCA is linear and it only measures variance. Structure living on a curved manifold, or in a higher moment, is invisible to it, and Chapter 18 already warned that covariance only sees linear co-movement.
Last, and operationally the one that bites: the rotation is fitted from data. Fit it on the training fold only and apply that same rotation to validation and test. Fitting PCA on the full dataset before splitting leaks the test set's covariance structure into the transform. Preprocessing does not feel like fitting, which is exactly why this one slips past people who would never dream of fitting a model before splitting. It is a fit, and it is Chapter 21's sin.
14One machine, both halves
Close the loop the chapter opened. Take the twenty correlated curve features, rotate them onto three principal components, and feed those three into the logistic regression we built at the start.
Look at what that pipeline has. The collinearity is gone by construction, because the components are uncorrelated. The coefficients are stable, and the p ≫ n pressure is gone. And the output is still a calibrated probability you can threshold by cost.
One machine, end to end: twenty columns in, one priced decision out. Before you drag — if a miss costs 100 false alarms, does the cut land near 0.5, or near 0.01?
PREDICT · THEN DRAG ↓
MISS : FALSE ALARM100 : 1
COMPONENTS FED TO THE MODEL
BACK AT 20 COLUMNS, THE FIT IS…
p̂ this applicant0.031
cut t*0.0099
flagged of 10,0002,015
expected cost2,016
coef sign flips0
p̂ 0.031 ≥ t .0099 · FLAG
What you're looking at — one machine: twenty columns in at the left, one priced decision out at the right. Every stage shows its own live artifact, not a label.
the twenty raw columns — correlated, so fitting on them directly is unstable
the components: eigenvectors of the covariance matrix, kept in order of variance
what the machine produces: the log-odds z, then the probability p̂
the cut, and the two mistakes it chooses between — misses and false alarms
Fig. 20. Everything this chapter built, wired into one machine, running left to right. Stage 1 is the raw input: twenty standardised columns for a single loan applicant, and you can see the disease in the bar profile — they all lean the same way, because these twenty columns were never twenty independent facts. Stage 2 rotates them. The three violet curves are the top three eigenvectors of the covariance matrix — flat (a level move), tilted (a slope move), bowed (a curvature move) — and projecting the twenty columns onto them keeps 99 per cent of the variance in three numbers. Stage 3 is the linear part: an intercept b of −4.75 plus one weighted contribution per component, and every one of those weights is measured in log-odds, which is the whole reason this model can still be explained to a credit committee. They sum to z = −3.44, a number on the real line with no upper or lower bound. Stage 4 is the only step that makes it a probability: σ(z) = 1/(1+e−z) squashes the whole real line into (0,1), and the dashed ceiling is the 1 that no linear fit could ever have respected. Note where the dot sits — flat on the floor. A rare event lives in the tail of the sigmoid, at p̂ = 0.031. Stage 5 is where the statistics stop and the business starts. Nothing in the fit tells you what to do with 0.031. Drag the cost slider: the threshold is priced, not chosen, at t* = 1/(1+R) where R is what a single missed default costs in false alarms. At R = 100 the cut lands at 0.0099 and this applicant is flagged; wind R down past about 31 and the same applicant, with the same p̂, is waved through. The model never changed — the price did. Stage 6 is the tally, and it must be read twice. Along the row (click the row line to light it) you are dividing by the applicants who really defaulted: P(flag | default), recall, 0.988. Down the column you are dividing by the applicants you flagged: P(default | flag), precision, 0.049. Same four cells, two different denominators, two different questions — Chapter 10's conditioning for the third and final time. Ninety-five per cent of your flags are false and the cost slider says that is correct, because 1,916 false alarms is cheaper than one more missed default at 100:1. Now push the components control to 20 raw. The rotation is bypassed, the fit gets no better at all (the expected cost lands on the same 2,016), and six coefficients flip sign — the prediction survives, the explanation does not. That is the sentence the two halves of this chapter share: fixing the output and fixing the input are the same instinct applied twice. When a quantity will not behave, you do not fight it in the coordinates you were handed — you find the coordinates where it is simple, and go there. Ahead, in Chapter 32, this same p̂ stops being the answer and becomes an input: multiplied by exposure and loss-given-default, it turns into money.
It has a name, principal component logistic regression, and it is exactly the shape Chapter 32 uses when it turns borrower features into a probability of default and multiplies by loss-given-default to get an expected loss. That is this entire chapter, both halves, doing one job for money.
Two things travel with you out of here. The first is a habit of asking two questions of any classifier somebody hands you. What threshold is this running at? And what do the two mistakes cost? If the answer to the first is 0.5 and nobody can answer the second, the model has not been evaluated yet.
The second is the instinct underneath all three squeezes, and it is the same instinct every time. A probability would not live on the real line, so we moved to the log-odds. A pile of correlated features would not give stable weights, so we rotated to the axes where the covariance is diagonal. When a quantity will not behave, do not fight it in the coordinates you were handed. Find the coordinates where it is simple, and go there.
That closes Part 3. Everything since Chapter 20 has been about estimation, which is the business of saying what returns and defaults are, from data, honestly. Chapter 25 asks a different question and it needs almost none of this machinery. Given that a claim pays off in some future state, what is it worth today? The surprise is where the answer starts. Not with probability, and not with stochastic calculus, but with a single sentence about two portfolios that pay the same.