← All Posts
Deep Learning · Data Preparation and Preprocessing · Splitting & Stratification

Data Leakage in Splitting

A classifier reaches 73% test accuracy on independent random features and random labels. There is no predictive relationship to discover. The score comes from a mistake: the test labels helped choose the features before the classifier was trained.

Data leakage is information crossing an evaluation boundary where it was not allowed to influence the system. A different CSV file is not enough protection. We must trace what influenced the features, preprocessing, model choices, and final predictions.

A high test score with no real signal

Generate 400 rows, each with 5,000 independent noise features, and give each row an independent random binary label. Reserve 200 rows for training and 200 for test. Because the labels are independent of the features, performance on fresh examples should be around chance.

We compare two procedures. Both select 25 features using their association with the labels and fit logistic regression. In the first procedure, selection sees all 400 rows and labels. In the second, selection sees only the 200 training rows. The train/test IDs and classifier settings are otherwise the same. Defining split IDs first does not help if a later fit ignores that assignment.

LEAKED PATHAll labels → choose features → split → fit classifierTest labels have already influenced which questions the classifier gets to ask.
PROPER PATHSplit → choose features on train → fit classifierApply the saved selection to test. Its labels are used only to score predictions.
The final classifier never needs to see test labels for leakage to occur. A learned step earlier in the pipeline can carry their influence into the model.
Measured accuracy on the same 200 test rows
Leaked
73.0%
Proper
50.5%
146 versus 101 correct predictions. The dashed marker is 50%. These results were computed from the code below with NumPy 2.5.3 and scikit-learn 1.9.1. This is one controlled noise experiment, not a general estimate of the size of leakage effects.

Why does selection matter so much? Among 5,000 noise columns, some will align with random labels by accident. Selecting columns using the test labels keeps accidents that also look useful on those test rows. The classifier is then evaluated on a sample that helped construct its input representation.

Proper selection can also find accidental patterns, but those patterns were chosen using training labels only. Independent test labels can expose their failure to generalize. This experiment follows the feature-selection leakage demonstration in scikit-learn’s common-pitfalls guide, using our own dataset and settings.

Run the complete noise experiment
import numpy as np
from sklearn.feature_selection import SelectKBest, f_classif
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.pipeline import make_pipeline

rng = np.random.default_rng(7)
X = rng.normal(size=(400, 5000))
y = rng.integers(0, 2, size=400)
train, test = train_test_split(
    np.arange(400), test_size=0.5, random_state=11, stratify=y
)

# Incorrect: held-out labels influence feature selection.
leaked_X = SelectKBest(f_classif, k=25).fit_transform(X, y)
leaked_model = LogisticRegression(max_iter=1000)
leaked_model.fit(leaked_X[train], y[train])
leaked_pred = leaked_model.predict(leaked_X[test])

# Correct: every learned step fits on training rows only.
proper_model = make_pipeline(
    SelectKBest(f_classif, k=25),
    LogisticRegression(max_iter=1000)
)
proper_model.fit(X[train], y[train])
proper_pred = proper_model.predict(X[test])

print("Leaked correct:", int((leaked_pred == y[test]).sum()))
print("Proper correct:", int((proper_pred == y[test]).sum()))
# Leaked correct: 146
# Proper correct: 101

The intentionally incorrect path is included to isolate the failure, not as a template to reuse. A different random dataset or software version can change the exact counts. The diagnostic is whether held-out outcomes influenced a learned choice, not whether a score happens to be high.

Preprocessing can leak without using labels

Suppose training values are 2, 4, 6, and one missing entry. A mean imputer should learn 4 from the observed training values. If it also sees a held-out value of 100, its mean becomes 28. The test example has changed the value inserted into the training data.

Where the imputer fitsObserved values it usesReplacement
Training only2, 4, 6(2 + 4 + 6) / 3 = 4
Training and held-out2, 4, 6, 100(2 + 4 + 6 + 100) / 4 = 28

This violates the usual evaluation setting where future examples do not help fit the system. It does not imply that every leaked transform raises every metric; some transformations have little effect for a particular model. The boundary is invalid regardless of the direction of the score change.

Use fit or fit_transform on training data, and the fitted object’s transform on held-out data. Scaling, imputation, learned vocabularies, principal components, and feature selection all need this distinction. A pipeline helps ensure these steps are refitted within each training fold during validation.

Can preprocessing ever use unlabeled evaluation inputs?

Some explicitly transductive or adaptation settings permit access to a particular unlabeled target collection before prediction. That is a different protocol and should be described and evaluated as such. It does not justify silently fitting ordinary preprocessing on a future test set while claiming performance for a system trained without that access.

Look for the other routes across the boundary

RouteConcrete exampleRepair
Related examplesChunks from the same source document appear in train and test.Split parent documents or duplicate families before distributing chunks.
Future informationA forecast feature contains an average that includes tomorrow’s values.Rebuild the feature from information available at the prediction time.
Outcome recorded after the decisionAn escalation predictor uses the final ticket-resolution code.Remove or reconstruct the field as it existed when the ticket arrived.
Resampling before splittingA duplicated minority example lands in training and test.Apply training resampling only after the evaluation split, inside each training fold.
Human model selectionRepeatedly read test errors and revise features until the test score improves.Use validation for revisions and retain fresh data for an independent assessment.

The detailed repairs for shared entities and delayed outcomes are in group splitting and time-series splitting. They are separate checks: a pipeline can handle preprocessing correctly while the split still shares users or uses future labels.

Audit an actual prediction, working backward

Choose one held-out prediction and trace its dependencies. Which model version produced it? Which rows fitted that model and its transforms? Which records supplied each feature, and when did those records become available? Which validation results influenced the selected settings?

Then look for a held-out row, related source, or unavailable outcome in that dependency chain. Save enough provenance—row and group assignments, timestamps, fitted pipeline, and selection record—to check the answer. Disjoint row IDs are useful evidence, but they do not prove every dependency is separate.

A practical check is to rerun the full procedure with label permutations appropriate to the data structure. Persistently strong performance after destroying the intended feature–label relationship can reveal problems. It is a diagnostic, not a universal proof: preserving group or time dependence may require a more careful permutation scheme, and ordinary leakage can exist even when this check shows chance performance.

Repair the experiment before interpreting the metric

If leakage is found, rebuild the affected features or pipeline and rerun the evaluation with the correct boundary. A preprocessing mistake does not automatically mean every row is unusable. But if test outcomes have already guided development decisions, that test set no longer supplies an independent assessment of those decisions; use a fresh hold-out or a properly designed outer evaluation.

Report the corrected procedure and result. A lower score can be more useful because it describes the task the deployed model will actually face. The next chapter covers reproducible splits, so that a repaired experiment remains stable across reruns.

References and further reading