A model training pipeline is the full, orchestrated sequence from raw training data to a tuned, saved, ready-to-deploy model — combining preprocessing, hyperparameter search, and final model selection into one reproducible script.
The Full Orchestration
import joblib
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.ensemble import RandomForestClassifier
# 1. Split RAW data first
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)
# 2. Build the preprocessing + model pipeline
numeric_features = ["age", "income"]
categorical_features = ["city"]
preprocessor = ColumnTransformer([
("num", Pipeline([("impute", SimpleImputer(strategy="median")), ("scale", StandardScaler())]), numeric_features),
("cat", Pipeline([("impute", SimpleImputer(strategy="most_frequent")), ("encode", OneHotEncoder(handle_unknown="ignore"))]), categorical_features),
])
full_pipeline = Pipeline([
("preprocessor", preprocessor),
("model", RandomForestClassifier(random_state=42)),
])
# 3. Tune preprocessing AND model hyperparameters together
param_grid = {
"model__n_estimators": [100, 200],
"model__max_depth": [None, 10, 20],
}
search = GridSearchCV(full_pipeline, param_grid, cv=5, scoring="f1", n_jobs=-1)
search.fit(X_train, y_train)
# 4. Evaluate the best model ONCE, on the held-out test set
best_model = search.best_estimator_
test_score = best_model.score(X_test, y_test)
print(f"Best params: {search.best_params_}, Test score: {test_score:.3f}")
# 5. Save the ENTIRE fitted pipeline -- preprocessing and model together
joblib.dump(best_model, "trained_pipeline.pkl")
Why Saving the Whole Pipeline (Not Just the Model) Matters
Saving only the trained model, without its preprocessing steps, creates a serious production risk — whoever loads that model later must somehow remember and exactly reproduce every preprocessing step manually. Saving the entire fitted pipeline (preprocessing + model together) guarantees that loading it and calling .predict() on raw new data always applies the identical transformations used during training.
The Reusable Structure
| Stage | What Happens |
|---|---|
| Split | Raw data → train/test, before anything else touches it |
| Build | Assemble preprocessing + model into one Pipeline object |
| Tune | Grid/random search over the combined pipeline's hyperparameters |
| Evaluate | Test-set score, exactly once |
| Persist | Save the entire fitted pipeline as one artifact |
Practical Use Cases
- The standard structure for any real, production-headed ML training script
- Reproducible experiments — rerunning the same script with the same data produces the same result
Common Mistakes
- Saving only the raw model, not the full fitted pipeline including preprocessing.
- Skipping the final held-out test evaluation, reporting the grid search's cross-validated score as if it were an unbiased final number.
Interview Relevance
Q: "Why do you save the entire fitted pipeline instead of just the trained model?" Because raw new data needs to go through the exact same preprocessing (imputation, scaling, encoding) the model was trained on before prediction makes sense — saving only the model would require manually reimplementing that preprocessing correctly every time, a fragile, error-prone approach the full pipeline avoids entirely.
Practice Question
Explain what would go wrong if a teammate saved only model (the final classifier) instead of full_pipeline, then tried to use it on new raw data six months later.