On this page
article
Scikit-learn Pipelines & Model Selection
Build ML pipelines with Scikit-learn — Pipeline, ColumnTransformer, cross-validation, hyperparameter tuning, and model persistence.
Production ML requires reproducible preprocessing and systematic model evaluation. Scikit-learn’s Pipeline and model selection tools make this straightforward.
Pipelines — Chain Preprocessing and Modeling
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.impute import SimpleImputer
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_breast_cancer
X, y = load_breast_cancer(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
pipe = Pipeline([
("imputer", SimpleImputer(strategy="median")),
("scaler", StandardScaler()),
("classifier", RandomForestClassifier(n_estimators=100, random_state=42)),
])
pipe.fit(X_train, y_train)
score = pipe.score(X_test, y_test)
print(f"Accuracy: {score:.3f}")
The pipeline ensures test data is never used during preprocessing — preventing data leakage.
ColumnTransformer — Mixed Feature Types
When features need different preprocessing:
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import OneHotEncoder
import pandas as pd
df = pd.DataFrame({
"age": [25, 30, 35, 28],
"salary": [50000, 60000, 75000, 55000],
"city": ["NYC", "LA", "NYC", "Chicago"],
})
preprocessor = ColumnTransformer([
("num", StandardScaler(), ["age", "salary"]),
("cat", OneHotEncoder(), ["city"]),
])
X_processed = preprocessor.fit_transform(df)
print(X_processed.shape)
Cross-Validation
from sklearn.model_selection import cross_val_score, StratifiedKFold
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
scores = cross_val_score(pipe, X, y, cv=cv, scoring="accuracy")
print(f"CV Accuracy: {scores.mean():.3f} (+/- {scores.std() * 2:.3f})")
Hyperparameter Tuning
from sklearn.model_selection import GridSearchCV
param_grid = {
"classifier__n_estimators": [50, 100, 200],
"classifier__max_depth": [None, 10, 20],
"classifier__min_samples_split": [2, 5],
}
grid = GridSearchCV(pipe, param_grid, cv=5, scoring="accuracy", n_jobs=-1)
grid.fit(X_train, y_train)
print(f"Best params: {grid.best_params_}")
print(f"Best CV score: {grid.best_score_:.3f}")
print(f"Test score: {grid.score(X_test, y_test):.3f}")
Randomized Search (Faster)
from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import randint
param_dist = {
"classifier__n_estimators": randint(50, 300),
"classifier__max_depth": randint(5, 30),
}
search = RandomizedSearchCV(
pipe, param_dist, n_iter=20, cv=5, random_state=42, n_jobs=-1
)
search.fit(X_train, y_train)
Evaluation Metrics
from sklearn.metrics import (
classification_report, confusion_matrix,
mean_squared_error, r2_score,
)
y_pred = grid.predict(X_test)
print(classification_report(y_test, y_pred))
print(confusion_matrix(y_test, y_pred))
Saving and Loading Models
import joblib
joblib.dump(grid.best_estimator_, "model.pkl")
loaded = joblib.load("model.pkl")
loaded.predict(X_test[:5])
Feature Importance
import numpy as np
model = grid.best_estimator_.named_steps["classifier"]
importances = model.feature_importances_
top_features = np.argsort(importances)[-10:]
print("Top 10 features:", top_features)
Systematic pipelines and model selection separate hobby ML from production-ready models.