Séparation entraînement-test
Dans le monde de l'apprentissage automatique, les données sont le carburant qui alimente chaque modèle. Cependant, la façon dont vous utilisez correctement ce carburant détermine si votre modèle devient un moteur intelligent capable de prédire avec précision l'avenir, ou un perroquet qui ne peut qu'apprendre par cœur.
Aujourd'hui, nous allons nous plonger en profondeur dans un concept crucial et fondamental de l'apprentissage automatique :la séparation entre les ensembles d'entraînement et de test. C'est la première étape de la construction de tout modèle fiable et la clé pour évaluer la véritable capacité d'un modèle.
En termes simples, diviser les ensembles d'entraînement et de test, c'est comme étudier et passer des examens à l'école :
- L'ensemble d'entraînementcorrespond aux manuels et aux exercices de l'élève ; le modèle l'utilise pour apprendre les règles et les schémas dans les données.
- Ensemble de testest l'examen final ; le modèle l'utilise pour vérifier s'il a vraiment maîtrisé les connaissances, plutôt que de simplement mémoriser les réponses aux exercices d'entraînement (ensemble d'entraînement).
Pourquoi est-il nécessaire de diviser les ensembles d'entraînement et de test ?
Imaginez un étudiant qui n'a révisé que les questions d'examen blanc données par le professeur, et les questions d'examen étaient exactement les mêmes questions d'examen blanc. Il a obtenu un score parfait. Cela prouve-t-il qu'il a vraiment compris le sujet ? Évidemment non. Il a peut-être simplement mémorisé les réponses.
En apprentissage automatique, si nous entraînons un modèle surtoutes les donnéeset utilisons ensuite lesmêmes donnéespour évaluer sa performance, nous commettons la même erreur. Le modèle semblera exceptionnellement bon parce qu'il a « vu » et « mémorisé » chaque détail des données, y compris le bruit et les coïncidences aléatoires. Ce phénomène est appelésurapprentissage。
Un modèle surajusté est comme un étudiant qui ne peut que réciter des exemples de questions ; une fois qu'il rencontre de nouvelles questions jamais vues (nouvelles données), il obtient de très mauvais résultats. Sa « capacité de généralisation » est très faible.
Par conséquent, nous devons diviser les données en deux parties :
- Ensemble d'entraînement: utilisé pourEnseignerentraîner le modèle, en lui permettant d'apprendre.
- Ensemble de test: utilisé pourexaminerévaluer le modèle et évaluer comment il se comporte face àde nouvelles données qu'il n'a jamais vues.
L'ensemble de test doit être complètement isolé de l'ensemble d'entraînement et ne doitpasêtre vu par le modèle pendant tout le processus d'entraînement. Ce n'est qu'ainsi que les résultats d'évaluation sur l'ensemble de test peuvent refléter objectivement la véritable capacité de généralisation du modèle.
Comment diviser : méthodes et stratégies courantes
Diviser les données semble simple, mais il y a beaucoup à savoir. Différentes stratégies de division conviennent à différents scénarios.
1. Division aléatoire simple
C'est la méthode la plus basique et la plus courante. Mélangez aléatoirement l'ensemble du jeu de données, puis divisez-le en deux parties selon un certain ratio.
Exemple
from sklearn.model_selection import train_test_split
# Supposons que X corresponde aux données de caractéristiques et y aux données d'étiquettes
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
print(f"Nombre d'échantillons de l'ensemble d'entraînement : {len(X_train)}")
print(f"Nombre d'échantillons de l'ensemble de test : {len(X_test)}")
Explication du code:
train_test_split: C'est la fonction principale de scikit-learn pour diviser les données.X, y: Les données de caractéristiques d'entrée et les étiquettes correspondantes.test_size=0.2: Spécifie que le ratio de taille de l'ensemble de test est de 20% (c.-à-d., l'ensemble d'entraînement est de 80%). Vous pouvez également utilisertrain_size=0.8pour le spécifier.random_state=42: Définissez une graine aléatoire. Cela garantit que le résultat de la division est exactement le même à chaque exécution du code, ce qui est crucial pour la reproductibilité des expériences. Vous pouvez la définir sur n'importe quel entier.
2. Division par échantillonnage stratifié
Dans les problèmes de classification, si la distribution des classes du jeu de données est déséquilibrée (par exemple, 90% de classe A, 10% de classe B), une simple division aléatoire peut entraîner des proportions de classes très différentes entre les ensembles d'entraînement et de test, affectant l'équité de l'évaluation.
L'échantillonnage stratifiégarantit que les proportions de chaque classe dans les ensembles d'entraînement et de test restent cohérentes avec le jeu de données d'origine.
Exemple
from sklearn.model_selection import train_test_split
# Supposons que y soit les étiquettes de classification
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, stratify=y, random_state=42)
# Vérifier la distribution des classes après la division
from collections import Counter
print("Distribution des classes des données d'origine :", Counter(y))
print("Distribution des classes de l'ensemble d'entraînement :", Counter(y_train))
print("Distribution des classes de l'ensemble de test :", Counter(y_test))
Explication du code:
stratify=y: C'est le paramètre clé. Il indique à la fonction de stratifier selonyla distribution des classes des étiquettes lors de l'exécution d'un échantillonnage stratifié.
3. Division des données de séries temporelles
Pour les données de séries temporelles (par exemple, les cours des actions, les températures quotidiennes), il existe des dépendances temporelles entre les points de données. Nous ne pouvons pas mélanger aléatoirement, car les données futures ne peuvent pas être utilisées pour prédire le passé.
La pratique habituelle consiste à découper dans l’ordre chronologique :Utilisez les 80% premiers des données par ordre temporel comme ensemble d’entraînement, et les 20% derniers comme ensemble de test.。
Exemple
split_index = int(len(X) * 0.8) # Calculer l’indice à la position 80%
X_train, X_test = X[:split_index], X[split_index:]
y_train, y_test = y[:split_index], y[split_index:]
print(f"Plage temporelle de l’ensemble d’entraînement : les {split_index} premiers échantillons")
print(f"Plage temporelle de l’ensemble de test : les {len(X) - split_index} derniers échantillons")
Comment choisir le ratio de découpage ?
C’est une question courante, mais il n’y a pas de réponse fixe. Les ratios courants incluent :
| Ratio (ensemble d’entraînement:ensemble de test) | Scénario d'application | Avantages | Inconvénients |
|---|---|---|---|
| 70:30 | Choix classique pour les jeux de données de petite à moyenne taille (de milliers à dizaines de milliers d’échantillons) | Équilibre la taille des données d’entraînement et la fiabilité de l’évaluation | Pour les jeux de données extrêmement petits, un ensemble de test de 30% peut contenir trop peu d'échantillons, ce qui rend l'évaluation instable |
| 80:20 | Actuellement, un choix par défaut plus populaire, particulièrement adapté à l'apprentissage profond | Fournit plus de données pour que le modèle puisse apprendre | L'ensemble de test est relativement petit, donc la variance de l'évaluation peut être légèrement plus grande |
| 90:10 ou 95:5 | Lorsque la quantité de données est très limitée | Maximise l'utilisation des données limitées pour l'entraînement | L'ensemble de test est trop petit, donc les résultats de l'évaluation peuvent être peu fiables et avoir une faible confiance |
Principes fondamentaux:
- Veiller à ce que l'ensemble d'entraînement soit suffisamment grand: le modèle a besoin de suffisamment de données pour apprendre des motifs efficaces.
- Veiller à ce que l'ensemble de test soit suffisamment grand: l'ensemble de test doit fournir une évaluation des performances statistiquement fiable. Habituellement, l'ensemble de test devrait contenir au moins quelques centaines d'échantillons pour des résultats d'évaluation stables.
- Plus le volume de données est grand,plus la proportion allouée à l'ensemble de test peut être relativementplus petite, car même une très petite proportion peut représenter un grand nombre d'échantillons.
Concepts avancés : ensemble de validation et validation croisée
Dans les projets réels, nous devons non seulement évaluer le modèle final, mais aussi régler leshyperparamètresdu modèle (comme le taux d'apprentissage, la profondeur d'arbre, etc.) pendant l'entraînement. Si nous utilisons directement l'ensemble de test pour régler les paramètres, l'ensemble de test devient à nouveau "contaminé", perdant son impartialité en tant qu'"examinateur final".
À cette fin, nous introduisons l'ensemble de validation。
Division des données en trois ensembles : entraînement, validation et test
- Ensemble d'entraînement: utilisé pour apprendre les paramètres du modèle.
- Ensemble de validation: utilisé pour régler les hyperparamètres, sélectionner des modèles ou effectuer un arrêt précoce pendant l'entraînement. Il équivaut à un "examen blanc".
- Ensemble de test: utilisé pour l'évaluation finale et unique des performances, une fois le modèle et les hyperparamètres déterminés. C'est l'"examen final".
Exemple
X_temp, X_test, y_temp, y_test = train_test_split(X, y, test_size=0.15, random_state=42) # Réserver d'abord 15% comme ensemble de test final
X_train, X_val, y_train, y_val = train_test_split(X_temp, y_temp, test_size=0.176, random_state=42) # Ensuite, à partir des 85% restants, réserver environ 15% comme ensemble de validation
# Calculer le ratio : 0.85 * 0.176 ≈ 0.15, le ratio final est d'environ 70:15:15
print(f"Ensemble d'entraînement : {len(X_train)}, Ensemble de validation : {len(X_val)}, Ensemble de test : {len(X_test)}")
Validation croisée K-fold
Lorsque la quantité de données n'est pas importante, réserver un ensemble de validation distinct réduit encore les données d'entraînement.La validation croisée K-foldest une solution plus puissante.
Son processus est le suivant, et il permet d'utiliser efficacement des données limitées :
Exemple
A[Jeu de données d'origine] --> B[Mélanger aléatoirement et répartir équitablement en K parties]
B --> C{Répéter pour K tours}
C --> D[Tour i : Utiliser la i-ème partie comme ensemble de validation]
D --> E[Fusionner les K-1 parties restantes comme ensemble d'entraînement]
E --> F[Entraîner le modèle sur l'ensemble d'entraînement pour ce tour]
F --> G[Évaluer le score Si sur l'ensemble de validation pour ce tour]
G --> C
C -- Après l'achèvement des K tours --> H[Calculer la moyenne des K scores comme évaluation finale]
Exemple
from sklearn.model_selection import cross_val_score
from sklearn.linear_model import LogisticRegression
model = LogisticRegression()
scores = cross_val_score(model, X, y, cv=5) # cv=5 signifie une validation croisée à 5 volets
print(f"Scores pour chaque pli : {scores}")
print(f"Score moyen : {scores.mean():.4f} (+/- {scores.std()*2:.4f})") # Afficher la moyenne et l'écart-type
Avantages de la validation croisée:
- Utilise pleinement toutes les données pour l'entraînement et la validation.
- Les résultats de l'évaluation sont plus stables et fiables (car ils correspondent à la moyenne de plusieurs évaluations).
- C'est la référence absolue pour la sélection de modèle et le réglage des hyperparamètres sur des jeux de données de petite à moyenne taille.
Pratique : expérimentez vous-même la division des données
Maintenant, entraînons-nous avec un jeu de données simple.
Exemple
import numpy as np
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
# 2. Charger le jeu de données Iris
iris = load_iris()
X, y = iris.data, iris.target
print(f"Forme du jeu de données : caractéristiques {X.shape}, étiquettes {y.shape}")
# 3. Répartition aléatoire simple (80% d'entraînement, 20% de test)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
print(f"Répartition aléatoire -> Ensemble d'entraînement : {X_train.shape}, Ensemble de test : {X_test.shape}")
# 4. Répartition aléatoire stratifiée
X_train_s, X_test_s, y_train_s, y_test_s = train_test_split(X, y, test_size=0.2, stratify=y, random_state=42)
print(f"Répartition stratifiée -> Ensemble d'entraînement : {X_train_s.shape}, Ensemble de test : {X_test_s.shape}")
# 5. Vérifier l'effet de la stratification
print("\n"Distribution des classes des données d'origine :", np.bincount(y))
print("Distribution de l'ensemble de test après répartition aléatoire :", np.bincount(y_test)) # Peut être déséquilibrée
print("Distribution de l'ensemble de test après répartition stratifiée :", np.bincount(y_test_s)) # Devrait être proportionnelle à la distribution d'origine
Votre tâche:
- Exécutez le code ci-dessus et observez les résultats de sortie.
- Essayez de modifier
test_sizeà 0.3, et observez la variation des tailles des ensembles d'entraînement et de test. - Essayez de modifier
random_stateà un autre nombre (par ex., 7), exécutez à nouveau, et observez si les résultats de la séparation changent. - (Défi) Ne définissez pas
random_statele paramètre, exécutez le code plusieurs fois, et observez si les résultats de la séparation sont les mêmes à chaque fois.
Résumé et points clés
- Objectif principal: La séparation des ensembles d'entraînement et de test vise àévaluer la capacité de généralisation du modèle, prévenir le surapprentissage et garantir que le modèle peut traiter de nouvelles données.
- Règle d'or:L'ensemble de test doit être gardé totalement confidentiel tout au long du processus d'entraînement, et utilisé uniquement pour l'évaluation finale.
- Méthodes de séparation:
- Séparation aléatoire: La plus couramment utilisée.
- Fractionnement stratifié: Convient aux données déséquilibrées dans les problèmes de classification.
- Fractionnement séquentiel: Convient aux données de séries temporelles.
- Ratio de fractionnement: Il n'existe pas de norme absolue ; un compromis est nécessaire entre « un entraînement suffisant » et « une évaluation fiable ». 80:20 ou 70:30 sont des points de départ courants.
- Outils avancés:
- Ensemble de validation: Utilisé pour le réglage du modèle, protégeant la pureté de l'ensemble de test.
- Validation croisée K-fold: Un outil puissant pour l'évaluation et le réglage sur des jeux de données de petite à moyenne taille, produisant des résultats plus robustes.