Vermeidung von Datenlecks: Ein Beispiel zur fehlerhaften Vorgehensweise bei der Vorverarbeitung eines Golf-Datensatzes
Vermeidung von Datenleckagen beim maschinellen Lernen: Ein Beispiel mit einem Golfspiel-Datensatz
In der Welt des maschinellen Lernens ist die Vermeidung von Datenleckagen entscheidend für die Erstellung zuverlässiger Modelle. In diesem Blogbeitrag wollen wir uns anschauen, wie Datenleckagen bei der Vorhersage eines einfachen Golfspiel-Datensatzes entstehen können. Dieses Beispiel dient ausschließlich zu Bildungszwecken und zeigt, was nicht getan werden sollte.
Was ist Datenleckage?
Datenleckage tritt auf, wenn Informationen aus dem Testdatensatz in den Trainingsdatensatz gelangen. Dies führt dazu, dass das Modell "schummelt" und unrealistische Genauigkeiten auf den Testdaten erzielt, da es bereits über Informationen verfügt, die es nicht haben sollte.
Beispiel für Datenleckage
Im folgenden Python-Code verwenden wir verschiedene Vorverarbeitungstechniken mithilfe des ColumnTransformer
von scikit-learn
. Dies kann jedoch zu Datenleckagen führen, wenn wir alle Transformationen auf dem gesamten Datensatz durchführen, bevor wir diesen in Trainings- und Testdatensätze aufteilen.
import pandas as pd
import numpy as np
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OrdinalEncoder, KBinsDiscretizer
from sklearn.impute import SimpleImputer
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score
from imblearn.pipeline import Pipeline
from imblearn.over_sampling import SMOTE
# Datensatz erstellen
dataset_dict = {
'Outlook': ['sunny', 'sunny', 'overcast', 'rain', 'rain', 'rain',
'overcast', 'sunny', 'sunny', 'rain', 'sunny', 'overcast',
'overcast', 'rain', 'sunny', 'overcast', 'rain', 'sunny',
'sunny', 'rain', 'overcast', 'rain', 'sunny', 'overcast',
'sunny', 'overcast', 'rain', 'overcast'],
'Temperature': [85.0, 80.0, 83.0, 70.0, 68.0, 65.0, 64.0,
72.0, 69.0, 75.0, 75.0, 72.0, 81.0, 71.0,
81.0, 74.0, 76.0, 78.0, 82.0, 67.0, 85.0,
73.0, 88.0, 77.0, 79.0, 80.0, 66.0, 84.0],
'Humidity': [85.0, 90.0, 78.0, 96.0, 80.0, 70.0, 65.0, 95.0,
70.0, 80.0, 70.0, 90.0, 75.0, 80.0, 88.0,
92.0, 85.0, 75.0, 92.0, 90.0, 85.0, 88.0,
65.0, 70.0, 60.0, 95.0, 70.0, 78.0],
'Wind': [False, True, False, False, False, True, True, False,
False, False, True, True, False, True, True,
False, False, True, False, True, True, False,
True, False, False, True, False, False],
'Play': ['No', 'No', 'Yes', 'Yes', 'Yes', 'No', 'Yes',
'No', 'Yes', 'Yes', 'Yes', 'Yes', 'Yes',
'No', 'No', 'Yes', 'Yes', 'No', 'No', 'No',
'Yes', 'Yes', 'Yes', 'Yes', 'Yes', 'Yes',
'No', 'Yes']
}
df = pd.DataFrame(dataset_dict)
X, y = df.drop('Play', axis=1), df['Play']
# Vorverarbeitung und Anwendung von SMOTE auf die gesamten Daten (führt zu Leakage)
preprocessor = ColumnTransformer(
transformers=[
... # Hier kommen die Vorverarbeitungsschritte
]
)
# Transformation der gesamten Daten und Anwendung von SMOTE
X_transformed = preprocessor.fit_transform(X)
smote = SMOTE(random_state=42)
X_resampled, y_resampled = smote.fit_resample(X_transformed, y)
# Aufteilung der bereits transformierten und resampleten Daten
X_train, X_test, y_train, y_test = train_test_split(X_resampled, y_resampled, test_size=0.5, shuffle=False)
# Klassifikator trainieren
clf = DecisionTreeClassifier(random_state=42)
clf.fit(X_train, y_train)
print(f"Testgenauigkeit (mit Leakage): {accuracy_score(y_test, clf.predict(X_test)):.2%}")
Warum ist dieser Ansatz problematisch?
- Bei dem oben gezeigten Beispiel sieht die Vorverarbeitung den gesamten Datensatz, einschließlich der Informationen aus den Testdaten, was zu unrealistisch hohen Testgenauigkeiten führen kann.
- Darüber hinaus wird der Einsatz von SMOTE (Synthetic Minority Over-sampling Technique) auf den gesamten Datensatz angewandt, was die Ergebnisse weiter verzerrt.
Der richtige Weg: Keine Datenleckage
Um Datenleckage zu vermeiden, ist es entscheidend, die Daten zuerst in Trainings- und Testdaten aufzuteilen, bevor jegliche Vorverarbeitungsschritte oder Resampling-Methoden angewendet werden. Hier ist die verbesserte Version des Codes:
# Split zuerst (vor jeglicher Verarbeitung)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5, shuffle=False)
# Erstellen der Pipeline mit Vorverarbeitung, SMOTE und Klassifikator
pipeline = Pipeline([
('preprocessor', ColumnTransformer(
transformers=[
... # Hier kommen die Vorverarbeitungsschritte
]
)),
('smote', SMOTE(random_state=42)),
('classifier', DecisionTreeClassifier(random_state=42))
])
# Pipeline nur auf Trainingsdaten anpassen
pipeline.fit(X_train, y_train)
print(f"Trainingsgenauigkeit: {accuracy_score(y_train, pipeline.predict(X_train)):.2%}")
print(f"Testgenauigkeit: {accuracy_score(y_test, pipeline.predict(X_test)):.2%}")
Wichtige Unterschiede zum vorigen Beispiel
- Zuerst die Daten aufteilen: Die Aufteilung erfolgt vor jeglicher Verarbeitung.
- Pipeline verwenden: Alle Transformationen, sowohl die Vorverarbeitung als auch SMOTE, sind innerhalb der Pipeline integriert.
- Echte Testdaten: Diese bleiben bis zur Vorhersage unberührt, was realistische Leistungsabschätzungen ermöglicht.
Fazit
Die korrekte Trennung von Trainings- und Testdaten ist entscheidend, um Verzerrungen und unrealistische Modellgenauigkeiten zu vermeiden. Indem wir sicherstellen, dass jeder Schritt der Datenverarbeitung nur mit den Trainingsdaten durchgeführt wird, können wir die Validität und Verlässlichkeit unserer Vorhersagemodelle verbessern. Achten Sie darauf, eine Pipeline zu verwenden, um diese Praktiken in Ihren eigenen Projekten zu implementieren!
Hinterlasse eine Antwort