La validation croisée (cross validation): tester le modèle sous plusieurs angles
Les limites du découpage unique et la nécessité de la validation croisée
Lorsqu’on entraîne un modèle, on a souvent recours à un découpage simple des données avec
train_test_split qu'on a vu dans la leçon consacrée à la
séparation du jeu de données en ensembles d'entraînement et de test. En effet, une partie des données est destinée à l’entraînement et l'autre pour le test. Mais ce découpage est fait "au hasard" et peut parfois favoriser artificiellement les performances. Par exemple, si par chance le jeu de test contient des exemples trop faciles ou trop représentatifs, le modèle semblera meilleur qu’il ne l’est réellement. À l’inverse, un découpage mal équilibré peut donner l’impression que le modèle est mauvais, alors qu’il aurait mieux réussi avec une autre répartition des données. Ce biais lié au hasard du découpage est un vrai problème pour juger de la qualité d’un modèle.
C’est précisément pour corriger ce biais que l’on utilise la
validation croisée (ou cross validation). Au lieu de se contenter d’un seul découpage, on répète l’opération plusieurs fois avec des partitions différentes du jeu de données. Chaque sous‑ensemble joue tour à tour le rôle de jeu de test et les performances sont ensuite moyennées. Ainsi, on obtient une estimation plus robuste et plus juste de la capacité du modèle à généraliser. La validation croisée n’est donc pas un luxe méthodologique mais il s'agit d'une étape essentielle pour fiabiliser l’évaluation et éviter que le hasard d’un découpage unique ne trompe notre analyse.
Comment fonctionne la validation croisée?
La validation croisée est une méthode qui consiste à découper le jeu de données en plusieurs parties appelées
folds (ou plis). L’idée est de ne pas se contenter d’un seul découpage arbitraire entre entraînement et test, mais de répéter l’opération plusieurs fois avec des répartitions différentes.
Concrètement, si l’on choisit une validation croisée en k folds, on divise le jeu de données en k sous‑ensembles de taille comparable. À chaque itération, on entraîne le modèle sur k-1 folds et on le teste sur le fold restant. On recommence jusqu’à ce que chaque fold ait servi une fois de jeu de test, puis on calcule la moyenne des performances obtenues.
Ce mécanisme permet de réduire le biais lié au hasard d’un découpage unique. Par exemple, dans une validation croisée à 5 folds, chaque observation du jeu de données est utilisée 4 fois pour l’entraînement et 1 fois pour la validation. Ainsi, aucune donnée n’est laissée de côté et l’évaluation est plus représentative de la capacité réelle du modèle à généraliser.
Cette figure représente un schéma simple de validation croisée en 5 folds où le dataset est découpé en 5 segments dont 4 sont utilisés pour l’entraînement et 1 pour le test de manière séquentielle.
Selon la nature des données, on peut adapter la manière dont les découpages sont faits. Parmi les techniques uitlisée pour le découpage on peut citer:
- Validation croisée stratifiée (Stratified Cross Validation): Souvent utilisé en classification lorsque les classes sont déséquilibrées. En effet, Chaque fold conserve la même proportion de classes que dans le jeu de données global. Cela empêche qu’un fold contienne trop peu d’exemples d’une classe minoritaire, ce qui fausserait l’évaluation. Par exemple, si 20 % des données appartiennent à la classe "positive" alors chaque fold respectera ce ratio.
- Validation croisée pour séries temporelles (TimeSeriesSplit): Adaptée aux données chronologiques (finance, météo, capteurs...). Avec cette approche, on ne mélange pas les données du passé et du futur, mais les folds sont créés en respectant l’ordre temporel. Donc, on simule une vraie prédiction en conditions réelles où le modèle apprend sur le passé pour anticiper l’avenir.
- Validation croisée par groupes (GroupKFold): Cette technique est tilisée lorsque les données sont regroupées par entités (patients, clients, documents...). Les observations d’un même groupe ne doivent jamais être séparées entre train et test pour éviter les fuites d’information lors de l'évaluation. Par exemple, si plusieurs mesures proviennent du même patient, elles doivent toutes être dans le même fold. Cela garantit une évaluation plus réaliste car le modèle est testé sur des groupes qu’il n’a jamais vus.
En résumé, la validation croisée repose sur une logique qui consiste à multiplier les points de vue sur les données en variant les découpages afin d’obtenir une estimation plus robuste et plus fiable de la performance du modèle.
Validation croisée avec l’arbre de décision sur le dataset Titanic
Ce code utilise les données du Titanic pour prédire la survie des passagers grâce à un modèle d’arbre de décision. Au lieu de se limiter à un seul test sur un découpage fixe des données, il applique une validation croisée. Cela signifie que le jeu de données est divisé en plusieurs segments et que le modèle est entraîné et évalué successivement sur chacun d’eux. Cette approche permet d’obtenir une estimation plus fiable de la performance globale du modèle.
import pandas as pd
from sklearn.model_selection import StratifiedKFold, cross_val_score
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import make_scorer, accuracy_score
import matplotlib.pyplot as plt
df = pd.read_csv("titanic.csv")
df = df[["Survived", "Pclass", "Sex", "Age", "SibSp", "Parch", "Fare"]].dropna()
df["Sex"] = df["Sex"].map({"male": 0, "female": 1})
X = df.drop("Survived", axis=1)
y = df["Survived"]
clf = DecisionTreeClassifier(max_depth=3, random_state=42)
# Définir la validation croisée stratifiée
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
# Évaluer le modèle avec cross-validation
scores = cross_val_score(clf, X, y, cv=cv, scoring=make_scorer(accuracy_score))
print("Scores par fold :", scores)
print(f"Exactitude moyenne : {scores.mean():.2f} ± {scores.std():.2f}")
Expliquons rapidement les parties qui nous intéressent:
On comment par importer deux outils essentiels de scikit‑learn pour mettre en place une validation croisée à savoir
StratifiedKFold et
cross_val_score:
from sklearn.model_selection import StratifiedKFold, cross_val_score
- StratifiedKFold: Une variante de la validation croisée qui conserve la proportion des classes dans chaque fold. C’est particulièrement utile en classification quand les classes sont déséquilibrées. En effet, il y a beaucoup plus de passagers non‑survivants que survivants dans le dataste Titanic.
- cross_val_score: Une fonction qui automatise le processus de validation croisée. Elle entraîne le modèle sur plusieurs découpages des données et retourne les scores obtenus pour chaque fold. On peut ensuite calculer la moyenne et l’écart‑type pour avoir une estimation robuste de la performance.
Ensuite, on définit la stratégie de validation croisée:
# Définir la validation croisée stratifiée
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
Voyons rapidement la significafiton des paramètres passés à la fonction
StratifiedKFold:
- n_splits=5: Le dataset est découpé en 5 parties. Le modèle sera entraîné 5 fois, chaque fois avec 4 folds pour l’entraînement et 1 fold pour le test.
- shuffle=True: Les données sont mélangées avant d’être découpées, ce qui évite que l’ordre initial influence les folds.
- random_state=42: Fixe la graine aléatoire pour rendre le découpage reproductible. Donc on obtient toujours les mêmes folds quand on réexécute le code.
Enfin, on évalue le modèle en utilisant la validation croisée:
# Évaluer le modèle avec cross-validation
scores = cross_val_score(clf, X, y, cv=cv, scoring=make_scorer(accuracy_score))
Regardons encore la significaiton des paramètres passée à la fonction
cross_val_score qui automatise le processus de la validation croisée:
- clf: Désigne le modèle utilisée pour la classifcation (ici un arbre de décision).
- X, y: Les données explicatives (features) et la cible (target) extraites du dataset après avoir appliquées les transformations habituelles (nettoyage et encodage).
- cv=cv: Indique la stratégie de découpage (ici un StratifiedKFold en 5 folds qu'on a spécifié juste avant).
- scoring=make_scorer(accuracy_score): Précise que la métrique utilisée est l’exactitude (accuracy).
La fonction retourne une liste des performances (scores) obtenues sur chaque fold et que l'on affiche ensuite en précisant la moyenne et l’écart‑type pour avoir une estimation robuste de la performance globale du modèle:
print("Scores par fold :", scores)
print(f"Exactitude moyenne : {scores.mean():.2f} ± {scores.std():.2f}")
L'exécution du code produit ce résultat:
Scores par fold : [0.81118881 0.82517483 0.76923077 0.8041958 0.84507042]
Exactitude moyenne : 0.81 ± 0.03
On constate que les scores varient d’un fold à l’autre, ce qui montre que la manière dont les données sont réparties influence directement la performance du modèle.
Dans cet exemple, nous avons évalué notre modèle en utilisant simplement la métrique d'exactitude (accuracy score). Néanmoins, on peut toujours utiliser d'autres métriques comme la précision, le rappel ou le score F1.