Skip to content
ZYNQORALABS

Data leakage in predictive models: when a model looks too good

Data leakage makes a model shine in validation and fail in production. The most common kinds, how to detect them, and how to validate on time-ordered data.

By ZynqoraLabs · · 3 min read

ShareLinkedInXWhatsApp

A churn model with an AUC of 0.98 in validation should trigger suspicion, not celebration. In most business problems, a result like that means the model had access, during training or validation, to information that won't exist at the real moment of prediction. That's data leakage, and it's one of the most common reasons models work in a notebook and fail in production.

The three most common kinds

Target leakage

An input variable carries the answer, directly or indirectly. The classic example: predicting customer churn using a "cancellation reason" field that is only filled in after the customer cancels. Less obvious examples are more dangerous:

  • "Support tickets in the last 30 days", when the window includes days after cancellation.
  • "Date of last invoice", when invoices stop being issued precisely for customers who cancelled.

The question that separates a valid feature from a leaked one is always the same: at the moment the model makes its prediction, does this value already exist?

Train-test contamination

Transformations fitted on the whole dataset before splitting leak test statistics into training. Scaling, missing-value imputation, feature selection and rare-category encoding are the typical cases. The effect is often small, but it grows with small datasets and high-cardinality features.

The fix is structural: every learned transformation belongs inside the pipeline and is fitted only on each split's training data.

pipeline.py
from sklearn.pipeline import make_pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
 
# The imputer and scaler are fitted inside each split, never on the full dataset.
model = make_pipeline(SimpleImputer(strategy="median"), StandardScaler(), LogisticRegression(max_iter=1000))

Temporal and group leakage

Random splits assume rows are independent. In business data, they almost never are.

  • Time: if you shuffle 2024 and 2025 transactions and validate randomly, the model learns patterns from the future to predict the past. In production it will only have the past.
  • Groups: if the same customer appears in both training and test, the model can memorize the customer instead of learning the pattern. The metric goes up; generalization doesn't.

Validate the way the model will be used

The most useful rule is simple: validation should mimic how the model will be used in production. If the model will be trained on data up to today to predict next month, validation should train up to a date and evaluate on the following period, repeatedly.

validation.py
from sklearn.model_selection import TimeSeriesSplit, GroupKFold, cross_val_score
 
# Rows sorted by date: each split trains on the past and validates on the next period.
temporal = TimeSeriesSplit(n_splits=5, gap=30)  # gap prevents overlapping feature windows
scores = cross_val_score(model, X_sorted, y_sorted, cv=temporal, scoring="roc_auc")
 
# When one customer produces many rows, keep the customer entirely on one side.
grouped = GroupKFold(n_splits=5)
scores_by_customer = cross_val_score(model, X, y, cv=grouped, groups=customer_id, scoring="roc_auc")

The gap parameter deserves attention: if a feature uses a 30-day window, the last training rows and the first validation rows share information. A gap between the two sets removes that overlap.

How to detect leakage

A few signals come up often:

  • Metrics too good for the problem's known difficulty.
  • One feature dominates importance by a wide margin. Investigate how and when it's computed.
  • A large drop between random and time-based validation. The difference is a rough measure of how much the model relied on the future.
  • Production performance far below validation in the first weeks, with no visible change in the data.

One effective practice is to document, for each feature, when it becomes available relative to the predicted event. That table often reveals the problem before any experiment does.

Build point-in-time features

The most robust way to avoid temporal leakage is to compute features with queries that take an explicit reference date and only see data before it. Instead of a customer table holding the "current state", you generate, for each training example, the customer's state as of that date.

That requires timestamped historical data, and sometimes it means discovering that the source system overwrites values without keeping history. Finding that out early is part of the work: an honest model with fewer features is worth more than a brilliant one that doesn't hold up.

Summary

Data leakage isn't a rare beginner's mistake. It appears naturally whenever business data is treated as independent samples. Validating the way the model will be used, keeping transformations inside the pipeline and computing point-in-time features solves most cases, and a slightly lower, truthful metric is what lets you make reliable decisions with the model.