Data Leakage: 5 Hidden Ways Your Model Cheats

โšก TL;DR: Data leakage happens when information the model will not have at prediction time sneaks into training or evaluation. The validation score looks brilliant and the deployed model disappoints. Split first, fit every preprocessing step on the training rows only, and wrap it all in a pipeline.

Data leakage is the most expensive bug in machine learning because it does not look like a bug. Nothing crashes. The model trains, the score is excellent, everyone is pleased, and then it meets real data and falls apart. This post covers the five common ways leakage creeps in, how to spot it, and the one habit that prevents most of it.

What data leakage is

A model is only useful if it predicts things it has not seen. That is why you hold back a test set, as covered in training and test data. Leakage is anything that lets information about the answers reach the model during training or evaluation when it would not be available in real use. The model then learns the leak instead of the pattern, and the test score measures the leak.

Data leakage diagram comparing the wrong order, fitting before the split, with the right order, splitting first, and a bar chart where features picked before the split score 91 percent on pure noise against 50 percent when picked inside each fold
On random noise with random labels, choosing features before the split scores 91% in cross-validation; choosing them inside each fold gives the honest answer, 50%.

The experiment behind the right-hand chart is worth understanding. It used 100 rows with 5,000 columns of pure random numbers and random 0/1 labels. There is nothing to learn. But among 5,000 random columns, a few will happen to line up with the labels by chance. Pick the 20 best-matching columns using all 100 rows and those chance matches include the test rows, so every fold is marked on questions the model has already peeked at. Pick them inside each fold, from training rows only, and the fake skill vanishes.

1. Target leakage

Target leakage means a feature contains the answer, or something only known after the answer. It is the most common kind and the hardest to see, because the feature looks perfectly reasonable.

๐Ÿงช The antibiotic column

You predict whether a patient has pneumonia, and one feature is took_antibiotics. Say that in the historical data 92% of pneumonia patients have it set to yes, against 8% of the rest. The model leans on it heavily and scores 0.97 AUC.

But patients receive antibiotics because they were diagnosed. At the moment you want a prediction, before diagnosis, that column is always no. In real use the model has lost its best feature and its accuracy collapses.

The test is simple: for each feature, ask “would I know this value at the exact moment I need the prediction?” If not, drop it.

Other classic examples: a days_overdue column in a loan default model, an account_closed_date in a churn model, or an ID number that was assigned in a different sequence to positive and negative cases.

2. Train-test contamination

Here the test rows influence training directly. The obvious version is training on data that includes the test set. The subtle version is tuning. If you try 200 hyperparameter settings and keep whichever scores best on the test set, you have fitted the test set by selection. Use a separate validation set, or cross-validation, for tuning, and touch the test set once at the very end.

3. Preprocessing fitted before the split

This is the leak in the figure, and it is everywhere in tutorials. Any step that learns something from the data counts: scaling learns a mean and standard deviation, imputation for missing data learns a fill value, target encoding learns category averages, and feature selection learns which columns matter.

Fit any of these on the full dataset and the test rows have helped shape the transformation. For scaling alone the effect is usually small. For target encoding and feature selection it can be enormous, as the 91% versus 50% result shows.

The rule: split first, then call fit on the training rows only, and transform on both. Never fit_transform the whole dataset.

4. Time leakage

With time-ordered data, a random split lets the model train on Thursday and be tested on Wednesday. It learns from the future. A stock model trained on a shuffled 2020 to 2025 dataset may score well because it has seen prices from the days either side of every test day.

Split by time instead: train on everything before a cut-off date and test on everything after. Features need the same care. A “30-day average sales” column must use only the 30 days before each row, not a window centred on it.

5. Duplicates and near-duplicates

If the same row, or the same person, appears in both training and test, the model can simply remember it. This happens with repeated customer records, several photos of the same patient, augmented copies of an image, or oversampled rows in imbalanced datasets. Remove exact duplicates before splitting, and when one entity has many rows, use a grouped split so all of that entity’s rows land on the same side.

LeakFix
Target leakageDrop any feature not known at prediction time
Train-test contaminationTune on validation or cross-validation; test once
Preprocessing before splitUse a pipeline so every fit sees training rows only
Time leakageSplit by date; build features from the past only
DuplicatesDe-duplicate first; group rows by entity when splitting

How to spot it

The strongest signal is a score that is too good to be true. If published work on a problem reaches 75% and you get 99% on your first attempt, assume a leak until you have proved otherwise.

โš ๏ธ Warning signs

  • One feature dominates. If a single column carries most of the feature importance, ask when its value becomes known.
  • Near-perfect scores on a hard problem. Real-world data is noisy; 0.99 AUC on messy human behaviour is suspicious.
  • A big gap between validation and production. A model that scored 94% offline and manages 68% after deployment almost always had a leak.
  • Random splits beat time-based splits by a wide margin. That gap is the model reading the future.

The fix: pipelines

Most preprocessing leaks come from doing steps by hand in the wrong order. A pipeline bundles every step with the model, so that whenever the pipeline is fitted, every step is fitted on the same training rows, and whenever it predicts, every step only transforms.

In scikit-learn that is pipe = make_pipeline(StandardScaler(), SelectKBest(k=20), LogisticRegression()), followed by cross_val_score(pipe, X, y, cv=5). Inside each fold the scaler and the feature selector see only that fold’s training rows. That is the right-hand bar of the figure: selection redone inside every fold, giving the honest 50% on noise.

โœ… One habit that prevents most leaks: split off your test set on day one, before any exploration, feature engineering or preprocessing, and put every learned step inside a pipeline. Pipelines also make deployment safer, because the exact same transformations travel with the model.

Pipelines do not catch target leakage or time leakage, though. Those need you to think about what is known when. No library can check that for you.

๐Ÿ”‘ Key Takeaways

  • Data leakage lets answers reach the model during training or evaluation, inflating the score.
  • Ask of every feature: would I know this value at the moment I need the prediction?
  • Split first, then fit scalers, imputers, encoders and feature selectors on training rows only.
  • Split time-ordered data by date and keep duplicates or related rows on one side of the split.
  • A score that is too good to be true usually is; pipelines prevent the preprocessing leaks.

Further reading

scikit-learn’s guide to common pitfalls and recommended practices walks through preprocessing leakage with runnable pipeline examples, and the Wikipedia article on leakage lists further forms and real cases.

Where to go next

Scroll to Top