~/wiki

scikit-learn

Mis à jour le 2026-08-07Confiance : high
scikit-learnsklearnmachine-learningpipelinepythonevaluation

Une API uniforme : tout estimateur a fit, tout transformateur a transform, tout prédicteur a predict. Le reste en découle.

Découper

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)

stratify=y conserve la proportion des classes — indispensable en classification déséquilibrée. random_state rend le découpage reproductible.

Le pipeline, à utiliser systématiquement

from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.impute import SimpleImputer
from sklearn.ensemble import RandomForestClassifier

numeric = ["age", "montant"]
categorical = ["region", "segment"]

pre = ColumnTransformer([
    ("num", Pipeline([
        ("impute", SimpleImputer(strategy="median")),
        ("scale", StandardScaler()),
    ]), numeric),
    ("cat", Pipeline([
        ("impute", SimpleImputer(strategy="most_frequent")),
        ("onehot", OneHotEncoder(handle_unknown="ignore")),
    ]), categorical),
])

model = Pipeline([("pre", pre), ("clf", RandomForestClassifier(random_state=42))])
model.fit(X_train, y_train)

Le pipeline n'est pas une élégance : il empêche la fuite de données. Un StandardScaler ajusté sur tout le jeu avant le split fait fuiter la moyenne du test dans l'entraînement, et le score devient un mensonge.

handle_unknown="ignore" évite un crash quand une catégorie inconnue apparaît en production.

Évaluer

from sklearn.metrics import classification_report, confusion_matrix, roc_auc_score

y_pred = model.predict(X_test)
print(classification_report(y_test, y_pred))
              precision    recall  f1-score   support

           0       0.91      0.95      0.93       248
           1       0.78      0.66      0.71        72

    accuracy                           0.89       320
   macro avg       0.84      0.80      0.82       320
weighted avg       0.88      0.89      0.88       320

Sur un jeu déséquilibré, l'accuracy ment : ici 89 % semble bon alors que la classe minoritaire n'est rattrapée qu'à 66 %. Regarder le rappel par classe, la matrice de confusion, et l'AUC.

proba = model.predict_proba(X_test)[:, 1]
print(roc_auc_score(y_test, proba))

Validation croisée

from sklearn.model_selection import cross_val_score, StratifiedKFold

cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
scores = cross_val_score(model, X, y, cv=cv, scoring="f1", n_jobs=-1)
print(scores.mean().round(3), scores.std().round(3))
0.712 0.031

L'écart-type compte autant que la moyenne : un modèle à 0.71 ± 0.03 est utilisable, à 0.71 ± 0.18 il est instable.

Recherche d'hyperparamètres

from sklearn.model_selection import GridSearchCV, RandomizedSearchCV

grid = {
    "clf__n_estimators": [100, 300],
    "clf__max_depth": [None, 10, 20],
    "clf__min_samples_leaf": [1, 5],
}

search = GridSearchCV(model, grid, cv=cv, scoring="f1", n_jobs=-1, verbose=1)
search.fit(X_train, y_train)
print(search.best_params_, search.best_score_)

Le double underscore adresse un paramètre à travers le pipeline : clf__max_depth vise le max_depth de l'étape nommée clf. Au-delà d'une centaine de combinaisons, RandomizedSearchCV(n_iter=50) donne presque le même résultat bien plus vite.

Estimateurs courants

Tâche Point de départ Ensuite
Classification tabulaire LogisticRegression HistGradientBoostingClassifier, RandomForest
Régression tabulaire Ridge HistGradientBoostingRegressor
Clustering KMeans DBSCAN, AgglomerativeClustering
Réduction de dimension PCA TSNE, UMAP (hors sklearn)

HistGradientBoosting* gère les valeurs manquantes nativement et bat presque toujours une forêt aléatoire sur du tabulaire.

Toujours établir une référence triviale avant de comparer :

from sklearn.dummy import DummyClassifier
DummyClassifier(strategy="most_frequent").fit(X_train, y_train).score(X_test, y_test)

Classes déséquilibrées

RandomForestClassifier(class_weight="balanced")
LogisticRegression(class_weight="balanced")

Et ajuster le seuil de décision plutôt que d'accepter 0.5 par défaut :

from sklearn.metrics import precision_recall_curve
p, r, seuils = precision_recall_curve(y_test, proba)

Sauvegarder

import joblib

joblib.dump(model, "model.joblib")
model = joblib.load("model.joblib")

Le pipeline entier est sérialisé, préprocessing compris — c'est le second intérêt majeur du pipeline : le même objet sert en entraînement et en production.

See also