Random Forest (Classification) : Guide Complet — Principes, Exemples et Implémentation Python

Random Forest (Classification) : Guide Complet — Principes, Exemples et Implémentation Python

Random Forest (Classification) : Guide complet — Principes, Exemples et Implémentation Python

Résumé

Le Random Forest est un algorithme d’apprentissage supervisé de type ensemble, conçu pour résoudre des problèmes de classification. Introduit par Léo Breiman en 2001, il combine le principe du bagging (Bootstrap Aggregating) avec un tirage aléatoire de sous-ensembles de features à chaque nœud de décision. Le résultat est une forêt d’arbres de décision dont le vote majoritaire détermine la classe prédite. Robuste, polyvalent et résistant au surapprentissage, le Random Forest reste l’un des algorithmes les plus utilisés en pratique, aussi bien en compétition de data science qu’en production industrielle.

Principe mathématique

Construction de la forêt

Le Random Forest de classification construit une forêt de B arbres de décision. Chaque arbre b est construit de manière indépendante :

  1. Échantillon bootstrap : on tire avec remise n échantillons parmi les n observations de l’ensemble d’entraînement. Chaque échantillon a une probabilité de 1/n d’être sélectionné à chaque tirage. En moyenne, environ 63,2 % des observations originales apparaissent dans l’échantillon bootstrap d’un arbre donné.
  2. Division des nœuds avec sélection aléatoire de features : à chaque nœud de chaque arbre, au lieu d’examiner toutes les p features pour trouver la meilleure division, on tire au hasard m features parmi les p disponibles (avec m ≪ p, typiquement m = √p pour la classification), puis on choisit la meilleure division parmi ces m features selon le critère de Gini ou l’entropie.

Formule de prédiction

Pour une observation $x$, chaque arbre $b$ produit une prédiction de classe $T_b(x) \in {1, 2, \ldots, K}$. La prédiction finale du Random Forest est déterminée par vote majoritaire :

$$
\hat{y}(x) = \arg\max_k \sum_{b=1}^{B} \mathbb{1}(T_b(x) = k)
$$

où I(·) est la fonction indicatrice et K est le nombre total de classes.

Erreur Out-of-Bag (OOB)

Les observations non sélectionnées dans l’échantillon bootstrap d’un arbre sont dites out-of-bag (OOB) pour cet arbre. En moyenne, chaque observation est OOB pour environ 36,8 % des arbres. On peut donc estimer l’erreur de généralisation sans jeu de validation séparé :

$$
\mathrm{OOB}{\mathrm{error}} = \frac{1}{n} \sum\right)
$$}^{n} \mathbb{1}\left(y_i \neq \hat{y}_i^{(\mathrm{OOB})

où ŷ_i^(OOB) est la prédiction obtenue par vote majoritaire des seuls arbres pour lesquels l’observation i est out-of-bag. L’OOB error est une estimation non biaisée de l’erreur de généralisation, comparable à une validation croisée.

Intuition : pourquoi une forêt bat un arbre seul

Un arbre de décision unique est fragile : une légère modification des données d’entraînement peut produire un arbre radicalement différent. C’est ce qu’on appelle la haute variance. L’arbre s’adapte trop spécifiquement aux données d’entraînement, capturant aussi le bruit.

Mais lorsque l’on combine de nombreux arbres, un phénomène remarquable se produit : la forêt devient robuste. Chaque arbre voit une version légèrement différente des données (grâce au bootstrap) et examine un sous-ensemble différent de features à chaque division. Chaque arbre prend une décision indépendante, imparfaite, mais diversifiée.

La foule décide mieux qu’un seul expert. C’est l’idée fondamentale de l’apprentissage par ensemble. La diversité des arbres fait que leurs erreurs se compensent mutuellement, tandis que leurs bonnes prédictions se renforcent. Mathématiquement, la variance totale de l’ensemble est réduite d’un facteur proportionnel à 1/B, où B est le nombre d’arbres.

Le tirage aléatoire des features (feature subsampling) est crucial : sans lui, tous les arbres auraient tendance à faire les mêmes divisions et seraient fortement corrélés. En forçant chaque arbre à examiner des features différentes, on augmente la décorrélation entre les arbres, ce qui augmente encore l’efficacité du vote majoritaire.

Implémentation Python avec scikit-learn

Exemple de base : RandomForestClassifier

from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report, confusion_matrix
import numpy as np

# 1. Générer un jeu de données synthétique
X, y = make_classification(
    n_samples=2000,
    n_features=20,
    n_informative=10,
    n_redundant=5,
    n_clusters_per_class=2,
    random_state=42
)

# 2. Séparer en ensemble d'entraînement et de test
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)

# 3. Créer et entraîner le Random Forest
rf = RandomForestClassifier(
    n_estimators=100,
    criterion='gini',
    max_depth=None,
    min_samples_split=2,
    min_samples_leaf=1,
    max_features='sqrt',
    bootstrap=True,
    oob_score=True,
    random_state=42,
    n_jobs=-1
)

rf.fit(X_train, y_train)

# 4. Évaluer
y_pred = rf.predict(X_test)
print("Classification Report :")
print(classification_report(y_test, y_pred))
print(f"OOB Score : {rf.oob_score_:.4f}")
print("Matrice de confusion :")
print(confusion_matrix(y_test, y_pred))

Importance des features

Le Random Forest fournit une mesure intrinsèque de l’importance de chaque feature, calculée comme la réduction moyenne du critère de division (Gini) pondérée par le nombre d’échantillons atteignant chaque nœud :

# Importance des features
importances = rf.feature_importances_
indices = np.argsort(importances)[::-1]

print("Importance des features (top 10) :")
for i in range(min(10, X.shape[1])):
    print(f"  Feature {indices[i]:2d} : {importances[indices[i]]:.4f}")

# Visualisation (si matplotlib est disponible)
import matplotlib.pyplot as plt
plt.figure(figsize=(10, 6))
plt.bar(range(min(10, X.shape[1])), importances[indices[:10]], align='center')
plt.xticks(range(min(10, X.shape[1])), [f"X{i}" for i in indices[:10]])
plt.xlabel('Feature')
plt.ylabel('Importance')
plt.title('Importance des Features — Random Forest')
plt.tight_layout()
plt.show()

Comparaison avec un seul arbre de décision

from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score

# Un seul arbre de décision
dt = DecisionTreeClassifier(max_depth=None, random_state=42)
dt.fit(X_train, y_train)

y_pred_rf = rf.predict(X_test)
y_pred_dt = dt.predict(X_test)

print(f"Random Forest (100 arbres) : {accuracy_score(y_test, y_pred_rf):.4f}")
print(f"Arbre de décision unique   : {accuracy_score(y_test, y_pred_dt):.4f}")
print(f"OOB Score (RF)              : {rf.oob_score_:.4f}")

On observe typiquement que le Random Forest surperforme nettement l’arbre de décision unique sur l’ensemble de test, car il généralise mieux. L’arbre unique souffre de surapprentissage : il peut atteindre une précision de 100 % sur l’ensemble d’entraînement mais voir ses performances chuter sur les données de test.

Impact de n_estimators et max_depth

import matplotlib.pyplot as plt

# Étude de l'impact du nombre d'arbres (n_estimators)
results = {'n_estimators': [], 'train_acc': [], 'test_acc': [], 'oob_score': []}

for n in [1, 5, 10, 25, 50, 100, 200, 500]:
    rf_n = RandomForestClassifier(
        n_estimators=n, max_depth=None, oob_score=True,
        random_state=42, n_jobs=-1
    )
    rf_n.fit(X_train, y_train)
    results['n_estimators'].append(n)
    results['train_acc'].append(accuracy_score(y_train, rf_n.predict(X_train)))
    results['test_acc'].append(accuracy_score(y_test, rf_n.predict(X_test)))
    results['oob_score'].append(rf_n.oob_score_)

plt.figure(figsize=(12, 5))

plt.subplot(1, 2, 1)
plt.plot(results['n_estimators'], results['train_acc'], 'b-', label='Train')
plt.plot(results['n_estimators'], results['test_acc'], 'g-', label='Test')
plt.plot(results['n_estimators'], results['oob_score'], 'r--', label='OOB')
plt.xlabel("Nombre d'arbres (n_estimators)")
plt.ylabel('Précision')
plt.legend()
plt.title('Impact de n_estimators')
plt.grid(True, alpha=0.3)

# Étude de l'impact de max_depth
results_depth = {'max_depth': [], 'train_acc': [], 'test_acc': []}

for depth in [1, 2, 3, 5, 10, 20, 50, None]:
    rf_d = RandomForestClassifier(
        n_estimators=100, max_depth=depth, random_state=42, n_jobs=-1
    )
    rf_d.fit(X_train, y_train)
    label = str(depth) if depth else 'None'
    results_depth['max_depth'].append(label)
    results_depth['train_acc'].append(accuracy_score(y_train, rf_d.predict(X_train)))
    results_depth['test_acc'].append(accuracy_score(y_test, rf_d.predict(X_test)))

plt.subplot(1, 2, 2)
plt.plot(range(len(results_depth['max_depth'])), results_depth['train_acc'], 'b-', label='Train')
plt.plot(range(len(results_depth['max_depth'])), results_depth['test_acc'], 'g-', label='Test')
plt.xticks(range(len(results_depth['max_depth'])), results_depth['max_depth'])
plt.xlabel('max_depth')
plt.legend()
plt.title('Impact de max_depth')
plt.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

L’analyse révèle deux tendances clés :

  • n_estimators : la performance augmente rapidement puis stabilise. Au-delà de 100-200 arbres, le gain marginal devient négligeable. Plus d’arbres n’entraînent jamais de surapprentissage (contrairement à la profondeur), donc on peut augmenter ce paramètre sans risque, au coût du temps de calcul.
  • max_depth : une profondeur trop faible cause du sous-apprentissage (biais élevé), tandis qu’une profondeur excessive augmente la variance des arbres individuels. Le Random Forest est plus tolérant qu’un arbre unique aux grandes profondeurs grâce au bagging, mais limiter max_depth à 10-20 peut améliorer la généralisation sur des données bruitées.

Hyperparamètres du Random Forest de Classification

Hyperparamètre Valeur par défaut Rôle Conseil pratique
n_estimators 100 Nombre d’arbres dans la forêt Augmenter jusqu’à stabilisation des scores (100-500). Pas de surapprentissage.
criterion ‘gini’ Critère de division : ‘gini’ ou ‘entropy’ ‘gini’ est plus rapide, ‘entropy’ (gain d’information) peut être légèrement meilleur sur certains jeux.
max_depth None Profondeur maximale de chaque arbre None = développement complet. Limiter à 10-20 si overfitting.
min_samples_split 2 Nombre minimal d’échantillons pour diviser un nœud Augmenter (5-20) pour réduire le surapprentissage sur les données bruitées.
min_samples_leaf 1 Nombre minimal d’échantillons dans une feuille 5-10 lisse les prédictions et réduit la variance.
max_features ‘sqrt’ Nombre de features tirées au hasard à chaque split ‘sqrt’ = √p (classification), ‘log2’ pour de grandes dimensions, float pour un contrôle fin.
bootstrap True Tirage avec remise (vrai bagging) Laisser à True. Si False, tous les arbres voient les mêmes données.
oob_score False Calcul du score out-of-bag Activer pour une validation interne sans jeu séparé.

Avantages et limites du Random Forest

Avantages

  1. Résistant au surapprentissage : le bagging et le tirage aléatoire de features réduisent considérablement la variance par rapport à un arbre unique. Augmenter le nombre d’arbres ne cause jamais d’overfitting.
  2. Peu de prétraitement requis : pas besoin de normalisation ou de mise à l’échelle des features. Gère naturellement les features numériques et catégorielles.
  3. Importance des features : fournit une mesure intrinsèque de l’importance de chaque variable, utile pour l’analyse exploratoire et la sélection de variables.
  4. Estimation OOB : permet de valider le modèle sans jeu de validation séparé, économisant ainsi des données précieuses.
  5. Robustesse aux outliers et au bruit : le vote majoritaire rend le modèle insensible aux valeurs aberrantes individuelles.
  6. Parallélisable : chaque arbre est construit indépendamment, permettant une accélération significative sur plusieurs cœurs.

Limites

  1. Moins interprétable qu’un arbre unique : une forêt de 200 arbres n’est pas visualisable. On perd la transparence de la décision arborescente.
  2. Coût en mémoire : stocker des centaines d’arbres exige plus de ressources qu’un modèle linéaire ou qu’un seul arbre.
  3. Inférences plus lentes : la prédiction nécessite de propager l’observation à travers chaque arbre, ce qui est lent sur de grands jeux.
  4. Extrapolation impossible : comme tous les arbres de décision, le Random Forest ne peut pas extrapoler au-delà de l’intervalle des valeurs vues à l’entraînement (moins critique en classification qu’en régression).
  5. Biais sur les classes déséquilibrées : avec des jeux déséquilibrés, les arbres tendent à favoriser la classe majoritaire. Il faut utiliser class_weight='balanced' ou du ré-échantillonnage.

4 Cas d’usage concrets

1. Diagnostic médical

Classification de pathologies à partir de données cliniques (analyses sanguines, imagerie médicale, antécédents). Le Random Forest est particulièrement adapté car il gère bien les données hétérogènes, résiste au bruit des mesures médicales, et fournit une importance des features qui aide les praticiens à comprendre les facteurs de risque dominants.

2. Détection de fraude bancaire

Classification binaire transaction frauduleuse vs transaction légitime à partir de centaines de variables (montant, lieu, heure, historique du client, type de terminal). Le Random Forest est idéal pour les données massives et déséquilibrées de la fraude, surtout avec class_weight='balanced' pour pénaliser les faux négatifs.

3. Classification de textes

Catégorisation automatique de documents (actualité, sport, économie, technologie) à partir de représentations TF-IDF ou d’embeddings de mots. Le Random Forest fonctionne bien sur des espaces de features de grande dimension et capture des interactions non linéaires entre les termes que les modèles linéaires manquent.

4. Évaluation de risque de crédit

Classification de la solvabilité d’un emprunteur (bon/mauvais payeur) à partir du profil financier, historique de remboursement, emploi, et données socio-démographiques. Les institutions financières utilisent massivement les Random Forests pour leur compromis entre précision et stabilité, combiné à la capacité d’expliquer les prédictions via l’analyse des features importantes.

Voir aussi

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur la façon dont les données de vos commentaires sont traitées.