ML Atlas

Le machine learning en partant de zéro · Leçon 4.3 · 22 sur 33 leçons prêtes · 12 min

Catégories et mise à l’échelle

En bref

kNN sur des vins : 0,695 sans mise à l’échelle, 0,963 standardisé. Des îles codées 0, 1, 2 simulent un ordre : le one-hot porte le R² de 0,469 à 0,546.

Échauffement Deux questions de leçons précédentes. Une minute, sans jamais bloquer la leçon.
De la leçon 4.2 : Données manquantes L’IMC manque chez les 110 patients les plus lourds. Que fait le remplissage par la moyenne des autres ?

Voir la réponse

Bonne réponse : Il affaiblit le lien IMC–maladie, car les plus malades se retrouvent à un IMC moyen

La corrélation tombe de 0,586 à 0,352. 110 patients à forte progression sont au même point : à la moyenne de 24,3.

De la leçon 4.1 : Exploration : regarder avant de modéliser Vrai ou faux : « les statistiques descriptives suffisent, dessiner les données est une perte de temps ».

Voir la réponse

Bonne réponse : Faux, car les mêmes nombres peuvent cacher un arc, des groupes ou un point lointain

Dans le quartet, les mêmes nombres cachent quatre images différentes, et chez 342 manchots une corrélation a le signe opposé à celui de chaque espèce. Une image le montre tout de suite.

Dans la leçon 1.4, le kNN reconnaissait 76,7 % des manchots sans mise à l’échelle et 96,1 % après standardisation, parce que la masse en grammes couvrait les autres mesures. Aujourd’hui, deux choses qui cassent un modèle avant même qu’il commence à apprendre. La première, ce sont des variables sur des échelles très différentes ; la mise à l’échelle les ramène à une plage comparable. La seconde, ce sont les catégories, des noms au lieu de nombres, comme l’île d’où vient un manchot. Le codage des catégories les transforme en nombres, et on peut mal le faire.

Prédisez, puis vérifiez Le kNN (k = 5) reconnaît le cépage d’un vin à partir de 13 mesures chimiques. La proline a un écart type de 314, la mesure la moins variable 0,124. Sans mise à l’échelle, l’exactitude en validation croisée vaut 0,695. Combien après standardisation, c’est-à-dire après avoir exprimé chaque mesure en écarts types par rapport à sa moyenne ?
Quel est votre degré de certitude ?

Labo interactif

Ce labo interactif fonctionne dans un navigateur avec JavaScript activé. Le texte qui l’entoure décrit ce qu’il montre.

Voir la réponse

Bonne réponse : Beaucoup plus, car après standardisation les 13 mesures comptent dans la distance

Après standardisation, 0,963 au lieu de 0,695. Sans mise à l’échelle, la distance entre vins est presque seulement la différence de proline. Après standardisation, chaque mesure a un écart type de 1 et vote à égalité avec les autres. Le min-max donne 0,958, la mise à l’échelle robuste 0,946.

À vous de jouer

  • Commencez par les vins sans mise à l’échelle. Quelle mesure occupe presque toute la hauteur du graphique ?
  • Passez au min-max, à la standardisation et à la mise à l’échelle robuste. Combien gagne le kNN, et à quel point les trois diffèrent-elles ?
  • Allez aux manchots. Comparez les codes 0, 1, 2 dans l’ordre alphabétique et dans l’ordre du fichier. Un modèle devrait-il dépendre de l’ordre dans lequel vous écrivez les îles ?
  • Choisissez le one-hot. Où se trouvent maintenant les lignes de prédiction par rapport aux points ?

Une fois vérifié dans le lab, mettons un nom sur ce que vous voyez.

L’idée en quelques mots

Le kNN juge deux vins semblables quand ils sont proches sur les 13 mesures à la fois. La proline a un écart type de 314, la mesure la moins variable seulement 0,124, donc dans la somme des différences la proline couvre tout le reste. La mise à l’échelle égalise ces plages. Le min-max ramène chaque mesure dans l’intervalle de 0 à 1, la standardisation à une moyenne de 0 et un écart type de 1, la mise à l’échelle robuste soustrait la médiane et divise par l’écart interquartile, si bien que des valeurs extrêmes isolées la déplacent moins. Sur les vins, le kNN obtient 0,958, 0,963 et 0,946 au lieu de 0,695. Le choix de la méthode compte moins que le fait de mettre à l’échelle.

Les catégories posent un autre problème. Une île, c’est Biscoe, Dream ou Torgersen, et une régression linéaire a besoin de nombres. L’idée la plus simple, ce sont les codes 0, 1, 2. Mais un nombre porte un ordre et une distance : un modèle avec un seul poids supposera que l’île de code 1 se trouve exactement à mi-chemin entre les deux autres. Dans le lab, une régression prédit la masse d’un manchot à partir de son île et de son sexe. Avec les codes alphabétiques, le R² en validation croisée vaut 0,469. Les mêmes codes dans l’ordre du fichier donnent 0,193, alors que les données sont identiques. Le one-hot donne à chaque île sa propre colonne 0/1 et son propre poids : R² 0,546, et l’erreur moyenne passe de 476 à 411 g.

Jak to policzono
  • Vins : Wine (Aeberhard et Forina, 1992 ; UCI, licence CC BY 4.0), 178 vins, 13 mesures chimiques, 3 cépages. kNN avec k = 5, validation croisée en 5 parties, répétée 10 fois avec un autre tirage. La transformation est ajustée seulement sur la partie d’entraînement de chaque découpage.
  • Manchots : Palmer Penguins (Gorman, Williams et Fraser, 2014 ; paquet palmerpenguins : Horst, Hill et Gorman, 2020), licence CC0. 333 manchots sur 344 : 11 n’ont pas de sexe renseigné. Régression linéaire : masse à partir de l’île et du sexe (une colonne 0/1), validation croisée en 5 parties, 10 répétitions. Lignes du graphique : le modèle ajusté sur les 333.
  • R² : la part de la variation de la masse expliquée sur les parties de validation (1 = toute, 0 = autant que la moyenne). Erreur moyenne : la différence absolue moyenne en grammes.

Pourquoi ça marche

La distance du kNN est la racine carrée de la somme des différences au carré. Une mesure très dispersée donne de grandes différences, et ce sont elles qui dominent la somme. La standardisation divise chaque mesure par son écart type, donc une différence typique a une taille semblable dans toutes les mesures. Les moyennes, écarts types, minima et maxima de la transformation sont calculés à partir des données, exactement comme les poids d’un modèle. C’est pourquoi on l’ajuste seulement sur la partie d’entraînement : dans le code ci-dessous, une standardisation ajustée sur tous les vins avant la validation croisée donne 0,965 au lieu de 0,962. Ici l’écart est petit, mais ce score a vu la partie de validation, donc ce n’est pas une estimation honnête.

Avec des codes d’île, la régression linéaire a un seul poids pour toute la colonne. Du code 0 au code 1 et du code 1 au code 2, la masse prédite change donc de la même quantité. Avec les codes alphabétiques, le modèle prédit que les manchots de Dream pèsent 625 g de moins que ceux de Biscoe, et ceux de Torgersen encore 625 g de moins. Dans les données, les manchots de Dream et de Torgersen pèsent presque pareil (3719 et 3709 g), ceux de Biscoe 4719 g. Dans l’ordre du fichier, Biscoe reçoit le code du milieu, et la droite des trois îles s’aplatit presque : 243 g par pas. Le one-hot ne suppose aucun ordre : le modèle prédit pour Dream et pour Torgersen 997 g de moins que pour Biscoe, comme dans les données.

Erreur fréquente

Vrai ou faux : « codez les catégories 1, 2, 3, le modèle se débrouillera ». Faux. Un nombre porte un ordre et des écarts égaux qui n’existent pas entre des îles. Dans le lab, il suffit de réordonner les îles pour que le R² passe de 0,469 à 0,193, alors que les données sont les mêmes. Le one-hot donne 0,546 quel que soit l’ordre. Dans le code ci-dessous, le one-hot bat les codes alphabétiques dans 49 plis de validation sur 50.

L’exception, c’est le sexe : avec deux catégories, une colonne 0/1 suffit, car il n’y a pas de milieu où quelque chose pourrait tomber. Le problème commence à trois catégories sans ordre naturel.

Quiz

Trois questions pour vérifier vos acquis. Choisissez une réponse ou dépliez la solution sous chaque question. Au clavier : les flèches choisissent, Entrée ou Espace valide.

1. Pourquoi le kNN sans mise à l’échelle n’obtient-il que 0,695 sur les vins ?
Voir la réponse

Bonne réponse : Parce que la proline est très dispersée et domine toute la distance

La proline a un écart type de 314, la mesure la moins variable 0,124. Sans mise à l’échelle, la distance est presque seulement la différence de proline. Après standardisation, l’exactitude monte à 0,963.

2. Les îles sont codées Biscoe = 0, Dream = 1, Torgersen = 2. Que suppose une régression linéaire avec un seul poids pour cette colonne ?
Voir la réponse

Bonne réponse : Que Dream est à mi-chemin entre Biscoe et Torgersen, à pas égaux

Un seul poids veut dire des pas égaux : le modèle prédit 625 g de moins de Biscoe à Dream et encore 625 g de Dream à Torgersen. Dans les données, Dream et Torgersen pèsent presque pareil.

3. Vrai ou faux : « codez les catégories 1, 2, 3, le modèle se débrouillera ».
Voir la réponse

Bonne réponse : Faux, car les codes introduisent un ordre et des écarts absents des données

Avec des codes, un modèle linéaire voit une échelle de pas égaux. Le one-hot donne à chaque catégorie son propre poids : R² 0,546 au lieu de 0,469 ou 0,193.

À retenir

  • Les modèles qui mesurent des distances, comme le kNN, ont besoin de variables à une échelle semblable : sur les vins 0,695 sans mise à l’échelle, 0,963 après standardisation.
  • Min-max, standardisation et mise à l’échelle robuste donnent ici des résultats proches ; le plus important est de mettre à l’échelle.
  • Ajustez la transformation seulement sur la partie d’entraînement, séparément dans chaque découpage.
  • Codez en one-hot les catégories sans ordre : les codes 0, 1, 2 imposent un ordre et des pas égaux que les données n’ont pas.

Pour aller plus loin : formule, code, pratique

Les maths

Min-max : x' = (x − min) / (max − min). Standardisation : z = (x − x̄) / s. Mise à l’échelle robuste : x' = (x − médiane) / IQR, où IQR = Q3 − Q1 est l’écart interquartile. Distance du kNN : d(a, b) = √(Σⱼ (aⱼ − bⱼ)²) ; une mesure avec un grand s donne de grands (aⱼ − bⱼ)² et couvre le reste.

Codes d’île dans une régression linéaire : ŷ = a + w · code + v · mâle. Avec les codes alphabétiques, ŷ(Dream) − ŷ(Biscoe) = ŷ(Torgersen) − ŷ(Dream) = w : des pas égaux. One-hot : ŷ = a + w_D · [Dream] + w_T · [Torgersen] + v · mâle, où [Dream] vaut 1 pour un manchot de Dream et 0 sinon. Chaque île a son propre décalage, Biscoe servant de référence.

En code (Python)

La même expérience avec scikit-learn : kNN sur les vins avec quatre mises à l’échelle en validation croisée 10 × 5 (la transformation est dans un pipeline, donc ajustée dans chaque découpage), une standardisation ajustée sur tous les vins pour comparer, et la masse des manchots avec l’île codée de trois façons.

import numpy as np
import pandas as pd
from sklearn.datasets import load_wine
from sklearn.model_selection import cross_val_score, RepeatedStratifiedKFold, RepeatedKFold
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import StandardScaler, MinMaxScaler, RobustScaler
from sklearn.pipeline import make_pipeline
from sklearn.linear_model import LinearRegression

X, y = load_wine(return_X_y=True)
cv = RepeatedStratifiedKFold(n_splits=5, n_repeats=10, random_state=0)
acc = {}
for name, scaler in [("no scaling", None), ("min-max", MinMaxScaler()), ("standardised", StandardScaler()), ("robust", RobustScaler())]:
    model = make_pipeline(scaler, KNeighborsClassifier(5)) if scaler else KNeighborsClassifier(5)  # la transformation dans un pipeline : ajustée dans chaque découpage
    acc[name] = cross_val_score(model, X, y, cv=cv)
print("kNN on 178 wines, 10 x 5 CV: " + ", ".join(f"{k} {v.mean():.3f}" for k, v in acc.items()))
print(f"standardised beats no scaling in {(acc['standardised'] > acc['no scaling']).sum()} of 50 folds")
leaky = cross_val_score(KNeighborsClassifier(5), StandardScaler().fit_transform(X), y, cv=cv)  # cette transformation a aussi vu les parties de validation
print(f"scaler fitted on all wines before the CV: {leaky.mean():.3f}")
df = pd.read_csv("data/raw/penguins.csv").dropna(subset=["sex", "body_mass_g"])
male = (df.sex == "MALE").to_numpy(float)[:, None]
codings = {
    "codes alphabetical": df.island.map({"Biscoe": 0, "Dream": 1, "Torgersen": 2}).to_numpy(float)[:, None],
    "codes in file order": df.island.map({"Torgersen": 0, "Biscoe": 1, "Dream": 2}).to_numpy(float)[:, None],
    "one-hot": pd.get_dummies(df.island).to_numpy(float),
}
cv2 = RepeatedKFold(n_splits=5, n_repeats=10, random_state=0)
r2 = {}
for name, isl in codings.items():
    Xe = np.hstack([isl, male])
    r2[name] = cross_val_score(LinearRegression(), Xe, df.body_mass_g, cv=cv2, scoring="r2")
    mae = -cross_val_score(LinearRegression(), Xe, df.body_mass_g, cv=cv2, scoring="neg_mean_absolute_error").mean()
    print(f"{len(df)} penguins, body mass from island + sex, {name}: R2 {r2[name].mean():.3f}, mean error {mae:.0f} g")
print(f"one-hot beats the alphabetical codes in {(r2['one-hot'] > r2['codes alphabetical']).sum()} of 50 folds")

Sortie

kNN on 178 wines, 10 x 5 CV: no scaling 0.706, min-max 0.963, standardised 0.962, robust 0.952
standardised beats no scaling in 50 of 50 folds
scaler fitted on all wines before the CV: 0.965
333 penguins, body mass from island + sex, codes alphabetical: R2 0.464, mean error 477 g
333 penguins, body mass from island + sex, codes in file order: R2 0.179, mean error 618 g
333 penguins, body mass from island + sex, one-hot: R2 0.542, mean error 412 g
one-hot beats the alphabetical codes in 49 of 50 folds

Les découpages ne sont pas ceux du lab, donc les nombres changent un peu aussi. L’image est la même : la standardisation bat l’absence de mise à l’échelle dans les 50 plis de validation, et le one-hot bat les codes alphabétiques dans 49 sur 50. L’ordre des codes décide encore du score : 0,464 dans l’ordre alphabétique, 0,179 dans l’ordre du fichier.

Pour les praticiens
  • Avec scikit-learn : StandardScaler, MinMaxScaler et RobustScaler pour les nombres, OneHotEncoder(handle_unknown="ignore") pour les catégories. ColumnTransformer envoie les colonnes numériques et catégorielles vers des transformations différentes, et le tout va dans make_pipeline, pour que la validation croisée les ajuste dans chaque découpage.
  • Les arbres de décision et les forêts aléatoires n’ont pas besoin de mise à l’échelle : ils coupent une mesure à un seuil, et le seuil se déplace avec l’échelle.
  • Un ordre qui existe vraiment (petit, moyen, grand ; niveau d’études) peut s’écrire en codes, mais dans un ordre donné explicitement (OrdinalEncoder(categories=...)).
  • Les catégories à centaines de valeurs (code postal, numéro de produit) donnent des centaines de colonnes en one-hot. On regroupe alors les valeurs rares dans un groupe « autres » ou on utilise un codage par la moyenne de la cible calculé hors du pli (une entrée de l’Atlas), car calculé sur toutes les données il fuit.

Aller plus loin dans l’Atlas

Articles de l’encyclopédie sur les notions de cette leçon :