Pipelines Scikit-Learn en 2026 : Feature Engineering et Questions d'Entretien

Maîtrisez les pipelines scikit-learn pour le feature engineering et préparez vos entretiens techniques. Guide complet avec ColumnTransformer, GridSearchCV et bonnes pratiques de déploiement.

Pipelines Scikit-Learn en 2026 : Feature Engineering et Questions d'Entretien

Les pipelines scikit-learn transforment un code de prétraitement chaotique en workflows reproductibles, prêts pour la production. Avec la version 1.9 sortie en juin 2026, Pipeline et ColumnTransformer restent les fondations de tout projet sérieux de machine learning, désormais enrichis du monitoring par callbacks, d'une visualisation HTML améliorée et du support Array API.

Point Clé pour les Entretiens

Les recruteurs demandent souvent : « Comment éviter les fuites de données pendant la validation croisée ? » La réponse : les pipelines. Ajuster les étapes de prétraitement à l'intérieur de la boucle CV garantit que les données de test n'influencent jamais les décisions de mise à l'échelle ou d'encodage.

Pourquoi les Pipelines Éliminent les Fuites de Données

Les fuites de données surviennent lorsque des informations du jeu de test influencent le prétraitement. Une erreur courante : ajuster un StandardScaler sur l'ensemble du dataset avant de le diviser. Le scaler apprend la moyenne et la variance à partir des échantillons de test, gonflant artificiellement les scores de validation croisée.

Les pipelines corrigent ce problème en chaînant transformers et estimateurs dans un seul objet qui ajuste toutes les étapes ensemble :

python
# pipeline_basics.py
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score

# Create a pipeline that scales features and trains a classifier
pipeline = Pipeline([
    ('scaler', StandardScaler()),  # Step 1: Normalize features
    ('classifier', LogisticRegression(max_iter=1000))  # Step 2: Train model
])

# cross_val_score fits the scaler separately for each fold
# No data leakage: test fold is never seen during scaler.fit()
scores = cross_val_score(pipeline, X, y, cv=5, scoring='accuracy')
print(f"Mean accuracy: {scores.mean():.3f} (+/- {scores.std():.3f})")

L'objet Pipeline implémente fit, predict et score, ce qui le rend interchangeable avec n'importe quel estimateur scikit-learn. Cela signifie que GridSearchCV, RandomizedSearchCV et tous les utilitaires de validation croisée fonctionnent sans modification.

ColumnTransformer pour les Types de Données Mixtes

Les datasets réels contiennent des colonnes numériques (âge, salaire) et des colonnes catégorielles (pays, type_produit). ColumnTransformer applique différents prétraitements à différents sous-ensembles de colonnes, puis concatène les résultats :

python
# column_transformer_example.py
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.impute import SimpleImputer
from sklearn.pipeline import Pipeline
import pandas as pd

# Sample dataset with mixed types
df = pd.DataFrame({
    'age': [25, 30, None, 45],
    'salary': [50000, 60000, 75000, None],
    'country': ['US', 'UK', 'DE', 'US'],
    'education': ['Bachelor', 'Master', 'PhD', 'Bachelor']
})

# Define numeric preprocessing: impute missing values, then scale
numeric_transformer = Pipeline([
    ('imputer', SimpleImputer(strategy='median')),
    ('scaler', StandardScaler())
])

# Define categorical preprocessing: impute with mode, then one-hot encode
categorical_transformer = Pipeline([
    ('imputer', SimpleImputer(strategy='most_frequent')),
    ('encoder', OneHotEncoder(handle_unknown='ignore', sparse_output=False))
])

# Combine transformers with ColumnTransformer
preprocessor = ColumnTransformer(
    transformers=[
        ('num', numeric_transformer, ['age', 'salary']),
        ('cat', categorical_transformer, ['country', 'education'])
    ],
    verbose_feature_names_out=True  # Prefix output names with transformer name
)

# Fit and inspect output shape
X_transformed = preprocessor.fit_transform(df)
print(f"Transformed shape: {X_transformed.shape}")
print(f"Feature names: {preprocessor.get_feature_names_out()}")

Le paramètre verbose_feature_names_out (amélioré dans la version 1.6 pour accepter des chaînes et des callables) contrôle la dénomination des features en sortie. Le définir sur True préfixe chaque feature avec le nom du transformer, évitant les collisions de noms lorsque plusieurs transformers génèrent des noms de colonnes similaires.

Automatiser la Sélection de Colonnes avec make_column_selector

Coder en dur les noms de colonnes casse le code lorsque les datasets changent. make_column_selector sélectionne automatiquement les colonnes par dtype :

python
# automatic_column_selection.py
from sklearn.compose import ColumnTransformer, make_column_selector
from sklearn.preprocessing import StandardScaler, OneHotEncoder

# Select columns by dtype: no hardcoded column names
preprocessor = ColumnTransformer(
    transformers=[
        ('num', StandardScaler(), make_column_selector(dtype_include='number')),
        ('cat', OneHotEncoder(handle_unknown='ignore'),
         make_column_selector(dtype_include='object'))
    ],
    remainder='passthrough'  # Keep unprocessed columns as-is
)

# Works with any DataFrame that has numeric and object columns
X_processed = preprocessor.fit_transform(df)

Le paramètre remainder contrôle ce qui arrive aux colonnes non sélectionnées par aucun transformer. Les options incluent 'drop' (par défaut), 'passthrough' (conserver tel quel), ou un transformer à appliquer aux colonnes restantes.

Prêt à réussir tes entretiens Data Science & ML ?

Entraîne-toi avec nos simulateurs interactifs, fiches express et tests techniques.

Feature Engineering à l'Intérieur des Pipelines

Les pipelines s'étendent au-delà du prétraitement pour inclure des étapes de feature engineering. Les transformers personnalisés héritent de BaseEstimator et TransformerMixin :

python
# custom_transformer.py
from sklearn.base import BaseEstimator, TransformerMixin
import numpy as np

class DateFeatureExtractor(BaseEstimator, TransformerMixin):
    """Extract year, month, day, and weekday from datetime columns."""
    
    def __init__(self, date_column='date'):
        self.date_column = date_column
    
    def fit(self, X, y=None):
        # No fitting required for date extraction
        return self
    
    def transform(self, X):
        X = X.copy()
        dates = pd.to_datetime(X[self.date_column])
        
        # Extract temporal features
        X['year'] = dates.dt.year
        X['month'] = dates.dt.month
        X['day'] = dates.dt.day
        X['weekday'] = dates.dt.weekday
        X['is_weekend'] = (dates.dt.weekday >= 5).astype(int)
        
        # Drop original date column
        return X.drop(columns=[self.date_column])
    
    def get_feature_names_out(self, input_features=None):
        # Required for Pipeline.get_feature_names_out() to work
        return ['year', 'month', 'day', 'weekday', 'is_weekend']

# Integrate into a full pipeline
full_pipeline = Pipeline([
    ('date_features', DateFeatureExtractor(date_column='signup_date')),
    ('preprocessor', preprocessor),
    ('model', LogisticRegression())
])

Implémenter get_feature_names_out permet à la nouvelle représentation HTML de scikit-learn 1.9 d'afficher les noms des features en sortie, facilitant le débogage et la documentation.

Optimisation des Hyperparamètres à Travers les Étapes du Pipeline

GridSearchCV ajuste les paramètres à travers toutes les étapes du pipeline en utilisant la convention de nommage step__parameter :

python
# pipeline_gridsearch.py
from sklearn.model_selection import GridSearchCV
from sklearn.ensemble import RandomForestClassifier

# Build pipeline with tunable components
pipeline = Pipeline([
    ('preprocessor', preprocessor),
    ('classifier', RandomForestClassifier(random_state=42))
])

# Define parameter grid: note the double underscore syntax
param_grid = {
    'preprocessor__num__imputer__strategy': ['mean', 'median'],
    'classifier__n_estimators': [100, 200],
    'classifier__max_depth': [10, 20, None]
}

# GridSearchCV handles all combinations automatically
grid_search = GridSearchCV(
    pipeline,
    param_grid,
    cv=5,
    scoring='f1_weighted',
    n_jobs=-1
)

grid_search.fit(X_train, y_train)
print(f"Best params: {grid_search.best_params_}")
print(f"Best score: {grid_search.best_score_:.3f}")

Le double underscore (__) accède aux paramètres imbriqués. Pour ColumnTransformer, le chemin inclut le nom du transformer : preprocessor__num__imputer__strategy atteint le paramètre strategy de l'imputer à l'intérieur du transformer numérique.

Mise en Cache des Transformers avec le Paramètre memory

Un prétraitement complexe sur de grands datasets peut être lent. Le paramètre memory met en cache les transformers ajustés sur le disque :

python
# cached_pipeline.py
from sklearn.pipeline import Pipeline
from tempfile import mkdtemp
import joblib

# Create a cache directory
cachedir = mkdtemp()

# Pipeline caches intermediate steps
cached_pipeline = Pipeline(
    [
        ('preprocessor', preprocessor),  # Cached after first fit
        ('classifier', RandomForestClassifier())
    ],
    memory=cachedir  # Or use joblib.Memory for more control
)

# First fit: slow (computes all transformations)
cached_pipeline.fit(X_train, y_train)

# Second fit with same X_train: fast (loads cached transformers)
cached_pipeline.fit(X_train, y_train)  # Skips preprocessor fitting

La mise en cache est particulièrement utile lors de la recherche d'hyperparamètres, où le même prétraitement s'applique à plusieurs configurations de modèles. L'estimateur final (classificateur ou régresseur) n'est jamais mis en cache, uniquement les transformers intermédiaires.

Monitoring de l'Entraînement avec les Callbacks (scikit-learn 1.9)

L'API de callbacks dans la version 1.9 ajoute le monitoring de progression aux pipelines :

python
# callback_monitoring.py
from sklearn.callback import ProgressBar, ScoringMonitor
from sklearn.linear_model import LogisticRegression

# Display progress during GridSearchCV
with ProgressBar():
    grid_search = GridSearchCV(
        pipeline,
        param_grid,
        cv=5,
        n_jobs=1  # Progress bars require single-threaded execution
    )
    grid_search.fit(X_train, y_train)

# Monitor scoring metrics per iteration
with ScoringMonitor(X_val, y_val, scoring='accuracy') as monitor:
    logreg = LogisticRegression(solver='lbfgs', max_iter=500)
    logreg.fit(X_train, y_train)
    print(f"Scores per iteration: {monitor.scores_}")

Les callbacks fonctionnent avec Pipeline, StandardScaler, LogisticRegression (solveur LBFGS) et toutes les classes de recherche CV. Cette fonctionnalité expérimentale offre une visibilité sur les ajustements longs sans code de logging personnalisé.

FeatureUnion pour l'Extraction Parallèle de Features

FeatureUnion exécute plusieurs transformers en parallèle et concatène leurs sorties horizontalement :

python
# feature_union_example.py
from sklearn.pipeline import FeatureUnion, Pipeline
from sklearn.decomposition import PCA
from sklearn.feature_selection import SelectKBest, f_classif

# Combine PCA and univariate selection
feature_union = FeatureUnion([
    ('pca', PCA(n_components=5)),
    ('select', SelectKBest(f_classif, k=10))
])

# Pipeline: preprocess, combine features, classify
pipeline = Pipeline([
    ('preprocessor', preprocessor),
    ('features', feature_union),
    ('classifier', LogisticRegression())
])

# Output contains 5 PCA components + 10 selected features = 15 features
pipeline.fit(X_train, y_train)

La version 1.7.2 a ajouté une validation garantissant que tous les transformers retournent des sorties 2D. Cela détecte les erreurs tôt lorsqu'un transformer personnalisé retourne accidentellement un tableau 1D.

Sérialisation et Déploiement

Les pipelines se sérialisent avec joblib, préservant tous les paramètres ajustés :

python
# model_deployment.py
import joblib

# Save the entire pipeline (preprocessing + model)
joblib.dump(pipeline, 'churn_predictor.pkl')

# Load in production
loaded_pipeline = joblib.load('churn_predictor.pkl')

# Predict on new data (same preprocessing automatically applied)
new_data = pd.DataFrame({'age': [32], 'salary': [65000], ...})
predictions = loaded_pipeline.predict(new_data)

Le fichier sérialisé contient tout : la logique de sélection de colonnes, les moyennes et variances ajustées du scaler, les catégories de l'encoder et les poids du modèle. Le déploiement ne nécessite que scikit-learn et joblib, aucun code de prétraitement personnalisé.

Compatibilité des Versions

Les pipelines sérialisés avec scikit-learn 1.8 peuvent ne pas se charger correctement sur 1.9 s'ils utilisent des paramètres dépréciés. Il est recommandé de toujours fixer la version de scikit-learn en production et de tester les mises à niveau avant le déploiement.

Questions d'Entretien sur les Pipelines Scikit-Learn

Q : Qu'est-ce qu'une fuite de données et comment les pipelines la préviennent-ils ?

La fuite de données survient lorsque des informations du jeu de validation ou de test influencent l'entraînement du modèle. L'exemple classique : ajuster un StandardScaler sur l'ensemble du dataset avant de le diviser. La moyenne et l'écart-type du scaler incluent les échantillons de test, rendant les scores de validation croisée artificiellement élevés.

Les pipelines préviennent cela en encapsulant le prétraitement à l'intérieur de la boucle de validation croisée. Lorsque cross_val_score appelle pipeline.fit(), chaque fold ajuste le scaler uniquement sur les données d'entraînement. Les statistiques du fold de test ne fuient jamais dans le prétraitement.

Q : Comment accéder et modifier les paramètres des étapes imbriquées du pipeline ?

Il faut utiliser la syntaxe du double underscore : pipeline.set_params(classifier__n_estimators=200). Pour ColumnTransformer, inclure le nom du transformer : pipeline.set_params(preprocessor__num__scaler__with_mean=False). La méthode get_params() retourne tous les paramètres imbriqués sous forme de dictionnaire plat, utile pour l'inspection et le logging.

Q : Quand utiliser FeatureUnion vs ColumnTransformer ?

ColumnTransformer applique différents transformers à différentes colonnes de la même entrée. FeatureUnion applique différents transformers à l'entrée entière et concatène horizontalement. Il convient d'utiliser ColumnTransformer pour le prétraitement de colonnes hétérogènes (numériques vs catégorielles). FeatureUnion est préférable pour l'augmentation de features, où plusieurs méthodes d'extraction (PCA, features polynomiales, extracteurs spécifiques au domaine) produisent des features complémentaires à partir des mêmes données.

Q : Comment déboguer un pipeline qui échoue ?

Définir verbose=True sur le pipeline permet de voir les temps d'exécution des étapes. Il est possible d'accéder aux résultats intermédiaires avec pipeline.named_steps['step_name'].transform(X) pour un pipeline ajusté. Dans scikit-learn 1.9, la représentation HTML affiche les attributs ajustés et les noms des features en sortie, simplifiant le débogage dans les notebooks Jupyter. Pour une inspection plus approfondie, utiliser pipeline[:-1].fit_transform(X, y) pour obtenir la sortie juste avant l'estimateur final.

Checklist de Production pour les Pipelines ML

  • Fixer la version de scikit-learn dans les requirements pour éviter les problèmes de sérialisation
  • Utiliser make_column_selector au lieu de noms de colonnes codés en dur lorsque les datasets peuvent changer
  • Définir handle_unknown='ignore' sur OneHotEncoder pour gérer les nouvelles catégories en production
  • Mettre en cache le prétraitement coûteux avec le paramètre memory pendant le développement
  • Valider la forme de sortie du pipeline et les noms de features dans les tests avant le déploiement
  • Stocker get_feature_names_out() aux côtés du modèle pour l'analyse de l'importance des features
  • Utiliser Pipeline à l'intérieur de GridSearchCV, jamais l'inverse, pour garantir un comportement CV correct

Passe à la pratique !

Teste tes connaissances avec nos simulateurs d'entretien et tests techniques.

Défi du jour

Tu saurais repérer le bug en Data Science & ML ?

Un vrai bout de code, un bug caché, une tentative par jour. Sans compte pour essayer.

Anthony Fillion-Maillet

Écrit par

Anthony Fillion-Maillet

Fondateur de SharpSkill

Développeur fullstack depuis plus de 10 ans. Il dirige SharpSkill et répond de tout ce qui y est publié.

Mis à jour le 2 septembre 2026

Partager

Articles similaires