Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 39

Streamlining Machine Learning with scikit-learn Pipelines

Recommended resources

Welcome to the final lesson in our module on feature engineering and preprocessing. In the last session, you learned how to select the most impactful features using correlation analysis and Recursive Feature Elimination (RFE). You saw how this involved several manual steps: scaling the data, fitting the selector, transforming the train and test sets separately, and finally training the model. While effective, this process can be cumbersome and, more importantly, prone to subtle but critical errors.

This lesson introduces the solution: scikit-learn Pipelines. A Pipeline is a powerful tool that chains together all your preprocessing and modeling steps into a single, cohesive object. By the end of this lesson, you will be able to build reproducible, robust, and clean workflows that are the gold standard for any serious machine learning project. This will be a foundational skill for all the models we build going forward.

1. The Core Problem: Data Leakage

Before we build a pipeline, it's crucial to understand the main problem it solves: data leakage. Data leakage occurs when information from your test set inadvertently "leaks" into your training process, leading to an overly optimistic and unreliable evaluation of your model's performance.

A common way this happens is by applying a preprocessing step, like scaling, to the entire dataset before splitting it into training and testing sets.

To understand why this is a mistake and how to correct it, please read the following article. It provides a crystal-clear explanation of data leakage and sets the stage perfectly for why pipelines are essential.

How to Avoid Data Leakage When Performing Data ...

This article by Machine Learning Mastery is a must-read for understanding data leakage.

Please read the following sections: Problem With Naive Data Preparation: This explains what data leakage is and why applying transformations to the full dataset is incorrect. Train-Test Evaluation With Correct Data Preparation: This shows the correct, manual way to prepare data after splitting. Cross-Validation Evaluation With Correct Data Preparation: This is the key section. It introduces the Pipeline class as the definitive solution for avoiding data leakage, especially within a cross-validation loop.

The key takeaway is that every step in your modeling process that learns parameters from the data (e.g., the mean/std for StandardScaler, the min/max for MinMaxScaler, the feature importances for RFE) must be fitted only on the training data. A Pipeline enforces this rule automatically.


2. Building a Basic Pipeline

A Pipeline chains together multiple steps: a sequence of transformers (for preprocessing) and a final estimator (the model). When you call .fit() on a pipeline, it intelligently calls fit_transform() on each transformer for the training data and passes the result to the next step, finally fitting the estimator. When you call .predict() or .transform() on new data, it correctly calls only transform() on each transformer.

The following video by Krish Naik provides a great walkthrough of creating your first simple pipeline.

Implementing Machine Learninng Pipelines USsing Sklearn And Python

Krish Naik's video, "Implementing Machine Learning Pipelines," offers a practical introduction.

Watch the first ~10 minutes to see a basic pipeline in action: Basic Pipeline: This covers the Pipeline class syntax, how to define steps as named tuples, and the basic idea of chaining a scaler and a classifier. Visualizing and Preparing: This shows a neat trick for visualizing the pipeline's structure. Fitting and Predicting: Pay close attention to his explanation of what happens internally during fit (on train data) and predict (on test data). This reinforces the concept of avoiding data leakage.

As you can see, the pipeline encapsulates the entire workflow. Instead of managing a separate scaler and model, you manage a single pipeline object. This drastically simplifies your code and makes it far more robust.


3. Handling Mixed Data Types with ColumnTransformer

Real-world datasets rarely contain only one type of data. You'll almost always have a mix of numerical and categorical features, each requiring different preprocessing steps (e.g., scaling for numbers, one-hot encoding for categories).

A simple Pipeline applies every step to all columns. To apply different transformers to different columns, we use ColumnTransformer.

This diagram illustrates how `ColumnTransformer` applies different preprocessing transformations (scaling, one-hot encoding, or passthrough) to different columns of a dataset in parallel.

ColumnTransformer is a special transformer that groups together other transformers. You tell it which transformer to apply to which set of columns. This ColumnTransformer can then be used as a single step inside a larger Pipeline.

The article below provides a fantastic, detailed guide. We'll focus on the section explaining ColumnTransformer.

Sklearn Pipeline: Complete Guide to Building ML Pipelines in ...

This guide from Kanaries is an excellent resource that you might want to bookmark.

Please read the section titled "ColumnTransformer for Mixed Data Types". It provides a clear code example showing how to: Define lists of numeric and categorical feature names. Create separate Pipeline objects for numeric and categorical transformations. Combine these into a ColumnTransformer. Place the ColumnTransformer as the first step in a final Pipeline that includes a classifier.

This pattern—using a ColumnTransformer to create a preprocessor step—is the most common and powerful way to build pipelines for real-world data.


4. Pipelines and Hyperparameter Tuning with GridSearchCV

Perhaps the most significant advantage of pipelines is their seamless integration with GridSearchCV. You can tune the hyperparameters of any step in the pipeline—including the preprocessing steps and the final model—all at once.

To do this, you create a parameter grid. The keys of the grid use a special stepname__parameter syntax (with a double underscore). For example, to tune the C parameter of a LogisticRegression step named 'classifier', you would use 'classifier__C'.

This is incredibly powerful. Let's say you want to find the best number of features to select with RFE and the best regularization strength for your model simultaneously. With a pipeline, GridSearchCV handles this automatically.

This diagram shows the complete workflow: separate preprocessing for numeric and categorical data using `ColumnTransformer`, followed by an estimator, all wrapped inside a `GridSearchCV` for hyperparameter tuning.

The following guide demonstrates this process clearly.

Sklearn Pipeline: Complete Guide to Building ML Pipelines in ...

Let's return to the Kanaries guide to see how tuning works.

Read the section "Pipeline with GridSearchCV". Focus on: The stepname__parameter syntax for defining the parameter grid. How to tune parameters of nested steps, such as those inside a ColumnTransformer (e.g., 'preprocessor__num__imputer__strategy'). The use of RandomizedSearchCV for larger search spaces, which is a common practice you should be aware of.

When you use GridSearchCV with a Pipeline, scikit-learn does the right thing: for each fold in the cross-validation, it refits the entire pipeline on the training part of the fold. This guarantees that no information from the validation part of the fold leaks into your preprocessing steps, giving you a trustworthy estimate of your model's performance.


5. Hands-on Practice: Building a Full Pipeline

Now, let's bring everything together. In the previous lesson, you performed feature selection using a manual, multi-step process. Your task is to refactor that workflow into a single, elegant Pipeline and then use GridSearchCV to find the best number of features to select.

Here is the setup code. Your goal is to complete the sections marked --- YOUR CODE GOES HERE ---.

import pandas as pd
import numpy as np
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.preprocessing import StandardScaler
from sklearn.feature_selection import RFE
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline




# 1. Generate data
# We'll use the same data generation as the previous lesson
X, y = make_classification(
    n_samples=1000,
    n_features=20,
    n_informative=5,
    n_redundant=8,
    n_classes=2,
    random_state=42
)
X = pd.DataFrame(X, columns=[f'feature_{i}' for i in range(20)])




# Split data
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42, stratify=y)





# --- YOUR CODE GOES HERE ---




# 2. Define the Pipeline
# We'll create a pipeline with three steps:
# a. 'scaler': A StandardScaler for numeric features.
# b. 'selector': An RFE step using a LogisticRegression estimator.
# c. 'classifier': The final LogisticRegression model.




# Note: Since all our features are numeric, we don't need a ColumnTransformer here.
# We apply the scaler to all features.

pipeline = Pipeline([
    ('scaler', StandardScaler()),
    ('selector', RFE(LogisticRegression(solver='liblinear'))),
    ('classifier', LogisticRegression(solver='liblinear'))
])





# 3. Define the Parameter Grid for GridSearchCV
# We want to tune two hyperparameters:
# a. The number of features to select in the RFE step ('selector__n_features_to_select').
# b. The regularization strength 'C' of the final classifier ('classifier__C').

param_grid = {
    'selector__n_features_to_select': [5, 7, 10, 15],
    'classifier__C': [0.01, 0.1, 1, 10]
}





# 4. Set up and run GridSearchCV
# Create a GridSearchCV object with the pipeline, parameter grid, 5-fold CV,
# and accuracy as the scoring metric.

grid_search = GridSearchCV(
    estimator=pipeline,
    param_grid=param_grid,
    cv=5,
    scoring='accuracy',
    n_jobs=-1  # Use all available CPU cores
)




# Fit the grid search on the training data. This will find the best hyperparameters.
grid_search.fit(X_train, y_train)





# 5. Evaluate the best model
print(f"Best parameters found: {grid_search.best_params_}")
print(f"Best cross-validation accuracy: {grid_search.best_score_:.4f}")




# The grid_search object automatically refits the best pipeline on the entire training set.
# We can now evaluate it on the test set.
test_accuracy = grid_search.score(X_test, y_test)
print(f"Test set accuracy: {test_accuracy:.4f}")

Solution Walkthrough

  1. Pipeline Definition: We create a Pipeline object with a list of three (name, object) tuples. This defines our reproducible workflow: scale the data, then select features with RFE, then classify.
  2. Parameter Grid: We define a dictionary to tell GridSearchCV what to tune. Using the stepname__parameter syntax, we specify that we want to test different values for n_features_to_select within the 'selector' step and different values for C within the 'classifier' step.
  3. Grid Search: We pass our pipeline object to GridSearchCV just like we would with a simple model. GridSearchCV is smart enough to handle the pipeline, refitting all steps for each fold and each parameter combination.
  4. Evaluation: After fitting, grid_search.best_params_ shows the winning combination of hyperparameters. The grid_search.score() method then uses the best-found pipeline to make predictions on the test set, giving us our final performance metric.

Notice how much cleaner and safer this is. All the logic is encapsulated in one object, and the hyperparameter tuning is both powerful and statistically sound.


Conclusion

You have now mastered one of the most important tools in the scikit-learn ecosystem. Building reproducible pipelines is a non-negotiable skill for production-level machine learning.

Key Takeaways:

  • Pipelines prevent data leakage by ensuring preprocessing steps are fitted only on training data, even during complex cross-validation.
  • They create reproducible workflows by bundling all preprocessing and modeling steps into a single object that can be saved, loaded, and deployed.
  • ColumnTransformer is used within a pipeline to apply different transformations to different types of columns.
  • Pipelines integrate seamlessly with GridSearchCV for tuning hyperparameters across the entire workflow, using the stepname__parameter syntax.

In our next module, we will dive into specific supervised learning algorithms, starting with the foundational Linear Regression model. From this point on, we will use the pipeline structure you've learned today to organize all our modeling experiments.