Aller au contenu principal

Concepts d'entrainement et d'evaluation de modeles

Theorie 45 min Module 2

Introduction

Avant de deployer un modele IA, vous devez l'entrainer correctement et evaluer ses performances de maniere rigoureuse. Un modele qui performe bien sur les donnees d'entrainement mais echoue en production est pire que pas de modele du tout — il donne une fausse confiance.

Analogie du monde reel

Imaginez un etudiant qui memorise toutes les reponses d'un examen passe sans comprendre les concepts. Il obtiendra 100% a cet examen specifique, mais echouera face a de nouvelles questions. C'est exactement ce qui arrive a un modele qui fait du surapprentissage (overfitting) sur ses donnees d'entrainement.


1. Le pipeline d'entrainement ML

Le pipeline d'entrainement est la sequence structuree d'etapes qui transforme des donnees brutes en un modele deployable.

Voir le pipeline d'entrainement ML

Chaque etape de ce pipeline est critique. Sauter ou brusquer une etape peut mener a des modeles qui semblent bons sur papier mais echouent en production.


2. Decoupage des donnees : Train / Validation / Test

Pourquoi decouper les donnees ?

Nous decoupons les donnees en ensembles separes pour obtenir une estimation honnete de la performance de notre modele sur des donnees qu'il n'a jamais vues.

Voir la strategie de decoupage des donnees
EnsembleObjectifUtilisationTaille typique
EntrainementApprendre les patterns a partir des featuresUtilise pendant model.fit()60-70%
ValidationAjuster les hyperparametres, selectionner le meilleur modeleUtilise pendant la selection du modele15-20%
TestEvaluation finale sans biaisUtilise une seule fois a la fin15-20%
Regle d'or

L'ensemble de test ne doit jamais etre utilise pour prendre des decisions pendant le developpement. Il n'est utilise que pour l'evaluation finale. Si vous regardez la performance sur le test et revenez modifier votre modele, vous avez contamine votre evaluation.

Exemple de code : Decoupage des donnees

from sklearn.model_selection import train_test_split

# Premier decoupage : separer l'ensemble de test
X_temp, X_test, y_temp, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)

# Deuxieme decoupage : entrainement et validation
X_train, X_val, y_train, y_val = train_test_split(
X_temp, y_temp, test_size=0.25, random_state=42, stratify=y_temp
)

# Resultat : 60% train, 20% validation, 20% test
print(f"Training: {len(X_train)} samples ({len(X_train)/len(X)*100:.0f}%)")
print(f"Validation: {len(X_val)} samples ({len(X_val)/len(X)*100:.0f}%)")
print(f"Test: {len(X_test)} samples ({len(X_test)/len(X)*100:.0f}%)")
Stratification

Le parametre stratify=y assure que chaque sous-ensemble a les memes proportions de classes que le jeu de donnees original. C'est essentiel pour les jeux de donnees desequilibres (ex. : 95% classe A, 5% classe B).


3. Validation croisee

Le probleme d'un decoupage unique

Un seul decoupage train/validation peut etre chanceux ou malchanceux. Peut-etre que tous les exemples "faciles" se sont retrouves dans l'ensemble d'entrainement. La validation croisee resout cela en testant plusieurs decoupages.

Analogie

Imaginez evaluer un restaurant en ne mangeant qu'un seul plat un seul jour. Peut-etre que le chef etait malade ce jour-la, ou au contraire c'etait son meilleur plat. Il vaut mieux revenir k fois et essayer differents plats pour une evaluation fiable.

Validation croisee K-Fold

TypeDescriptionQuand l'utiliser
K-FoldDecoupe en K folds, chacun servant de test a tour de roleUsage general, K=5 ou K=10
K-Fold stratifieK-Fold preservant les proportions de classesClassification desequilibree
Leave-One-Out (LOO)K = nombre d'echantillonsTres petits jeux de donnees (< 100)
K-Fold repeteRepete K-Fold plusieurs fois avec des graines differentesEstimation tres robuste
Time Series SplitRespecte l'ordre temporel des donneesDonnees sequentielles / series temporelles

Exemple de code : Validation croisee

from sklearn.model_selection import cross_val_score, StratifiedKFold
from sklearn.ensemble import RandomForestClassifier

model = RandomForestClassifier(n_estimators=100, random_state=42)

# Validation croisee stratifiee 5-Fold
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
scores = cross_val_score(model, X_train, y_train, cv=cv, scoring='accuracy')

print(f"Scores par fold : {scores}")
print(f"Precision moyenne : {scores.mean():.4f} ± {scores.std():.4f}")
# Plusieurs metriques a la fois
from sklearn.model_selection import cross_validate

results = cross_validate(
model, X_train, y_train, cv=cv,
scoring=['accuracy', 'f1_weighted', 'precision_weighted', 'recall_weighted'],
return_train_score=True
)

for metric in ['accuracy', 'f1_weighted']:
train_score = results[f'train_{metric}'].mean()
test_score = results[f'test_{metric}'].mean()
print(f"{metric}: Train={train_score:.4f}, Val={test_score:.4f}")

4. Ajustement des hyperparametres

Les hyperparametres sont des reglages que vous choisissez avant l'entrainement (contrairement aux parametres du modele, qui sont appris pendant l'entrainement).

ParametreHyperparametreAppris pendant l'entrainement ?
Poids du modele✅ Oui
Taux d'apprentissage❌ Non (vous le choisissez)
Nombre d'arbres (n_estimators)❌ Non
Profondeur maximale (max_depth)❌ Non
Regularisation (C, alpha)❌ Non

Teste toutes les combinaisons possibles. Exhaustif mais couteux.

from sklearn.model_selection import GridSearchCV

param_grid = {
'n_estimators': [50, 100, 200],
'max_depth': [3, 5, 10, None],
'min_samples_split': [2, 5, 10],
'min_samples_leaf': [1, 2, 4]
}
# Total : 3 × 4 × 3 × 3 = 108 combinaisons × 5 folds = 540 ajustements

grid_search = GridSearchCV(
RandomForestClassifier(random_state=42),
param_grid,
cv=5,
scoring='f1_weighted',
n_jobs=-1,
verbose=1
)
grid_search.fit(X_train, y_train)

print(f"Best params: {grid_search.best_params_}")
print(f"Best score: {grid_search.best_score_:.4f}")
best_model = grid_search.best_estimator_

Echantillonne aleatoirement l'espace de recherche. Plus efficace quand l'espace est grand.

from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import randint, uniform

param_distributions = {
'n_estimators': randint(50, 500),
'max_depth': [3, 5, 10, 20, None],
'min_samples_split': randint(2, 20),
'min_samples_leaf': randint(1, 10),
'max_features': uniform(0.1, 0.9)
}

random_search = RandomizedSearchCV(
RandomForestClassifier(random_state=42),
param_distributions,
n_iter=50, # seulement 50 combinaisons aleatoires (vs 108+ pour grid)
cv=5,
scoring='f1_weighted',
n_jobs=-1,
random_state=42
)
random_search.fit(X_train, y_train)

print(f"Best params: {random_search.best_params_}")
print(f"Best score: {random_search.best_score_:.4f}")
MethodeAvantagesInconvenientsQuand l'utiliser
Grid SearchExhaustif, garanti de trouver le meilleurTres lent (exponentiel)Petit espace de recherche
Random SearchPlus rapide, explore mieux l'espacePas de garantie d'optimaliteGrand espace de recherche
Optimisation bayesienneIntelligent, converge rapidementPlus complexe a implementerModeles couteux a entrainer

5. Metriques d'evaluation

Metriques de classification

La matrice de confusion

La matrice de confusion est la base de toutes les metriques de classification.

Analogie : Le detecteur de fumee
  • Vrai Positif : Il y a un incendie, l'alarme sonne ✅
  • Faux Positif : Pas d'incendie, l'alarme sonne quand meme (toast brule) 🚨
  • Faux Negatif : Il y a un incendie, mais l'alarme ne sonne pas 💀
  • Vrai Negatif : Pas d'incendie, l'alarme reste silencieuse ✅

Un FN (manquer un vrai incendie) est beaucoup plus grave qu'un FP (fausse alarme). Le choix de la metrique depend du cout des erreurs.

Metriques derivees de la matrice de confusion

MetriqueFormuleQuestion a laquelle elle repond
Exactitude (Accuracy)(VP + VN) / TotalQuelle fraction des predictions est correcte ?
PrecisionVP / (VP + FP)Parmi les predictions positives, combien sont vraies ?
Rappel (Sensibilite)VP / (VP + FN)Parmi les vrais positifs, combien ont ete detectes ?
Score F12 × (Precision × Rappel) / (Precision + Rappel)Moyenne harmonique de la Precision et du Rappel
SpecificiteVN / (VN + FP)Parmi les vrais negatifs, combien ont ete identifies ?
from sklearn.metrics import (
accuracy_score, precision_score, recall_score,
f1_score, confusion_matrix, classification_report
)

y_pred = model.predict(X_test)

print(f"Accuracy: {accuracy_score(y_test, y_pred):.4f}")
print(f"Precision: {precision_score(y_test, y_pred, average='weighted'):.4f}")
print(f"Recall: {recall_score(y_test, y_pred, average='weighted'):.4f}")
print(f"F1-Score: {f1_score(y_test, y_pred, average='weighted'):.4f}")

print("\nClassification Report:")
print(classification_report(y_test, y_pred))
import matplotlib.pyplot as plt
from sklearn.metrics import ConfusionMatrixDisplay

fig, ax = plt.subplots(figsize=(8, 6))
ConfusionMatrixDisplay.from_estimator(model, X_test, y_test, ax=ax, cmap='Purples')
ax.set_title("Confusion Matrix")
plt.tight_layout()
plt.savefig("confusion_matrix.png", dpi=150)
plt.show()

Courbe AUC-ROC

La courbe ROC (Receiver Operating Characteristic) trace le taux de vrais positifs vs le taux de faux positifs a differents seuils de classification. L'AUC (Area Under the Curve) resume cela en un seul chiffre.

Valeur AUCInterpretation
1.0Classificateur parfait
0.9 - 1.0Excellent
0.8 - 0.9Bon
0.7 - 0.8Acceptable
0.5Aleatoire (aucune competence)
< 0.5Pire qu'aleatoire
from sklearn.metrics import roc_curve, roc_auc_score

# Pour la classification binaire
y_proba = model.predict_proba(X_test)[:, 1]
fpr, tpr, thresholds = roc_curve(y_test, y_proba)
auc_score = roc_auc_score(y_test, y_proba)

plt.figure(figsize=(8, 6))
plt.plot(fpr, tpr, color='#7c3aed', lw=2, label=f'ROC Curve (AUC = {auc_score:.4f})')
plt.plot([0, 1], [0, 1], 'k--', lw=1, label='Random Classifier')
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('ROC Curve')
plt.legend()
plt.grid(alpha=0.3)
plt.tight_layout()
plt.show()

Quand utiliser quelle metrique ?

ScenarioMetrique prioritairePourquoi
Detection de spamPrecisionEviter de marquer des emails legitimes comme spam (minimiser les FP)
Depistage du cancerRappelNe pas manquer de vrais cancers (minimiser les FN)
Classes equilibreesExactitude ou F1Les deux types d'erreur sont egalement couteux
Classes desequilibreesF1, AUC-ROCL'exactitude est trompeuse avec un desequilibre de classes
Classement des predictionsAUC-ROCMesure la qualite du classement a travers les seuils
Le piege de l'exactitude

Sur un jeu de donnees avec 95% de classe A et 5% de classe B, un modele qui predit toujours A aura 95% d'exactitude. C'est pourquoi l'exactitude seule est insuffisante pour les jeux de donnees desequilibres. Utilisez toujours F1, Precision, Rappel et AUC-ROC en complement.

Metriques de regression

MetriqueFormuleInterpretation
MSE (Mean Squared Error)Σ(y - ŷ)² / nPenalise fortement les grandes erreurs
RMSE (Root MSE)√MSEMeme unite que la variable cible
MAE (Mean Absolute Error)Σ|y - ŷ| / nErreur moyenne en valeur absolue
(Coefficient de determination)1 - (SS_res / SS_tot)Proportion de variance expliquee (0 a 1)
MAPEΣ|y - ŷ|/|y| × 100 / nErreur en pourcentage
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
import numpy as np

y_pred = model.predict(X_test)

mse = mean_squared_error(y_test, y_pred)
rmse = np.sqrt(mse)
mae = mean_absolute_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)

print(f"MSE: {mse:.4f}")
print(f"RMSE: {rmse:.4f}")
print(f"MAE: {mae:.4f}")
print(f"R²: {r2:.4f}")

6. Surapprentissage vs Sous-apprentissage

Voir la comparaison biais-variance
CaracteristiqueSous-apprentissageBon ajustementSurapprentissage
Exactitude train❌ Faible✅ Elevee✅ Tres elevee
Exactitude test❌ Faible✅ Elevee❌ Faible
Complexite du modeleTrop simpleJuste bienTrop complexe
BiaisEleveFaibleFaible
VarianceFaibleFaibleElevee

Comment detecter

# Entrainer et evaluer sur les deux ensembles
model.fit(X_train, y_train)
train_score = model.score(X_train, y_train)
test_score = model.score(X_test, y_test)

gap = train_score - test_score
print(f"Train: {train_score:.4f}")
print(f"Test: {test_score:.4f}")
print(f"Gap: {gap:.4f}")

if train_score < 0.7 and test_score < 0.7:
print("⚠️ Underfitting: model too simple")
elif gap > 0.10:
print("⚠️ Overfitting: model too complex")
else:
print("✅ Good generalization")

Remedes

ProblemeSolutions
Sous-apprentissageAugmenter la complexite du modele, ajouter plus de features, reduire la regularisation, entrainer plus longtemps
SurapprentissageAjouter plus de donnees, augmenter la regularisation, reduire les features, utiliser le dropout, arret precoce, validation croisee

7. Compromis biais-variance

Le compromis biais-variance est un concept fondamental en machine learning qui decrit la tension entre deux sources d'erreur.

Analogie : Le tireur sportif
  • Biais eleve : Le tireur vise systematiquement trop a gauche (erreur systematique)
  • Variance elevee : Les tirs sont disperses partout autour de la cible (instabilite)
  • Objectif : Tirs groupes au centre (biais faible + variance faible)
Variance faibleVariance elevee
Biais faible✅ Ideal (generalisation)⚠️ Surapprentissage
Biais eleve⚠️ Sous-apprentissage❌ Pire cas

8. Courbes d'apprentissage

Les courbes d'apprentissage tracent la performance du modele en fonction de la taille de l'ensemble d'entrainement ou du nombre d'iterations. C'est l'outil de diagnostic visuel le plus puissant pour detecter le surapprentissage et le sous-apprentissage.

from sklearn.model_selection import learning_curve
import numpy as np
import matplotlib.pyplot as plt

train_sizes, train_scores, val_scores = learning_curve(
RandomForestClassifier(n_estimators=100, random_state=42),
X_train, y_train,
cv=5,
train_sizes=np.linspace(0.1, 1.0, 10),
scoring='accuracy',
n_jobs=-1
)

train_mean = train_scores.mean(axis=1)
train_std = train_scores.std(axis=1)
val_mean = val_scores.mean(axis=1)
val_std = val_scores.std(axis=1)

plt.figure(figsize=(10, 6))
plt.fill_between(train_sizes, train_mean - train_std, train_mean + train_std, alpha=0.1, color='#7c3aed')
plt.fill_between(train_sizes, val_mean - val_std, val_mean + val_std, alpha=0.1, color='#f59e0b')
plt.plot(train_sizes, train_mean, 'o-', color='#7c3aed', label='Training Score')
plt.plot(train_sizes, val_mean, 's-', color='#f59e0b', label='Validation Score')
plt.xlabel('Training Set Size')
plt.ylabel('Accuracy')
plt.title('Learning Curve')
plt.legend(loc='lower right')
plt.grid(alpha=0.3)
plt.tight_layout()
plt.show()

Interpreter les courbes d'apprentissage

PatternDiagnosticAction
Les deux courbes basses et convergentesSous-apprentissageUtiliser un modele plus complexe ou plus de features
Train elevee, validation basse, ecart persistantSurapprentissageAjouter plus de donnees, regulariser, simplifier le modele
Les deux courbes elevees et convergentesBon ajustementLe modele est pret pour le deploiement
La courbe de validation monte encore a la finPlus de donnees necessairesCollecter plus de donnees d'entrainement

9. Tout assembler

Voici un exemple complet combinant tous les concepts :

import numpy as np
import pandas as pd
from sklearn.datasets import load_iris
from sklearn.model_selection import (
train_test_split, StratifiedKFold, GridSearchCV
)
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report, accuracy_score

# 1. Load data
data = load_iris()
X, y = data.data, data.target

# 2. Split: 60% train, 20% val, 20% test
X_temp, X_test, y_temp, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
X_train, X_val, y_train, y_val = train_test_split(
X_temp, y_temp, test_size=0.25, random_state=42, stratify=y_temp
)

# 3. Build a pipeline (preprocessing + model)
pipeline = Pipeline([
('scaler', StandardScaler()),
('clf', RandomForestClassifier(random_state=42))
])

# 4. Hyperparameter tuning with cross-validation
param_grid = {
'clf__n_estimators': [50, 100, 200],
'clf__max_depth': [3, 5, None],
}
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)

grid_search = GridSearchCV(
pipeline, param_grid, cv=cv,
scoring='f1_weighted', n_jobs=-1
)
grid_search.fit(X_train, y_train)

print(f"Best hyperparameters: {grid_search.best_params_}")
print(f"Best CV F1-score: {grid_search.best_score_:.4f}")

# 5. Validate on validation set
best_model = grid_search.best_estimator_
val_pred = best_model.predict(X_val)
print(f"\nValidation Accuracy: {accuracy_score(y_val, val_pred):.4f}")

# 6. Final evaluation on test set
test_pred = best_model.predict(X_test)
print(f"\nTest Set Results:")
print(classification_report(y_test, test_pred, target_names=data.target_names))

Resume

🔑 Points cles a retenir
  1. Decoupage des donnees : Toujours decouper en train/validation/test. L'ensemble de test n'est que pour l'evaluation finale.
  2. Validation croisee : Utiliser K-Fold (K=5 ou 10) pour des estimations de performance robustes.
  3. Ajustement des hyperparametres : GridSearch pour les petits espaces, RandomSearch pour les grands espaces.
  4. Metriques : Choisir la metrique en fonction du cout des erreurs (Precision vs Rappel).
  5. Matrice de confusion : Base de toutes les metriques de classification. Apprenez a la lire.
  6. Surapprentissage : Performance d'entrainement elevee, performance de test faible → modele trop complexe.
  7. Sous-apprentissage : Performance faible partout → modele trop simple.
  8. Biais-Variance : L'objectif est de minimiser les deux simultanement.
  9. Courbes d'apprentissage : Outil visuel essentiel pour diagnostiquer les problemes.

Lectures complementaires

RessourceLien
Guide de selection de modele scikit-learnsklearn.model_selection
Validation croisee : evaluer la performance des estimateurssklearn Cross-Validation
Metriques et scoringsklearn Metrics
Comprendre le compromis biais-varianceScott Fortmann-Roe