Classification, Cross-Entropy, and How Not to Fool Yourself
Build a logistic classifier from Chapter 2's loss and Chapter 3's descent, then find out why 98 % accuracy can be a model that finds nothing at all.
On this page
A model that answers this part is fine about every part coming off the belt is right 98.15 % of the time. It is also worthless: of the 74 defective parts in the test set, it catches none.
Both sentences describe the same model. The distance between them is this chapter.
The first half builds the classifier. It needs almost nothing new: Chapter 2 gave the recipe for turning an assumption about how data is produced into a loss function, and Chapter 3 gave the machinery for walking downhill on whatever loss that recipe hands you. Apply both to a yes/no question and logistic regression falls out, plus one new idea — a logit — that will be charged for again in Chapter 17.
The second half is the harder one. Everything after this point in the course is judged by a number that somebody measured, and if you cannot tell a real improvement from a measurement artefact, every chapter that follows is decoration. So: the confusion matrix, precision and recall, the three splits, leakage, and the question almost nobody answers honestly — how many test examples do I actually need?
The arithmetic here runs over 20,000 rows, so it is vectorised throughout — NumPy has been doing the work since Chapter 2, and from here on it stops being worth remarking on.
The belt, with a rarer question
Link to the section: The belt, with a rarer questionSame factory as Chapter 1, harder question. Instead of accept or reject, the question is is this part defective — and defects are rare, which makes the measuring half of this chapter hard and the modelling half deceptively easy.
import numpy as np
rng = np.random.default_rng(4)
N = 20_000
width = rng.normal(22.0, 0.9, N) # millimetres
weight = rng.normal(57.0, 3.0, N) # grams
z_true = -5.90 + 1.90 * (width - 22.0) + 0.42 * (weight - 57.0)
y = (rng.random(N) < 1 / (1 + np.exp(-z_true))).astype(float)
perm = rng.permutation(N)
train, val, test = perm[:12_000], perm[12_000:16_000], perm[16_000:]N = 20000 defects = 337 base rate = 0.0169
defects per split = 203 60 74Three splits, not two. The reason is worth its own section and gets one below; for now, train on the first, tune on the second, and do not look at the third.
The features are standardised — mean subtracted, divided by the standard deviation — using the training statistics only, for the reason Chapter 1 demonstrated with the perceptron's convergence bound: uncentred data makes the geometry hostile. Which rows you are allowed to compute that mean from becomes a live question later in this chapter.
From a verdict to a probability
Link to the section: From a verdict to a probabilityThe perceptron returned a sign. A sign cannot distinguish reject from reject, but only just, and that difference is exactly what a factory needs to decide which parts a human should re-inspect first.
So follow Chapter 2's recipe literally. Write down what you claim about how a label is produced, take the likelihood, take the log, negate it, and you have a loss. For a yes/no outcome the claim is a Bernoulli distribution: there is a probability that the part is defective, and
which is just a compact way of writing " if , and if ". Take the log of that and negate it, and the loss for one example is
This is binary cross-entropy. It was not chosen because it is convenient; it is the negative log-likelihood of the only distribution a coin flip can have. Nothing else was available.
What is still missing is where comes from. The model computes a weighted sum , which is a real number and ranges over the whole line, and a probability has to live in . The function that moves between them is the logistic sigmoid:
logit -4.0 -> p = 0.0180 loss when y=1 and p=0.9 : 0.1054
logit -1.0 -> p = 0.2689 loss when y=1 and p=0.5 : 0.6931
logit 0.0 -> p = 0.5000 loss when y=1 and p=0.01 : 4.6052
logit 4.0 -> p = 0.9820Read the right-hand column as a price list. Being right with 90 % confidence costs 0.105. Refusing to commit costs 0.693 — which is , the price of a shrug. Being confidently wrong costs 4.6, forty-four times more, and the price rises without limit as the model grows more certain about a mistake. Cross-entropy does not merely count errors: it charges for arrogance.
The gradient is prediction minus truth
Link to the section: The gradient is prediction minus truthChapter 3 said: to train anything, get the derivative of the loss with respect to each parameter. Do it for one example. With and :
Show details
The two lines that make the mess cancel. The sigmoid has an unusually pleasant derivative, . And the loss differentiates to
Multiply the two by the chain rule and the appears once on top and once on the bottom. It cancels exactly, and is what survives. That cancellation is not a coincidence — it is what happens whenever the loss is the negative log-likelihood of a distribution and the output function is the one that distribution naturally uses. That pairing has a name — a generalised linear model — and the tidy gradient is its fingerprint.1
So the update is prediction minus truth, times the input. Nothing else. Here is the entire trainer, which is Chapter 3's descent with one line changed:
def sigmoid(z):
return np.where(z >= 0, 1.0 / (1.0 + np.exp(-z)),
np.exp(np.minimum(z, 0)) / (1.0 + np.exp(np.minimum(z, 0))))
def fit_logistic(X, y, lr=0.5, epochs=4000):
w, b = np.zeros(X.shape[1]), 0.0
for _ in range(epochs):
p = sigmoid(X @ w + b)
g = p - y
w -= lr * (X.T @ g) / len(y)
b -= lr * g.sum() / len(y)
return w, bThe np.where in sigmoid is not cosmetic. Computing directly overflows for large negative ; the branch picks whichever algebraically identical form keeps the exponent negative. This is Chapter 2's floating-point box collecting its first debt, and it will collect a bigger one two sections from now.
Why not squared error, and why the answer is about the gradient
Link to the section: Why not squared error, and why the answer is about the gradientThe standard explanation for preferring cross-entropy over squared error is the likelihood argument above: squared error is what you get from assuming Gaussian noise, labels are not Gaussian, therefore do not. It is correct and it convinces nobody, because you can write over a sigmoid and it will train.
The argument that lands is about the gradient. Put squared error on top of a sigmoid and the chain rule gives
That extra is the one that cancelled before. Now it does not, and it goes to zero whenever the model is confident — including when the model is confidently wrong. Evaluate both at a few scores, for an example whose true label is 1:
| score | cross-entropy | squared error | ratio | |
|---|---|---|---|---|
| 0.000335 | 1,491 | |||
| 0.017986 | 28.3 | |||
| 0.119203 | 4.8 | |||
| 0.500000 | 2.0 | |||
| 0.880797 | 4.8 |
At the model is as wrong as it is possible to be, and squared error responds with a gradient 1,491 times smaller than cross-entropy's. The worse the mistake, the less the model learns from it. Cross-entropy's gradient, meanwhile, saturates at : maximally wrong produces a maximally large signal, and no larger.
Run the race. Two thousand balanced points, identical starting weights chosen to be confidently wrong (), identical learning rate, only the loss differs. Both runs are scored with cross-entropy so the columns are comparable.
| epoch | cross-entropy loss | accuracy | squared-error loss | accuracy |
|---|---|---|---|---|
| 1 | 5.4865 | 0.2300 | 5.9499 | 0.2290 |
| 10 | 1.5525 | 0.2460 | 5.9042 | 0.2290 |
| 50 | 0.4642 | 0.7780 | 5.6913 | 0.2320 |
| 100 | 0.4639 | 0.7770 | 5.3955 | 0.2410 |
| 200 | 0.4639 | 0.7770 | 4.6311 | 0.2745 |
| 500 | 0.4639 | 0.7770 | 0.5291 | 0.7660 |
| 1,000 | 0.4639 | 0.7770 | 0.4640 | 0.7765 |
Cross-entropy is finished by epoch 50. Squared error is still at 24 % accuracy at epoch 100 — and had not moved from 23 % at epoch 10 — — worse than guessing, because it started confidently wrong and the gradient that would rescue it has been multiplied by 0.0007. It escapes at around epoch 500 and lands in the same place. So the honest summary is that squared error over a sigmoid is not incorrect; it is slow exactly where speed matters most. On a two-parameter model you lose 450 epochs. On a network with a hundred layers, where some unit somewhere is always confidently wrong, you lose the training run.
Entropy, cross-entropy and KL, in one page
Link to the section: Entropy, cross-entropy and KL, in one pageThree quantities, needed properly in Chapter 8 for perplexity and in Chapter 11 for the penalty that keeps a fine-tuned policy near its reference. They are easier than their reputation.2
Entropy is the average number of bits you must spend to communicate a draw from a distribution, if you use the best possible code for it:
Cross-entropy is what you spend when you use a code built for on data that actually comes from :
KL divergence is the excess — the waste, in bits, caused by believing when the truth is :
Check all three on the belt:
test defect rate = 0.0185
entropy of that coin = 0.1329 bits
cross-entropy of the constant predictor on test = 0.1330 bits
KL(test coin || fair coin) = 0.8671 bits
H + KL = 1.0000 bits
cross-entropy of the p=0.5 predictor on test = 1.0000 bitsTwo things are visible there. First, a model that simply reports the training base rate, 1.69 %, achieves a cross-entropy of 0.1330 bits, almost exactly the entropy of the test labels — as it must, since it has the right distribution and no other information. Entropy is the floor that ignorance-of-the-individual buys you. Second, a model that shrugs and says 0.5 pays exactly 1 bit, and the gap between the two, 0.8671 bits, is precisely the KL divergence. is not an identity to memorise; it is a bill you can watch being added up.
And the connection back to training: when the label is a single known class, the "true" distribution is one-hot, its entropy is zero, and cross-entropy equals the KL divergence. Minimising cross-entropy and pulling the model's distribution toward the truth are the same act.
More than two answers: softmax, and the shift that costs nothing
Link to the section: More than two answers: softmax, and the shift that costs nothingDefective is not one thing. In moulding, a part can come out as a short shot (not enough material), flash (too much, squeezed out of the mould), or burn. Four outcomes, so four logits, and they must become four probabilities that sum to one. That is softmax:
It has a property that looks like an accident and is in fact the whole implementation:
for any constant , because and the cancels top and bottom. Only differences between logits mean anything. The absolute level is not information.
Fortunately so, because the absolute level is what breaks the computer:
logits = [800. 801. 799.]
naive softmax = [nan nan nan]
shifted by -max = [0.2447 0.6652 0.09 ]
same softmax after adding 1000 to every logit: True overflows a 64-bit float, the sum becomes infinity, and infinity divided by infinity is nan — not an error, not a crash, just a silent hole where three probabilities used to be. Subtracting the maximum logit changes nothing mathematically and everything numerically, because the largest exponent becomes exactly . This is Chapter 2's logsumexp trick wearing its work clothes, and every serious implementation does it:
def softmax(Z):
Z = Z - Z.max(axis=1, keepdims=True)
E = np.exp(Z)
return E / E.sum(axis=1, keepdims=True)
def fit_softmax(X, Y, lr=1.0, epochs=6000):
W, b = np.zeros((X.shape[1], Y.shape[1])), np.zeros(Y.shape[1])
for _ in range(epochs):
G = (softmax(X @ W + b) - Y) / len(X)
W -= lr * (X.T @ G)
b -= lr * G.sum(0)
return W, bThe gradient is again prediction minus truth, now with one-hot. The binary case was a special case all along.
Trained on 3,000 parts and tested on 1,000, with three measurements each (width, weight, melt temperature), it reaches 94.00 % accuracy. Here is what that number is hiding:
| truth ↓ / predicted → | ok | short shot | flash | burn | recall |
|---|---|---|---|---|---|
| ok | 850 | 5 | 9 | 0 | 0.984 |
| short shot | 22 | 21 | 0 | 0 | 0.488 |
| flash | 20 | 0 | 30 | 1 | 0.588 |
| burn | 3 | 0 | 0 | 39 | 0.929 |
| precision | 0.950 | 0.808 | 0.769 | 0.975 |
The model finds fewer than half the short shots. Accuracy cannot see this, because 86 % of the parts are fine and getting those right is enough to carry the average. Macro F1 — the mean of the per-class F1 scores, which weights a rare class the same as a common one — is 0.7983, against a micro F1 of 0.9400 that is by definition identical to accuracy. Whenever someone reports one F1 number, ask which.
That is the last of the modelling. The rest of the chapter is about the numbers.
Three models, one accuracy
Link to the section: Three models, one accuracyTake the trained binary model and make two variants by multiplying every logit by a constant: 0.35 for a hesitant version, 4 for an overconfident one. Multiplying by a positive number cannot change any sign, so all three models predict exactly the same label for all 4,000 test parts. Accuracy cannot tell them apart. Cross-entropy has no trouble at all:
| model | accuracy | cross-entropy | mean loss when right | mean loss when wrong | worst single loss |
|---|---|---|---|---|---|
| hesitant (logits × 0.35) | 0.9830 | 0.1549 | 0.1369 | 1.1990 | 2.80 |
| as trained | 0.9830 | 0.0564 | 0.0147 | 2.4689 | 7.82 |
| overconfident (logits × 4) | 0.9830 | 0.1563 | 0.0009 | 9.1427 | 27.63 |
The hesitant model pays a small tax on every part, including the thousands it gets right. The overconfident one is nearly free when right and catastrophic when wrong — one part in that test set costs it 27.63 nats on its own. The two land at almost the same total by opposite routes, and the trained model, whose probabilities are calibrated to the data, sits three times below both.
This is the sharpest way to state the difference between a loss and a metric. The loss is what you optimise: it must be differentiable, and it sees everything the model said, including how sure it was. The metric is what you are judged on: it can be a step function, a business rule, a count of missed defects. They are not the same object and they do not always agree — which is why you define both before you start, and never let the loss stand in for the metric because it happens to be on the screen.
The stupid baseline goes first
Link to the section: The stupid baseline goes firstBefore any model, the requirement: what does the laziest possible answer score? On this belt, always say fine:
always-say-fine baseline: accuracy = 0.9815
confusion (tn, fp, fn, tp) = (3926, 0, 74, 0)98.15 %. Now the trained logistic model, at the default threshold of 0.5:
logistic @0.5: accuracy=0.9830 precision=0.8000 recall=0.1081 F1=0.1905
confusion (tn, fp, fn, tp) = (3924, 2, 66, 8)98.30 %. It beat the baseline by 0.15 of a percentage point, and any report that stops at accuracy will call that a win. The confusion matrix says what actually happened:
| predicted fine | predicted defective | |
|---|---|---|
| actually fine | 3,924 | 2 |
| actually defective | 66 | 8 |
It found 8 defective parts out of 74 and let 66 through. Three numbers name the three ways of reading that table:
- Precision . Of the parts it flagged, how many were really defective. This is the cost of wasted inspections.
- Recall . Of the defective parts, how many it caught. This is the cost of shipping a bad part to a customer.
- F1 , their harmonic mean, which stays near the smaller of the two and therefore refuses to be flattered by one of them alone.
Which matters depends on the factory, not on the mathematics: an inspection costs a few seconds and a shipped defect costs a recall notice, so here recall dominates and 0.108 is a failure.
But the model is not the problem. The threshold is, and the threshold is not part of the model — it is a business decision applied afterwards to a probability. Sweep it:
| threshold | TP | FP | FN | accuracy | precision | recall | F1 |
|---|---|---|---|---|---|---|---|
| 0.500 | 8 | 2 | 66 | 0.9830 | 0.800 | 0.108 | 0.190 |
| 0.200 | 27 | 28 | 47 | 0.9812 | 0.491 | 0.365 | 0.419 |
| 0.100 | 42 | 118 | 32 | 0.9625 | 0.263 | 0.568 | 0.359 |
| 0.050 | 54 | 236 | 20 | 0.9360 | 0.186 | 0.730 | 0.297 |
| 0.020 | 67 | 570 | 7 | 0.8558 | 0.105 | 0.905 | 0.188 |
| 0.005 | 71 | 1,360 | 3 | 0.6593 | 0.050 | 0.959 | 0.094 |
Read the accuracy column downward. It falls the whole way — from 98.30 % to 65.93 % — while the model goes from catching 8 defects to catching 71 of 74. Every useful thing this model can do makes its accuracy worse. A team optimising the headline number would ship the version that finds nothing.
Show details
Class weighting does not create signal, it moves the operating point. The usual first reflex with imbalanced classes is to weight the rare class in the loss. Doing that, with weights of 1, 10 and 60 on the positives:
| weight on positives | accuracy | precision | recall | F1 | AUC |
|---|---|---|---|---|---|
| 1 | 0.9830 | 0.800 | 0.108 | 0.190 | 0.9363 |
| 10 | 0.9605 | 0.253 | 0.581 | 0.352 | 0.9361 |
| 60 | 0.8290 | 0.091 | 0.919 | 0.166 | 0.9361 |
Precision and recall move a long way. The AUC — the probability that the model ranks a random defective part above a random good one, which ignores the threshold entirely — moves by 0.0002, which is nothing. Reweighting slid the same model along the same trade-off curve. That is often what you want, and it is never new information: if the ranking is bad, no weighting scheme will save it.
Three splits, and the leak you are about to find
Link to the section: Three splits, and the leak you are about to findWhy three splits and not two? Because the moment you use a set of examples to choose anything — a threshold, a learning rate, which of six models to ship — that set has been used for fitting, and its score stops being unbiased.3 Measured on this belt: sweeping the threshold on the validation set picks 0.196, and the model then scores F1 = 0.4122 on the untouched test set. Had the sweep been run on the test set directly, the best achievable there was 0.4186 — a number nobody is entitled to report.
The gap is small here, 0.006, because that is one hyperparameter swept once against 4,000 validation examples. It grows with every extra decision and every shrink of the validation set. Note also that the direction is not guaranteed on a single run: the chosen threshold scored 0.3902 on validation and 0.4122 on test, so validation understated it this time. The bias is systematic across many decisions, not visible in one.4
Now the exercise. The belt log arrives with a third column, station_seconds: how long each part spent at the inspection station. Adding it is a one-line change to the preprocessing. Here is what it does:
| model | accuracy | precision | recall | F1 | cross-entropy | AUC |
|---|---|---|---|---|---|---|
| width + weight | 0.9830 | 0.800 | 0.108 | 0.190 | 0.0564 | 0.9363 |
| + station_seconds | 0.9920 | 0.792 | 0.770 | 0.781 | 0.0236 | 0.9970 |
Recall goes from 10.8 % to 77.0 %. F1 more than quadruples. And notice what accuracy did: 98.30 % → 99.20 %, a gain of nine tenths of a point, which is the kind of number that gets rounded to "about 99 % either way" in a summary slide. Accuracy failed to see the failure earlier and now fails to see the fraud.
Before reading on: the model is cheating. Find out how.
How to hunt a leak, in the order that finds it fastest.
-
Compare train and test. Overfitting shows up as a large gap. Here: honest model 0.9838 train / 0.9830 test; leaky model 0.9936 train / 0.9920 test. Both gaps are under 0.2 points. A leak does not look like overfitting — the leaky feature is just as available at test time, so the model generalises beautifully to a world that does not exist.
-
Train one model per feature, alone. Anything that carries the answer will announce itself:
feature alone accuracy recall F1 AUC width 0.9815 0.014 0.026 0.8691 weight 0.9815 0.000 0.000 0.7914 station_seconds0.9850 0.405 0.500 0.9960 One column, on its own, ranks defects at AUC 0.9960. Two measurements taken by a caliper and a scale manage 0.87 and 0.79. That asymmetry is the alarm.
-
Ask when each number was written down. Mean dwell time: 2.23 seconds for parts that passed, 15.56 seconds for parts that failed. Of course it is. A part dwells at the station because an inspector pulled it off the belt — which happens after, and only because, somebody decided it was defective. The column is not a measurement of the part. It is a measurement of the verdict.
station = 1.8 + rng.exponential(0.35, N) # a part just passing through
audited = rng.random(N) < 0.006 # random spot checks
station[audited] += rng.uniform(6.0, 26.0, audited.sum())
station[y == 1] = 9.0 + rng.exponential(7.0, (y == 1).sum()) The highlighted line is the leak: a defective part's dwell time is drawn from a different distribution, because a human took it off the belt. This is the most common serious bug in applied machine learning, and it has a name: target leakage — information in the training features that would not be available at the moment the prediction has to be made.5 It throws no exception. It produces a better number. Every incentive in a project points toward keeping it.
The defence is one question, asked of every column: at the instant I need this prediction, does this value exist yet? On a live belt, station_seconds is unknown until after the part has been inspected — which is the thing the model was supposed to replace.
How many test examples do I need?
Link to the section: How many test examples do I need?Suppose you score a model on 20 examples and it gets 17 right. You report 85 %.
17 correct out of 20 -> accuracy 0.8500
Wilson 95% CI : [0.6396, 0.9476]
bootstrap 95% CI : [0.7000, 1.0000]
P(a 65% model scores 17 or more out of 20) = 0.0444
P(an 85% model scores 17 or more out of 20) = 0.6477The honest reading of 17/20 is somewhere between 64 % and 95 %. A genuinely 65 % model produces this result 4.4 % of the time — one run in twenty-three — and if you tried a handful of prompts and reported the best, you manufactured that run yourself. Seventeen out of twenty cannot distinguish an 85 % model from a 65 % one.
Two ways to put an interval on a rate, and both belong in your toolkit:
def wilson(k, n, z=1.959963985):
"""95% interval for k successes in n trials. Correct at small n; no simulation."""
ph, d = k / n, 1 + z * z / n
centre = (ph + z * z / (2 * n)) / d
half = z * (ph * (1 - ph) / n + z * z / (4 * n * n)) ** 0.5 / d
return centre - half, centre + half
def bootstrap_ci(correct, n_resamples=10_000, alpha=0.05, seed=0):
"""95% interval for the mean of any per-example score array. Works on F1 too."""
rng = np.random.default_rng(seed)
correct = np.asarray(correct, dtype=float)
draws = correct[rng.integers(0, len(correct), size=(n_resamples, len(correct)))]
lo, hi = np.quantile(draws.mean(axis=1), [alpha / 2, 1 - alpha / 2])
return float(correct.mean()), float(lo), float(hi)Use Wilson6 for a plain success rate; it stays well behaved at any and needs no randomness. Note above that at the bootstrap's upper end is 1.0000 — resampling 20 points can easily draw 20 correct ones, so it cannot represent an interval narrower than its own granularity. Use the bootstrap7 where no formula exists, which is most of the interesting cases: F1, macro-averages, BLEU, pass@1, the score of a rubric-based judge. On this belt, the tuned model's F1 of 0.4122 carries a bootstrap interval of [0.3009, 0.5156] — which is the number that should appear in the report, because the point estimate alone invites a comparison it cannot support.
One more measurement, because it changes how you should compare two models. Two models scored on the same 500 examples:
model A: 0.8580 95% CI [0.8260, 0.8880]
model B: 0.8120 95% CI [0.7780, 0.8460]
the two intervals overlap: True
paired difference A-B: 0.0460 95% CI [0.0260, 0.0680]
they disagree on 31 of 500 examples (A right 27, B right 4)Their intervals overlap, and the folk rule — overlapping error bars means no significant difference — would call the comparison inconclusive. It is not. The two models ran on the same examples, so the right quantity is the per-example difference, whose interval is [0.0260, 0.0680], comfortably above zero. They disagree on only 31 of 500 items, and A wins 27 of those disagreements; the shared examples, easy and hard alike, cancel out instead of adding noise. Compare models paired, and you reach the same conclusion from a fraction of the data.
Where this goes next
Link to the section: Where this goes nextYou now have a model that outputs calibrated probabilities, a loss derived from a claim about the data rather than chosen for convenience, a gradient that is literally prediction minus truth, and — more importantly — the machinery to find out whether any of it works. The ten-line Wilson interval above is reused verbatim: it carries the prompt variants in Chapter 15, the retrieval tables in Chapter 19, and the golden set in Chapter 29. The bootstrap is what you reach for when no formula exists.
But the model is still one layer. It draws a line, and Chapter 1 proved with four rows of XOR that a line is not enough. The fix is to stack: a first layer that bends the space, a second that draws the line in the bent space.
That is where the tidy gradient of this chapter runs out. Everything above worked because could be written down by hand, once, for a model with one layer between the input and the loss. Put a second layer in the middle and the question changes shape: what is the derivative of the loss with respect to a weight that does not touch the output at all — one whose influence arrives only through another layer, possibly along several paths at once?
That derivative exists. Computing it by hand is hopeless for anything larger than a toy, and computing it one parameter at a time is hopeless at a different scale. What is needed is a procedure that gets every derivative in the network from a single backward pass over the same graph the forward pass just walked.
That is Chapter 5, and it is the engine the rest of this course runs on.
Sources and method
Link to the section: Sources and methodAlso worth reading alongside this chapter: Bishop, Pattern Recognition and Machine Learning §1.2, §1.5, §1.6 and §4.3, which covers probability, decision theory, information theory and linear classification in the order this chapter follows; Murphy, Probabilistic Machine Learning: An Introduction, chapters 6 and 10; Prince, Understanding Deep Learning §5.4–5.7; and Saito and Rehmsmeier, The Precision-Recall Plot Is More Informative than the ROC Plot When Evaluating Binary Classifiers on Imbalanced Datasets (PLOS ONE, 2015) — why the AUC quoted above should not be the only threshold-free number you look at when 1.7 % of the parts are defective.
References
Link to the section: References-
Ma, T. and Ng, A. CS229 Lecture Notes, Stanford University, chapters 2 and 3. Where the cancellation that produces stops looking like luck: choose the exponential-family distribution that matches your output, use its canonical link, and the gradient is always prediction minus truth. ↩
-
Olah, C. Visual Information Theory (2015),
colah.github.io/posts/2015-09-Visual-Information. The clearest available account of entropy, cross-entropy and KL divergence as costs in bits rather than as formulas. ↩ -
Abu-Mostafa, Y. S., Magdon-Ismail, M. and Lin, H.-T. Learning From Data (AMLBook, 2012), lectures 13 and 17 of the Caltech course. Lecture 13 is validation; lecture 17, on the three learning principles, is where data snooping is named. Between them they are the source of the discipline in this chapter: every look at a data set is a fitting decision, whether or not you ran an optimiser. ↩
-
James, G., Witten, D., Hastie, T. and Tibshirani, R. An Introduction to Statistical Learning, 2nd edition (Springer, 2021), chapters 2 and 5, for the bias–variance decomposition and for resampling. The companion volume is where the selection trap is stated outright: Hastie, Tibshirani and Friedman, The Elements of Statistical Learning, 2nd edition, §7.10.2, The Wrong and Right Way to Do Cross-validation. ↩
-
Kaufman, S., Rosset, S., Perlich, C. and Stitelman, O. Leakage in Data Mining: Formulation, Detection, and Avoidance. ACM Transactions on Knowledge Discovery from Data 6(4), 2012. A formal treatment of the failure demonstrated above, with case studies from competitions won by a model that had learned an artefact of how the data was assembled. ↩
-
Wilson, E. B. Probable Inference, the Law of Succession, and Statistical Inference. Journal of the American Statistical Association 22(158), pp. 209–212 (1927). The score interval used in
wilson()above, still the right default for a proportion. The textbook interval is the one to avoid: it gives nonsense near 0 and 1, and undercovers badly at small . ↩ -
Efron, B. Bootstrap Methods: Another Look at the Jackknife. The Annals of Statistics 7(1), pp. 1–26 (1979). The idea that lets you put an interval on any statistic you can compute, including the ones with no sampling theory. ↩