Évaluation des modèles

CSI 4506 - Automne 2024

Marcel Turcotte

Version: sept. 30, 2024 08h54

Préambule

Citation du jour

Objectifs d’apprentissage

  • Clarifier les concepts de sous-apprentissage et de surapprentissage en apprentissage automatique.
  • Décrire les principales mesures utilisées pour évaluer la performance d’un modèle.
  • Contraster les mesures de performance moyennes pondérées (micro) et non pondérées (macro).

Ajustement du modèle

Ajustement du modèle

Pendant nos discussions en classe, nous avons abordé les concepts de sous-apprentissage et de surapprentissage. Pour approfondir ces sujets, examinons-les dans le contexte de la régression polynomiale.

from sklearn.linear_model import LinearRegression

lin_reg = LinearRegression()
lin_reg.fit(X, y)

Un ensemble de données non linéaire

import numpy as np
np.random.seed(42)

X = 6 * np.random.rand(100, 1) - 3
y = 0.5 * X ** 2 - X + 2 + np.random.randn(100, 1)

Régression linéaire

Un modèle linéaire représente mal ce jeu de données

Définition

L’ingénierie des attributs (feature engineering) est le processus de création, transformation et sélection de variables (attributs) à partir de données brutes afin d’améliorer la performance des modèles d’apprentissage automatique.

PolynomialFeatures

from sklearn.preprocessing import PolynomialFeatures

poly_features = PolynomialFeatures(degree=2, include_bias=False)
X_poly = poly_features.fit_transform(X)
X[0]
array([-0.75275929])
X_poly[0]
array([-0.75275929,  0.56664654])

Génère une nouvelle matrice d’attributs consistant en toutes les combinaisons polynomiales des attributs avec un degré inférieur ou égal au degré spécifié. Par exemple, si un échantillon d’entrée est bidimensionnel et de la forme \([a, b]\), les attributs polynomiales de degré 2 sont \([1, a, b, a^2, ab, b^2]\).

PolynomialFeatures

Avec deux attributs \(a\) et \(b\), PolynomialFeatures avec degree=3 ajouterait \(a^2\), \(a^3\), \(b^2\), \(b^3\), ainsi que \(ab\), \(a^2b\), \(ab^2\)!

Avertissement

PolynomialFeatures(degree=d) ajoute \(\frac{(D+d)!}{d!D!}\) attributs, où \(D\) est le nombre initial de attributs.

Régression polynomiale

LinearRegression sur PolynomialFeatures

lin_reg = LinearRegression()
lin_reg.fit(X_poly, y)

Régression polynomiale

Les données ont été générées selon l’équation suivante, avec l’inclusion de bruit gaussien.

\[ y = 0.5 x^2 + 1.0 x + 2.0 \]

Le modèle appris est présenté ci-dessous.

\[ \hat{y} = 0.56 x^2 + (-1.06) x + 1.78 \]

lin_reg.coef_, lin_reg.intercept_
(array([[-1.06633107,  0.56456263]]), array([1.78134581]))

Surapprentissage et sous-apprentissage

Une faible valeur de perte sur l’ensemble d’entraînement n’indique pas nécessairement un “meilleur” modèle.

Sous-apprentissage et surapprentissage

  • Sous-apprentissage (underfitting):
    • Votre modèle est trop simple (ici, linéaire).
    • attributs non informatives.
    • Mauvaises performances à la fois sur les données d’entraînement et sur les données de test.
  • Surapprentissage (overfitting):
    • Votre modèle est trop complexe (arbre de décision trop profond, réseaux neuronaux profonds et larges, etc.).
    • Trop de attributs par rapport au nombre d’exemples disponibles.
    • Excellentes performances sur l’ensemble d’entraînement, mais mauvaises performances sur l’ensemble de test.

Courbes d’apprentissage

  • Une manière d’évaluer nos modèles est de visualiser les courbes d’apprentissage :
    • Une courbe d’apprentissage montre la performance de notre modèle, ici en utilisant la RMSE, à la fois sur l’ensemble d’entraînement et l’ensemble de test.
    • Plusieurs mesures sont obtenues en formant à plusieurs reprises le modèle sur des sous-ensembles de données de plus en plus grands.

Sous-apprentissage

Mauvaise performance à la fois sur les données d’entraînement et les données de test.

Surapprentissage

Excellentes performances sur l’ensemble d’entraînement, mais mauvaises performances sur l’ensemble de test.

Surapprentissage - réseaux profonds

Surapprentissage - réseaux profonds

Compromis biais/variance

  • Biais :
    • Erreur provenant de modèles trop simples
    • Un biais élevé peut entraîner un sous-apprentissage
  • Variance :
    • Erreur provenant de modèles trop complexes
    • Sensibilité aux fluctuations dans les données d’entraînement
    • Une variance élevée peut entraîner un surapprentissage
  • Compromis :
    • Objectif : un modèle qui généralise bien à de nouvelles données
    • Méthodes : validation croisée, régularisation, apprentissage ensembliste

Vidéos connexes

Autres vidéos :

Mesures de performance

Matrice de confusion

Positif (Prédit) Négatif (Prédit)
Positif (Réel) Vrai positif (VP) Faux négatif (FN)
Négatif (Réel) Faux positif (FP) Vrai négatif (VN)

sklearn.metrics.confusion_matrix

from sklearn.metrics import confusion_matrix

y_actual = [0, 0, 0, 1, 1, 1, 1, 1, 1, 1]
y_pred   = [0, 1, 1, 0, 0, 0, 1, 1, 1, 1]

confusion_matrix(y_actual,y_pred)
array([[1, 2],
       [3, 4]])
tn, fp, fn, tp = confusion_matrix(y_actual, y_pred).ravel()
(tn, fp, fn, tp)
(1, 2, 3, 4)

Prédiction parfaite

y_actual = [0, 1, 0, 0, 1, 1, 1, 0, 1, 1]
y_pred   = [0, 1, 0, 0, 1, 1, 1, 0, 1, 1]

confusion_matrix(y_actual,y_pred)
array([[4, 0],
       [0, 6]])
tn, fp, fn, tp = confusion_matrix(y_actual, y_pred).ravel()    
(tn, fp, fn, tp)
(4, 0, 0, 6)

Matrice de confusion - plusieurs classes

Code source

import numpy as np
np.random.seed(42)

from sklearn.datasets import load_digits
digits = load_digits()

X = digits.data
y = digits.target

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.1)

from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)

from sklearn.linear_model import LogisticRegression
from sklearn.multiclass import OneVsRestClassifier

clf = OneVsRestClassifier(LogisticRegression())

clf = clf.fit(X_train, y_train)

import matplotlib.pyplot as plt
from sklearn.metrics import ConfusionMatrixDisplay

X_test = scaler.transform(X_test)
y_pred = clf.predict(X_test)

ConfusionMatrixDisplay.from_predictions(y_test, y_pred)

plt.show()

Visualisation des erreurs

mask = (y_test == 9) & (y_pred == 8)

X_9_as_8 = X_test[mask]

y_9_as_8 = y_test[mask]

Matrice de confusion - plusieurs classes

Exactitude

Quelle est l’exactitude de ce résultat ?

\[ \mathrm{exactitude} = \frac{\mathrm{VP}+\mathrm{VN}}{\mathrm{VP}+\mathrm{VN}+\mathrm{FP}+\mathrm{FN}} = \frac{\mathrm{VP}+\mathrm{VN}}{\mathrm{N}} \]

from sklearn.metrics import accuracy_score

y_actual = [0, 0, 0, 1, 1, 1, 1, 1, 1, 1]
y_pred   = [0, 1, 1, 0, 0, 0, 1, 1, 1, 1]

accuracy_score(y_actual,y_pred)
0.5

Exactitude

y_actual = [0, 1, 0, 0, 1, 1, 1, 0, 1, 1]
y_pred   = [1, 0, 1, 1, 0, 0, 0, 1, 0, 0]

accuracy_score(y_actual,y_pred)
0.0
y_actual = [0, 1, 0, 0, 1, 1, 1, 0, 1, 1]
y_pred   = [0, 1, 0, 0, 1, 1, 1, 0, 1, 1]

accuracy_score(y_actual,y_pred)
1.0

L’exactitude peut induire en erreur

y_actual = [0, 0, 0, 0, 1, 1, 0, 0, 0, 0]
y_pred   = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

accuracy_score(y_actual,y_pred)
0.8

Précision

Aussi appelée valeur prédictive positive (PPV).

\[ \mathrm{précision} = \frac{\mathrm{VP}}{\mathrm{VP}+\mathrm{FP}} \]

from sklearn.metrics import precision_score

y_actual = [0, 0, 0, 1, 1, 1, 1, 1, 1, 1]
y_pred   = [0, 1, 1, 0, 0, 0, 1, 1, 1, 1]

precision_score(y_actual, y_pred)
0.6666666666666666

La précision seule ne suffit pas

y_actual = [0, 0, 0, 1, 1, 1, 1, 1, 1, 1]
y_pred   = [0, 0, 0, 0, 0, 0, 1, 0, 0, 0]

precision_score(y_actual,y_pred)
1.0

Rappel

Aussi appelé sensibilité ou taux de vrais positifs (TVP). \[ \mathrm{rappel} = \frac{\mathrm{VP}}{\mathrm{VP}+\mathrm{FN}} \]

from sklearn.metrics import recall_score

y_actual = [0, 0, 0, 1, 1, 1, 1, 1, 1, 1]
y_pred   = [0, 1, 1, 0, 0, 0, 1, 1, 1, 1]

recall_score(y_actual,y_pred)
0.5714285714285714

Score F\(_1\)

\[ \begin{align*} F_1~\mathrm{score} &= \frac{2}{\frac{1}{\mathrm{précision}}+\frac{1}{\mathrm{rappel}}} = 2 \times \frac{\mathrm{précision}\times\mathrm{rappel}}{\mathrm{précision}+\mathrm{rappel}} \\ &= \frac{\mathrm{VP}}{\mathrm{FP}+\frac{\mathrm{FN}+\mathrm{FP}}{2}} \end{align*} \]

from sklearn.metrics import f1_score

y_actual = [0, 0, 0, 1, 1, 1, 1, 1, 1, 1]
y_pred   = [0, 1, 1, 0, 0, 0, 1, 1, 1, 1]

f1_score(y_actual,y_pred)
0.6153846153846154

Mesures de performance micro

Les mesures de performance micro agrègent les contributions de toutes les classes pour calculer la moyenne des mesures de performance, telles que la précision, le rappel ou le score F1. Cette approche considère chaque prédiction individuelle de manière égale, ce qui permet une évaluation équilibrée en mettant l’accent sur la performance des classes fréquentes.

Mesures de performance macro

Les mesures de performance macro calculent les mesures de performance indépendamment pour chaque classe et ensuite les moyennent. Cette approche considère chaque classe de manière égale, quelle que soit sa fréquence, fournissant une évaluation qui accorde la même importance aux performances sur les classes fréquentes et rares.

Précision micro/macro

from sklearn.metrics import ConfusionMatrixDisplay

# Données d'exemple
y_true = ['Chat'] * 42 + ['Chien'] *  7 + ['Renard'] * 11
y_pred = ['Chat'] * 39 + ['Chien'] *  1 + ['Renard'] *  2 + \
         ['Chat'] *  4 + ['Chien'] *  3 + ['Renard'] *  0 + \
         ['Chat'] *  5 + ['Chien'] *  1 + ['Renard'] *  5

ConfusionMatrixDisplay.from_predictions(y_true, y_pred)

Précision micro/macro

from sklearn.metrics import classification_report, precision_score

print(classification_report(y_true, y_pred), "\n")

print("Précision micro : {:.2f}".format(precision_score(y_true, y_pred, average='micro')))
print("Précision macro : {:.2f}".format(precision_score(y_true, y_pred, average='macro')))
              precision    recall  f1-score   support

        Chat       0.81      0.93      0.87        42
       Chien       0.60      0.43      0.50         7
      Renard       0.71      0.45      0.56        11

    accuracy                           0.78        60
   macro avg       0.71      0.60      0.64        60
weighted avg       0.77      0.78      0.77        60
 

Précision micro : 0.78
Précision macro : 0.71

La précision macro-pondérée est calculée comme la moyenne des scores de précision pour chaque classe : \(\frac{0.81 + 0.60 + 0.71}{3} = 0.71\).

Alors que la précision micro-pondérée est calculée en utilisant la formule \(\frac{VP}{VP+FP}\) et les données provenant de toute la matrice de confusion \(\frac{39+3+5}{39+3+5+9+2+2} = \frac{47}{60} = 0.78\)

Rappel micro/macro

              precision    recall  f1-score   support

        Chat       0.81      0.93      0.87        42
       Chien       0.60      0.43      0.50         7
      Renard       0.71      0.45      0.56        11

    accuracy                           0.78        60
   macro avg       0.71      0.60      0.64        60
weighted avg       0.77      0.78      0.77        60
 

Rappel micro : 0.78
Rappel macro : 0.60

Le rappel macro-moyen est calculé comme la moyenne des scores de rappel pour chaque classe : \(\frac{0.93 + 0.43 + 0.45}{3} = 0.60\).

Alors que le rappel micro-moyen est calculé en utilisant la formule \(\frac{VP}{VP+FN}\) et les données provenant de toute la matrice de confusion \(\frac{39+3+5}{39+3+5+3+4+6} = \frac{47}{60} = 0.78\)

Mesures micro/macro (données médicales)

Mesures micro/macro (données médicales)

              precision    recall  f1-score   support

      Normal       1.00      0.99      1.00       990
      Tumeur       0.55      0.60      0.57        10

    accuracy                           0.99      1000
   macro avg       0.77      0.80      0.78      1000
weighted avg       0.99      0.99      0.99      1000
 

Précision micro : 0.99
Précision macro : 0.77


Rappel micro : 0.99
Rappel macro : 0.80

Chiffres manuscrits (réexaminés)

Chargement du jeu de données

import numpy as np
np.random.seed(42)

from sklearn.datasets import fetch_openml

digits = fetch_openml('mnist_784', as_frame=False)
X, y = digits.data, digits.target

Affichage des cinq premiers exemples

Ces images ont des dimensions de ( 28 ) pixels.

Création d’une tâche de classification binaire

# Création d'une tâche de classification binaire (un contre le reste)

some_digit = X[0]
some_digit_y = y[0]

y = (y == some_digit_y)
y
array([ True, False, False, ..., False,  True, False])
# Création des ensembles d'entraînement et de test
from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.1)

SGDClassifier

from sklearn.linear_model import SGDClassifier

clf = SGDClassifier()
clf.fit(X_train, y_train)

clf.predict(X[0:5]) # petite vérification de cohérence
array([ True, False, False, False, False])

Performance

from sklearn.metrics import accuracy_score

y_pred = clf.predict(X_test)

accuracy_score(y_test, y_pred)
0.9572857142857143

Wow !

Pas si vite

y_pred = dummy_clf.predict(X_test)

accuracy_score(y_test, y_pred)
0.906

Compromis précision-rappel

Compromis précision-rappel

Courbe précision/rappel

Courbe ROC

Receiver Operating Characteristics (ROC) curve

  • Taux de vrais positifs (TVP) contre taux de faux positifs (TFP)
  • Un classificateur idéal a un TVP proche de 1.0 et un TFP proche de 0.0
  • \(\mathrm{TVP} = \frac{\mathrm{VP}}{\mathrm{VP}+\mathrm{FN}}\) (rappel, sensibilité)
  • TVP approche de 1 lorsque le nombre de faux négatifs est faible
  • \(\mathrm{TFP} = \frac{\mathrm{FP}}{\mathrm{FP}+\mathrm{VN}}\) (aussi appelé~[1-spécificité])
  • TFP approche de 0 lorsque le nombre de faux positifs est faible

Courbe ROC

AUC/ROC

Les 7 étapes de l’apprentissage automatique

Prologue

Lectures complémentaires

Prochain cours

  • Nous examinerons la validation croisée et le réglage des hyperparamètres.

Références

Chollet, François. 2017. Deep learning with Python. Manning Publications.
Géron, Aurélien. 2022. Hands-on Machine Learning with Scikit-Learn, Keras, and TensorFlow. 3ᵉ éd. O’Reilly Media, Inc.
Hastie, Trevor, Robert Tibshirani, et Jerome H. Friedman. 2009. The Elements of Statistical Learning: Data Mining, Inference, and Prediction, 2nd Edition. Springer Series in Statistics. Springer. https://doi.org/10.1007/978-0-387-84858-7.
Japkowicz, Nathalie, et Mohak Shah. 2011. Evaluating Learning Algorithms: a classification perspective. Cambridge: Cambridge University Press. http://assets.cambridge.org/97805211/96000/cover/9780521196000.jpg.
Russell, Stuart, et Peter Norvig. 2020. Artificial Intelligence: A Modern Approach. 4ᵉ éd. Pearson. http://aima.cs.berkeley.edu/.

Marcel Turcotte

[email protected]

École de science informatique et de génie électrique (SIGE)

Université d’Ottawa