Notes / Generalization & Model Fitting
Preprocessing: Fit on Train, Apply Everywhere
Table of Contents
One of the most important (and most tested) principles in applied ML:
Any transformation you fit on training data must be frozen and reapplied identically to validation, test, and inference data.
Feature selection, scaling, imputation, encoding — all are preprocessing transformations subject to this rule.
The Rule
Once a transformation’s parameters are learned from training data, that decision becomes a fixed part of the pipeline:
- Validation → apply the same transformation
- Test → apply the same transformation
- Inference / production → apply the same transformation
If you don’t, the model receives inputs with the wrong shape / feature ordering / scale — it was trained to expect a specific set of features in a specific form. Mismatch → broken or meaningless predictions.
The Critical Part: WHERE You Compute the Statistics
Compute all statistics on the training set only. Never recompute on val/test.
Example — variance-threshold feature selection: decide which columns to drop using training data variances, then drop those same columns everywhere.
Two reasons you must not recompute on val/test:
- Consistency — recomputing on val might select a different column set than train, leaving mismatched features. The model breaks or its inputs become meaningless.
- Data leakage — computing statistics on val/test lets evaluation-set information influence your pipeline, making val/test scores optimistically biased (they no longer simulate truly unseen data). At inference you often have a single sample, where a variance can’t even be computed.
Mental Model: fit vs transform
sklearn splits every preprocessor into two methods for exactly this reason:
selector.fit(X_train) # LEARN parameters (which columns to drop) — TRAIN ONLYselector.transform(X_train) # APPLYselector.transform(X_val) # APPLY the same decision — no re-fittingselector.transform(X_test) # APPLY the same decisionfit— learn parameters (columns to drop, mean/std, fill values). Train only.transform— apply the learned decision. Everywhere.fit_transform— both combined; use only on the training set.
Same principle for StandardScaler (mean/std from train), imputation (fill values from train),
encoders (categories from train), etc.
The Safe Way: Pipelines
Wrap all steps in a single pipeline so you cannot accidentally re-fit on val/test:
from sklearn.pipeline import Pipelinepipe = Pipeline([ ("var", VarianceThreshold(0.0)), ("scale", StandardScaler()), ("model", ...),])pipe.fit(X_train, y_train) # every step fits on TRAIN onlypipe.predict(X_val) # every step transforms consistentlyWith cross-validation, put the pipeline inside the CV loop so preprocessing is re-fit on each fold’s training portion only — otherwise stats leak across folds.
Interview one-liner
“Preprocessing parameters are learned from training data and frozen; validation, test, and inference only apply them. Recomputing on evaluation data causes leakage and inconsistent features.”
Related: [[feature-selection]], [[regression-metrics]], [[overfitting-underfitting]]
