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) :
- É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.
- 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/3pour la régression). - Entraînement indépendant : Entraîner un arbre de décision sur chaque échantillon bootstrap avec la contrainte de feature aléatoire.
- 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énario | Random Forest ? | Pourquoi |
|---|---|---|
| Données tabulaires avec types de features mixtes | Oui | Gère les features numériques et catégorielles, pas de scaling nécessaire |
| Vous avez besoin de classements d'importance des features | Oui | Attribut feature_importances_ intégré |
| Jeux de données petits à moyens (jusqu'à ~100K lignes) | Oui | Entraînement rapide avec traitement parallèle |
| Classification déséquilibrée | Oui | Supporte class_weight='balanced' |
| Vous avez besoin de prédictions interprétables | Modéré | Les arbres individuels sont interprétables, mais l'ensemble l'est moins |
| Données très dimensionnelles et clairsemées (texte) | Non | Les modèles linéaires ou le gradient boosting sont généralement meilleurs |
| Inférence en temps réel avec latence stricte | Attention | Les 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ètre | Défaut | Description | Conseil de réglage |
|---|---|---|---|
n_estimators | 100 | Nombre d'arbres dans la forêt | Plus d'arbres = meilleures performances mais plus lent. 100-500 est typique. |
max_depth | None | Profondeur maximale de chaque arbre | None signifie pleinement développé. Définir à 10-30 pour réduire le surapprentissage. |
min_samples_split | 2 | Échantillons minimums pour diviser un nœud | Augmenter à 5-20 pour éviter le surapprentissage sur des données bruyantes. |
min_samples_leaf | 1 | Échantillons minimums dans un nœud feuille | Augmenter à 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. |
bootstrap | True | Utiliser l'échantillonnage bootstrap | Mettre à False pour les petits jeux de données pour utiliser toutes les données par arbre. |
class_weight | None | Poids pour chaque classe | Utiliser 'balanced' pour les jeux de données déséquilibrés. |
n_jobs | None | Nombre de jobs parallèles | Définir à -1 pour utiliser tous les cœurs CPU. |
oob_score | False | Utiliser les échantillons out-of-bag pour l'évaluation | Mettre à 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ètre | Impact | Priorité | Notes |
|---|---|---|---|
n_estimators | Élevé | 1er | Plus d'arbres aide presque toujours jusqu'à des rendements décroissants (~200-500) |
max_depth | Élevé | 2ème | Contrôle directement le surapprentissage. Essayer None, 10, 20, 30 |
min_samples_leaf | Moyen | 3ème | Lisse les prédictions. Essayer 1, 2, 5, 10 |
max_features | Moyen | 4ème | Contrôle la diversité des arbres. 'sqrt' est généralement bien pour la classification |
min_samples_split | Faible | 5ème | Moins d'impact que min_samples_leaf en pratique |
bootstrap | Faible | 6ème | True 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éthode | Avantages | Inconvénients | Idéal pour |
|---|---|---|---|
Basée sur l'impureté (feature_importances_) | Rapide, pas de calcul supplémentaire | Biaisée vers les features à haute cardinalité | Screening rapide, exploration initiale |
| Importance par permutation | Non biaisée, fonctionne sur les données de test | Plus lente, affectée par les features corrélées | Sé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
| Feature | Random Forest | XGBoost | Gradient Boosting | Decision Tree |
|---|---|---|---|---|
| Type d'ensemble | Bagging (parallèle) | Boosting (séquentiel) | Boosting (séquentiel) | Modèle unique |
| Précision | Élevée | Très élevée | Très élevée | Modérée |
| Vitesse d'entraînement | Rapide (parallélisable) | Modérée | Lente (séquentielle) | Très rapide |
| Vitesse de prédiction | Modérée | Rapide | Modérée | Très rapide |
| Risque de surapprentissage | Faible | Faible (avec réglage) | Faible (avec réglage) | Élevé |
| Sensibilité aux hyperparamètres | Faible | Élevée | Élevée | Modérée |
| Scaling des features requis | Non | Non | Non | Non |
| Gestion des valeurs manquantes | Non (nécessite imputation) | Oui (intégré) | Non (nécessite imputation) | Non |
| Importance des features intégrée | Oui | Oui | Oui | Oui |
| Interprétabilité | Modérée | Faible | Faible | Élevée |
| Idéal pour | Usage général, premier modèle | Compétitions Kaggle, précision maximale | Données tabulaires structurées | Baselines 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.