Pipeline sklearn & data leakage
1. Cos'Γ¨ il data leakageβ
Il data leakage Γ¨ la situazione in cui informazioni del test set (o del futuro, in problemi temporali) influenzano il training. Causa metriche di validazione artificialmente buone che NON si confermano in produzione.
Γ il bug piΓΉ sottile e dannoso del ML applicato. Γ anche il piΓΉ frequente.
2. I tre tipi di leakage in regressione tabularβ
2.1 Leakage da preprocessing globaleβ
Sintomo: imputazione, scaling, encoding fatti sull'INTERO dataset prima dello split.
# WRONG: leakage!
df['LotFrontage'] = df['LotFrontage'].fillna(df['LotFrontage'].median()) # mediana globale
df_scaled = StandardScaler().fit_transform(df.values) # mu/sigma globali
X_train, X_test = train_test_split(df_scaled, ...) # tardi
PerchΓ© Γ¨ leakage: la mediana e mu/sigma sono statistiche calcolate su training+test insieme. In produzione il test set non Γ¨ disponibile, quindi le statistiche sarebbero diverse β predizioni diverse β metriche di validazione non rappresentano la realtΓ .
Soluzione: usare sklearn.pipeline.Pipeline che esegue fit solo sul training e transform su test/inferenza usando le statistiche del training.
# RIGHT: niente leakage
pipeline = Pipeline([
('imputer', SimpleImputer(strategy='median')),
('scaler', StandardScaler()),
('model', Ridge()),
])
pipeline.fit(X_train, y_train) # statistiche da X_train
pipeline.predict(X_test) # riusa quelle statistiche
2.2 Leakage da feature derivate dal targetβ
Sintomo: una feature Γ¨ una funzione (anche indiretta) del target.
# WRONG: la feature usa SalePrice!
df['price_per_sqft'] = df['SalePrice'] / df['GrLivArea'] # leakage diretto
df['neighborhood_avg'] = df.groupby('Neighborhood')['SalePrice'].transform('mean') # target encoding senza CV
In Ames, il rischio principale Γ¨ il target encoding (sostituire una categoria con la media del target per quella categoria). Va sempre fatto dentro la CV, non sul dataset completo.
Regola: nessuna trasformazione che dipende da y deve essere applicata fuori dalla pipeline.
2.3 Leakage da split temporale ignoratoβ
Sintomo: in dati con dimensione temporale (eventi datati), uno split casuale mette nel training osservazioni successive a quelle del test set.
In Ames Housing, le case sono state vendute fra 2006 e 2010. Un modello realistico per predire prezzi futuri dovrebbe addestrarsi sul 2006-2008 e validarsi su 2009-2010, NON splitare casualmente. Per il nostro project work questo non Γ¨ richiesto (split casuale Γ¨ ammesso), ma Γ¨ un'ottima estensione.
3. La struttura della pipeline Amesβ
Il design "no-leakage by construction" del nostro progetto:
βββββββββββββββββββββββββββββββ
load_raw() β Trasformazioni "semantiche"β
β β che NON usano statistiche β
β β β si possono fare prima β
β fill_structural_missing β dello split. β
β (NaNβ'None' / 0) β - NaN β 'None' β
β β - rimozione outlier β
β remove_grliv_area_outliers β (regola fissa di De Cock)β
β βββββββββββββββββββββββββββββββ
βΌ
train_test_split (stratify)
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββ
β Pipeline sklearn (fit_transform su train,β
β transform su test): β
β β
β 1. AmesFeatureEngineer β
β ββ deterministic, no statistiche β
β β
β 2. ColumnTransformer: β
β ββ SimpleImputer(median) sul train β
β ββ OrdinalEncoder fitted sul train β
β ββ OneHotEncoder fitted sul train β
β β
β 3. StandardScaler (solo Ridge): β
β ββ ΞΌ, Ο dal training β
β β
β 4. Modello (Ridge / RF / XGB) β
βββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
TransformedTargetRegressor (log1p / expm1):
wrappa tutta la pipeline. Predict ritorna $.
Ogni statistica Γ¨ calcolata solo sul training. K-fold CV applica la pipeline ad ognuno dei K fold separatamente: se un fold ha statistiche diverse, va bene β Γ¨ proprio questa la varianza che vogliamo misurare.
4. Pre-split vs in-pipeline: quando Γ¨ OK fuori dalla pipelineβ
Posso fare una trasformazione fuori dalla pipeline solo se Γ¨ deterministica e non dipende dal sample. Esempi:
| Operazione | Dove? | PerchΓ© |
|---|---|---|
Conversione tipo (str β int) | fuori | deterministica |
| Rinominare colonne | fuori | deterministica |
| Mappare NaN strutturali a 'None' | fuori | regola fissa, indipendente dal sample |
Rimozione outlier con regola fissa (GrLivArea > 4000 e prezzo < 300k) | fuori | regola fissa |
| Imputazione con mediana | DENTRO | mediana dipende dal sample |
| Scaling | DENTRO | mu/sigma dipendono dal sample |
| OneHotEncoder | DENTRO | il vocabolario dipende dal sample |
5. Cross-validation con la pipelineβ
cross_val_score(pipeline, X, y, cv=5) esegue, per ognuno dei 5 fold:
- Split di X, y in
(X_tr, y_tr)e(X_va, y_va). pipeline.fit(X_tr, y_tr)β tutte le statistiche calcolate sul fold di training.pipeline.predict(X_va)β riusa quelle statistiche sul fold di validation.- Calcolo metrica su
(y_va, y_pred).
Ogni fold Γ¨ una mini-simulazione del deployment. Le metriche CV stimano la performance attesa in produzione.
6. Errori che ho visto fare nei progettiβ
- Standardize tutto il dataset prima del split β leakage dello scaling.
OneHotEncoder().fit_transform(df_completo)prima del split β vocabolario contaminato dal test.- Tuning iperparametri sul test set ("guardo il test, ottimizzo, riguardo, riottimizzo") β test set non piΓΉ valido.
- Feature engineering basata su statistiche di gruppo (es. media prezzo per quartiere) calcolata sull'intero dataset.
pd.cut(df['SalePrice'], bins=...)per stratificare β in regressione lo stratify deve usarepd.qcutsu SalePrice, ma il calcolo dei quintili deve essere esplicitamente suly_traine poi applicato ayper iltrain_test_split. Nel nostro codice Γ¨ fatto correttamente.- Salvare il dataframe pre-processato e poi splittarlo β uno qualsiasi dei punti sopra puΓ² essere giΓ successo.
7. Sanity check: il modello "shuffle test"β
Per assicurarsi che NON ci sia leakage, un check rapido:
y_shuffled = y.sample(frac=1, random_state=0).reset_index(drop=True)
score = cross_val_score(pipeline, X, y_shuffled, scoring='r2', cv=5).mean()
print(f"RΒ² con target casualizzato: {score:.4f}")
Atteso: (il modello non puΓ² imparare nulla da casuale). Se ottieni , c'Γ¨ leakage.
8. Riferimentiβ
- Sklearn user guide: Pipelines and composite estimators.
- Kapoor & Narayanan (2023), Leakage and the Reproducibility Crisis in ML-based Science, Patterns 4(9).
- Kaufman et al. (2012), Leakage in Data Mining: Formulation, Detection, and Avoidance, ACM TKDD 6(4).