Arbre de décision
Apprenez comment fonctionnent les arbres de décision, comment construire des arbres de classification et de régression en Python avec scikit-learn.
Un arbre de décision est un algorithme d'apprentissage automatique supervisé qui effectue des prédictions en apprenant une hiérarchie de règles si-alors-sinon à partir des données d'entraînement. Chaque nœud interne teste une caractéristique, chaque branche représente un résultat de ce test, et chaque nœud feuille contient une prédiction (une étiquette de classe pour la classification, ou une valeur numérique pour la régression).
Ce chapitre couvre :
- Comment les arbres de décision divisent les données à l'aide de mesures d'impureté (Gini et entropie)
- Construction d'un arbre de classification et d'un arbre de régression en Python avec
scikit-learn - Contrôle de la profondeur de l'arbre et prévention du sur-apprentissage grâce aux hyperparamètres
- Visualisation et inspection d'un arbre entraîné
- Avantages, limites et cas d'usage des arbres de décision
Comment un arbre de décision divise les données
Lors de l'entraînement, l'algorithme examine chaque caractéristique et chaque seuil possible pour trouver la division qui réduit le plus l'impureté — une mesure du degré de mélange des classes dans un nœud.
Deux mesures d'impureté sont couramment utilisées dans scikit-learn :
Impureté de Gini
L'impureté de Gini mesure la probabilité de classer incorrectement un échantillon choisi au hasard s'il était étiqueté selon la distribution des classes dans le nœud.
Gini(node) = 1 - Σ pᵢ²Un nœud pur (tous les échantillons appartiennent à une seule classe) a un Gini = 0. Un nœud maximalement mixte a un Gini approchant 0,5 pour la classification binaire.
Entropie et gain d'information
L'entropie provient de la théorie de l'information. Elle est maximisée lorsque les classes sont également réparties et nulle lorsque le nœud est pur.
Entropy(node) = -Σ pᵢ log₂(pᵢ)Le gain d'information est la réduction d'entropie après une division. L'algorithme choisit la division qui produit le plus grand gain d'information. Dans scikit-learn, vous choisissez entre les deux via le paramètre criterion ("gini" est la valeur par défaut).
Division récursive
La division se répète récursivement sur chaque nœud enfant jusqu'à ce qu'une condition d'arrêt soit atteinte : le nœud est pur, aucune caractéristique n'améliore l'impureté, ou une limite de profondeur ou de taille est atteinte. Cela produit la structure d'arbre binaire.
Arbre de classification en Python
Le jeu de données Iris contient 150 échantillons et 4 caractéristiques numériques. L'objectif est de prédire l'une des trois espèces de fleurs.
from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, classification_report
# Load dataset
data = load_iris()
X, y = data.data, data.target
# Split: 80 % train, 20 % test
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# Train — limit depth to 3 to keep the tree readable
clf = DecisionTreeClassifier(criterion="gini", max_depth=3, random_state=42)
clf.fit(X_train, y_train)
# Evaluate
y_pred = clf.predict(X_test)
print(f"Accuracy: {accuracy_score(y_test, y_pred):.2f}")
print(classification_report(y_test, y_pred, target_names=data.target_names))Sortie attendue :
Accuracy: 1.00
precision recall f1-score support
setosa 1.00 1.00 1.00 10
versicolor 1.00 1.00 1.00 9
virginica 1.00 1.00 1.00 11
accuracy 1.00 30
macro avg 1.00 1.00 1.00 30
weighted avg 1.00 1.00 1.00 30Le jeu de données Iris est linéairement séparable avec une profondeur 3, donc l'arbre atteint une précision parfaite sur l'ensemble de test. Les jeux de données réels seront plus complexes.
Prédiction de nouveaux échantillons
Après l'entraînement, appelez predict() pour classifier de nouvelles observations et predict_proba() pour obtenir les probabilités de classe :
import numpy as np
# A new flower: sepal length 5.1, sepal width 3.5, petal length 1.4, petal width 0.2
new_sample = np.array([[5.1, 3.5, 1.4, 0.2]])
predicted_class = clf.predict(new_sample)
predicted_proba = clf.predict_proba(new_sample)
print("Predicted class:", data.target_names[predicted_class[0]])
print("Class probabilities:", predicted_proba)Sortie attendue :
Predicted class: setosa
Class probabilities: [[1. 0. 0.]]Arbre de régression en Python
Les arbres de décision peuvent également traiter des cibles continues. Utilisez DecisionTreeRegressor à la place de DecisionTreeClassifier.
from sklearn.tree import DecisionTreeRegressor
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, r2_score
import numpy as np
# Synthetic regression dataset
X_reg, y_reg = make_regression(
n_samples=300, n_features=5, noise=20, random_state=42
)
X_train_r, X_test_r, y_train_r, y_test_r = train_test_split(
X_reg, y_reg, test_size=0.2, random_state=42
)
reg = DecisionTreeRegressor(max_depth=5, random_state=42)
reg.fit(X_train_r, y_train_r)
y_pred_r = reg.predict(X_test_r)
mse = mean_squared_error(y_test_r, y_pred_r)
r2 = r2_score(y_test_r, y_pred_r)
print(f"MSE : {mse:.2f}")
print(f"R² : {r2:.2f}")Un arbre de régression divise les données en minimisant l'erreur quadratique moyenne (MSE) dans chaque nœud et prédit la valeur cible moyenne de tous les échantillons d'entraînement qui atteignent une feuille.
Réglage des hyperparamètres
Sans limites, un arbre de décision croîtra jusqu'à ce que chaque feuille soit pure, mémorisant parfaitement l'ensemble d'entraînement (sur-apprentissage). Les hyperparamètres contrôlent la complexité de l'arbre :
| Paramètre | Par défaut | Effet |
|---|---|---|
max_depth | None | Nombre maximum de niveaux. Plus faible = arbre plus simple. |
min_samples_split | 2 | Nombre minimum d'échantillons requis pour diviser un nœud. Plus élevé = moins de divisions. |
min_samples_leaf | 1 | Nombre minimum d'échantillons requis dans une feuille. Plus élevé = frontières plus lisses. |
max_features | None | Nombre de caractéristiques à considérer à chaque division (utile pour la sélection de caractéristiques). |
criterion | "gini" | Mesure d'impureté : "gini" ou "entropy" pour les classificateurs ; "squared_error" pour les régresseurs. |
Utilisez la validation croisée et la recherche par grille pour trouver la meilleure combinaison :
from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import load_iris
from sklearn.model_selection import GridSearchCV
data = load_iris()
X, y = data.data, data.target
param_grid = {
"max_depth": [2, 3, 4, 5, None],
"min_samples_split": [2, 5, 10],
"criterion": ["gini", "entropy"],
}
grid_search = GridSearchCV(
DecisionTreeClassifier(random_state=42),
param_grid,
cv=5,
scoring="accuracy",
)
grid_search.fit(X, y)
print("Best params :", grid_search.best_params_)
print(f"Best CV score: {grid_search.best_score_:.3f}")Sortie attendue (les valeurs peuvent légèrement varier selon les versions de scikit-learn) :
Best params : {'criterion': 'gini', 'max_depth': 3, 'min_samples_split': 2}
Best CV score: 0.973Gestion des caractéristiques catégorielles
Les arbres de décision de scikit-learn nécessitent des entrées numériques. Encodez les colonnes catégorielles avant l'entraînement :
- Catégories ordinales (ex. taille : petit < moyen < grand) : utilisez
OrdinalEncoder. - Catégories nominales (ex. couleur : rouge, vert, bleu) : utilisez
OneHotEncoderpour éviter d'imposer un ordre.
from sklearn.preprocessing import OrdinalEncoder
import numpy as np
# Encode only the categorical column; keep the numeric column as-is
sizes = np.array([["small"], ["large"], ["medium"], ["large"]])
weights = np.array([1.2, 3.4, 2.1, 4.0])
# Explicit category order: large=0, medium=1, small=2
enc = OrdinalEncoder(categories=[["large", "medium", "small"]])
sizes_encoded = enc.fit_transform(sizes)
X_encoded = np.column_stack([sizes_encoded, weights])
print(X_encoded)Sortie attendue :
[[2. 1.2]
[0. 3.4]
[1. 2.1]
[0. 4. ]]Consultez le chapitre Données catégorielles pour un guide complet.
Visualisation d'un arbre de décision
Inspecter la structure de l'arbre révèle quelles caractéristiques sont à l'origine du plus grand nombre de divisions et rend le modèle auditable.
Représentation textuelle
from sklearn.tree import DecisionTreeClassifier, export_text
from sklearn.datasets import load_iris
data = load_iris()
clf = DecisionTreeClassifier(max_depth=2, random_state=42)
clf.fit(data.data, data.target)
print(export_text(clf, feature_names=list(data.feature_names)))Sortie attendue :
|--- petal length (cm) <= 2.45
| |--- class: 0
|--- petal length (cm) > 2.45
| |--- petal width (cm) <= 1.75
| | |--- class: 1
| |--- petal width (cm) > 1.75
| | |--- class: 2Tracé graphique
import matplotlib.pyplot as plt
from sklearn.tree import DecisionTreeClassifier, plot_tree
from sklearn.datasets import load_iris
data = load_iris()
clf = DecisionTreeClassifier(max_depth=2, random_state=42)
clf.fit(data.data, data.target)
plt.figure(figsize=(10, 5))
plot_tree(
clf,
feature_names=data.feature_names,
class_names=data.target_names,
filled=True,
rounded=True,
)
plt.title("Iris Decision Tree (max_depth=2)")
plt.tight_layout()
plt.savefig("iris_tree.png", dpi=150)
plt.show()filled=True colore chaque nœud selon sa classe majoritaire ; des teintes plus foncées indiquent une plus grande pureté de classe.
Importance des caractéristiques
Après l'entraînement, feature_importances_ attribue à chaque caractéristique un score entre 0 et 1, où une valeur plus élevée signifie que la caractéristique a davantage contribué à réduire l'impureté dans toutes les divisions :
from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import load_iris
import numpy as np
data = load_iris()
clf = DecisionTreeClassifier(max_depth=3, random_state=42)
clf.fit(data.data, data.target)
importances = clf.feature_importances_
for name, imp in sorted(
zip(data.feature_names, importances), key=lambda x: x[1], reverse=True
):
print(f"{name:30s}: {imp:.4f}")Sortie attendue :
petal length (cm) : 0.5856
petal width (cm) : 0.4144
sepal length (cm) : 0.0000
sepal width (cm) : 0.0000Les caractéristiques avec une importance de 0 n'ont jamais été utilisées par aucune division et pourraient être supprimées pour simplifier le modèle.
Avantages et limites
Quand utiliser les arbres de décision
- Vous avez besoin d'un modèle interprétable — les règles peuvent être affichées en langage clair.
- Votre jeu de données contient un mélange de caractéristiques numériques et catégorielles (après encodage).
- Vous souhaitez une référence rapide avant d'essayer des méthodes d'ensemble.
- La relation entre les caractéristiques et la cible est non linéaire ou implique des interactions.
Limites
| Limite | Atténuation |
|---|---|
| Sur-apprentissage facile sans réglage | Contraindre max_depth, min_samples_leaf ; utiliser la validation croisée |
| Variance élevée (de petits changements de données → un arbre différent) | Utiliser des méthodes d'ensemble : Forêt aléatoire / Bootstrap Aggregation |
| Biais vers les caractéristiques avec plus de valeurs uniques | Utiliser max_features ou normaliser les critères de division |
| Mauvaise extrapolation au-delà de la plage des données d'entraînement | Préférer les modèles linéaires pour les tâches d'extrapolation |
| Divisions uniquement alignées sur les axes | Des arbres obliques existent mais ne sont pas disponibles dans scikit-learn |
Arbres de décision vs. algorithmes connexes
| Algorithme | Différence clé |
|---|---|
| Régression logistique | Frontière linéaire ; meilleure pour les données linéairement séparables ; ne gère pas automatiquement les interactions |
| K plus proches voisins | Basé sur les instances ; pas de modèle explicite ; nécessite une mise à l'échelle des caractéristiques |
| Arbre de décision | Non linéaire ; pas de mise à l'échelle nécessaire ; hautement interprétable |
| Forêt aléatoire (voir Bootstrap Aggregation) | Ensemble de nombreux arbres ; variance bien plus faible ; moins interprétable |
Points clés à retenir
- Les arbres de décision divisent les données en maximisant le gain d'information (ou en minimisant l'impureté de Gini) à chaque nœud ; le processus se répète récursivement.
DecisionTreeClassifieretDecisionTreeRegressordans scikit-learn partagent la même API et les mêmes noms d'hyperparamètres.- Définissez toujours
max_depthoumin_samples_leafpour éviter le sur-apprentissage ; réglez-les avec la recherche par grille et la validation croisée. feature_importances_révèle les caractéristiques sur lesquelles l'arbre s'appuie le plus — utile pour la sélection de caractéristiques.- Les arbres individuels constituent une bonne référence interprétable, mais les méthodes d'ensemble comme la Forêt aléatoire les surpassent presque toujours sur des données réelles.