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.
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:
Click any topic to jump in
The golden rule of evaluation — never test on training data. Data splits, stratification, leakage, and temporal considerations.
Classification and regression metrics quantify performance on held-out data
Beyond accuracy — precision, recall, F1, ROC/AUC, and choosing the right metric for imbalanced classes.
MSE, RMSE, MAE, R², and MAPE — measuring continuous prediction quality and selecting the right loss.
K-fold, stratified, and nested CV — robust performance estimates that don't depend on a single lucky split.
Learning curves reveal problems, tuning fixes them
Diagnosing bias and variance visually — when to get more data vs. increase model complexity.
Grid search, random search, and Bayesian optimization — systematic approaches to finding the best model configuration.
Never evaluate a model on the data it was trained on. The model may have memorized the training data (overfitting). We need separate test data to estimate real-world performance.
Golden Rule: Test data must be completely unseen during training and hyperparameter tuning.
Training error is optimistically biased. A model that memorized all data has 0% training error but fails on new data. Test error estimates generalization.
A model with parameters can perfectly memorize any dataset of points (Stone-Weierstrass theorem for universal approximators). Training error is therefore a biased estimator: . 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.
Model achieves 99% on training data, 60% on new data. What happened?
Typical ratio: 80% train, 20% test
Randomly divide data. Train set for learning, test set for final evaluation. Never touch test until the end! Use random_state for reproducibility.
With total samples and test fraction , you get training samples and test samples. The test error variance is , 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 .
1000 samples, 80/20 split. How many in each set?
Typical: 60% train, 20% val, 20% test
Add a validation set for hyperparameter tuning. Test set is only for final evaluation. Validation guides model selection without contaminating test.
Three-way splits allocate data to three roles with different bias profiles. Validation error guides hyperparameter selection — this introduces selection bias proportional to when choosing from 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.
Try 10 different model configs, pick best on validation. Then report test score. Why not pick best on test?
Preserve class ratios in each split. Critical for imbalanced datasets. sklearn: train_test_split(stratify=y).
For a dataset with class proportion and split size , each class count follows with variance . With rare classes () and small , the actual proportion can deviate substantially: . Stratification forces exactly in every split, eliminating this variance and ensuring each split faithfully represents the population.
Data: 90% class A, 10% class B. Random split could give test set with 2% class B. Problem?
Information from test/future leaks into training. Examples: fitting scaler on all data, using future info for past predictions, target leakage. Causes overoptimistic results.
Leakage means the model's estimate is conditioned on information that would not be available at inference time: . The training objective optimizes to exploit , producing optimistically biased error: . Common forms include fitting scalers on all data (leaking test statistics into training) and using future-dependent features (reverse causality).
Feature 'account_closed_date' predicts churn with 99% accuracy. Suspicious?
For time series or evolving data, always split by time. Train on past, test on future. Simulates real deployment conditions.
Time series violate the i.i.d. assumption: samples have temporal correlation . A random split that puts in training and in test lets the model exploit future information, inflating accuracy. Temporal splits enforce , simulating the real deployment condition where you only have past data. The effective sample size is reduced by autocorrelation: .
Stock data 2020-2023. Random split trains on 2023 data, tests on 2021. Problem?
When samples are grouped (e.g., multiple images per patient), keep groups together. GroupKFold prevents same group appearing in both train and test.
When samples share a group identifier (e.g., patient), observations within a group have correlation . 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 (exclusive or), so the model must generalize across groups. The effective test set size becomes the number of unique groups, not individual samples.
10 photos per patient. Random split puts 5 photos of Patient A in train, 5 in test. Issue?
You standardize your entire dataset (compute mean/std on all data), then split into train/test. Your model achieves 95% accuracy. What's wrong?