• 10 hours
  • Medium

Free online content available in this course.

course.header.alt.is_certifying

Got it!

Last updated on 4/23/24

Améliorez le modèle

Dans ce chapitre nous allons plonger dans la recherche des meilleurs paramètres d'un modèle, dans le but d'accroître ses performances.

On a beaucoup parlé jusqu'à maintenant d'optimisation des modèles sans montrer en quoi cela consistait exactement. En effet, les modèles de régression linéaire et logistique n'ont pas de paramètres. Dans une régression linéaire tout dépend des prédicteurs utilisés. Il est donc impossible de jouer sur leurs paramètres pour améliorer leur performance.

Reconnaissez un modèle qui sous-performe

Nous avons vu tout au début de ce cours, au chapitre 2, partie 1, que pour évaluer les performances d'un modèle, on scinde le dataset en 2 sous-ensembles (entraînement et test) et que l'on évalue la performance d'un modèle par son score sur le sous-ensemble de test.

Dans la suite, on note  score(train)  et  score(test)  les scores du modèle sur les données d'entraînement et de test.

Le calcul du score sur les données d'entraînement et de test vont nous permettre de distinguer un bon modèle d'un mauvais modèle.

  1. Le modèle ne comprend rien aux données.
    Son  score(train)  est faible, il n'arrive simplement pas à comprendre les données d'entraînement. Conséquence directe, les données de test seront aussi incomprises, donc le  score(test)  sera faible. On parle dans ce cas de biais du modèle.

  2. Le modèle est bon et sait généraliser.
    Les  score(train)  et  score(test)  sont satisfaisants. Youhou ! Your job is done. Prochaine étape : la production.

  3. Le modèle ne sait pas généraliser.
    Le  score(train)  est bon mais le  score(test)  est faible. Le modèle colle trop aux données d'apprentissage et ne sait pas extrapoler aux données qu'il n'a pas rencontrées.

On peut illustrer ces 3 cas par la figure suivante :

Représentation graphique des 3 cas : biais, bon modèle et overfit

(Il existe encore un dernier cas : bon score(test) mais mauvais score(train). Il reflète la plupart du temps une anomalie statistique et arrive très rarement. On ne le prend pas en compte.)

Nous allons illustrer tout cela ainsi que les remèdes potentiels pour pallier les faiblesses du modèle en biais ou en overfit.

Mais avant, fourbissons nos armes. Il nous faut un dataset et un modèle qui dépendent de paramètres.

Nous allons construire un modèle qui prédit le stade de développement de l'arbre.

Un cas de classification a 4 catégories que nous avons déjà vues :

  1. Jeune (arbre) 

  2. Jeune (arbre)Adulte 

  3. Adulte

  4. Mature

Dans le dataset, ces catégories sont numérisées en 1, 2, 3 et 4.

Pourquoi prendre un arbre de décision ?

Simplement parce que ce type de modèle se prête parfaitement à la démonstration d'overfitting et de biais. Les modèles linéaires de régression sont incapables d'overfitter autrement que sur des jeux de données de démonstrations, et le k-means serait peu réaliste dans notre contexte.

Comprenez la notion d’arbre de décision

Un arbre de décision est un enchaînement de règles de classification établies automatiquement à partir des variables prédictrices.

On peut représenter un arbre graphiquement comme un arbre renversé. Pour l'instant nous nous contenterons de cette introduction simple.

Voici un exemple :

Il a des plumes ? Si vrai, peut-il voler ? Si vrai, alors Faucon, si faux,  alors Pingouin. Il a des plumes ? Si faux, a des nageoires ? Si vrai, alors Dauphin, si faux, alors Ours.
Arbre de décision simple à 2 niveaux de profondeur

Dans le Machine Learning, les règles de bifurcation se feront à partir de critères statistiques sur les variables.

Par exemple :

si [hauteur_m > 10 & libelle_francais = 'Platane' ]
Alors stade de développement = Mature
Sinon [autre règle]

L'algorithme d'entraînement détermine les critères des différentes règles (variables concernées, seuils, valeurs catégoriques) qui sont les plus efficaces pour faire aboutir la tâche de classification. L'algorithme répond à un critère prédéfini pour établir la règle de bifurcation.

Une caractéristique saute aux yeux, c'est le nombre de nœuds de l'arbre, autrement dit sa profondeur.

Exemple d'arbre de décision de faible profondeur sur le dataset Iris.
Arbre de décision de faible profondeur sur le dataset Iris

Et voici un arbre de décision profond :

Exemple d'arbre de décision profond sur le dataset Iris.
Arbre de décision profond sur le dataset Iris

Minimisez le biais du modèle

Chargeons les données :

filename = 'https://raw.githubusercontent.com/OpenClassrooms-Student-Center/8063076-Initiez-vous-au-Machine-Learning/master/data/paris-arbres-numerical-2023-09-10.csv'
data = pd.read_csv(filename)
X = data[['domanialite', 'arrondissement', 'libelle_francais', 'genre', 'espece', 'circonference_cm', 'hauteur_m']]
y = data.stade_de_developpement.values

Scindons les données en  train  et  test  :

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split( X, y, train_size=0.8, random_state=808)

Importons le classificateur DecisionTreeClassifier :

from sklearn.tree import DecisionTreeClassifier

Limitons la profondeur de l'arbre à 3 et entraînons le modèle :

clf = DecisionTreeClassifier(
        max_depth = 3,
    random_state = 808
)
clf.fit(X_train, y_train)

Pour le score, on utilise l'AUC :

from sklearn.metrics import roc_auc_score
train_auc = roc_auc_score(y_train, clf.predict_proba(X_train), multi_class='ovr')
test_auc = roc_auc_score(y_test, clf.predict_proba(X_test), multi_class='ovr')
print("train",train_auc)
print("test", test_auc)

train 0.89
test 0.89

Voici la matrice de confusion :

from sklearn.metrics import confusion_matrix
y_train_hat = clf.predict(X_train)
y_test_hat = clf.predict(X_test)
print(confusion_matrix(y_test, y_test_hat))

[[  5570  1402   300    2]
 [  1060  3847  2750    8]
 [   211  1481 13328  466]
 [     4     7   565  877]]

ainsi que le rapport de classification qui donne plusieurs scores :

from sklearn.metrics import classification_report
print(classification_report(y_test, y_test_hat))

      precision recall  f1-score   support

1       0.81    0.77    0.79        7274
2       0.57    0.50    0.53        7665
3       0.79    0.86    0.82       15486
4       0.65    0.60    0.63        1453

accuracy                            0.74 31878
macro avg       0.70    0.68        0.69 31878
weighted avg    0.73    0.74        0.74 31878

On voit que la  precision  (le ratio de bonne pioche parmi tous les positifs) est faible pour les catégories  Jeune (arbre)Adulte  et  Mature  (respectivement 0,57 et 0,67) et nous observons un recall de 0,5 pour la catégorie  Jeune (arbre)  aussi très faible.

Donc ce modèle a besoin de tendresse.
C'est un bon exemple d'un modèle biaisé.

Notez cependant que le score(test) et le score(train) sont presque égaux :

clf.score(X_train, y_train)
0.731
clf.score(X_test, y_test)
0.74

Quels remèdes pour minimiser le biais du modèle ?

Plusieurs stratégies sont envisageables :

  • Ajouter des données. Sur un dataset trop petit, le modèle n'aura pas assez d'exemples pour assimiler les dynamiques internes. Ajouter des données pourra l'aider. On peut soit collecter plus de données et les ajouter au dataset, soit utiliser des techniques d'augmentation de données qui créent des échantillons artificiels et gonflent donc artificiellement le dataset d'entraînement.

  • Le fameux feature engineering où l'on va s'efforcer de transformer ou d'ajouter des variables pour encoder plus d'informations exploitables par le modèle.

  • Modifier les paramètres du modèle pour améliorer sa performance. C'est ce que nous allons faire maintenant.

Identifiez et compensez l'overfit

Reprenons maintenant notre arbre de décision, mais cette fois sans limiter sa profondeur.

Pour cela on fixe la valeur  max_depth = None  .

clf = DecisionTreeClassifier(
    max_depth = None,
    random_state = 808
)
clf.fit(X_train, y_train)
clf.score(X_test, y_test)

0.81

Le modèle est en effet meilleur : on passe de 0,74 à 0,81. Mais on remarque que sur le sous-ensemble d'entraînement on a carrément 0,94 !

clf.score(X_train, y_train)
0.935048231511254

Nous sommes bien dans un cas d'overfitting où :

  • le modèle colle aux données d'entraînement (le score(train) est excellent) ;

  • et ne sait pas reproduire la même performance sur les données de test (le score(test) est faible).

Donc à un moment, entre  maxdepth = 3  et  max_depth = infini  , les score(train) et score(test) ont divergé. Il nous faut trouver un juste milieu pour ce paramètre.

Faisons croître  max_depth  en enregistrant les scores sur train et test.

L'AUC est plus parlante que l'accuracy pour cette démonstration :

scores = []
for depth in np.arange(2, 30, 2):
    clf = DecisionTreeClassifier(
        max_depth = depth,
        random_state = 808
    )

    clf.fit(X_train, y_train)

    train_auc = roc_auc_score(y_train, clf.predict_proba(X_train), multi_class='ovr')
    test_auc = roc_auc_score(y_test, clf.predict_proba(X_test), multi_class='ovr')
    scores.append({
        'max_depth': depth,
        'train': train_auc,
        'test': test_auc,
    })

scores = pd.DataFrame(scores)

On obtient la figure ci-dessous, avec  max_depth  en abscisse et  AUC  en ordonnée :

Tracé des AUC(train) et AUC(test) pour des valeurs de max_depth de 2 à 28. On observe une divergence prononcée des scores pour des grandes valeurs de max_depth.

Quand on augmente  max_depth  , l'AUC(train) croît jusqu'à presque atteindre un score parfait de 1. Ce score atteint ensuite un plateau. Augmenter la profondeur de l'arbre ne sert plus à rien à partir de max_depth = 20 .

L'AUC(test) par contre, croît jusqu'à atteindre un maximum autour de  0.94  (  max_depth = 9  ). Elle décroît ensuite jusqu'à  0.87  pour rejoindre elle aussi un plateau vers  max_depth = 20  .

On observe bien les 3 cas de comportement du modèle :

  • à gauche, le modèle sous-performe (biais) ;

  • à droite, il overfit ;

  • au milieu, on obtient la meilleure performance sur le test set.

On a ainsi trouvé la valeur optimale du paramètre  max_depth  pour ce modèle et ce dataset donnés, soit  9  ou  10  .

Comment remédier à l'overfit en général ?

Un modèle qui overfit est un modèle trop complexe. Dans notre contexte, la complexité se traduit par la profondeur de l'arbre. Dans le cas d'une régression, la complexité serait traduite par trop de variables prédictives et pour le k-means(), trop de clusters.

La première stratégie de remédiation est d'augmenter la taille du dataset d'entraînement. En effet, plus il y aura d'échantillons, plus la complexité du modèle sera diluée dans la masse d'informations. Mais cela n'est pas toujours possible. Ce qui nous amène au concept de régularisation que l’on verra dans le chapitre suivant.

En résumé

  • En comparant les scores de performance sur les sous-ensembles d'entraînement et de test, on peut distinguer deux cas de sous-performance du modèle :

    • le sous-apprentissage, ou biais, aboutit à un score faible sur les 2 sous-ensembles. L'ajout de nouvelles données ou la sélection de paramètres plus efficaces pour le modèle sont des stratégies de remédiation classiques ;

    • le sur-apprentissage, ou overfit, se traduit par un écart significatif entre les scores sur les sous-ensembles d'entraînement (élevé) et de test (bien plus faible).

Dans le chapitre suivant, nous allons voir deux techniques pour accroître la capacité des modèles à faire des prédictions sur des données qu'ils n'ont pas déjà rencontrées : la régularisation et la validation croisée.

Example of certificate of achievement
Example of certificate of achievement