Skip to content

Sklearn Random Forest : Guide complet de classification et régression en Python

Updated on

Vous avez construit un arbre de décision qui atteint 95% de précision sur l'entraînement, puis il obtient 62% sur de nouvelles données. Un arbre de décision unique mémorise l'ensemble d'entraînement -- chaque division, chaque feuille est ajustée aux échantillons exacts qu'il a vus. Le résultat est un modèle qui semble excellent sur le papier mais échoue en production.

Ce problème de surapprentissage n'est pas seulement théorique. Les équipes déploient des modèles qui performent bien dans les notebooks de développement mais génèrent des prédictions peu fiables sur les données en production. Un arbre de décision unique a une variance élevée : de petits changements dans les données d'entraînement produisent des structures d'arbre entièrement différentes. Vous ne pouvez pas faire confiance à un modèle qui est aussi sensible à ses données d'entraînement.

Random Forest résout cela en construisant des centaines d'arbres de décision sur des sous-ensembles aléatoires de données et de features, puis en combinant leurs prédictions par vote majoritaire (classification) ou moyenne (régression). Cette approche en ensemble réduit dramatiquement la variance tout en maintenant la précision. Les implémentations RandomForestClassifier et RandomForestRegressor de Scikit-learn fournissent une solution prête pour la production avec importance des features intégrée, évaluation out-of-bag et entraînement parallèle.

📚

Qu'est-ce que Random Forest ?

Random Forest est une méthode d'apprentissage en ensemble qui combine plusieurs arbres de décision pour produire une prédiction unique et plus robuste. Elle utilise une technique appelée bagging (Bootstrap Aggregating) :

  1. Échantillonnage bootstrap : Créer plusieurs sous-ensembles aléatoires des données d'entraînement par échantillonnage avec remplacement. Chaque sous-ensemble représente environ 63% des données originales.
  2. Sélection aléatoire de features : À chaque division dans chaque arbre, ne considérer qu'un sous-ensemble aléatoire de features (typiquement sqrt(n_features) pour la classification, n_features/3 pour la régression).
  3. Entraînement indépendant : Entraîner un arbre de décision sur chaque échantillon bootstrap avec la contrainte de feature aléatoire.
  4. Agrégation : Combiner les prédictions par vote majoritaire (classification) ou moyenne (régression).

L'aléatoire dans l'échantillonnage des données et la sélection des features garantit que les arbres individuels sont décorrélés. Même si un arbre surapprend un motif particulier, la majorité des autres arbres ne le fera pas, et l'ensemble moyenne le bruit.

Quand utiliser Random Forest

ScénarioRandom Forest ?Pourquoi
Données tabulaires avec types de features mixtesOuiGère les features numériques et catégorielles, pas de scaling nécessaire
Vous avez besoin de classements d'importance des featuresOuiAttribut feature_importances_ intégré
Jeux de données petits à moyens (jusqu'à ~100K lignes)OuiEntraînement rapide avec traitement parallèle
Classification déséquilibréeOuiSupporte class_weight='balanced'
Vous avez besoin de prédictions interprétablesModéréLes arbres individuels sont interprétables, mais l'ensemble l'est moins
Données très dimensionnelles et clairsemées (texte)NonLes modèles linéaires ou le gradient boosting sont généralement meilleurs
Inférence en temps réel avec latence stricteAttentionLes forêts volumineuses peuvent être lentes au moment de la prédiction

RandomForestClassifier : Exemple de classification

Voici un exemple complet de classification utilisant le jeu de données wine :

from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report, accuracy_score
from sklearn.datasets import load_wine
 
# Load dataset
wine = load_wine()
X, y = wine.data, wine.target
feature_names = wine.feature_names
 
print(f"Dataset: {X.shape[0]} samples, {X.shape[1]} features")
print(f"Classes: {wine.target_names}")
 
# Split data
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)
 
# Train Random Forest
rf = RandomForestClassifier(
    n_estimators=100,
    max_depth=None,
    min_samples_split=2,
    min_samples_leaf=1,
    random_state=42,
    n_jobs=-1
)
rf.fit(X_train, y_train)
 
# Evaluate
y_pred = rf.predict(X_test)
print(f"\nAccuracy: {accuracy_score(y_test, y_pred):.4f}")
print(f"\nClassification Report:")
print(classification_report(y_test, y_pred, target_names=wine.target_names))

Paramètres clés expliqués

ParamètreDéfautDescriptionConseil de réglage
n_estimators100Nombre d'arbres dans la forêtPlus d'arbres = meilleures performances mais plus lent. 100-500 est typique.
max_depthNoneProfondeur maximale de chaque arbreNone signifie pleinement développé. Définir à 10-30 pour réduire le surapprentissage.
min_samples_split2Échantillons minimums pour diviser un nœudAugmenter à 5-20 pour éviter le surapprentissage sur des données bruyantes.
min_samples_leaf1Échantillons minimums dans un nœud feuilleAugmenter à 2-10 pour des prédictions plus lisses.
max_features'sqrt'Features considérées à chaque division'sqrt' pour la classification, 'log2' ou une fraction pour des alternatives.
bootstrapTrueUtiliser l'échantillonnage bootstrapMettre à False pour les petits jeux de données pour utiliser toutes les données par arbre.
class_weightNonePoids pour chaque classeUtiliser 'balanced' pour les jeux de données déséquilibrés.
n_jobsNoneNombre de jobs parallèlesDéfinir à -1 pour utiliser tous les cœurs CPU.
oob_scoreFalseUtiliser les échantillons out-of-bag pour l'évaluationMettre à True pour une estimation de validation intégrée sans ensemble de validation.

Score Out-of-Bag (OOB)

Chaque arbre est entraîné sur environ 63% des données. Les 37% restants (échantillons out-of-bag) peuvent être utilisés comme ensemble de validation gratuit :

from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split
 
wine = load_wine()
X_train, X_test, y_train, y_test = train_test_split(
    wine.data, wine.target, test_size=0.2, random_state=42, stratify=wine.target
)
 
rf = RandomForestClassifier(
    n_estimators=200,
    oob_score=True,
    random_state=42,
    n_jobs=-1
)
rf.fit(X_train, y_train)
 
print(f"OOB Score:  {rf.oob_score_:.4f}")
print(f"Test Score: {rf.score(X_test, y_test):.4f}")

Le score OOB vous donne une estimation de validation sans avoir besoin d'un ensemble de validation séparé. Il est particulièrement utile quand les données sont limitées.

RandomForestRegressor : Exemple de régression

La régression Random Forest prédit des valeurs continues en moyennant les sorties de tous les arbres :

from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
from sklearn.datasets import fetch_california_housing
import numpy as np
 
# Load California housing dataset
housing = fetch_california_housing()
X, y = housing.data, housing.target
feature_names = housing.feature_names
 
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)
 
# Train regressor
rf_reg = RandomForestRegressor(
    n_estimators=200,
    max_depth=20,
    min_samples_leaf=5,
    random_state=42,
    n_jobs=-1
)
rf_reg.fit(X_train, y_train)
y_pred = rf_reg.predict(X_test)
 
# Evaluation metrics
r2 = r2_score(y_test, y_pred)
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
mae = mean_absolute_error(y_test, y_pred)
 
print(f"R-squared: {r2:.4f}")
print(f"RMSE:      {rmse:.4f}")
print(f"MAE:       {mae:.4f}")

Comparaison des régresseurs

from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor
from sklearn.linear_model import LinearRegression, Ridge
from sklearn.tree import DecisionTreeRegressor
from sklearn.model_selection import cross_val_score, train_test_split
from sklearn.datasets import fetch_california_housing
import numpy as np
 
housing = fetch_california_housing()
X_train, X_test, y_train, y_test = train_test_split(
    housing.data, housing.target, test_size=0.2, random_state=42
)
 
regressors = {
    'Linear Regression': LinearRegression(),
    'Ridge': Ridge(alpha=1.0),
    'Decision Tree': DecisionTreeRegressor(max_depth=10, random_state=42),
    'Random Forest': RandomForestRegressor(n_estimators=100, max_depth=20, random_state=42, n_jobs=-1),
    'Gradient Boosting': GradientBoostingRegressor(n_estimators=100, max_depth=5, random_state=42),
}
 
print(f"{'Model':<25} {'CV R² (mean)':>12} {'CV R² (std)':>12}")
print("-" * 52)
 
for name, model in regressors.items():
    scores = cross_val_score(model, X_train, y_train, cv=5, scoring='r2', n_jobs=-1)
    print(f"{name:<25} {scores.mean():>12.4f} {scores.std():>12.4f}")

Random Forest surpasse généralement un arbre de décision unique et les modèles linéaires sur les jeux de données avec des relations non linéaires, tout en étant compétitif avec le gradient boosting.

Optimisation des hyperparamètres

GridSearchCV : Recherche exhaustive

GridSearchCV teste chaque combinaison des valeurs de paramètres spécifiées :

from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import GridSearchCV, train_test_split
from sklearn.datasets import load_wine
 
wine = load_wine()
X_train, X_test, y_train, y_test = train_test_split(
    wine.data, wine.target, test_size=0.2, random_state=42, stratify=wine.target
)
 
param_grid = {
    'n_estimators': [100, 200, 300],
    'max_depth': [None, 10, 20],
    'min_samples_split': [2, 5, 10],
    'min_samples_leaf': [1, 2, 4],
}
 
rf = RandomForestClassifier(random_state=42, n_jobs=-1)
grid_search = GridSearchCV(
    rf,
    param_grid,
    cv=5,
    scoring='accuracy',
    n_jobs=-1,
    verbose=1
)
grid_search.fit(X_train, y_train)
 
print(f"Best Parameters: {grid_search.best_params_}")
print(f"Best CV Score:   {grid_search.best_score_:.4f}")
print(f"Test Score:      {grid_search.score(X_test, y_test):.4f}")

RandomizedSearchCV : Recherche efficace

Quand l'espace des paramètres est vaste, RandomizedSearchCV échantillonne un nombre fixe de combinaisons de paramètres au lieu de toutes les essayer :

from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import RandomizedSearchCV, train_test_split
from sklearn.datasets import load_wine
from scipy.stats import randint, uniform
 
wine = load_wine()
X_train, X_test, y_train, y_test = train_test_split(
    wine.data, wine.target, test_size=0.2, random_state=42, stratify=wine.target
)
 
param_distributions = {
    'n_estimators': randint(50, 500),
    'max_depth': [None, 5, 10, 15, 20, 30],
    'min_samples_split': randint(2, 20),
    'min_samples_leaf': randint(1, 10),
    'max_features': ['sqrt', 'log2', 0.3, 0.5, 0.7],
    'bootstrap': [True, False],
}
 
rf = RandomForestClassifier(random_state=42, n_jobs=-1)
random_search = RandomizedSearchCV(
    rf,
    param_distributions,
    n_iter=100,
    cv=5,
    scoring='accuracy',
    random_state=42,
    n_jobs=-1,
    verbose=1
)
random_search.fit(X_train, y_train)
 
print(f"Best Parameters: {random_search.best_params_}")
print(f"Best CV Score:   {random_search.best_score_:.4f}")
print(f"Test Score:      {random_search.score(X_test, y_test):.4f}")

Importance des paramètres pour l'optimisation

Tous les paramètres n'ont pas un impact égal. Concentrez votre budget d'optimisation sur les paramètres qui comptent le plus :

ParamètreImpactPrioritéNotes
n_estimatorsÉlevé1erPlus d'arbres aide presque toujours jusqu'à des rendements décroissants (~200-500)
max_depthÉlevé2èmeContrôle directement le surapprentissage. Essayer None, 10, 20, 30
min_samples_leafMoyen3èmeLisse les prédictions. Essayer 1, 2, 5, 10
max_featuresMoyen4èmeContrôle la diversité des arbres. 'sqrt' est généralement bien pour la classification
min_samples_splitFaible5èmeMoins d'impact que min_samples_leaf en pratique
bootstrapFaible6èmeTrue est presque toujours mieux. Essayer False uniquement sur de très petits jeux de données

Importance des features

L'un des avantages les plus forts de Random Forest est l'importance des features intégrée. Comprendre quelles features pilotent les prédictions aide pour l'interprétation du modèle, la sélection de features et les insights métier.

Importance des features basée sur l'impureté

L'attribut par défaut feature_importances_ mesure combien chaque feature diminue l'impureté (Gini pour la classification, variance pour la régression) à travers tous les arbres :

from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
import numpy as np
 
wine = load_wine()
X_train, X_test, y_train, y_test = train_test_split(
    wine.data, wine.target, test_size=0.2, random_state=42, stratify=wine.target
)
 
rf = RandomForestClassifier(n_estimators=200, random_state=42, n_jobs=-1)
rf.fit(X_train, y_train)
 
# Get feature importances
importances = rf.feature_importances_
feature_names = wine.feature_names
indices = np.argsort(importances)[::-1]
 
# Print ranked features
print("Feature Ranking:")
for i, idx in enumerate(indices):
    print(f"  {i+1}. {feature_names[idx]:25s} ({importances[idx]:.4f})")
 
# Plot
plt.figure(figsize=(10, 6))
plt.barh(range(len(indices)), importances[indices[::-1]], align='center')
plt.yticks(range(len(indices)), [feature_names[i] for i in indices[::-1]])
plt.xlabel('Feature Importance (Gini)')
plt.title('Random Forest Feature Importance - Wine Dataset')
plt.tight_layout()
plt.savefig('rf_feature_importance.png', dpi=150)
plt.show()

Importance par permutation

L'importance basée sur l'impureté peut être biaisée vers les features à haute cardinalité. L'importance par permutation mesure la chute de performance du modèle quand les valeurs d'une feature sont mélangées aléatoirement :

from sklearn.ensemble import RandomForestClassifier
from sklearn.inspection import permutation_importance
from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
import numpy as np
 
wine = load_wine()
X_train, X_test, y_train, y_test = train_test_split(
    wine.data, wine.target, test_size=0.2, random_state=42, stratify=wine.target
)
 
rf = RandomForestClassifier(n_estimators=200, random_state=42, n_jobs=-1)
rf.fit(X_train, y_train)
 
# Compute permutation importance on the test set
perm_imp = permutation_importance(
    rf, X_test, y_test,
    n_repeats=30,
    random_state=42,
    n_jobs=-1
)
 
# Sort and display
sorted_idx = perm_imp.importances_mean.argsort()[::-1]
 
print("Permutation Importance (test set):")
for idx in sorted_idx:
    mean = perm_imp.importances_mean[idx]
    std = perm_imp.importances_std[idx]
    print(f"  {wine.feature_names[idx]:25s}: {mean:.4f} +/- {std:.4f}")
 
# Plot with error bars
plt.figure(figsize=(10, 6))
plt.barh(
    range(len(sorted_idx)),
    perm_imp.importances_mean[sorted_idx[::-1]],
    xerr=perm_imp.importances_std[sorted_idx[::-1]],
    align='center'
)
plt.yticks(range(len(sorted_idx)), [wine.feature_names[i] for i in sorted_idx[::-1]])
plt.xlabel('Decrease in Accuracy')
plt.title('Permutation Importance - Wine Dataset')
plt.tight_layout()
plt.savefig('rf_permutation_importance.png', dpi=150)
plt.show()

Quelle méthode d'importance utiliser ?

MéthodeAvantagesInconvénientsIdéal pour
Basée sur l'impureté (feature_importances_)Rapide, pas de calcul supplémentaireBiaisée vers les features à haute cardinalitéScreening rapide, exploration initiale
Importance par permutationNon biaisée, fonctionne sur les données de testPlus lente, affectée par les features corréléesSélection finale de features, reporting

Validation croisée avec Random Forest

Validation croisée de base

from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score
from sklearn.datasets import load_wine
 
wine = load_wine()
X, y = wine.data, wine.target
 
rf = RandomForestClassifier(n_estimators=200, random_state=42, n_jobs=-1)
scores = cross_val_score(rf, X, y, cv=5, scoring='accuracy')
 
print(f"CV Accuracy: {scores.mean():.4f} (+/- {scores.std():.4f})")
print(f"Per-fold:    {scores}")

StratifiedKFold pour les données déséquilibrées

StratifiedKFold préserve la distribution des classes dans chaque fold, ce qui est critique pour les jeux de données déséquilibrés :

from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import StratifiedKFold, cross_val_score
from sklearn.datasets import load_wine
import numpy as np
 
wine = load_wine()
X, y = wine.data, wine.target
 
# Stratified 10-fold cross-validation
skf = StratifiedKFold(n_splits=10, shuffle=True, random_state=42)
rf = RandomForestClassifier(n_estimators=200, random_state=42, n_jobs=-1)
 
scores = cross_val_score(rf, X, y, cv=skf, scoring='accuracy')
print(f"Stratified 10-Fold Accuracy: {scores.mean():.4f} (+/- {scores.std():.4f})")
 
# Multiple metrics
from sklearn.model_selection import cross_validate
 
results = cross_validate(
    rf, X, y, cv=skf,
    scoring=['accuracy', 'f1_weighted', 'precision_weighted', 'recall_weighted'],
    n_jobs=-1
)
 
for metric in ['test_accuracy', 'test_f1_weighted', 'test_precision_weighted', 'test_recall_weighted']:
    vals = results[metric]
    name = metric.replace('test_', '')
    print(f"{name:>20s}: {vals.mean():.4f} (+/- {vals.std():.4f})")

Gestion des données déséquilibrées

Quand une classe a beaucoup plus d'échantillons que les autres, un modèle peut atteindre une haute précision en prédisant toujours la classe majoritaire. Random Forest fournit plusieurs outils pour gérer cela.

Utilisation de class_weight='balanced'

Le paramètre class_weight='balanced' ajuste automatiquement les poids inversement proportionnels aux fréquences des classes :

from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from sklearn.datasets import make_classification
 
# Create imbalanced dataset (95% class 0, 5% class 1)
X, y = make_classification(
    n_samples=2000,
    n_features=20,
    weights=[0.95, 0.05],
    flip_y=0,
    random_state=42
)
 
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, random_state=42, stratify=y
)
 
# Without class weight
rf_default = RandomForestClassifier(n_estimators=200, random_state=42, n_jobs=-1)
rf_default.fit(X_train, y_train)
print("=== Without class_weight ===")
print(classification_report(y_test, rf_default.predict(X_test)))
 
# With balanced class weight
rf_balanced = RandomForestClassifier(
    n_estimators=200,
    class_weight='balanced',
    random_state=42,
    n_jobs=-1
)
rf_balanced.fit(X_train, y_train)
print("=== With class_weight='balanced' ===")
print(classification_report(y_test, rf_balanced.predict(X_test)))

Intégration de SMOTE pour le suréchantillonnage

SMOTE (Synthetic Minority Oversampling Technique) crée des échantillons synthétiques pour la classe minoritaire. Utilisez-le avec le pipeline de imblearn :

from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from sklearn.datasets import make_classification
from imblearn.over_sampling import SMOTE
from imblearn.pipeline import Pipeline as ImbPipeline
 
# Create imbalanced dataset
X, y = make_classification(
    n_samples=2000,
    n_features=20,
    weights=[0.95, 0.05],
    flip_y=0,
    random_state=42
)
 
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, random_state=42, stratify=y
)
 
# SMOTE + Random Forest pipeline
pipeline = ImbPipeline([
    ('smote', SMOTE(random_state=42)),
    ('rf', RandomForestClassifier(n_estimators=200, random_state=42, n_jobs=-1))
])
pipeline.fit(X_train, y_train)
 
print("=== SMOTE + Random Forest ===")
print(classification_report(y_test, pipeline.predict(X_test)))

Évaluation du modèle

Rapport de classification et matrice de confusion

from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import (
    classification_report, confusion_matrix,
    ConfusionMatrixDisplay, accuracy_score
)
from sklearn.datasets import load_wine
import matplotlib.pyplot as plt
 
wine = load_wine()
X_train, X_test, y_train, y_test = train_test_split(
    wine.data, wine.target, test_size=0.2, random_state=42, stratify=wine.target
)
 
rf = RandomForestClassifier(n_estimators=200, random_state=42, n_jobs=-1)
rf.fit(X_train, y_train)
y_pred = rf.predict(X_test)
 
# Metrics
print(f"Accuracy: {accuracy_score(y_test, y_pred):.4f}")
print(f"\n{classification_report(y_test, y_pred, target_names=wine.target_names)}")
 
# Confusion matrix plot
cm = confusion_matrix(y_test, y_pred)
disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=wine.target_names)
disp.plot(cmap='Blues')
plt.title('Random Forest - Wine Classification')
plt.tight_layout()
plt.savefig('rf_confusion_matrix.png', dpi=150)
plt.show()

Courbe ROC pour la classification binaire

from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_curve, roc_auc_score, RocCurveDisplay
from sklearn.datasets import load_breast_cancer
import matplotlib.pyplot as plt
 
cancer = load_breast_cancer()
X_train, X_test, y_train, y_test = train_test_split(
    cancer.data, cancer.target, test_size=0.2, random_state=42, stratify=cancer.target
)
 
rf = RandomForestClassifier(n_estimators=200, random_state=42, n_jobs=-1)
rf.fit(X_train, y_train)
 
# Predict probabilities
y_prob = rf.predict_proba(X_test)[:, 1]
auc = roc_auc_score(y_test, y_prob)
 
# Plot ROC curve
RocCurveDisplay.from_estimator(rf, X_test, y_test)
plt.title(f'Random Forest ROC Curve (AUC = {auc:.4f})')
plt.tight_layout()
plt.savefig('rf_roc_curve.png', dpi=150)
plt.show()

Random Forest vs autres algorithmes

FeatureRandom ForestXGBoostGradient BoostingDecision Tree
Type d'ensembleBagging (parallèle)Boosting (séquentiel)Boosting (séquentiel)Modèle unique
PrécisionÉlevéeTrès élevéeTrès élevéeModérée
Vitesse d'entraînementRapide (parallélisable)ModéréeLente (séquentielle)Très rapide
Vitesse de prédictionModéréeRapideModéréeTrès rapide
Risque de surapprentissageFaibleFaible (avec réglage)Faible (avec réglage)Élevé
Sensibilité aux hyperparamètresFaibleÉlevéeÉlevéeModérée
Scaling des features requisNonNonNonNon
Gestion des valeurs manquantesNon (nécessite imputation)Oui (intégré)Non (nécessite imputation)Non
Importance des features intégréeOuiOuiOuiOui
InterprétabilitéModéréeFaibleFaibleÉlevée
Idéal pourUsage général, premier modèleCompétitions Kaggle, précision maximaleDonnées tabulaires structuréesBaselines rapides, petits jeux de données

Quand choisir Random Forest plutôt que les alternatives :

  • Vous avez besoin d'un modèle de base solide avec un réglage minimal
  • La vitesse d'entraînement est importante et vous disposez de plusieurs cœurs CPU
  • Vous souhaitez des estimations fiables de l'importance des features
  • Vous ne cherchez pas le dernier 0,5% de précision que les méthodes de boosting pourraient fournir

Pipeline réel : Exemple de bout en bout

Ce pipeline combine le prétraitement, l'ingénierie des features, l'entraînement du modèle, l'évaluation et la prédiction dans un flux de travail de style production :

from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split, cross_val_score, StratifiedKFold
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.compose import ColumnTransformer
from sklearn.metrics import classification_report, confusion_matrix
from sklearn.datasets import load_breast_cancer
import numpy as np
import pandas as pd
 
# Load and prepare data
cancer = load_breast_cancer()
df = pd.DataFrame(cancer.data, columns=cancer.feature_names)
df['target'] = cancer.target
 
# Introduce some missing values to simulate real data
np.random.seed(42)
mask = np.random.random(df.shape) < 0.05
df_missing = df.mask(mask.astype(bool))
df_missing['target'] = cancer.target  # Keep target clean
 
X = df_missing.drop('target', axis=1)
y = df_missing['target']
 
# Split
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)
 
# Build preprocessing + model pipeline
numeric_features = X.columns.tolist()
numeric_transformer = Pipeline([
    ('imputer', SimpleImputer(strategy='median')),
    ('scaler', StandardScaler()),
])
 
preprocessor = ColumnTransformer(
    transformers=[
        ('num', numeric_transformer, numeric_features),
    ]
)
 
pipeline = Pipeline([
    ('preprocessor', preprocessor),
    ('classifier', RandomForestClassifier(
        n_estimators=300,
        max_depth=20,
        min_samples_leaf=2,
        class_weight='balanced',
        random_state=42,
        n_jobs=-1
    ))
])
 
# Cross-validation
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
cv_scores = cross_val_score(pipeline, X_train, y_train, cv=skf, scoring='accuracy')
print(f"Cross-validation accuracy: {cv_scores.mean():.4f} (+/- {cv_scores.std():.4f})")
 
# Train final model
pipeline.fit(X_train, y_train)
y_pred = pipeline.predict(X_test)
 
# Evaluation
print(f"\nTest Set Results:")
print(classification_report(y_test, y_pred, target_names=cancer.target_names))
 
# Make predictions on new data
sample = X_test.iloc[:3]
predictions = pipeline.predict(sample)
probabilities = pipeline.predict_proba(sample)
 
print(f"\nSample Predictions:")
for i, (pred, prob) in enumerate(zip(predictions, probabilities)):
    class_name = cancer.target_names[pred]
    confidence = prob[pred]
    print(f"  Sample {i+1}: {class_name} (confidence: {confidence:.2%})")

Sauvegarde et chargement du modèle

import joblib
 
# Save the trained pipeline
joblib.dump(pipeline, 'rf_pipeline.joblib')
 
# Load and use later
loaded_pipeline = joblib.load('rf_pipeline.joblib')
new_predictions = loaded_pipeline.predict(X_test[:5])
print(f"Loaded model predictions: {new_predictions}")

Exploration des résultats avec PyGWalker

Après avoir entraîné votre modèle Random Forest, vous avez souvent besoin d'explorer en détail les patterns d'importance des features, les distributions de prédictions et les cas de mauvaise classification. PyGWalker (opens in a new tab) vous permet de transformer votre DataFrame de résultats en une interface d'exploration interactive de type Tableau directement dans Jupyter :

import pandas as pd
import pygwalker as pyg
 
# Build a results DataFrame
results = pd.DataFrame(X_test.values, columns=cancer.feature_names)
results['actual'] = y_test.values
results['predicted'] = y_pred
results['correct'] = y_test.values == y_pred
results['prob_malignant'] = pipeline.predict_proba(X_test)[:, 0]
results['prob_benign'] = pipeline.predict_proba(X_test)[:, 1]
 
# Launch interactive exploration
walker = pyg.walk(results)

Glissez les features sur les axes, filtrez par échantillons mal classés, et codez les couleurs par confiance de prédiction pour identifier où le modèle rencontre des difficultés. Ce type d'analyse visuelle vous aide à décider quelles features ingénier ou quels échantillons nécessitent une inspection plus approfondie.

Pour exécuter votre flux de travail complet d'expérimentation ML -- du chargement des données à la comparaison des modèles jusqu'à l'évaluation finale -- RunCell (opens in a new tab) fournit un environnement Jupyter alimenté par l'IA qui vous aide à itérer plus rapidement sur les expériences, à générer automatiquement le code d'évaluation et à gérer votre flux de travail notebook.

FAQ

Combien d'arbres dois-je utiliser dans un Random Forest ?

Commencez avec 100-200 arbres. La précision s'améliore généralement avec plus d'arbres mais atteint un plateau après un certain point. Utilisez la validation croisée pour trouver le point optimal. Au-delà de 500 arbres, les gains sont généralement négligeables tandis que le temps d'entraînement augmente. Surveillez le score OOB en augmentant n_estimators -- quand il cesse de s'améliorer, vous avez suffisamment d'arbres.

Random Forest a-t-il besoin de scaling des features ?

Non. Random Forest effectue des divisions basées sur des seuils de valeurs de features, donc l'échelle absolue des features n'affecte pas les décisions de division. Contrairement à la régression logistique, SVM ou réseaux de neurones, Random Forest gère naturellement les features avec différentes plages de valeurs. Cependant, si votre pipeline inclut d'autres composants (comme PCA ou du prétraitement basé sur la distance), le scaling peut encore être nécessaire pour ces étapes.

Comment Random Forest gère-t-il les valeurs manquantes ?

Les RandomForestClassifier et RandomForestRegressor de Scikit-learn ne gèrent pas les valeurs manquantes nativement. Vous devez imputer les données manquantes avant l'entraînement -- utilisez SimpleImputer avec la stratégie médiane ou moyenne pour les features numériques, ou utilisez des méthodes d'imputation plus avancées comme IterativeImputer. Certaines autres implémentations comme H2O ou LightGBM peuvent gérer les valeurs manquantes directement.

Quelle est la différence entre Random Forest et Gradient Boosting ?

Random Forest construit les arbres indépendamment en parallèle (bagging), tandis que Gradient Boosting construit les arbres séquentiellement où chaque arbre corrige les erreurs du précédent (boosting). Random Forest réduit la variance, Gradient Boosting réduit le biais. En pratique, Gradient Boosting (surtout XGBoost) atteint souvent une précision légèrement supérieure, mais Random Forest est plus facile à régler et moins sujet au surapprentissage.

Random Forest peut-il être utilisé pour la sélection de features ?

Oui. Utilisez feature_importances_ pour un classement rapide ou permutation_importance pour une estimation plus fiable. Vous pouvez ensuite supprimer les features peu importantes et réentraîner. Alternativement, utilisez SelectFromModel avec un estimateur Random Forest dans un pipeline pour sélectionner automatiquement les features au-dessus d'un seuil.

Conclusion

Random Forest est l'un des algorithmes les plus fiables et polyvalents en machine learning. Il réduit le surapprentissage en combinant des centaines d'arbres de décision décorrélés, gère les tâches de classification et régression sans scaling des features, et fournit des classements d'importance des features intégrés. Pour la plupart des problèmes de données tabulaires, il sert d'excellent premier modèle qui performe souvent suffisamment bien pour un usage en production.

Commencez avec RandomForestClassifier ou RandomForestRegressor avec les paramètres par défaut comme baseline. Réglez d'abord n_estimators pour une analyse des rendements décroissants, puis max_depth et min_samples_leaf pour contrôler le surapprentissage. Utilisez class_weight='balanced' pour les données déséquilibrées, l'importance par permutation pour des classements de features fiables, et la validation croisée StratifiedKFold pour une évaluation robuste. Quand vous avez besoin de la précision absolue la plus élevée sur des données structurées, envisagez Gradient Boosting ou XGBoost, mais Random Forest reste le choix par défaut le plus sûr qui échoue rarement de manière catastrophique.

📚