W3docs

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 distancesK-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 gradientLogistic 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) / std

C'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) / IQR

où 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 :

ScalerPlage de sortieSensible aux valeurs aberrantesIdéal pour
StandardScalernon bornée (mean=0, std=1)OuiDonnées distribuées normalement, modèles linéaires régularisés
MinMaxScaler[0, 1] (configurable)OuiRéseaux de neurones, données de pixels d'images
RobustScalernon bornée (median=0)NonDonnées avec valeurs aberrantes significatives
MaxAbsScaler[−1, 1]OuiMatrices 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 transformation

Pour 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.96

Le 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.973

Cela 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
Was this page helpful?