W3docs

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        30

Le 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ètrePar défautEffet
max_depthNoneNombre maximum de niveaux. Plus faible = arbre plus simple.
min_samples_split2Nombre minimum d'échantillons requis pour diviser un nœud. Plus élevé = moins de divisions.
min_samples_leaf1Nombre minimum d'échantillons requis dans une feuille. Plus élevé = frontières plus lisses.
max_featuresNoneNombre 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.973

Gestion 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 OneHotEncoder pour é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: 2

Tracé 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.0000

Les 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

LimiteAtténuation
Sur-apprentissage facile sans réglageContraindre 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 uniquesUtiliser max_features ou normaliser les critères de division
Mauvaise extrapolation au-delà de la plage des données d'entraînementPréférer les modèles linéaires pour les tâches d'extrapolation
Divisions uniquement alignées sur les axesDes arbres obliques existent mais ne sont pas disponibles dans scikit-learn

Arbres de décision vs. algorithmes connexes

AlgorithmeDifférence clé
Régression logistiqueFrontière linéaire ; meilleure pour les données linéairement séparables ; ne gère pas automatiquement les interactions
K plus proches voisinsBasé sur les instances ; pas de modèle explicite ; nécessite une mise à l'échelle des caractéristiques
Arbre de décisionNon 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.
  • DecisionTreeClassifier et DecisionTreeRegressor dans scikit-learn partagent la même API et les mêmes noms d'hyperparamètres.
  • Définissez toujours max_depth ou min_samples_leaf pour é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.
Was this page helpful?