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.
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.
Choisissez une réponse et votre degré de certitude.
Répondez pour débloquer le labo.
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.
À 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.
Télécharger le notebook (.ipynb). Ouvrez-le dans Jupyter ou importez-le dans Google Colab ; il télécharge lui-même les données manquantes.
Pour les praticiens
- Avec scikit-learn :
StandardScaler,MinMaxScaleretRobustScalerpour les nombres,OneHotEncoder(handle_unknown="ignore")pour les catégories.ColumnTransformerenvoie les colonnes numériques et catégorielles vers des transformations différentes, et le tout va dansmake_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.
Cela vous a-t-il aidé ?
Merci ! Cela nous aide à nous améliorer.
Aller plus loin dans l’Atlas
Articles de l’encyclopédie sur les notions de cette leçon :