DS ML Pipelines

A machine learning pipeline links every step, from raw input to prediction, into a single object. Filling gaps, encoding categories, scaling numbers, and training a model all run in a fixed order. Pipelines prevent mistakes, shorten code, and make models easy to save and reuse.

A car factory line offers a good picture. Each station performs one job, and the product moves along the same path every time.

Why Pipelines Matter

Problem Without PipelinesFix With Pipelines
Scaler learns from test data (leakage)Steps fit on training data only
Forgetting a step at prediction timeOne object repeats every step
Long, messy notebooksShort, readable code
Hard to save the whole processSave one file

Pipeline Structure

 Raw data
    |
 [ Impute missing values ]
    |
 [ Encode categories / Scale numbers ]
    |
 [ Select features (optional) ]
    |
 [ Model ]
    |
 Prediction

A Simple Pipeline

from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression

pipe = Pipeline([
    ("scale", StandardScaler()),
    ("model", LogisticRegression())
])

pipe.fit(X_train, y_train)
print(pipe.score(X_test, y_test))

Calling fit runs each step in order on the training data. Calling predict applies the learned scaling to new data automatically.

Different Steps for Different Columns

Numeric and text columns need different handling. ColumnTransformer applies the right steps to the right columns.

import pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import OneHotEncoder, StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.ensemble import RandomForestClassifier

num_cols = ["age", "income"]
cat_cols = ["city", "plan"]

num_steps = Pipeline([
    ("fill", SimpleImputer(strategy="median")),
    ("scale", StandardScaler())
])

cat_steps = Pipeline([
    ("fill", SimpleImputer(strategy="most_frequent")),
    ("encode", OneHotEncoder(handle_unknown="ignore"))
])

prep = ColumnTransformer([
    ("num", num_steps, num_cols),
    ("cat", cat_steps, cat_cols)
])

pipe = Pipeline([
    ("prep", prep),
    ("model", RandomForestClassifier(n_estimators=200, random_state=0))
])
              +--> numeric columns --> fill median --> scale --+
 Raw table ---+                                                 +--> Model
              +--> text columns -----> fill common --> encode -+

Tune the Whole Pipeline

from sklearn.model_selection import GridSearchCV

params = {
    "model__n_estimators": [100, 200],
    "model__max_depth": [5, 10, None]
}

search = GridSearchCV(pipe, params, cv=5, scoring="accuracy")
search.fit(X_train, y_train)
print(search.best_params_)
print(search.best_score_)

The double underscore reaches inside a step. Cross-validation refits the preprocessing for every fold, which keeps the evaluation honest.

Save and Reuse the Pipeline

import joblib

joblib.dump(search.best_estimator_, "customer_pipeline.pkl")
loaded = joblib.load("customer_pipeline.pkl")

new_row = pd.DataFrame([{"age": 34, "income": 52000,
                         "city": "Pune", "plan": "basic"}])
print(loaded.predict(new_row))

The saved file carries every cleaning and encoding rule. A web service only loads the file and calls predict.

Custom Steps

from sklearn.preprocessing import FunctionTransformer
import numpy as np

log_step = FunctionTransformer(np.log1p)

FunctionTransformer wraps any function as a pipeline step. Write custom classes with fit and transform methods for logic that needs learning.

Best Practices

  • Split data before any step that learns from data.
  • Name steps clearly, such as prep and model.
  • Set handle_unknown to ignore, so new categories at prediction time do not crash the code.
  • Keep a fixed random_state for repeatable results.
  • Store the library versions beside the saved file.

Example: Predicting Customer Cancellations

A subscription company stores age, income, city, and plan type for every customer. Some ages are blank, and city names are text. Without a pipeline, the analyst writes separate cleaning code for training and for new customers, and the two versions drift apart over months. With a pipeline, one object fills blank ages, converts cities into columns of zeros and ones, scales income, and predicts cancellation risk. The same object serves training, testing, and live predictions. New team members read one short block of code and understand the whole workflow.

Common Errors and Fixes

ErrorCauseFix
Unknown category at prediction timeNew city never seen in trainingSet handle_unknown to ignore in the encoder
Column not foundNew data uses different column namesMatch names exactly, and validate inputs before prediction
Score too good to be truePreprocessing fit on all dataPlace every learning step inside the pipeline
Different results after loadingLibrary versions changedPin versions in requirements.txt

Pipelines in Production

Teams often schedule pipelines to retrain on fresh data each week. Larger systems use workflow tools that run each step as a separate task, log results, and retry failures. Scikit-learn pipelines cover the modeling part, and workflow tools cover the scheduling part. Learning the small version first makes the larger tools easy to grasp.

Key Points

  • A pipeline chains preprocessing and modeling into one object.
  • ColumnTransformer sends each column type through suitable steps.
  • Tuning and saving the whole pipeline prevents leakage and deployment mistakes.