Sigmoidal
  • Home
  • LinkedIn
  • About me
  • Contact
No Result
View All Result
  • Português
  • Home
  • LinkedIn
  • About me
  • Contact
No Result
View All Result
Sigmoidal
No Result
View All Result

Scikit-Learn Pipelines: Prevent Data Leakage

Carlos Melo by Carlos Melo
July 6, 2026
in Machine Learning, tutorials
0
51
VIEWS
Share on LinkedInShare on FacebookShare on Whatsapp

Can a machine-learning model appear reliable because its preprocessing has already seen the test set? This failure is called data leakage. It occurs when information that should remain unavailable during training influences feature engineering, model selection, or evaluation. The resulting score may look impressive while overstating how the system will behave on genuinely unseen data.

Scikit-learn pipelines address an important part of this problem structurally. They connect preprocessing and estimation in one object, ensuring that each transformation is fitted at the correct point in training and cross-validation. In this article, you will examine the distinction between fit and transform, reproduce a numerical leakage example, and build a complete pipeline for mixed numerical and categorical data.

Open the executed notebook in Google Colab

Why must you split data before preprocessing?

A test set is a proxy for future observations. Its purpose is to estimate how a trained system will perform on data that played no role in model development. Once a statistic from that set influences training, the boundary between training and evaluation has been weakened.

Consider standardization. To transform a value $x$ into a standardized value $z$, StandardScaler uses a mean $\mu$ and a standard deviation $\sigma$:

\[ z = \frac{x – \mu}{\sigma} \]

If $\mu$ and $\sigma$ are calculated from the complete dataset, the test observations influence the coordinate system in which the model is trained. Their labels may never be exposed, but information about their feature distribution has already entered the process. This is why leakage can occur without an obvious programming error or direct access to the target.

The safe order is therefore:

  1. define the features and target;
  2. create the train-test split;
  3. fit every learned transformation on the training set;
  4. apply those fitted transformations to the test set;
  5. evaluate the model once on the untouched test data.

The companion notebook uses the Titanic dataset distributed through Seaborn. It retains survival as the target and five predictors: passenger class, sex, age, fare, and port of embarkation.

import seaborn as sns
from sklearn.model_selection import train_test_split

df = sns.load_dataset("titanic")
columns = ["survived", "pclass", "sex", "age", "fare", "embarked"]
df = df[columns]

X = df.drop(columns="survived")
y = df["survived"]

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, stratify=y, random_state=42
)

With this deterministic split, the executed notebook contains 712 training passengers and 179 test passengers. The original data also contain 177 missing ages and two missing embarkation values. Those missing values are useful here because imputation is itself a learned transformation and can leak information when applied in the wrong order.

What do fit, transform, and fit_transform actually do?

Scikit-learn transformers follow a consistent interface. fit learns state from data: a mean, a median, a vocabulary of categories, principal components, selected features, or another set of parameters. transform applies the operation using the state that has already been learned. fit_transform performs both steps on the same input and is typically used on training data.

The distinction is more than API vocabulary. It encodes which information an object is allowed to learn. In the manual standardization example, each scaler is fitted only on non-missing training values:

from sklearn.preprocessing import StandardScaler

age_scaler = StandardScaler().fit(X_train[["age"]].dropna())
fare_scaler = StandardScaler().fit(X_train[["fare"]].dropna())

print(age_scaler.mean_[0], age_scaler.scale_[0])
print(fare_scaler.mean_[0], fare_scaler.scale_[0])

The executed result gives an age mean of 29.81 with a standard deviation of 14.47, and a fare mean of 31.82 with a standard deviation of 48.03. These values become fitted attributes of the scaler. When the test set is transformed later, the training mean and standard deviation are reused; the test set does not receive a new coordinate system.

Histograms comparing original Titanic fares with fares standardized using training-set statistics

Standardization changes location and scale, not the underlying shape of the fare distribution. The plot was generated by the executed English notebook.

The long right tail remains visible after standardization. This matters because scaling is not a general remedy for skewness or outliers. It places features on comparable numerical scales, which can improve the optimization of models such as logistic regression and support vector machines, but it does not make a distribution Gaussian.

How can a small numerical difference invalidate an evaluation?

Leakage is often difficult to notice because the contaminated value may remain plausible. The notebook fits one fare scaler correctly on X_train and another incorrectly on all observations:

correct_scaler = StandardScaler().fit(X_train[["fare"]].dropna())
leaked_scaler = StandardScaler().fit(X[["fare"]].dropna())

print(correct_scaler.mean_[0])
print(leaked_scaler.mean_[0])

The training-only mean is 31.8198; the leaked mean is 32.2042. A difference of less than one monetary unit may seem harmless, but magnitude is not the governing principle. The second calculation uses information from the 179 held-out passengers before evaluation begins.

The effect becomes more consequential when preprocessing is sensitive to the empirical distribution. Examples include target encoding, feature selection, outlier thresholds, high-dimensional dimensionality reduction, nearest-neighbor imputation, and resampling. In these settings, a seemingly modest leak can change the representation or even which features enter the model.

The scikit-learn guide to common pitfalls makes the same point: preprocessing decisions must be learned from the appropriate training subset and then applied consistently to later data.

How does ColumnTransformer handle mixed feature types?

Real tables rarely contain one homogeneous feature type. Age and fare are numerical, while passenger class, sex, and embarkation port are categorical. Each group requires a different sequence of operations.

For the numerical branch, the notebook fills missing values with the training median and then standardizes the result. For the categorical branch, it fills missing values with the most frequent training category and applies one-hot encoding. handle_unknown="ignore" prevents an unseen category at inference time from causing an exception.

from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler

numeric_features = ["age", "fare"]
categorical_features = ["pclass", "sex", "embarked"]

numeric_transformer = Pipeline(steps=[
    ("imputer", SimpleImputer(strategy="median")),
    ("scaler", StandardScaler()),
])

categorical_transformer = Pipeline(steps=[
    ("imputer", SimpleImputer(strategy="most_frequent")),
    ("encoder", OneHotEncoder(handle_unknown="ignore")),
])

preprocessor = ColumnTransformer(transformers=[
    ("numeric", numeric_transformer, numeric_features),
    ("categorical", categorical_transformer, categorical_features),
])

The important property is not only convenience. Both imputers and the scaler remain inside an estimable object. When preprocessor.fit(X_train) is called, every learned quantity comes from the training data. The fitted object can then apply the same transformations to validation, test, or production observations.

This composition also preserves columns that require different representations without forcing them into separate manual workflows. The official documentation on pipelines and composite estimators describes this design as a way to assemble several transformations and a final estimator while exposing a unified interface.

How does a scikit-learn pipeline prevent leakage?

The final estimator connects preprocessor to logistic regression:

from sklearn.linear_model import LogisticRegression

model = Pipeline(steps=[
    ("preprocessing", preprocessor),
    ("classifier", LogisticRegression(max_iter=1000)),
])

model.fit(X_train, y_train)

print(model.score(X_train, y_train))
print(model.score(X_test, y_test))

One call to fit now learns the imputation values, scaling parameters, encoded categories, and classifier coefficients in the correct order. The executed notebook reports 0.801 training accuracy and 0.771 test accuracy.

Those two numbers are close, but this alone does not prove that the model generalizes or that logistic regression is optimal for the problem. It only shows that the demonstration does not exhibit a large training-test accuracy gap. The stronger claim is procedural: because the test set did not participate in fitting any step, the reported test accuracy is a legitimate held-out estimate for this specific split.

The pipeline also defines an operational contract. model.predict(new_data) first applies the fitted preprocessing and then computes the prediction. There is no need to reproduce scaling, imputation, and encoding in a separate production script. The same fitted object can be serialized, versioned, and tested as one unit.

Why are pipelines essential during cross-validation?

Cross-validation does not remove leakage automatically. Suppose you standardize the complete training set and only then call cross_val_score. Each validation fold has already influenced the scaling parameters used by the other folds. The outer test set remains untouched, but the cross-validation estimate used for model selection is contaminated.

When the entire pipeline is passed to cross_val_score, scikit-learn clones and fits it inside each training fold. The preprocessing state is therefore relearned five times in a five-fold evaluation:

from sklearn.model_selection import cross_val_score

scores = cross_val_score(
    model, X_train, y_train, cv=5, scoring="accuracy"
)

The executed scores are 0.790, 0.762, 0.796, 0.831, and 0.810, for a mean accuracy of 0.798 ± 0.023. Their proximity to the held-out test accuracy is reassuring, although it should not be overinterpreted: five folds on one modest dataset do not characterize every subgroup, threshold, or deployment condition.

If hyperparameters are tuned, the same principle extends to model selection. Keep preprocessing inside the estimator supplied to GridSearchCV or RandomizedSearchCV. For an unbiased final estimate after extensive tuning, retain a separate test set or use nested cross-validation. The scikit-learn cross-validation guide details these evaluation patterns.

What does a pipeline not protect you from?

A pipeline prevents leakage only for operations placed inside it and evaluated with a correct splitting strategy. It cannot repair information that was already introduced upstream.

Several risks remain:

  • Temporal leakage: a random split may allow future observations to inform a model intended to predict the past. Time-dependent problems need chronological validation.
  • Group leakage: records from the same patient, customer, device, or site may appear in both training and validation sets. Group-aware splitting is required.
  • Target-derived features: a feature calculated from the label, or from data collected after the prediction moment, remains invalid even when wrapped in a pipeline.
  • Duplicate entities: near-duplicate images, documents, or transactions can make train and test sets artificially similar.
  • Selection on the test set: repeatedly checking the test score and changing the model turns the test set into an informal validation set.

The pipeline is therefore a mechanism for enforcing transformation boundaries, not a substitute for understanding how data were generated. A reliable evaluation begins by defining the unit of independence, the prediction moment, and the operational population before choosing a splitter.

Takeaways

  • Split before fitting transformations. The test set must not influence means, medians, categories, selected features, or any other learned preprocessing state.
  • fit learns; transform applies. This distinction determines which data contribute information to a transformer.
  • A small leaked statistic still invalidates the boundary. The issue is unauthorized information flow, not only the numerical size of the difference.
  • ColumnTransformer gives each feature type its own path. Numerical and categorical preprocessing remain explicit while sharing one fitted interface.
  • Pipeline joins preprocessing and estimation. The same object can support training, validation, prediction, persistence, and deployment.
  • Cross-validation must refit preprocessing inside every fold. Passing the complete pipeline to model-selection tools preserves this isolation.
  • Data design remains decisive. Temporal, grouped, duplicated, or target-derived information requires a splitting strategy that reflects the real prediction task.
ShareShare1Send
Previous Post

Binary Cross-Entropy and Logistic Regression

Next Post

DETR: Object Detection as Set Prediction

Carlos Melo

Carlos Melo

Computer Vision Engineer with a degree in Aeronautical Sciences from the Air Force Academy (AFA), Master in Aerospace Engineering from the Technological Institute of Aeronautics (ITA), and founder of Sigmoidal.

Related Posts

Urban YOLO26 scene with one bus and four pedestrians, each outlined once to represent end-to-end object detection
Computer Vision

YOLO26: What Changed in Object Detection

by Carlos Melo
August 28, 2026
Follower drone using a four-microphone array to estimate bearing and range from the natural propeller sound of a leader drone
Aerospace Engineering

SonicFly: How Drones Pursue Each Other by Sound

by Carlos Melo
August 21, 2026
Urban intersection in which a DETR-style detector assigns one prediction box to each pedestrian, cyclist, vehicle, bus, and traffic light
Computer Vision

DETR: Object Detection as Set Prediction

by Carlos Melo
August 20, 2026
Probability surface and linear decision boundary learned by logistic regression on two overlapping classes
Data Science

Binary Cross-Entropy and Logistic Regression

by Carlos Melo
June 1, 2026
Conceptual 3D reconstruction pipeline connecting photographs, estimated camera poses, and a sparse point cloud
Computer Vision

Feature Detection for 3D Reconstruction

by Carlos Melo
April 11, 2026
Next Post
Urban intersection in which a DETR-style detector assigns one prediction box to each pedestrian, cyclist, vehicle, bus, and traffic light

DETR: Object Detection as Set Prediction

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

  • Trending
  • Comments
  • Latest

Real-time Human Pose Estimation using MediaPipe

September 11, 2023
ORB-SLAM 3: A Tool for 3D Mapping and Localization

ORB-SLAM 3: A Tool for 3D Mapping and Localization

April 10, 2023

Build a Surveillance System with Computer Vision and Deep Learning

1
ORB-SLAM 3: A Tool for 3D Mapping and Localization

ORB-SLAM 3: A Tool for 3D Mapping and Localization

1
Point Cloud Processing with Open3D and Python

Point Cloud Processing with Open3D and Python

1

Fundamentals of Image Formation

0
Urban YOLO26 scene with one bus and four pedestrians, each outlined once to represent end-to-end object detection

YOLO26: What Changed in Object Detection

August 28, 2026
Follower drone using a four-microphone array to estimate bearing and range from the natural propeller sound of a leader drone

SonicFly: How Drones Pursue Each Other by Sound

August 21, 2026
Urban intersection in which a DETR-style detector assigns one prediction box to each pedestrian, cyclist, vehicle, bus, and traffic light

DETR: Object Detection as Set Prediction

August 20, 2026
Industrial data pipeline with English-labeled stages for scaling, imputation, encoding, and modeling

Scikit-Learn Pipelines: Prevent Data Leakage

July 6, 2026
Instagram Youtube LinkedIn Twitter
Sigmoidal

O melhor conteúdo técnico de Data Science, com projetos práticos e exemplos do mundo real.

Seguir no Instagram

Categories

  • Aerospace Engineering
  • Blog
  • Carreira
  • Computer Vision
  • Data Science
  • Deep Learning
  • Featured
  • Iniciantes
  • Machine Learning
  • Posts
  • Tutoriais
  • tutorials

Navegar por Tags

3d 3d machine learning 3d vision bayer filter camera calibration clahe computer vision custom dataset data science deep learning depth anything depth estimation digital image processing fine-tuning grad-cam histogram histogram equalization image formation job lens machine learning machine learning engineering mediapipe object detection open3d opencv python pytorch quantization redes neurais resnet roboflow rocket sampling scikit-learn space tensorflow transfer-learning transformer tutorial vision-transformer visão computacional vit yolov8 yolov9

© 2024 Sigmoidal - Aprenda Data Science, Visão Computacional e Python na prática.

Welcome Back!

Login to your account below

Forgotten Password?

Retrieve your password

Please enter your username or email address to reset your password.

Log In

Add New Playlist

No Result
View All Result
  • Home
  • Mentoria
  • Cursos
  • Blog
  • Sobre Mim
  • Contato
  • Português

© 2024 Sigmoidal - Aprenda Data Science, Visão Computacional e Python na prática.