Building a model is only half the job. The other half is proving it works, and works on data it has never seen. In this module you'll learn the measures data scientists use every day, and how to spot the most common trap in machine learning: overfitting.
- Level: Beginner
- Time: about 40 minutes
- Needs: Modules 1 to 4
By the end of this module you will be able to
- Read a confusion matrix and explain each of its four boxes
- Calculate precision, recall and F1 score, and know when each matters
- Read an ROC curve and explain what AUC means
- Explain overfitting and underfitting, and spot them in results
- Use regularisation and other methods to reduce overfitting
- Use k-fold cross-validation for a more reliable score
- Choose the right measure for a real business problem
5.1Why evaluation matters
Imagine a driving instructor who says "you're ready for your test" without ever watching you drive. You wouldn't trust them. A model is the same: until you've measured it properly, you don't know if it works.
You've already met some measures in this track:
| Problem type | Measures you know | Where |
|---|---|---|
| Regression (predicting a number) | MAE, MSE, RMSE, R² | Module 3 |
| Classification (predicting a category) | Accuracy, confusion matrix | Module 4 |
In Module 4 you also met the accuracy trap: when one answer is rare, a useless model can still score very high accuracy. This module gives you the tools to see through that, and to check whether your model will really work on new data.
The golden rule of evaluation
Always judge a model on data it has never seen. A score on the training data only tells you how well the model memorised its homework, not how well it will do in the real world.
5.2The confusion matrix, properly
Throughout this module, we'll use one example. A bank has built a model to catch fraudulent card payments. It's tested on 1,000 payments, and 50 of them are really fraud.
| Predicted: genuine | Predicted: fraud | |
|---|---|---|
| Actually genuine (950) | 920 True negative (TN) Genuine, correctly let through | 30 False positive (FP) Genuine, wrongly blocked |
| Actually fraud (50) | 10 False negative (FN) Fraud that slipped through | 40 True positive (TP) Fraud correctly caught |
An easy way to read the names
The second word is what the model predicted (positive = fraud, negative = genuine). The first word says whether it was right (true) or wrong (false). So a "false positive" is a wrong "fraud!" alarm, and a "false negative" is a fraud the model wrongly let through.
The model's accuracy is (920 + 40) ÷ 1,000 = 96%. Sounds great. But it missed 10 frauds and blocked 30 genuine customers. To understand if that's good enough, we need better measures.
5.3Precision and recall
These are the two most important classification measures. They answer two different questions.
| Precision | Recall | |
|---|---|---|
| The question | When the model says "fraud", how often is it right? | Of all the real frauds, how many did the model find? |
| Formula | TP ÷ (TP + FP) | TP ÷ (TP + FN) |
| Bank example | 40 ÷ (40 + 30) = 57% | 40 ÷ (40 + 10) = 80% |
| In plain English | "Only 57% of the payments we block are really fraud" | "We catch 80% of all fraud" |
| Other names | Positive predictive value | Sensitivity, true positive rate |
| Hurt by | False alarms (FP) | Missed cases (FN) |
Think of fishing for salmon
Precision: of all the fish in your net, what share are salmon? Recall: of all the salmon in the river, what share are in your net? A huge net catches every salmon (high recall) but lots of other fish too (low precision). A tiny, careful net catches only salmon (high precision) but misses most of them (low recall).
Which one matters more?
| Prioritise | When | Examples |
|---|---|---|
| Recall | Missing a real case is very costly | Cancer screening, fraud detection, safety faults, finding customers about to leave |
| Precision | A false alarm is very costly or annoying | Spam filter (don't bin a job offer!), recommending products, blocking accounts |
As you saw with the threshold in Module 4, there's usually a trade-off: raising one tends to lower the other.
5.4The F1 score
Sometimes you want one number that balances precision and recall. That's the F1 score.
For the bank: 2 × (0.57 × 0.80) ÷ (0.57 + 0.80) = 0.67.
F1 is a special kind of average (called a harmonic mean) that is pulled down hard by whichever score is lower. A model can't hide a terrible recall behind a great precision:
| Precision | Recall | Normal average | F1 score |
|---|---|---|---|
| 0.80 | 0.80 | 0.80 | 0.80 |
| 0.95 | 0.60 | 0.78 | 0.74 |
| 1.00 | 0.10 | 0.55 | 0.18 |
The last row is a model that almost never says "fraud", so it's never wrong when it does, but it misses 90% of fraud. The normal average (0.55) makes it look okay. F1 (0.18) tells the truth.
5.5ROC curves and AUC
Precision, recall and F1 all depend on the threshold you choose. An ROC curve shows how the model performs at every threshold at once. It plots:
- True positive rate (recall): the share of frauds caught, going up the side.
- False positive rate: the share of genuine payments wrongly flagged, going along the bottom.
Try it with a fraud model scoring 200 payments (40 are fraud). Move the threshold and watch the dot travel along the curve.
Try it: explore the trade-off
What is AUC?
AUC stands for "Area Under the Curve". It squeezes the whole ROC curve into one number between 0 and 1.
| AUC | Meaning |
|---|---|
| 1.0 | Perfect: the model always ranks every fraud above every genuine payment |
| 0.9 – 1.0 | Excellent |
| 0.8 – 0.9 | Good |
| 0.7 – 0.8 | Fair |
| 0.5 | Useless: no better than tossing a coin (the dashed diagonal line) |
A simple way to think about AUC
Pick one fraud payment and one genuine payment at random. AUC is the chance that the model gives the fraud a higher "fraud score" than the genuine one. An AUC of 0.9 means it gets this right 90% of the time. Because AUC doesn't depend on any threshold, it's great for comparing two models.
5.6Overfitting and underfitting
This is the most important idea in this module. A model can fail in two opposite ways.
| Underfitting | Good fit | Overfitting | |
|---|---|---|---|
| What happens | Model is too simple to learn the pattern | Model learns the real pattern | Model memorises the training data, noise and all |
| Training score | Poor | Good | Excellent (suspiciously so) |
| Test score | Poor | Good | Poor |
| Student analogy | Didn't revise at all | Understood the topic | Memorised last year's answers word for word |
| Technical name | High bias | Balanced | High variance |
The tell-tale sign of overfitting is a big gap between the training score and the test score. As a model gets more complex, training error keeps dropping, but test error eventually starts rising again:
5.7How to fix overfitting and underfitting
| Problem | Fix | Why it works |
|---|---|---|
| Overfitting | Get more training data | Harder to memorise lots of examples, so the model has to learn the real pattern |
| Use a simpler model | Fewer ways to bend and wiggle around the noise | |
| Remove unhelpful features | Less noise for the model to latch onto | |
| Regularisation | Adds a penalty for being too complex (see below) | |
| Early stopping | Stop training before the model starts memorising (used for neural networks) | |
| Underfitting | Use a more powerful model | More flexibility to follow the real pattern |
| Add better features | Feature engineering from Module 2 gives the model more to work with | |
| Reduce regularisation | Let the model be a bit more complex |
Regularisation: Ridge and Lasso
In Module 3, linear regression chose coefficients to make the error as small as possible. Regularisation changes the goal to: make the error small, but keep the coefficients small too. Big coefficients mean a model is leaning very heavily on certain features, which is often a sign of overfitting.
| Method | What it does | Useful when | scikit-learn |
|---|---|---|---|
| Ridge (L2) | Shrinks all coefficients towards zero | You have many features that each help a little | Ridge(alpha=1.0) |
| Lasso (L1) | Can shrink some coefficients all the way to zero, removing those features | You suspect many features are useless | Lasso(alpha=0.1) |
The setting alpha controls how strong the penalty is: higher alpha means a simpler model. Logistic regression is regularised by default in scikit-learn, controlled by a setting called C (where a smaller C means stronger regularisation).
Settings like alpha and C have a name
They're called hyperparameters: settings you choose before training, rather than values the model learns. Finding the best hyperparameters is called tuning, and cross-validation (next lesson) is how we do it fairly.
5.8Cross-validation
A single train/test split has a weakness: you might get lucky (or unlucky) with which rows end up in the test set. Your score could change a lot just by splitting differently.
K-fold cross-validation fixes this. The training data is cut into k equal parts (usually 5 or 10), called folds. The model is trained and tested k times, each time using a different fold as the test set. Then we average the scores.
| Benefit | Why it matters |
|---|---|
| More reliable score | Averaging 5 results is less affected by luck than 1 result |
| Shows consistency | If scores range from 0.60 to 0.95, the model is unstable. If they're all close, you can trust it |
| Uses all the data | Every row is used for both training and testing |
| Fair tuning | Compare hyperparameter settings without touching the final test set |
Keep a final test set locked away
Use cross-validation on the training data to compare and tune models. Only use the separate test set once, at the very end, to report the final score. If you keep checking the test set while tuning, you're slowly "overfitting to the test set", and your final score won't be honest.
5.9Evaluating a model in Python
Now for a real dataset. scikit-learn includes a famous medical dataset of 569 breast tumour scans, each with 30 measurements and a label saying whether it was benign (harmless) or malignant (cancer). Missing a malignant tumour is very serious, so recall for "malignant" is the measure that matters most.
We also use a Pipeline, which bundles scaling and the model together. As promised in Module 2, this stops data leakage automatically, even inside cross-validation.
import numpy as np
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
from sklearn.linear_model import LogisticRegression, LinearRegression
from sklearn.metrics import (confusion_matrix, classification_report,
roc_auc_score, mean_squared_error)
# 1. Load a real medical dataset: 569 tumour scans, 30 measurements each
X, y = load_breast_cancer(return_X_y=True)
y = 1 - y # make 1 = malignant (the case we want to catch)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.25, random_state=42, stratify=y)
# 2. A Pipeline scales and trains in one step (no leakage)
model = Pipeline([
("scale", StandardScaler()),
("clf", LogisticRegression(max_iter=1000)),
])
model.fit(X_train, y_train)
# 3. Confusion matrix and the full report
pred = model.predict(X_test)
print(confusion_matrix(y_test, pred))
print(classification_report(y_test, pred,
target_names=["benign", "malignant"], digits=2))
# 4. ROC AUC uses probabilities, not just yes/no
prob = model.predict_proba(X_test)[:, 1]
print("ROC AUC:", round(roc_auc_score(y_test, prob), 3))
# 5. 5-fold cross-validation on the training data
scores = cross_val_score(model, X_train, y_train, cv=5, scoring="recall")
print("Recall per fold:", scores.round(2))
print("Average recall:", round(scores.mean(), 3))
# 6. Spot overfitting: 40 noisy points, three models of rising complexity
rng = np.random.default_rng(0)
x = rng.uniform(0, 10, 40).reshape(-1, 1)
target = 3 * np.sin(x).ravel() + x.ravel() + rng.normal(0, 1, 40)
x_tr, x_te, t_tr, t_te = train_test_split(x, target, test_size=0.5, random_state=0)
for degree in [1, 4, 15]:
curve = Pipeline([("poly", PolynomialFeatures(degree)),
("scale", StandardScaler()),
("reg", LinearRegression())])
curve.fit(x_tr, t_tr)
train_err = mean_squared_error(t_tr, curve.predict(x_tr)) ** 0.5
test_err = mean_squared_error(t_te, curve.predict(x_te)) ** 0.5
print(f"Degree {degree:2}: train RMSE={train_err:5.2f} test RMSE={test_err:5.2f}")
[[89 1]
[ 4 49]]
precision recall f1-score support
benign 0.96 0.99 0.97 90
malignant 0.98 0.92 0.95 53
accuracy 0.97 143
macro avg 0.97 0.96 0.96 143
weighted avg 0.97 0.97 0.96 143
ROC AUC: 0.996
Recall per fold: [0.94 1. 0.94 0.97 0.94]
Average recall: 0.956
Degree 1: train RMSE= 1.80 test RMSE= 2.11
Degree 4: train RMSE= 1.20 test RMSE= 1.26
Degree 15: train RMSE= 0.60 test RMSE=28.47What the output tells us
- Confusion matrix: of 53 malignant tumours in the test set, the model caught 49 and missed 4. Only 1 benign tumour was wrongly flagged.
- Classification report: for malignant, precision is 0.98 (when it says cancer, it's nearly always right) and recall is 0.92 (it finds 92% of cancers). The 4 missed cases are the ones a doctor would worry about, so in practice we might lower the threshold to push recall higher.
- ROC AUC of 0.996 means the model is excellent at ranking malignant tumours above benign ones.
- Cross-validation recall is consistent across all 5 folds (0.94 to 1.00), so this isn't a lucky split.
- The overfitting check shows all three patterns. Degree 1 is poor on both (underfitting). Degree 4 is good on both, with a tiny gap (just right). Degree 15 has the best training score but a terrible test score (overfitting).
macro avg vs weighted avg
In the report, macro avg is a simple average of both classes, treating them equally. Weighted avg gives more weight to the class with more examples. When classes are unbalanced, macro avg is the more honest one.
5.10Choosing the right measure
There's no single "best" measure. The right one depends on the cost of each kind of mistake in your business. Here's a cheat sheet.
| Situation | Focus on | Why |
|---|---|---|
| Classes are balanced, both mistakes cost the same | Accuracy | Simple and fair when neither class dominates |
| Missing a case is dangerous (disease, fraud, safety) | Recall | Catch as many real cases as possible |
| False alarms are costly (spam filter, blocking accounts) | Precision | Only say "yes" when you're sure |
| Rare class and both mistakes matter | F1 score | Balances precision and recall in one number |
| Comparing models before choosing a threshold | ROC AUC | Measures ranking quality across all thresholds |
| Predicting a number, need to explain to a manager | MAE | Easy to understand, in real units |
| Predicting a number, big errors are very bad | RMSE | Punishes large misses more |
In a job interview
If an interviewer asks "how would you evaluate this model?", don't just say "accuracy". Say which mistake is more costly for the business, pick the measure that matches, and mention checking for overfitting with cross-validation. That answer shows you think like a data scientist.
SummaryKey takeaways
- Always evaluate on data the model has never seen.
- The confusion matrix has four boxes: TP, TN, FP (false alarm) and FN (missed case).
- Precision = how often a "yes" is right. Recall = how many real cases were found.
- F1 balances precision and recall, and is pulled down by the weaker one.
- The ROC curve shows performance at every threshold; AUC sums it up (0.5 = coin toss, 1 = perfect).
- Overfitting = great on training data, poor on test data. Underfitting = poor on both.
- Fight overfitting with more data, simpler models, fewer features and regularisation (Ridge, Lasso).
- K-fold cross-validation gives a more reliable score; keep the final test set for the very end.
- Choose your measure based on which mistake costs the business more.
Check your understanding
Your machine learning roadmap
- 01Introduction to Machine LearningWhat ML is and how it works
- 02Preparing Data for Machine LearningFeatures, encoding, scaling, train/test split
- 03Linear RegressionPredicting numbers
- 04Logistic RegressionPredicting yes or no
- 05Evaluating ModelsYou are here
- 06K-Nearest NeighboursLearning from similar examples
- 07Decision TreesFlowcharts that learn
- 08Random Forest and Gradient BoostingMany models working together
- 09Support Vector MachinesFinding the best boundary
- 10Naive BayesProbability-based classification
- 11Clustering with K-MeansFinding groups without labels
- 12Dimensionality Reduction with PCASimplifying big datasets
- 13Capstone ProjectBuild and present a full ML project
Next module
K-Nearest Neighbours