Developing a Machine Learning Model

Machine Learning Notes · note 5 of 6 · Developing a model ← Multiclass and the softmax Decision trees, forests, boosting →

Building a model is the easy part. You have written the cost function, the gradients and the network; you train it, and the predictions are not good enough. Now what? You could collect more data, add features, remove features, engineer polynomial terms, raise the regularization, lower it, or make the network bigger — each of which costs days or weeks, and most of which will do nothing. This note is about how to tell, before spending the time, which of them is worth trying. It is the difference between six months of guessing and two weeks of diagnosis.

In one line. Split the data three ways, compare $J_{\text{train}}$ and $J_{\text{cv}}$ against a baseline of what is achievable, and let the two gaps tell you whether you have a bias problem or a variance problem — because the fixes for one are useless for the other.

Contents
  1. Evaluating a model
  2. Model selection and the cross-validation set
  3. Bias and variance
  4. Regularization and bias/variance
  5. Establishing a baseline
  6. Learning curves
  7. Deciding what to try next
  8. Neural networks and the tradeoff
  9. The iterative loop of development
  10. Error analysis
  11. Adding data
  12. The full cycle of a project
  13. Skewed datasets: precision and recall
  14. Where this goes next

Evaluating a model

The train/test split

The cost after training tells you almost nothing. A model with enough parameters can drive $J$ to zero by memorising the training set — the wild degree-nine fit in linear-regression.md did exactly that — while being useless on anything it has not seen. What matters is generalization: performance on data that took no part in fitting the parameters.

So hold some data back. Split the examples, typically $70\%$ / $30\%$, fit the parameters on the training portion only, and evaluate on the untouched test portion. Write $m_{\text{train}}$ and $m_{\text{test}}$ for the two sizes.

Test error for regression and classification

The two errors are the same cost you already minimise, evaluated on the two different sets. For regression with squared error:

$$ \begin{align} J_{\text{train}}(\vec{w}, b) &= \frac{1}{2m_{\text{train}}}\sum_{i=1}^{m_{\text{train}}} \left(f_{\vec{w},b}\left(\vec{x}^{(i)}_{\text{train}}\right) - y^{(i)}_{\text{train}}\right)^2, \tag{1.1} \\ J_{\text{test}}(\vec{w}, b) &= \frac{1}{2m_{\text{test}}}\sum_{i=1}^{m_{\text{test}}} \left(f_{\vec{w},b}\left(\vec{x}^{(i)}_{\text{test}}\right) - y^{(i)}_{\text{test}}\right)^2 . \tag{1.2} \end{align} $$
Equation 1: Training and test error for a regression model. Identical formulas over different examples; only the set being summed over changes.

For classification, the same idea with the logistic loss:

$$ J_{\text{test}}(\vec{w}, b) = -\frac{1}{m_{\text{test}}}\sum_{i=1}^{m_{\text{test}}} \left[\,y^{(i)}_{\text{test}} \log f_{\vec{w},b}\left(\vec{x}^{(i)}_{\text{test}}\right) + \left(1 - y^{(i)}_{\text{test}}\right)\log\left(1 - f_{\vec{w},b}\left(\vec{x}^{(i)}_{\text{test}}\right)\right)\right]. \tag{2} $$
Equation 2: Test error for a classification model, using the log loss the model was trained on

For classification there is a second, more readable option: the fraction misclassified. Threshold each prediction at $0.5$ and count how often $\hat{y} \neq y$. That number is the one worth reporting to a human — "$5\%$ of test emails were sorted wrongly" means something in a way that a log loss of $0.18$ does not.

The evaluation cost carries no regularization

One detail that is easy to get wrong. The cost being minimised during training includes the regularization term; the errors being reported do not:

$$ J^{\text{reg}}_{\text{train}}(\vec{w}, b) = \frac{1}{2m}\sum_{i=1}^{m}\left(f_{\vec{w},b}\left(\vec{x}^{(i)}\right) - y^{(i)}\right)^2 + \frac{\lambda}{2m}\lVert\vec{w}\rVert^2 . \tag{3} $$
Equation 3: The regularized training objective. The $\lambda$ term exists to shape the fit, so it belongs only to the quantity gradient descent minimises — never to the numbers you use to judge the model.

The penalty is a device for controlling overfitting, not part of the model's error. Including it in $J_{\text{test}}$ would mean two models with different $\lambda$ could not be compared at all, since each would be scored on a different quantity.


Model selection and the cross-validation set

Choosing with the test set, and why it breaks

Suppose you are choosing the degree $d$ of a polynomial model. The obvious procedure: fit all ten candidates on the training set, evaluate each on the test set, take the winner, and report its test error as the estimate of generalization performance.

The last step is where it fails. Say $d = 5$ wins with $J_{\text{test}}\left(w^{\langle 5\rangle}, b^{\langle 5\rangle}\right)$. That number is now an optimistic estimate, because $d$ was itself chosen by looking at the test set. The degree is an extra parameter, fitted to the test data by the act of picking the smallest number — exactly as $w$ and $b$ were fitted to the training data. Reporting it makes the same mistake as reporting $J_{\text{train}}$: the score comes from data the choice was tuned on.

The three-way split

The fix is a third split. Divide the data into training, cross-validation and test sets — a common ratio is $60/20/20$ — and give each one job. The cross-validation set is also called the validation or dev set.

$$ \begin{align} J_{\text{train}} &= \frac{1}{2m_{\text{train}}}\sum_{i=1}^{m_{\text{train}}}\left(f\left(\vec{x}^{(i)}_{\text{train}}\right) - y^{(i)}_{\text{train}}\right)^2, \tag{4.1} \\ J_{\text{cv}} &= \frac{1}{2m_{\text{cv}}}\sum_{i=1}^{m_{\text{cv}}}\left(f\left(\vec{x}^{(i)}_{\text{cv}}\right) - y^{(i)}_{\text{cv}}\right)^2, \tag{4.2} \\ J_{\text{test}} &= \frac{1}{2m_{\text{test}}}\sum_{i=1}^{m_{\text{test}}}\left(f\left(\vec{x}^{(i)}_{\text{test}}\right) - y^{(i)}_{\text{test}}\right)^2 . \tag{4.3} \end{align} $$
Equation 4: The three errors. Same formula, three disjoint sets of examples; only the training one carries the regularization term during fitting.
SetFits the parameters?Chooses between models?Reports the final number?
Trainingyesnono
Cross-validationnoyesno
Testnonoyes
Table 1: What each split is for. Every dataset does exactly one job, and a set that has been used for one job can no longer be trusted for another.

The correct procedure

  1. Train every candidate on the training set, giving $w^{\langle d\rangle}, b^{\langle d\rangle}$ for each.
  2. Choose the candidate with the lowest $J_{\text{cv}}$.
  3. Report $J_{\text{test}}$ for that one candidate — a set no decision has touched, so the number is an honest estimate of generalization error.

Nothing about this is specific to polynomial degree. The same three steps choose a neural network architecture — train a small, a medium and a large network, pick by $J_{\text{cv}}$, report $J_{\text{test}}$ — or a regularization strength, or a feature set. Any decision you make by looking at a dataset must be made on the cross-validation set, or the test set stops being a fair test.


Bias and variance

Three fits, and their signature

Fit a straight line, a quadratic and a degree-four polynomial to the same curved data and you get the three canonical outcomes — and, crucially, each leaves a distinct fingerprint in the two errors:

Model$J_{\text{train}}$$J_{\text{cv}}$DiagnosisWhat is wrong
Too simple (linear)highhighhigh bias — underfitNot flexible enough to fit even the training data.
Just right (quadratic)lowlowgoodNothing.
Too complex (degree 4+)lowhighhigh variance — overfitFits the training data and its noise; does not transfer.
Table 2: The diagnostic table. You never need to look at the fitted curve: the pair of numbers identifies the problem on its own, which is what makes it work in twenty dimensions where nothing can be plotted.

The single most useful sentence in this note: high bias shows as a large $J_{\text{train}}$; high variance shows as a large gap between $J_{\text{train}}$ and $J_{\text{cv}}$.

The two curves against model complexity

Sweep the degree and plot both errors together, and the whole picture appears at once.

Figure 1: A real sweep: polynomial models of degree $1$ to $10$ fitted to $15$ noisy samples of a smooth curve, scored on a held-out set of $400$. The training error falls monotonically — more parameters always fit the training data better, so this curve can never tell you when to stop. The cross-validation error falls, bottoms out at degree $3$ with $J_{\text{cv}} = 0.0046$, and then climbs by three orders of magnitude to $5.5$ at degree $10$. The gap between the curves is the variance; the height of the left end is the bias. Note the logarithmic vertical axis: without it the right-hand blow-up would flatten everything else to a line.

Read the two ends. On the left, both curves are high and close together: the model cannot fit even the data it was given, and giving it more data would not change that. On the right, the training curve is at the floor while the validation curve has left the chart: the model fits its training set perfectly and has learned nothing transferable.

Both at once

High bias and high variance are not opposite ends of one dial — a model can have both. It shows as a training error that is already well above the baseline and a validation error well above the training error. In practice it happens when part of the input space is overfitted and part is underfitted, and it is more common with neural networks than with polynomials.


Regularization and bias/variance

What $\lambda$ does to the fit

The regularization strength $\lambda$ is a second, continuous, control on the same tradeoff, and it runs in the opposite direction to model complexity:

How $\lambda$ pushes the weights down

The mechanism is visible in one line of calculus. Differentiating the regularized cost of Equation 3 gives an extra term in every weight's gradient:

$$ \frac{\partial J}{\partial w_j} = \frac{1}{m}\sum_{i=1}^{m}\left(f_{\vec{w},b}\left(\vec{x}^{(i)}\right) - y^{(i)}\right)x_j^{(i)} + \frac{\lambda}{m}w_j, \qquad w_j := w_j - \alpha\left(\frac{1}{m}\sum_{i=1}^{m}\left(f_{\vec{w},b}\left(\vec{x}^{(i)}\right) - y^{(i)}\right)x_j^{(i)} + \frac{\lambda}{m}w_j\right). \tag{5} $$
Equation 5: The regularized gradient and update. The extra $\frac{\lambda}{m}w_j$ is proportional to the weight itself, so every step shrinks $w_j$ toward zero by a constant fraction before the data term gets its say — which is why regularization is also called weight decay.

The weights therefore settle where the pull from the data balances the constant shrinkage from the penalty. Features that genuinely help keep their weight; features that only fit noise cannot justify theirs and are pulled to nothing.

Choosing $\lambda$ by cross-validation

$\lambda$ is chosen exactly like the degree was, by the procedure of §2.3. The standard search is a doubling ladder — $0$, $0.01$, $0.02$, $0.04$, $0.08$, and so on up to about $10$ — because what matters is the order of magnitude, not the third decimal place. Train each, take the lowest $J_{\text{cv}}$, report $J_{\text{test}}$ for the winner.

Figure 2: The same experiment as the previous figure, but with the degree fixed at $10$ and $\lambda$ swept instead. It is the mirror image: the training error now rises monotonically with $\lambda$ (more penalty, worse fit to the training data), while the validation error is U-shaped, bottoming at $\lambda = 0.0004$ with $J_{\text{cv}} = 0.0062$. Off the left of the chart, at $\lambda = 0$ exactly, the same degree-10 model gives $J_{\text{train}} = 0.0007$ and $J_{\text{cv}} = 3.06$ — the unregularized overfit. Regularization recovers essentially all of the performance that choosing a smaller degree would have bought.

Put Figure 1 and Figure 2 side by side and they are the same picture reflected. Increasing the degree and decreasing $\lambda$ both move you from bias to variance; the validation curve is U-shaped in both, and its minimum is the model you want.


Establishing a baseline

Is $J_{\text{train}} = 10.8\%$ high or low? The question has no answer on its own. For recognising printed digits it would be a disaster; for transcribing noisy telephone speech it might be excellent. Before either error means anything you need a baseline: the error rate that is plausibly achievable on this problem. Reasonable sources are human performance on the same task, a competing algorithm's published number, or prior experience.

With a baseline, the two comparisons that matter become gaps rather than absolute levels:

$$ \underbrace{J_{\text{train}} - \text{baseline}}_{\textbf{bias gap}}, \qquad \underbrace{J_{\text{cv}} - J_{\text{train}}}_{\textbf{variance gap}} . \tag{6} $$
Equation 6: The two gaps. The first measures how far the model is from what is achievable at all; the second measures how much worse it does on data it did not see.

Three sets of numbers, all with the same baseline of $10.6\%$, show how completely the diagnosis can change while the raw errors barely move:

Case ACase BCase C
Baseline$10.6\%$$10.6\%$$10.6\%$
$J_{\text{train}}$$10.8\%$$15.0\%$$15.0\%$
$J_{\text{cv}}$$14.8\%$$15.5\%$$19.7\%$
bias gap$0.2\%$$4.4\%$$4.4\%$
variance gap$4.0\%$$0.5\%$$4.7\%$
Diagnosishigh variancehigh biasboth
Table 3: Three scenarios against a baseline of 10.6 percent. Case A and case B have almost the same cross-validation error and opposite problems; without the baseline row, case B would look like a variance problem because its training error is high.

Case A is the one people misread most often. A training error of $10.8\%$ looks bad in isolation — but the baseline says $10.6\%$ is the most anyone can do, so the model is essentially perfect on the training distribution and the entire problem is variance.


Learning curves

Bias and variance can also be diagnosed by varying the amount of data rather than the model. Train the same model on subsets of increasing size and plot both errors against $m_{\text{train}}$.

The shape, and why it has that shape

Two things happen as the training set grows, and they run in opposite directions:

The two curves therefore approach each other from opposite sides and flatten. Where they flatten is the diagnosis.

High bias against high variance

Figure 3: Learning curves for two models on the same data, both scored against the noise floor of the problem (dashed) — the error a perfect model would still make because the data is noisy. Left, a straight-line model: the curves meet quickly and flatten at about seven times the floor. The flat part is the message — going from 20 examples to 80 changes nothing, and going to 80,000 would change nothing either. Right, a degree-9 model: the training error stays at the floor while the validation error starts enormous and keeps falling toward it as data arrives, so the gap is closing and more data is exactly the right investment. Note the right panel uses a logarithmic axis to fit its range.

That gives the most valuable prediction in this note, and it is worth stating baldly because it saves the most time:

If a model has high bias, more training data will not help. The curves have already flattened; adding examples moves them along a plateau. You need a more capable model — more features, polynomial terms, a bigger network, less regularization. If instead the model has high variance, the gap is still closing and more data is likely the single most effective thing you can do.

Plotting learning curves is expensive — you have to train the model many times over — but compared with six months spent collecting data that could never have helped, it is cheap.


Deciding what to try next

Now the list from the opening paragraph can be sorted. Every standard fix acts on one of the two problems, and applying a variance fix to a bias problem does nothing at all:

What to tryFixesWhy
Get more training exampleshigh varianceMore data pins down the parameters; the gap closes.
Try a smaller set of featureshigh varianceFewer parameters, less capacity to memorise noise.
Increase $\lambda$high varianceStronger penalty, smaller weights, smoother fit.
Get additional featureshigh biasGives the model information it currently does not have.
Add polynomial featureshigh biasLets the same features bend; more flexible hypothesis.
Decrease $\lambda$high biasLess penalty, weights free to grow, closer fit.
Table 4: The six standard remedies, each labelled with the problem it actually solves. Diagnose first with the two gaps of §5, then pick from the matching column; anything from the other column is wasted effort.

Three of the six increase model flexibility and three decrease it. That is the whole table: bias means not flexible enough, variance means too flexible, and every remedy is a move along that one axis.


Neural networks and the tradeoff

The recipe

Classically, bias and variance were a genuine dilemma: reducing one increased the other, and the job was to find the balance. Large neural networks changed that, because a big enough network is a low-bias machine — given enough units it can fit almost any training set you hand it. That turns the balancing act into a straightforward loop with two questions:

Figure 4: The neural network development loop, read left to right. The spine along the top is the path when things go well; each question is answered by one number, and a no drops you to the remedy below it. A training error that is too high is a bias problem and wants a bigger network; a good training error with a poor validation error is a variance problem and wants more data. Both remedies end in retraining, after which you re-enter at the left and ask both questions again.

The loop has two practical limits. A bigger network eventually becomes computationally expensive — this is where GPUs enter the story — and "more data" is sometimes simply unavailable, which is what §11 is about.

Bigger is almost never worse, if you regularize

The obvious objection is that a huge network should overfit. It usually does not, provided the regularization is chosen appropriately: a larger network with a well-chosen $\lambda$ will usually do as well as or better than a smaller one. The cost is compute time, not accuracy. The regularized cost sums the penalty over every weight in the network:

$$ J(W, B) = \frac{1}{m}\sum_{i=1}^{m} L\left(f\left(\vec{x}^{(i)}\right), y^{(i)}\right) + \frac{\lambda}{2m}\sum_{\text{all weights } W} w^2 . \tag{7} $$
Equation 7: A regularized neural network cost. The penalty runs over all weight matrices $W$ in the network; biases are conventionally left unpenalised.

In code the change is one argument per layer:

layer_1 = Dense(units=25, activation="relu",    kernel_regularizer=L2(0.01))
layer_2 = Dense(units=15, activation="relu",    kernel_regularizer=L2(0.01))
layer_3 = Dense(units=1,  activation="sigmoid", kernel_regularizer=L2(0.01))
model   = Sequential([layer_1, layer_2, layer_3])

The iterative loop of development

Zooming out from the model to the project, the same shape appears at a larger scale. Machine learning development is a loop, not a pipeline, and you go round it many times:

  1. Choose an architecture — the model, the features, the data, the hyperparameters.
  2. Train the model.
  3. Run diagnostics — bias, variance, and error analysis.
  4. Go back to step 1 with what you learned.

Take a spam classifier as the running example. The input $\vec{x}$ is a list of the top $10{,}000$ words, with $x_j$ recording whether word $j$ appears in the email; $y$ is $1$ for spam. The first round takes an afternoon. What the loop then produces is a sequence of decisions — add features for email routing, build a spelling normaliser, get more data of a specific kind — each one justified by a diagnostic rather than a guess. The two diagnostics that drive it are bias/variance and the subject of the next section.


Error analysis

Bias and variance tell you what kind of problem you have. Error analysis tells you what the problem is about, and it is nothing more than reading the mistakes:

  1. Take the cross-validation examples the model got wrong.
  2. If there are more than about a hundred, sample a hundred — the point is to find the big categories, and a sample of a hundred finds them as well as a thousand would.
  3. Read them, and sort them into common themes by hand.
  4. Count each theme.

For the spam classifier, a hundred misclassified emails might come back like this:

CategoryCountWhat it suggests
Pharmaceutical spam$21$Collect more pharma examples; add targeted features
Phishing, stealing passwords$18$Add features for URLs and routing; collect more
Unusual email routing$7$Add features from the header
Spam hidden in an embedded image$5$Would need image processing — expensive
Deliberate misspellings ("w4tches", "med1cine")$3$Low impact; deprioritise
Table 5: Error analysis on 100 misclassified emails out of 500 in the cross-validation set. Categories overlap and need not be exhaustive; what matters is their relative size. The counts point straight at where the next week of work should go — and, just as usefully, where it should not.

The deliberate-misspellings row is the point of the exercise. Building a spell-normaliser is a fun, tractable, obviously-useful-sounding project — and it could fix at most $3$ of $100$ errors. Pharma and phishing together account for $39$. Error analysis is mostly a tool for deciding what not to work on.

Its limitation is honest and worth knowing: it works best where a human can look at an example and say what the right answer was. For spam, a person can read the email and tell. For predicting which advertisement someone will click, no human can look at a failure and say what went wrong — and there, error analysis has much less to offer.


Adding data

When the diagnosis is high variance, more data helps — but "collect more data" is often the slowest, most expensive step in the project. There are cheaper routes to the same place.

Data augmentation

Augmentation modifies an existing training example to create a new one, with the label unchanged. For an image of the letter A, all of these are still an A: rotated, enlarged or shrunk, warped, drawn in a different colour or stroke width, partially occluded by background lines. One labelled example becomes ten, at no labelling cost.

The same idea works beyond images. For speech recognition, take one recording of "what is today's weather?" and overlay crowd noise, car noise, or the degradation of a bad phone connection — one clip becomes four, each with the same transcript.

The rule that makes it work. The distortion must be representative of the noise the model will actually meet at test time. Crowd noise helps because deployed audio really does have crowd noise. Adding purely random, meaningless per-pixel noise usually does not help, because nothing in the real world produces it — you have manufactured examples of a problem the model will never face, and spent capacity learning to ignore them.

Synthetic data

Augmentation modifies real examples; synthesis creates them from nothing. The classic case is photo OCR: instead of cropping and labelling letters from real photographs, generate them — take the fonts already on your computer, render text in various colours, sizes and distortions, and you have a large training set whose labels are known by construction.

The tradeoff is coverage. Real data contains oddities nobody would think to generate; synthetic data contains exactly what you told it to. It is at its best when the input is something a computer can plausibly render — text, code, structured audio — and at its weakest when the world is messier than your generator.

Data-centric development

Both techniques are instances of a broader shift in emphasis:

$$ \underbrace{\text{AI} = \boxed{\text{Code}} + \text{Data}}_{\text{conventional, model-centric}} \qquad\qquad \underbrace{\text{AI} = \text{Code} + \boxed{\text{Data}}}_{\text{data-centric}} \tag{8} $$
Equation 8: Two ways to spend your effort. The boxed term is the one being worked on; the other is held fixed.

For decades the effort went into the algorithm, with the dataset held fixed — that is what a research benchmark encourages. In applied work the algorithms are now largely commodities, well implemented in libraries, and the leverage has moved to systematically improving the data: augmenting it, synthesising it, relabelling the noisy parts, and collecting more of the specific category error analysis pointed at.

Transfer learning

The last option applies when you have very little data and no way to make more. Transfer learning uses data from a different task:

  1. Supervised pre-training. Take a network trained on a large dataset of the same input type — say a million images across a thousand classes of animals, vehicles and people. You rarely do this yourself; you download the parameters.
  2. Fine-tuning. Replace the output layer with one shaped for your task — ten units for handwritten digits, say — and continue training on your own, much smaller dataset.
OptionWhat trainsUse when
1 — freeze the bodyOnly the new output layer; all earlier layers stay fixedYou have a small labelled set, perhaps 50 examples
2 — fine-tune everythingAll parameters, starting from the pre-trained valuesYou have a moderate or large set, perhaps 1000 and up
Table 6: The two fine-tuning options. Which one to use is decided by how much labelled data you have for the new task.

Why it works is the interesting part, and it is the same fact the deep-network reference makes about feature hierarchies. The early layers of an image network learn generic structure — the first detects edges, the next corners, the next curves and basic shapes — and none of that is specific to the task. Edges are edges whether the image contains a cat or a handwritten seven. Only the last layers are specialised, so only they need replacing. The one restriction: the input type must match. An image network transfers to images, an audio network to audio; neither transfers to the other.


The full cycle of a project

Everything above sits inside a still larger loop, which is what a real project looks like end to end:

Figure 5: The full cycle. The last box is the honest part of the diagram: a project rarely runs forwards once. Deployment turns up data you never collected and performance that drifts as the live inputs stop resembling the training set, both of which send you back to the data step; error analysis during training can send you back just as far.

The last box hides more engineering than it looks. Deploying typically means putting the model behind an inference server: the application makes an API call with the input, the server returns the prediction. Around that sits the work of making it reliable — efficient predictions, scaling with load, logging inputs and outputs, monitoring for the day the live data stops resembling the training data, and a process for pushing model updates. That discipline has a name, MLOps, and on a production system it is a larger body of work than training the model was.


Skewed datasets: precision and recall

Why accuracy lies

Consider a classifier for a rare disease, $y = 1$ if present. You train it and get $1\%$ error on the test set — $99\%$ of diagnoses correct. That sounds excellent until you learn that only $0.5\%$ of patients have the disease, because then this program does better:

def predict(x):
    return 0          # "nobody has the disease"

It is right $99.5\%$ of the time. It has $0.5\%$ error, beating your model, and it is completely worthless. On a skewed dataset — one class far rarer than the other — accuracy is dominated by the majority class and stops measuring anything you care about.

The same effect on the real classifier used for the figures below: predicting "negative" for every one of $4000$ patients scores $97.25\%$ accuracy, while the trained model scores $97.98\%$. Judged on accuracy the two are nearly identical, and one of them detects no cases at all.

The confusion matrix

The fix is to stop compressing the outcome into one number too early. There are four outcomes, not two, and they are laid out in the confusion matrix:

Actually $1$Actually $0$Row total
Predicted $1$True positive: $15$False positive: $5$$20$ predicted positive
Predicted $0$False negative: $10$True negative: $70$$80$ predicted negative
Column total$25$ actually positive$75$ actually negative$100$
Table 7: The confusion matrix for 100 patients. The naming convention is that the second word is what you predicted and the first says whether that prediction was right: a false positive is a positive prediction that was wrong. The rare class is always the positive one.

Precision and recall

Two ratios pull the useful information out of that table, and the difference between them is entirely in the denominator:

$$ \text{Precision} = \frac{\text{TP}}{\underbrace{\text{TP} + \text{FP}}_{\text{predicted positive}}} = \frac{15}{15 + 5} = 0.75, \qquad \text{Recall} = \frac{\text{TP}}{\underbrace{\text{TP} + \text{FN}}_{\text{actually positive}}} = \frac{15}{15 + 10} = 0.60 . \tag{9} $$
Equation 9: Precision divides by what you predicted; recall divides by what was actually there. Both use the same numerator, so any disagreement between them is a statement about which kind of mistake the model is making.

In words, and it is worth learning them as sentences rather than formulas:

Notice that the "predict $0$ always" program has recall exactly $0$, and its precision is undefined because it never predicts a positive at all. The two metrics catch the cheat instantly where accuracy did not.

Trading them off

You are not stuck with whatever precision and recall the model happens to give, because the threshold is yours to choose. Logistic regression outputs a probability; the decision to call it positive is a separate step:

$$ \hat{y} = \begin{cases} 1 & \text{if } f_{\vec{w},b}(\vec{x}) \geq \text{threshold},\\[2pt] 0 & \text{otherwise.}\end{cases} \tag{10} $$
Equation 10: The prediction rule with an explicit threshold. The conventional $0.5$ has no special status — it is one choice among a continuum.
Figure 6: The precision-recall curve for a real logistic model on a skewed problem: 4000 examples, 2.75 percent positive, scored at every threshold from 0.01 to 0.99. Each point is one threshold, and no threshold escapes the tradeoff — over the useful range the curve slopes down and to the right, so every gain in recall is paid for in precision. The wobble at the far left is sampling noise rather than a real free lunch: at a threshold that high only a handful of examples are predicted positive, so one of them changing flips the ratio. The three marked thresholds show the range on offer from the same trained model: at 0.9 it is right about 80 percent of the time it speaks but finds only 4 percent of cases, while at 0.1 it finds 68 percent of cases at the price of being wrong two times in three. Choosing among these points is a judgement about consequences, not a question the data can answer.

The $F_1$ score

Two numbers are awkward when you have to rank candidates. The obvious repair — average them — fails badly, and the failure is instructive:

AlgorithmPrecision $P$Recall $R$Average$F_1$
1$0.50$$0.40$$0.450$$\mathbf{0.444}$balanced — the best of the three
2$0.70$$0.10$$0.400$$0.175$recall far too low
3$0.02$$1.00$$\mathbf{0.510}$$0.039$the print("y=1") cheat
Table 8: Three algorithms compared by the plain average and by $F_1$. The average crowns algorithm 3, which achieves perfect recall by predicting positive for everybody and is therefore right 2 percent of the time it speaks. $F_1$ ranks it last, where it belongs.

The $F_1$ score is the harmonic mean of precision and recall, which is the average that refuses to be gamed:

$$ F_1 = \frac{1}{\frac{1}{2}\left(\frac{1}{P} + \frac{1}{R}\right)} = 2\,\frac{P R}{P + R} . \tag{11} $$
Equation 11: The $F_1$ score. Averaging the reciprocals means the smaller of the two numbers dominates the result, so a score can only be high when both are high.

The mechanism is worth seeing. If $R \to 0$ then $1/R \to \infty$, the denominator explodes, and $F_1 \to 0$ no matter how good $P$ is. The harmonic mean is close to the smaller number, where the arithmetic mean sits halfway between. So $F_1$ is high only when precision and recall are both high, and it cannot be raised by sacrificing one for the other — which is exactly the property you want from a metric on a skewed problem.


Where this goes next

The diagnostic toolkit here is model-agnostic on purpose. Train/cv/test, bias and variance, learning curves, error analysis and $F_1$ apply just as well to a decision tree or a gradient boosted ensemble as to the networks they were introduced with — and the next note is about exactly those: decision trees, which split on features rather than weighting them, are trained by maximising information gain rather than by gradient descent, and then combine into random forests and XGBoost.

Backwards from here: multiclass-softmax.md builds the classifier this note evaluates, logistic-regression.md and linear-regression.md hold the models and the regularization underneath it, and neural-networks-reference.md covers the architectures the recipe in §8 makes bigger.

Machine Learning Notes · note 5 of 6 · Developing a model ← Multiclass and the softmax Decision trees, forests, boosting →