Feature-Skalierung in Python
Feature-Skalierung in Python mit scikit-learn: StandardScaler, MinMaxScaler, RobustScaler und MaxAbsScaler — mit Beispielen und Ausgabe.
Machine-Learning-Modelle werden auf Datensätzen trainiert, bei denen jedes Merkmal einen sehr unterschiedlichen numerischen Bereich abdecken kann. Die Pixelhelligkeit kann von 0 bis 255 reichen, während das Einkommen von 0 bis 1.000.000 geht. Wenn sich die Größenordnungen der Merkmale um mehrere Potenzen unterscheiden, können Algorithmen, die auf Distanzen (k-NN, SVM, k-Means) oder Gradientenabstieg (logistische Regression, neuronale Netze) basieren, den größten Merkmalen ein unverhältnismäßig hohes Gewicht beimessen. Die Feature-Skalierung bringt alle Merkmale in einen vergleichbaren Bereich, sodass das Modell gleichmäßig von allen lernen kann.
Dieses Kapitel erklärt, warum Skalierung wichtig ist, stellt die vier in scikit-learn verfügbaren Skalierer vor, zeigt deren Ausgaben anhand realer Daten und demonstriert den richtigen Weg, die Skalierung in eine Machine-Learning-Pipeline zu integrieren, ohne Testdaten zu leaken.
Warum Feature-Skalierung wichtig ist
Betrachten wir einen einfachen Datensatz mit zwei Merkmalen — age (20–60) und income (20.000–100.000). Die Spalte income hat Werte, die rund 1.000-mal größer sind als age. Für Algorithmen, die den euklidischen Abstand messen, dominiert eine Änderung um eine Einheit in income eine Änderung um eine Einheit in age vollständig.
Konkrete Situationen, in denen Skalierung unverzichtbar ist:
- Distanzbasierte Algorithmen — K-Nearest Neighbors und K-Means Clustering berechnen Abstände zwischen Datenpunkten. Unskalierte Merkmale machen das Abstandsmaß bedeutungslos.
- Gradientenabstieg-Optimierer — Logistische Regression und Lineare Regression konvergieren deutlich schneller, wenn alle Merkmale in ähnlichen Bereichen liegen.
- Regularisierte Modelle — Ridge, Lasso und ElasticNet bestrafen große Koeffizienten gleichmäßig. Ein Merkmal mit großer Skala zieht künstlich einen kleinen Koeffizienten an und verbirgt so seine wahre Bedeutung.
Wann Skalierung NICHT nötig ist: Baumbasierte Modelle (Decision Trees, Random Forests, Gradient Boosting) teilen auf der Basis einzelner Merkmalsschwellenwerte auf. Die absolute Skala eines Merkmals beeinflusst nicht, wo die beste Aufteilung liegt, sodass die Skalierung keine Auswirkung auf deren Genauigkeit hat.
Die vier scikit-learn-Skalierer
StandardScaler
StandardScaler (auch z-Score-Normalisierung genannt) transformiert jedes Merkmal so, dass es einen Mittelwert von 0 und eine Standardabweichung von 1 hat.
Formel pro Merkmal:
z = (x − mean) / stdDies ist die Standardwahl, wenn es keinen triftigen Grund für eine andere Option gibt. Er funktioniert gut bei normalverteilten Daten und wird von den meisten regularisierten linearen Modellen erwartet.
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))Ausgabe:
Training set — mean per feature (should be ~0):
[ 0. -0. -0. -0.]
Training set — std per feature (should be ~1):
[1. 1. 1. 1.]Beachte, dass fit_transform nur auf dem Trainingssatz aufgerufen wird. Wenn man es auch auf dem Testsatz aufruft, würden Teststatistiken in die Skaliererparameter einfließen — siehe den Abschnitt Datenleckage weiter unten.
MinMaxScaler
MinMaxScaler skaliert jedes Merkmal auf einen festen Bereich, standardmäßig [0, 1].
Formel pro Merkmal:
x_scaled = (x − min) / (max − min)Verwende MinMaxScaler, wenn Werte in einem begrenzten Bereich benötigt werden — zum Beispiel beim Einspeisen von Daten in ein neuronales Netz mit Sigmoid-Ausgabeeinheiten oder wenn der nachgelagerte Algorithmus nicht-negative Eingaben erwartet.
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))Ausgabe:
Min per feature (should be 0): [0. 0. 0. 0.]
Max per feature (should be 1): [1. 1. 1. 1.]Achtung: MinMaxScaler reagiert sehr empfindlich auf Ausreißer. Ein einzelner extremer Wert komprimiert alle anderen Werte in ein enges Band an einem Ende des Bereichs. Wenn die Daten Ausreißer enthalten, sollte stattdessen RobustScaler in Betracht gezogen werden.
RobustScaler
RobustScaler verwendet den Median und den Interquartilsabstand (IQR) anstelle von Mittelwert und Standardabweichung, was ihn robust gegenüber Ausreißern macht.
Formel pro Merkmal:
x_scaled = (x − median) / IQRwobei IQR = Q3 − Q1 (75. Perzentile minus 25. Perzentile).
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))Ausgabe:
Median per feature (should be ~0):
[0. 0. 0. 0.]RobustScaler ist die richtige Wahl, wenn der Datensatz Ausreißer enthält, die nicht entfernt werden können oder sollen.
MaxAbsScaler
MaxAbsScaler dividiert jeden Wert durch den maximalen Absolutwert im Trainingssatz und platziert jedes Merkmal im Bereich [−1, 1], ohne die Daten zu verschieben.
Formel pro Merkmal:
x_scaled = x / max(|x|)Dies ist der einzige Skalierer, der nullzentrierte Daten zentriert lässt. Er ist speziell für sparse Matrizen (z. B. TF-IDF-Vektoren aus Text) konzipiert, bei denen eine Verschiebung des Mittelwerts die Sparsity zerstören würde.
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))Ausgabe:
Max absolute value per feature (should be 1):
[1. 1. 1. 1.]Vergleich aller vier Skalierer
Die folgende Tabelle fasst zusammen, wann welcher Skalierer eingesetzt werden sollte:
| Skalierer | Ausgabebereich | Empfindlich gegenüber Ausreißern | Am besten geeignet für |
|---|---|---|---|
StandardScaler | unbegrenzt (mean=0, std=1) | Ja | Normalverteilte Daten, regularisierte lineare Modelle |
MinMaxScaler | [0, 1] (konfigurierbar) | Ja | Neuronale Netze, Bildpixeldaten |
RobustScaler | unbegrenzt (median=0) | Nein | Daten mit signifikanten Ausreißern |
MaxAbsScaler | [−1, 1] | Ja | Sparse Matrizen (Textmerkmale) |
Datenleckage vermeiden
Datenleckage tritt auf, wenn Informationen aus dem Testsatz den Vorverarbeitungsschritt beeinflussen. Der typische Fehler besteht darin, den Skalierer auf dem gesamten Datensatz zu fitten, bevor dieser aufgeteilt wird:
# 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
)Die korrekte Reihenfolge ist immer erst aufteilen, dann den Skalierer nur auf den Trainingsdaten fitten:
# 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 transformationWeitere Informationen zur Train/Test-Aufteilung selbst finden sich unter Train/Test Split.
Pipeline verwenden, um die Skalierung sicher zu halten
Eine scikit-learn-Pipeline ist die robusteste Möglichkeit, Skalierung mit einem Modell zu kombinieren. Die Pipeline stellt sicher, dass fit_transform auf den Trainingsdaten aufgerufen wird und bei zurückgehaltenen Folds nur transform — auch während der Kreuzvalidierung.
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))Ausgabe:
CV accuracy scores: [0.967 1. 0.933 0.9 1. ]
Mean accuracy: 0.96Die Pipeline eliminiert automatisch das Risiko einer Datenleckage über alle fünf Kreuzvalidierungs-Folds.
Hyperparameter des Skalierers abstimmen
Die Wahl des Skalierers kann zusammen mit Modell-Hyperparametern mithilfe der Grid Search optimiert werden:
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))Ausgabe:
Best scaler: StandardScaler
Best C: 10.0
Best score: 0.973So lässt die Datenlage selbst erkennen, welcher Skalierer für ein gegebenes Modell und einen gegebenen Datensatz am besten geeignet ist.
Verwandte Kapitel
- Train/Test Split — Daten aufteilen, bevor ein Skalierer gefittet wird
- Categorical Data — nicht-numerische Merkmale vor der Skalierung kodieren
- K-Nearest Neighbors — distanzbasiertes Modell, das skalierte Merkmale erfordert
- K-Means Clustering — Clustering-Algorithmus, der empfindlich auf die Merkmalsskala reagiert
- Linear Regression — Gradientenabstieg-Modell, das mit Skalierung schneller konvergiert
- Logistic Regression — regularisierter Klassifikator, der von StandardScaler profitiert
- Cross Validation — Pipeline verwenden, um sicherzustellen, dass die Skalierung während der CV leckagefrei ist
- Grid Search — Skalierer-Wahl zusammen mit Modell-Hyperparametern optimieren