Mise à l'échelle des features en Python
Apprenez la mise à l'échelle des features en Python avec scikit-learn : StandardScaler, MinMaxScaler, RobustScaler et MaxAbsScaler, avec exemples.
Les modèles de machine learning sont entraînés sur des jeux de données où chaque feature peut couvrir des plages numériques très différentes. La luminosité d'un pixel peut aller de 0 à 255, tandis que le revenu varie de 0 à 1 000 000. Lorsque les magnitudes des features diffèrent de plusieurs ordres de grandeur, les algorithmes qui s'appuient sur des distances (k-NN, SVM, k-means) ou sur la descente de gradient (régression logistique, réseaux de neurones) peuvent accorder un poids disproportionné aux features les plus grandes. La mise à l'échelle des features ramène toutes les features dans une plage comparable afin que le modèle puisse apprendre de chacune d'elles de manière équitable.
Ce chapitre explique pourquoi la mise à l'échelle est importante, présente les quatre scalers disponibles dans scikit-learn, montre leurs résultats sur des données réelles et illustre la bonne façon d'intégrer la mise à l'échelle dans un pipeline de machine learning sans laisser fuiter les données de test.
Pourquoi la mise à l'échelle des features est importante
Considérons un jeu de données simple avec deux features — age (20–60) et income (20 000–100 000). La colonne income a des valeurs environ 1 000 fois plus grandes que age. Pour les algorithmes qui mesurent la distance euclidienne, une variation d'une unité dans income domine complètement une variation d'une unité dans age.
Situations spécifiques où la mise à l'échelle est indispensable :
- Algorithmes basés sur les distances — K-Nearest Neighbors et K-Means Clustering calculent les distances entre les points de données. Des features non mises à l'échelle rendent la métrique de distance insignifiante.
- Optimiseurs par descente de gradient — Logistic Regression et Linear Regression convergent beaucoup plus rapidement quand toutes les features se trouvent dans des plages similaires.
- Modèles régularisés — Ridge, Lasso et ElasticNet pénalisent les grands coefficients de façon uniforme. Une feature à grande échelle attire artificiellement un petit coefficient, masquant sa véritable importance.
Quand la mise à l'échelle n'est PAS nécessaire : les modèles basés sur les arbres (Decision Trees, Random Forests, Gradient Boosting) découpent sur des seuils individuels de features. L'échelle absolue d'une feature n'a pas d'incidence sur la position du meilleur découpage, donc la mise à l'échelle n'a aucun effet sur leur précision.
Les quatre scalers de scikit-learn
StandardScaler
StandardScaler (aussi appelé normalisation z-score) transforme chaque feature pour obtenir une moyenne de 0 et un écart-type de 1.
Formule par feature :
z = (x − mean) / stdC'est le choix par défaut quand vous n'avez pas de raison forte de préférer un autre scaler. Il fonctionne bien pour les données distribuées normalement et est attendu par la plupart des modèles linéaires régularisés.
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
import numpy as np
data = load_iris()
X = data.data # shape (150, 4)
y = data.target
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train) # fit + transform on training data
X_test_scaled = scaler.transform(X_test) # transform only (no re-fit)
# Inspect the result
print("Training set — mean per feature (should be ~0):")
print(np.round(X_train_scaled.mean(axis=0), 4))
print("\nTraining set — std per feature (should be ~1):")
print(np.round(X_train_scaled.std(axis=0), 4))Sortie :
Training set — mean per feature (should be ~0):
[ 0. -0. -0. -0.]
Training set — std per feature (should be ~1):
[1. 1. 1. 1.]Notez que fit_transform est appelé uniquement sur l'ensemble d'entraînement. L'appeler également sur l'ensemble de test ferait fuiter les statistiques du test dans les paramètres du scaler — voir la section Éviter la fuite de données ci-dessous.
MinMaxScaler
MinMaxScaler redimensionne chaque feature vers une plage fixe, par défaut [0, 1].
Formule par feature :
x_scaled = (x − min) / (max − min)Utilisez MinMaxScaler quand vous avez besoin de valeurs dans une plage bornée — par exemple pour alimenter des données dans un réseau de neurones avec des unités de sortie sigmoïde, ou quand l'algorithme en aval attend des entrées non négatives.
from sklearn.preprocessing import MinMaxScaler
mm_scaler = MinMaxScaler() # default feature_range=(0, 1)
X_train_mm = mm_scaler.fit_transform(X_train)
X_test_mm = mm_scaler.transform(X_test)
print("Min per feature (should be 0):", np.round(X_train_mm.min(axis=0), 4))
print("Max per feature (should be 1):", np.round(X_train_mm.max(axis=0), 4))Sortie :
Min per feature (should be 0): [0. 0. 0. 0.]
Max per feature (should be 1): [1. 1. 1. 1.]Attention : MinMaxScaler est très sensible aux valeurs aberrantes. Une seule valeur extrême compresse toutes les autres valeurs dans une bande étroite à l'une des extrémités de la plage. Si vos données contiennent des valeurs aberrantes, envisagez plutôt RobustScaler.
RobustScaler
RobustScaler utilise la médiane et l'écart interquartile (IQR) au lieu de la moyenne et de l'écart-type, ce qui le rend résistant aux valeurs aberrantes.
Formule par feature :
x_scaled = (x − median) / IQRoù IQR = Q3 − Q1 (75e percentile moins 25e percentile).
from sklearn.preprocessing import RobustScaler
rb_scaler = RobustScaler()
X_train_rb = rb_scaler.fit_transform(X_train)
X_test_rb = rb_scaler.transform(X_test)
print("Median per feature (should be ~0):")
print(np.round(np.median(X_train_rb, axis=0), 4))Sortie :
Median per feature (should be ~0):
[0. 0. 0. 0.]RobustScaler est le bon choix quand votre jeu de données contient des valeurs aberrantes que vous ne pouvez pas ou ne souhaitez pas supprimer.
MaxAbsScaler
MaxAbsScaler divise chaque valeur par la valeur absolue maximale dans l'ensemble d'entraînement, plaçant chaque feature dans la plage [−1, 1] sans déplacer les données.
Formule par feature :
x_scaled = x / max(|x|)C'est le seul scaler qui laisse les données centrées sur zéro centrées. Il est spécifiquement conçu pour les matrices creuses (ex. : vecteurs TF-IDF issus de texte) où déplacer la moyenne détruirait la parcimonie.
from sklearn.preprocessing import MaxAbsScaler
ma_scaler = MaxAbsScaler()
X_train_ma = ma_scaler.fit_transform(X_train)
X_test_ma = ma_scaler.transform(X_test)
print("Max absolute value per feature (should be 1):")
print(np.round(np.abs(X_train_ma).max(axis=0), 4))Sortie :
Max absolute value per feature (should be 1):
[1. 1. 1. 1.]Comparaison des quatre scalers
Le tableau ci-dessous résume quand utiliser chaque scaler :
| Scaler | Plage de sortie | Sensible aux valeurs aberrantes | Idéal pour |
|---|---|---|---|
StandardScaler | non bornée (mean=0, std=1) | Oui | Données distribuées normalement, modèles linéaires régularisés |
MinMaxScaler | [0, 1] (configurable) | Oui | Réseaux de neurones, données de pixels d'images |
RobustScaler | non bornée (median=0) | Non | Données avec valeurs aberrantes significatives |
MaxAbsScaler | [−1, 1] | Oui | Matrices creuses (features textuelles) |
Éviter la fuite de données
La fuite de données se produit quand des informations de l'ensemble de test influencent l'étape de prétraitement. L'erreur typique est d'adapter le scaler sur l'ensemble complet des données avant de le diviser :
# WRONG — leaks test-set statistics into the scaler
scaler = StandardScaler()
X_all_scaled = scaler.fit_transform(X) # uses test rows
X_train_bad, X_test_bad, _, _ = train_test_split(
X_all_scaled, y, test_size=0.2, random_state=42
)L'ordre correct est toujours diviser d'abord, puis adapter le scaler uniquement sur les données d'entraînement :
# CORRECT
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train) # fit on train only
X_test_scaled = scaler.transform(X_test) # apply the same transformationPour en savoir plus sur la division train/test elle-même, voir Train/Test Split.
Utiliser un Pipeline pour sécuriser la mise à l'échelle
Un Pipeline scikit-learn est la façon la plus robuste de combiner la mise à l'échelle avec un modèle. Le pipeline garantit que fit_transform est appelé sur les données d'entraînement et seulement transform sur tout ensemble mis de côté — même lors de la validation croisée.
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score
from sklearn.datasets import load_iris
import numpy as np
X, y = load_iris(return_X_y=True)
pipe = Pipeline(steps=[
("scaler", StandardScaler()),
("model", LogisticRegression(max_iter=200)),
])
scores = cross_val_score(pipe, X, y, cv=5, scoring="accuracy")
print("CV accuracy scores:", np.round(scores, 3))
print("Mean accuracy: ", round(scores.mean(), 3))Sortie :
CV accuracy scores: [0.967 1. 0.933 0.9 1. ]
Mean accuracy: 0.96Le pipeline élimine automatiquement le risque de fuite de données sur l'ensemble des cinq plis de validation croisée.
Réglage des hyperparamètres du scaler
Vous pouvez régler le choix du scaler en même temps que les hyperparamètres du modèle grâce à la recherche par grille :
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler, MinMaxScaler, RobustScaler
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import GridSearchCV
from sklearn.datasets import load_iris
import numpy as np
X, y = load_iris(return_X_y=True)
pipe = Pipeline(steps=[
("scaler", StandardScaler()),
("model", LogisticRegression(max_iter=300)),
])
param_grid = {
"scaler": [StandardScaler(), MinMaxScaler(), RobustScaler()],
"model__C": [0.1, 1.0, 10.0],
}
grid = GridSearchCV(pipe, param_grid, cv=5, scoring="accuracy")
grid.fit(X, y)
print("Best scaler:", grid.best_params_["scaler"].__class__.__name__)
print("Best C: ", grid.best_params_["model__C"])
print("Best score: ", round(grid.best_score_, 3))Sortie :
Best scaler: StandardScaler
Best C: 10.0
Best score: 0.973Cela permet aux données de vous indiquer quel scaler fonctionne le mieux pour un modèle et un jeu de données donnés.
Chapitres connexes
- Train/Test Split — divisez vos données avant d'adapter un scaler
- Categorical Data — encodez les features non numériques avant la mise à l'échelle
- K-Nearest Neighbors — modèle basé sur les distances qui nécessite des features mises à l'échelle
- K-Means Clustering — algorithme de clustering sensible à l'échelle des features
- Linear Regression — modèle par descente de gradient qui converge plus vite avec la mise à l'échelle
- Logistic Regression — classificateur régularisé qui bénéficie de StandardScaler
- Cross Validation — utilisez un Pipeline pour garantir une mise à l'échelle sans fuite lors de la CV
- Grid Search — régler le choix du scaler en même temps que les hyperparamètres du modèle