v9.1.1
Menu

Chapter 4: Model Evaluation

Week 7-8

Master the critical skill of measuring model performance honestly. Learn why train/test splits are essential, how to interpret classification metrics beyond accuracy, use cross-validation for robust estimates, and systematically tune hyperparameters.

Topics
6
Demo
1
Videos
3
Min read
77

Chapter Overview

Proper model evaluation is perhaps the most important skill in applied machine learning. A beautiful model that achieves 99% accuracy is worthless if that accuracy was measured on training data or if the metric doesn't capture what actually matters.

The fundamental principle is simple but often violated: never evaluate a model on data it has seen during training. This requires disciplined data management—splitting into train, validation, and test sets, and being rigorous about when each is used.

Beyond data splits, choosing the right metrics is crucial. Accuracy can be catastrophically misleading with imbalanced classes. A spam detector predicting "not spam" for everything achieves 99% accuracy if only 1% of emails are spam—yet it's completely useless. Precision, recall, F1, and AUC each tell different parts of the story.

Cross-validation provides more reliable performance estimates by averaging over multiple train/test splits. This is especially important with limited data, where a single unlucky split could give misleading results.

Finally, hyperparameter tuning requires systematic methods. Random search often outperforms grid search while being more efficient, and modern techniques like Bayesian optimization learn from past evaluations.

This chapter covers:

  • Train/Test Split: The golden rule of evaluation, data leakage, and proper data handling
  • Classification Metrics: Precision, recall, F1, ROC curves, PR curves, and when each matters
  • Regression Metrics: MSE, MAE, R², and choosing the right metric for your problem
  • Cross-Validation: K-fold, stratified, and time series methods for robust performance estimation
  • Learning Curves: Diagnosing bias, variance, and deciding next steps
  • Hyperparameter Tuning: Grid search, random search, Bayesian optimization, and best practices

Chapter Roadmap

Click any topic to jump in

Explainer videoInteractive demo
1
Train/Test Split

The golden rule of evaluation — never test on training data. Data splits, stratification, leakage, and temporal considerations.

Why Split Data?Train/Test SplitTrain/Validation/TestStratified SplitData LeakageTemporal SplitsGroup Splits
Measuring what matters

Classification and regression metrics quantify performance on held-out data

2
Classification Metrics

Beyond accuracy — precision, recall, F1, ROC/AUC, and choosing the right metric for imbalanced classes.

Confusion MatrixAccuracyPrecisionRecall (Sensitivity/TPR)Specificity (TNR)F1 ScoreROC Curve & AUCPrecision-Recall CurveLog Loss
3
Regression Metrics

MSE, RMSE, MAE, R², and MAPE — measuring continuous prediction quality and selecting the right loss.

Mean Squared Error (MSE)Root Mean Squared Error (RMSE)Mean Absolute Error (MAE)MSE vs MAER² (Coefficient of Determination)Adjusted R²Mean Absolute Percentage Error (MAPE)Choosing Metrics
More reliable estimates
4
Cross-Validation

K-fold, stratified, and nested CV — robust performance estimates that don't depend on a single lucky split.

K-Fold Cross-ValidationChoosing KStratified K-FoldRepeated K-FoldLeave-One-Out (LOO)Time Series CVGroup K-FoldNested Cross-Validation
Diagnose and optimize

Learning curves reveal problems, tuning fixes them

5
Learning Curves

Diagnosing bias and variance visually — when to get more data vs. increase model complexity.

Learning Curve: Data SizeHigh Bias PatternHigh Variance PatternGood Fit PatternTraining CurveValidation CurveDiagnosing from Curves
6
Hyperparameter Tuning

Grid search, random search, and Bayesian optimization — systematic approaches to finding the best model configuration.

Hyperparameters vs ParametersGrid SearchRandom SearchWhy Random Often Beats GridBayesian OptimizationSuccessive Halving / HyperbandEarly StoppingCross-Validation in Tuning

Topics

6 topics in this chapter

A model that scores 99 percent on the data it was trained on can still fail on the first real customer. The previous chapter, Classification, built logistic regression, Naive Bayes, and other classifiers, but it never asked the harder question: how do we know any of them will work on data they have never seen? Training performance cannot answer it, because a flexible model can memorize its training set.

This chapter, Model Evaluation, is about measuring generalization honestly, and this first topic covers the foundation: holding data out. We start with why training error is optimistically biased. We then cover the basic train-test split and the three-way train, validation, and test split that keeps tuning away from the final score. Next comes stratified splitting, which preserves class ratios. We then study data leakage, the most common way evaluations lie, and finish with two structured splits: temporal splits for data that evolves over time, and group splits for data where several rows come from the same patient, user, or device.

Definition

A train-test split partitions a labeled dataset into a training set used to fit the model and a held-out test set used only to estimate performance on unseen data. A validation set may be split off as well for tuning. The split must mirror deployment, so that no information from held-out examples reaches training.

In this topic

  1. 1Why Split Data?
  2. 2Train/Test Split
  3. 3Train/Validation/Test
  4. 4Stratified Split
  5. 5Data Leakage
  6. 6Temporal Splits
  7. 7Group Splits
1 of 7
Why Split Data?

Training error measures how well a model fits examples it has already seen, which is not the question we care about. The fitting process tunes parameters specifically to those examples, so training error is biased downward, and the more flexible the model, the larger the bias. A lookup table that memorizes every training row scores perfectly yet predicts nothing useful. To estimate generalization error, the expected error on new examples drawn from the same distribution, we must score the model on data it never trained on. Everything else in this chapter refines that idea: how to hold data out, how much, and how to avoid contaminating it.

Mathematical Intuition

A model with kk parameters can perfectly memorize any dataset of ≤k\leq k points (Stone-Weierstrass theorem for universal approximators). Training error is therefore a biased estimator: E[train error]≤E[true error]\mathbb{E}[\text{train error}] \leq \mathbb{E}[\text{true error}]. The gap is the optimism, and it grows with model complexity. Test error on unseen data is an unbiased estimator of generalization error because the model had no opportunity to fit noise in the test set.

Example:

Model achieves 99% on training data, 60% on new data. What happened?

2 of 7
Train/Test Split

Typical ratio: 80% train, 20% test

The simplest honest evaluation holds out a random fraction of the data, commonly 20 to 30 percent, as a test set. The model is fitted only on the training portion and scored once on the test portion, giving an unbiased estimate of generalization error. Fix the random seed, the random_state in scikit-learn, so the split is reproducible. The tradeoff is data efficiency: a larger test set gives a more precise estimate but leaves less for training. With small datasets a single split is also noisy, since a lucky or unlucky draw can move the score by several points, which motivates cross-validation later in this chapter.

Mathematical Intuition

With nn total samples and test fraction ff, you get n(1−f)n(1-f) training samples and nfnf test samples. The test error variance is Var(ϵ^)≈σ2/(nf)\text{Var}(\hat{\epsilon}) \approx \sigma^2 / (nf), so a larger test set gives a tighter confidence interval. But fewer training samples means a less accurate model. The 80/20 split is a practical compromise — enough data to train, enough to get a reliable error estimate with standard error proportional to 1/0.2n1/\sqrt{0.2n}.

Example:

1000 samples, 80/20 split. How many in each set?

3 of 7
Train/Validation/Test

Typical: 60% train, 20% val, 20% test

Once we compare several models or hyperparameter settings, the test set can no longer be used to choose among them. Every time we pick the configuration with the best test score, we fit a little to the test set's noise, and the winning score becomes optimistic. The fix is a third split. The training set fits parameters, the validation set chooses among configurations, and the test set is touched once at the very end to report the final number. A common ratio is 60/20/20. If the test score is ever used to make a decision, it has become another validation set and is no longer unbiased.

Mathematical Intuition

Three-way splits allocate data to three roles with different bias profiles. Validation error guides hyperparameter selection — this introduces selection bias proportional to log⁡(m)/nval\sqrt{\log(m) / n_{\text{val}}} when choosing from mm configurations. The test set remains untouched, giving an unbiased final estimate. Without the validation set, optimizing on test data conflates model selection with evaluation, underestimating true error by the selection bias term.

Example:

Try 10 different model configs, pick best on validation. Then report test score. Why not pick best on test?

4 of 7
Stratified Split

Random splitting can distort class proportions, especially when one class is rare or the dataset is small. If the test set happens to contain few minority examples, metrics such as recall become unreliable, and if the training set is short of them, the model sees too few to learn from. Stratified splitting fixes this by splitting each class separately in the same ratio, so every subset keeps the original class proportions. In scikit-learn, pass stratify=y to train_test_split. For regression, a similar effect comes from binning the target and stratifying on the bins. It costs nothing and should be the default for classification.

Mathematical Intuition

For a dataset with class proportion pp and split size nn, each class count follows Binomial(n,p)\text{Binomial}(n, p) with variance np(1−p)np(1-p). With rare classes (p≪1p \ll 1) and small nn, the actual proportion can deviate substantially: p^∼p±p(1−p)/n\hat{p} \sim p \pm \sqrt{p(1-p)/n}. Stratification forces p^=p\hat{p} = p exactly in every split, eliminating this variance and ensuring each split faithfully represents the population.

Example:

Data: 90% class A, 10% class B. Random split could give test set with 2% class B. Problem?

5 of 7
Data Leakage

Data leakage happens when information unavailable at prediction time enters training or feature construction, producing evaluation scores that collapse in production. Target leakage uses features that are consequences of the label, such as a cancellation date when predicting churn. Train-test contamination lets test data shape training, for example fitting a scaler, imputer, or feature selector on the full dataset before splitting. Temporal leakage uses future information to predict the past. The warning sign is a score that looks too good. The fixes: fit every preprocessing step inside the training fold, with a scikit-learn Pipeline, and ask of each feature whether it would exist at prediction time.

Mathematical Intuition

Leakage means the model's estimate f^(x)\hat{f}(x) is conditioned on information II that would not be available at inference time: P(y∣x,I)≠P(y∣x)P(y \mid x, I) \neq P(y \mid x). The training objective optimizes f^\hat{f} to exploit II, producing optimistically biased error: E[leaked error]<E[true error]\mathbb{E}[\text{leaked error}] < \mathbb{E}[\text{true error}]. Common forms include fitting scalers on all data (leaking test statistics μtest,σtest\mu_{\text{test}}, \sigma_{\text{test}} into training) and using future-dependent features (reverse causality).

Example:

Feature 'account_closed_date' predicts churn with 99% accuracy. Suspicious?

6 of 7
Temporal Splits

When data has a time order, a random split lets the model train on the future and predict the past, which deployment never allows. Prices, user behavior, and fraud patterns drift, so a random split also mixes neighboring periods that share conditions, inflating scores. A temporal split trains on everything before a cutoff date and tests on everything after, reproducing how the model will actually be used. Leave a gap between the two periods if labels take time to arrive. Expect temporal test scores to be lower than random-split scores; that difference is the optimism the random split was hiding.

Mathematical Intuition

Time series violate the i.i.d. assumption: samples have temporal correlation Cor(xt,xt+h)≠0\text{Cor}(x_t, x_{t+h}) \neq 0. A random split that puts x2023x_{2023} in training and x2021x_{2021} in test lets the model exploit future information, inflating accuracy. Temporal splits enforce ttrain<ttestt_{\text{train}} < t_{\text{test}}, simulating the real deployment condition where you only have past data. The effective sample size is reduced by autocorrelation: neff=n/(1+2∑hρ(h))n_{\text{eff}} = n / (1 + 2\sum_h \rho(h)).

Example:

Stock data 2020-2023. Random split trains on 2023 data, tests on 2021. Problem?

7 of 7
Group Splits

Many datasets contain several rows from the same source: multiple scans per patient, many sessions per user, or frames from one video. Rows from one group share traits, so if a group lands in both training and test sets, the model can recognize the group instead of learning the general pattern, and the test score is inflated. Group splitting assigns whole groups to one side. In scikit-learn, GroupShuffleSplit and GroupKFold take a groups array, such as patient IDs. Ask what the model will face in deployment: new patients means the test set must contain patients it has never seen.

Mathematical Intuition

When samples share a group identifier gg (e.g., patient), observations within a group have correlation ρwithin≫0\rho_{\text{within}} \gg 0. Random splits that place group members in both train and test let the model exploit within-group similarity rather than learning generalizable features. GroupKFold ensures g∈train⊕g∈testg \in \text{train} \oplus g \in \text{test} (exclusive or), so the model must generalize across groups. The effective test set size becomes the number of unique groups, not individual samples.

Example:

10 photos per patient. Random split puts 5 photos of Patient A in train, 5 in test. Issue?

Theory Exercise

Problem:

You standardize your entire dataset (compute mean/std on all data), then split into train/test. Your model achieves 95% accuracy. What's wrong?

Hints:
  • What information did the scaler learn?
  • Did test data influence training?
  • This is a form of data leakage

Coding Exercise

Problem:

Build an imbalanced 2-class dataset (~90/10) with make_classification, then compare the positive-class proportion in the test set from a plain train_test_split versus a stratified split (stratify=y). Show stratification preserves the class ratio.

Hints:
  • Use make_classification with weights=[0.9, 0.1] and random_state=42 to create imbalance.
  • Call train_test_split twice with the same random_state: once without stratify, once with stratify=y.
  • Compare y_test.mean() (positive fraction) against the full dataset's y.mean().