Pipelines and saving models
Chaining preprocessing with a model so nothing leaks, and persisting the whole pipeline for consistent inference.
Why a Pipeline
If you scale or impute before splitting, statistics from the test set sneak into training and your score is optimistic. A Pipeline applies every step inside each cross-validation fold, which removes that entire class of bug.
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import OneHotEncoder, StandardScaler
from sklearn.impute import SimpleImputer
from sklearn.linear_model import LogisticRegression
num = Pipeline([("impute", SimpleImputer(strategy="median")),
("scale", StandardScaler())])
cat = Pipeline([("impute", SimpleImputer(strategy="constant", fill_value="missing")),
("onehot", OneHotEncoder(handle_unknown="ignore"))])
pre = ColumnTransformer([("num", num, numeric_cols),
("cat", cat, categorical_cols)])
model = Pipeline([("pre", pre), ("clf", LogisticRegression(max_iter=1000))])
model.fit(X_train, y_train)
model.predict(X_test)💡
handle_unknown="ignore" matters in production: a category you never saw in training would otherwise raise an error at inference time.Tuning without fooling yourself
from sklearn.model_selection import GridSearchCV
grid = {"clf__C": [0.01, 0.1, 1, 10],
"clf__class_weight": [None, "balanced"]}
search = GridSearchCV(model, grid, cv=5, scoring="roc_auc", n_jobs=-1)
search.fit(X_train, y_train)
print(search.best_params_, search.best_score_)- Note the
step__parameternaming — the double underscore reaches into a pipeline step. - Search on the training folds only; keep the test set untouched until the final report.
- Randomised search (
RandomizedSearchCV) often finds a good enough configuration far faster than a full grid.
Saving the whole pipeline
import joblib
joblib.dump(search.best_estimator_, "churn_v3.joblib")
# later, in the service
model = joblib.load("churn_v3.joblib")
model.predict(new_rows) # preprocessing travels with the model⚠️
Persist the pipeline, not just the estimator. Saving a bare model forces the serving code to reimplement preprocessing by hand, and the two will drift until predictions silently change meaning.
FAQ
How do I version a model?
Record the training data snapshot, code commit, hyperparameters and metric together. A model file with no provenance is impossible to debug or justify.
What about monitoring in production?
Log inputs and outputs, track the metric where labels eventually arrive, and alert on input drift. Accuracy decays without any code change when the world moves.
Related
Evaluation and overfitting Series and DataFrame
Last refreshed 2026-09-18.