Data & leakage
- Data leakage is the #1 silent killer of ML projects - causes models that look great in evaluation but fail in production
- Rooted in the fundamental rule: the model must never see information it would not have at prediction time
- Appears in subtle forms: feature leakage, target leakage, temporal leakage
Data leakage occurs when information from outside the training window (or from the target itself) sneaks into the model features. The model learns a shortcut that disappears in production, producing falsely optimistic validation scores.
- Underlies proper train/validation/test split discipline
- Critical for time-series models, survival models, and any data with temporal ordering
- Connects to the broader ML workflow and feature engineering best practices
- Define target leakage, train-test contamination, and group leakage with one-sentence rules.
- Choose the right split strategy for tabular, time-series, and grouped data.
- Recognise three real-world leakage horror stories and explain what went wrong.
- Audit a preprocessing pipeline to confirm the test set never touches anything fitted on it.
TL;DR
Leakage is when information that would not be available at prediction time leaks into training or evaluation. The three flavours are target leakage (a feature is downstream of the label), train-test contamination (preprocessing fitted on the full data), and group leakage (the same entity appears in train and test). All three produce optimistic offline metrics that collapse in production.
Intuition
Leakage is when your training or evaluation pipeline uses information that, at prediction time in production, will not exist or will be different.
If you are predicting whether a customer will churn next month, and one of your features is “customer cancelled subscription”, you will get 100% accuracy. Of course you will: the feature is the label, recorded slightly later. This is target leakage in its most obvious form. Every leakage horror story is a less obvious version of the same mistake.
Kaufman et al. [1] formalise leakage as a violation of the temporal or population separation between training data and the prediction context. Their decomposition into the three flavours above is now standard.
Split strategies
| Strategy | When to use | Failure mode if misapplied | Standard tool |
|---|---|---|---|
| Random split | IID tabular data with no time, group, or natural ordering. | Catastrophic on time series or grouped data; gives an optimistic estimate. | sklearn train_test_split. |
| Stratified random | Classification with imbalanced classes; small datasets. | Same caveats as random split if temporal/group structure exists. | sklearn StratifiedKFold. |
| Time-based | Any data with a time stamp where the model will be deployed forward in time. | Random split is leakage by construction: training on the future. | sklearn TimeSeriesSplit; manual cutoff date. |
| Group-based | Each entity (user, patient, device) can appear in multiple rows. | Random split lets the model memorise per-entity behaviour and inflates accuracy. | sklearn GroupKFold; sklearn GroupShuffleSplit. |
| Group + time | Entities appear over time and time matters too (most production data). | Either of the above failures. | Custom: split by group, then for each group split by time. |
| Leave-one-out | Tiny datasets where every sample matters; model is cheap to fit. | O(n) cost. High variance for noisy targets. Often overstates performance. | sklearn LeaveOneOut. |
How to do a time-aware split
# DO NOT DO THIS on time-series data.
# Shuffling lets the model train on rows from after the test rows.
# The test metric will be wildly optimistic.
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
df = pd.read_csv("transactions.csv") # has a 'timestamp' column
X = df.drop(columns=["fraud", "timestamp"])
y = df["fraud"]
# WRONG: random split breaks temporal ordering.
X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.2, random_state=42)
clf = LogisticRegression().fit(X_tr, y_tr)
print("BAD eval:", clf.score(X_te, y_te)) # looks great, will collapse in prod
The most subtle leakage: fitting a scaler, imputer, encoder, or selection step on the whole dataset before splitting. Even if your split is correct, a StandardScaler fitted on full data has seen the test rows’ mean and standard deviation. The fix is Pipeline (sklearn) or hand-discipline: every fit happens on training, every transform applies to both.
Three real-world horror stories
| Year | System | What went wrong | Reference |
|---|---|---|---|
| 2015 | Hospital pneumonia risk model (asthma rule) | Asthmatic patients had LOWER pneumonia mortality in training data because hospital protocol routed them straight to ICU. A neural net trained without intelligibility constraints learned "asthma -> low risk" and would have sent asthmatics home. Caught only by switching to an interpretable model that surfaced the rule. | Caruana et al. 2015 KDD. |
| 2020-21 | COVID-19 chest X-ray classifiers | Many models hit 95-99% AUC offline. Roberts et al. surveyed 232 papers and found NONE clinically usable; common leakage included combining different hospital sources for COVID and non-COVID images, where institution-specific markers (collimation, layout) leaked the label. | Roberts et al. 2021 Nature Machine Intelligence. |
| Various | Kaggle competition leaderboards | Repeated leak patterns: image filenames encoding labels, ID columns correlated with the target, public-test rows leaked into training feeds, and meta-features computed across the full data. Several Kaggle winners were quietly disqualified after leakage post-mortems. | Kaufman et al. 2012; Kaggle public discussions. |
In Caruana et al. [2], a neural network and a generalised additive model were both trained to predict pneumonia mortality. Both reached high AUC. The neural net was a black box; the GAM was inspectable. Looking at the GAM, the team discovered the rule “patients with asthma have lower pneumonia mortality risk” baked into the model.
Clinically, this is backwards: asthma is a serious comorbidity for pneumonia. The rule existed in the training data because hospital protocol routed asthmatic pneumonia patients to ICU, where they got more aggressive care and better outcomes than the general ward. The model correctly learned the conditional probability in the data, but the data was a function of the treatment decision, not the underlying physiology.
If the neural network had been deployed, it would have advised sending high-risk asthmatic pneumonia patients home. The lesson is not “neural networks are dangerous” but “any model that learns from observational data can learn the treatment policy instead of the disease”.
Roberts et al. [3] reviewed 232 ML papers from the early pandemic that claimed to detect COVID-19 from chest X-rays or CT scans. Of those, zero were judged clinically usable. The most common failure modes:
- Source leakage. Most papers combined COVID images from one hospital with non-COVID images from another. The model learned to identify the source of the image (collimation, scanner, view positioning) rather than the disease.
- Pediatric leakage. Several models combined pediatric chest X-rays (typically used for the negative class) with adult X-rays (positive class). The model learned to detect children versus adults.
- Inappropriate splits. Time-based splits or hospital-based splits would have caught both. Random splits did not.
The paper is the canonical case study in why leakage audits should be a default part of any high-stakes ML system.
Before trusting any offline metric:
- Freeze train. Compute a hash of the training rows and feature columns. Do not modify after split.
- Hash test ids. Maintain a
setof test-row primary keys. Assert at every preprocessing stage that none appear in training. - Audit features for downstream-of-label. For each feature, ask “does this exist before the label exists?”. If you cannot answer in 30 seconds, the feature is suspect.
- Time-based split if there is a clock. Default to TimeSeriesSplit unless you have an explicit reason not to.
- Group-based split if there is an entity. Default to GroupKFold whenever a user, device, or patient can appear in multiple rows.
- Re-fit preprocessing on training only. Use sklearn
Pipelineor DIY discipline; never call.fit()on combined data.
Huyen [4] argues that leakage audits are the single highest-ROI piece of process discipline an ML team can add. Once a project has a leakage runbook, every project after it inherits it for free.
Interactive Demo
Explore three data leakage scenarios on a 12-month timeline. Toggle between Correct Split, Feature Leakage, and Target Leakage to see which patterns introduce leakage and why they cause models to fail in production.
Data Leakage Visualizer
Toggle between scenarios to see which data patterns constitute leakage and why.
Training data uses only historical information. The feature "last month's sales" is always available at prediction time — no leakage.
12-month data timeline
Uses only data from the prior month — information that would always be available in production.
Pop quiz
- 1. You are predicting whether a credit-card transaction is fraudulent. Which feature is most likely a target leak?
- 2. You have 1M rows of patient data; each patient appears in roughly 10 rows over time. What is the right cross-validation strategy?
- 3. You fit a StandardScaler on the full dataset, then split into train and test. What kind of leakage is this?
- 4. What was the central failure mode in the COVID chest-X-ray ML papers reviewed by Roberts et al. 2021?
Interview Q&A
Define target leakage, train-test contamination, and group leakage.phone
Target leakage is when a feature contains information from the label or from events downstream of the label. Example: “policy_cancelled_at” as a feature when predicting policy cancellation. The model gets perfect offline accuracy and zero production accuracy.
Train-test contamination is when the test set has informed any part of the training pipeline. The classic case is fitting a scaler on the full data, but it also covers feature selection on full data, hyperparameter tuning that uses the test set, or accidentally including test rows in the training set.
Group leakage is when the same entity (user, patient, device) appears in both train and test. The model learns per-entity signal and overstates generalisation to new entities. Fix by using GroupKFold.
You inherit an ML system that claims 0.95 AUC offline but 0.65 in production. How do you debug?onsite
The gap is too large to be statistical noise; one of three things is happening.
- Leakage in offline evaluation. Audit the split: was it random when it should have been time-based or group-based? Audit features for downstream-of-label fields. Re-evaluate with a corrected split and see if the offline number drops to match production.
- Distributional shift between training and production. Compare feature distributions in training vs current production traffic. If they have drifted significantly, retrain on a more recent window.
- Label distribution shift. The relationship between features and label has changed (concept drift), e.g. fraudsters adapted to the previous model. Online performance is the only ground truth here; retrain frequently.
The most likely root cause is leakage [1], because shifts of 30 AUC points are usually a process bug, not a real distributional change. Start there.
Walk through how you would set up evaluation for a model that recommends products to users over time.research
The data has a user dimension and a time dimension; both must be respected.
- Group + time split. Hold out the most recent N days of interactions for evaluation. Within those held-out days, split by user so that some users appear only in training and some only in evaluation. This tests both temporal generalisation (next week) and population generalisation (new users).
- Cold-start evaluation. Separately, hold out a subset of brand-new users with no training history and evaluate on their first session. Recommendation systems often look great until they hit cold-start.
- Offline metric tied to online metric. NDCG or MAP at the recommendation depth used in production, computed on the held-out sessions. Calibrate against a previous online A/B test so the offline metric is a credible predictor of online lift.
- Counterfactual evaluation. Use logged propensities (if available) to estimate what would have happened under the new policy. This handles the feedback-loop bias inherent in recommender systems.
- Slice metrics. Performance by user segment, item category, time of day, and country.
Without all five, the offline AUC is mostly a vanity metric.
References
- Kaufman, S., Rosset, S., Perlich, C., and Stitelman, O. (2012). Leakage in Data Mining: Formulation, Detection, and Avoidance. ACM Transactions on Knowledge Discovery from Data 6(4). doi:10.1145/2382577.2382579
- Caruana, R., Lou, Y., Gehrke, J., Koch, P., Sturm, M., and Elhadad, N. (2015). Intelligible Models for HealthCare: Predicting Pneumonia Risk and Hospital 30-Day Readmission. KDD. doi:10.1145/2783258.2788613
- Roberts, M., Driggs, D., Thorpe, M., Gilbey, J., Yeung, M., et al. (2021). Common pitfalls and recommendations for using machine learning to detect and prognosticate for COVID-19 using chest radiographs and CT scans. Nature Machine Intelligence 3(3), 199-217. doi:10.1038/s42256-021-00307-0
- Huyen, C. (2022). Designing Machine Learning Systems. O'Reilly. ISBN 978-1-098-10796-3