CadetX
CX Learn | Complete Machine Learning Guide
Scikit-Learn · Python
CadetX logo CadetX CX Learn
ML-101 · Module 5 Beginner about 40 minutes 11 Lessons Prereq: Modules 1 to 4

Model Evaluation

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 typeMeasures you knowWhere
Regression (predicting a number)MAE, MSE, RMSE, R²Module 3
Classification (predicting a category)Accuracy, confusion matrixModule 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: genuinePredicted: 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.

Twenty transactions: eight fraud and twelve genuine. The model flags nine of them: six fraud and three genuine. Two fraud transactions are missed.Flagged as fraud by the modelDashed rings: fraud the model missed
Figure 1. Amber = fraud, blue = genuine. The model's "net" catches 9 payments. Precision asks: of the 9 caught, how many are really fraud? (6 of 9 = 67%). Recall asks: of the 8 real frauds, how many did we catch? (6 of 8 = 75%).
PrecisionRecall
The questionWhen the model says "fraud", how often is it right?Of all the real frauds, how many did the model find?
FormulaTP ÷ (TP + FP)TP ÷ (TP + FN)
Bank example40 ÷ (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 namesPositive predictive valueSensitivity, true positive rate
Hurt byFalse 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?

PrioritiseWhenExamples
RecallMissing a real case is very costlyCancer screening, fraud detection, safety faults, finding customers about to leave
PrecisionA false alarm is very costly or annoyingSpam 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.

F1 = 2 × (Precision × Recall) ÷ (Precision + Recall)

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:

PrecisionRecallNormal averageF1 score
0.800.800.800.80
0.950.600.780.74
1.000.100.550.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

Fraud caught (TP)
Fraud missed (FN)
False alarms (FP)
Genuine passed (TN)

What is AUC?

AUC stands for "Area Under the Curve". It squeezes the whole ROC curve into one number between 0 and 1.

AUCMeaning
1.0Perfect: the model always ranks every fraud above every genuine payment
0.9 – 1.0Excellent
0.8 – 0.9Good
0.7 – 0.8Fair
0.5Useless: 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.

Three models fitted to the same points: a straight line that is too simple, a smooth curve that fits well, and a wild wiggly curve that passes through every point but fails on new data.UnderfittingToo simple: misses the patternJust rightFollows the real patternOverfittingMemorises the noise
Figure 2. The same 20 training points, fitted by three models. The straight line is too simple. The smooth curve captures the real pattern. The wild curve bends to hit almost every point, including the random noise, so it will make terrible predictions for new points.
UnderfittingGood fitOverfitting
What happensModel is too simple to learn the patternModel learns the real patternModel memorises the training data, noise and all
Training scorePoorGoodExcellent (suspiciously so)
Test scorePoorGoodPoor
Student analogyDidn't revise at allUnderstood the topicMemorised last year's answers word for word
Technical nameHigh biasBalancedHigh 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:

As a model gets more complex, training error keeps falling. Test error falls at first, then rises again. The best model sits at the lowest point of the test error curve.Sweet spot← UnderfittingOverfitting →Training errorTest errorModel complexity →Error →
Figure 3. The goal is not the lowest training error. It's the lowest test error: the sweet spot between too simple and too complex.

5.7How to fix overfitting and underfitting

ProblemFixWhy it works
OverfittingGet more training dataHarder to memorise lots of examples, so the model has to learn the real pattern
Use a simpler modelFewer ways to bend and wiggle around the noise
Remove unhelpful featuresLess noise for the model to latch onto
RegularisationAdds a penalty for being too complex (see below)
Early stoppingStop training before the model starts memorising (used for neural networks)
UnderfittingUse a more powerful modelMore flexibility to follow the real pattern
Add better featuresFeature engineering from Module 2 gives the model more to work with
Reduce regularisationLet 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.

MethodWhat it doesUseful whenscikit-learn
Ridge (L2)Shrinks all coefficients towards zeroYou have many features that each help a littleRidge(alpha=1.0)
Lasso (L1)Can shrink some coefficients all the way to zero, removing those featuresYou suspect many features are uselessLasso(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.

Five-fold cross-validation. The data is split into five parts. In each of five rounds, a different part is used for testing and the other four for training.Round 1TestTrainTrainTrainTrainScore: 0.94Round 2TrainTestTrainTrainTrainScore: 1.00Round 3TrainTrainTestTrainTrainScore: 0.94Round 4TrainTrainTrainTestTrainScore: 0.97Round 5TrainTrainTrainTrainTestScore: 0.94Average: 0.96
Figure 4. 5-fold cross-validation. Every row of data gets used for testing exactly once. The scores shown are real results from the Python example in lesson 5.9.
BenefitWhy it matters
More reliable scoreAveraging 5 results is less affected by luck than 1 result
Shows consistencyIf scores range from 0.60 to 0.95, the model is unstable. If they're all close, you can trust it
Uses all the dataEvery row is used for both training and testing
Fair tuningCompare 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.

evaluate_model.py
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}")
Output
[[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.47

What 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.

SituationFocus onWhy
Classes are balanced, both mistakes cost the sameAccuracySimple and fair when neither class dominates
Missing a case is dangerous (disease, fraud, safety)RecallCatch as many real cases as possible
False alarms are costly (spam filter, blocking accounts)PrecisionOnly say "yes" when you're sure
Rare class and both mistakes matterF1 scoreBalances precision and recall in one number
Comparing models before choosing a thresholdROC AUCMeasures ranking quality across all thresholds
Predicting a number, need to explain to a managerMAEEasy to understand, in real units
Predicting a number, big errors are very badRMSEPunishes 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

  1. 01
    Introduction to Machine LearningWhat ML is and how it works
  2. 02
    Preparing Data for Machine LearningFeatures, encoding, scaling, train/test split
  3. 03
    Linear RegressionPredicting numbers
  4. 04
    Logistic RegressionPredicting yes or no
  5. 05
    Evaluating ModelsYou are here
  6. 06
    K-Nearest NeighboursLearning from similar examples
  7. 07
    Decision TreesFlowcharts that learn
  8. 08
    Random Forest and Gradient BoostingMany models working together
  9. 09
    Support Vector MachinesFinding the best boundary
  10. 10
    Naive BayesProbability-based classification
  11. 11
    Clustering with K-MeansFinding groups without labels
  12. 12
    Dimensionality Reduction with PCASimplifying big datasets
  13. 13
    Capstone ProjectBuild and present a full ML project