Erstellung einer robusten Machine Learning-Pipeline: Ein Entscheidungsbaum-Ansatz
14. Februar 2025 - Matthias
Einleitung: Warum Machine Learning immer noch relevant ist
Machine Learning (ML) bleibt auch in Zeiten von Deep Learning und grossen Sprachmodellen (LLMs) ein wertvolles Werkzeug für viele praktische Anwendungen. Insbesondere in Bereichen wie Finanzen, Medizin und Recht ist erklärbare KI von entscheidender Bedeutung. Im Gegensatz zu Deep Learning, das oft als „Black Box“ fungiert, ermöglichen klassischere ML-Modelle eine detaillierte Nachvollziehbarkeit der Modellentscheidungen. Diese Transparenz ist notwendig, um Vertrauen in die Modelle zu schaffen, insbesondere wenn sie zur Entscheidungsfindung in kritischen Bereichen verwendet werden.
Ein klassisches ML-Modell, wie etwa Random Forest oder Logistische Regression, kann zwar in bestimmten Szenarien nicht mit der Leistung von Deep Learning-Modellen konkurrieren, aber in Bereichen, in denen Transparenz und Nachvollziehbarkeit der Entscheidungen erforderlich sind, bieten diese Modelle nach wie vor einen erheblichen Vorteil. In vielen Anwendungsbereichen – z.B. Kreditvergabe oder medizinische Diagnosen – sind erklärbare Modelle notwendig, um potenzielle Verzerrungen zu erkennen und die Entscheidungsprozesse nachvollziehbar zu gestalten.
Dieser Beitrag führt dich durch alle wesentlichen Schritte, die beim Aufbau einer robusten ML-Pipeline erforderlich sind, und zeigt dir, welche verschiedenen Varianten in scikit-learn
zur Verfügung stehen.
Schritt 1: Datenanalyse und Vorverarbeitung
Bevor du mit der Bereinigung und Vorverarbeitung deiner Daten beginnst, ist es wichtig, eine erste Analyse durchzuführen, um das Datenverständnis zu vertiefen. Durch die Analyse erhältst du wertvolle Informationen, die dir helfen, den nächsten Schritt der Bereinigung und Transformation zu optimieren.
1. Datenstatistik und grundlegende Deskriptivanalysen
Die ersten Statistiken zu deinen Daten bieten wichtige Einblicke in die Verteilung der Werte und helfen, offensichtliche Probleme wie fehlende oder fehlerhafte Werte zu identifizieren.
Beispiel zur Berechnung grundlegender Statistiken:
import pandas as pd
# Lade deinen Datensatz
data = pd.read_csv('your_data.csv')
# Berechne grundlegende Statistiken
statistics = data.describe()
print(statistics)
- Was passiert hier? Die Methode
describe()
gibt eine Zusammenfassung der wichtigsten statistischen Kennzahlen, wie:- count: Anzahl der nicht-fehlenden Werte.
- mean: Mittelwert.
- std: Standardabweichung.
- min / max: Minimal- und Maximalwerte.
- 25% / 50% / 75%: Quartile der Daten.
Was lässt sich daraus ableiten?
- Du kannst sofort erkennen, ob einige Features ungewöhnliche Werte haben, wie z.B. extreme Ausreisser oder falsche Datentypen (z.B. negative Werte für Altersdaten).
- Fehlen viele Werte in bestimmten Spalten? Die
count
-Spalte zeigt dir, wie viele Datenpunkte in jeder Spalte vorhanden sind.
2. Kurtosis und Schiefe (Skewness)
Kurtosis und Schiefe geben Hinweise darauf, ob deine Daten einer Normalverteilung folgen oder ob sie asymmetrisch sind.
- Wann wählen? Wenn du verstehen möchtest, wie die Verteilung der Daten aussieht und ob eine Transformation (wie Log-Transformation) erforderlich ist.
Beispiel zur Berechnung von Schiefe und Kurtosis:
# Berechnung der Schiefe und Kurtosis
skewness = data.skew() # Skewness
kurtosis = data.kurtosis() # Kurtosis
print("Schiefe:", skewness)
print("Kurtosis:", kurtosis)
- Was passiert hier?
- Schiefe zeigt an, wie asymmetrisch die Verteilung der Daten ist. Eine positive Schiefe bedeutet, dass die rechte Seite der Verteilung länger ist, während eine negative Schiefe auf eine längere linke Seite hinweist.
- Kurtosis gibt an, wie steil oder flach die Verteilung ist im Vergleich zu einer Normalverteilung. Eine hohe Kurtosis deutet auf mehr extreme Werte (Ausreisser) hin, während niedrige Werte auf eine flachere Verteilung hindeuten.
Was lässt sich daraus ableiten?
- Wenn deine Daten stark schief oder stark kurtotisch sind, könnte es sinnvoll sein, eine Transformation der Variablen vorzunehmen (z.B. logarithmische Transformation), um sie näher an eine Normalverteilung zu bringen. Viele Modelle (wie lineare Regression) setzen Normalverteilung der Daten voraus.
3. Einzigartige Werte und Kategorische Daten
Eine Analyse der einzigartigen Werte in deinen Daten hilft zu verstehen, wie kategorische Variablen verteilt sind und ob es Probleme wie falsche Kategorien oder ungewöhnliche Werte gibt.
Beispiel zur Identifikation einzigartiger Werte:
# Unique Werte für jede Spalte anzeigen
unique_values = data.nunique()
print("Anzahl einzigartiger Werte pro Spalte:", unique_values)
- Was passiert hier? Die Funktion
nunique()
gibt an, wie viele einzigartige Werte in jeder Spalte vorhanden sind.
Was lässt sich daraus ableiten?
- Wenn du viele verschiedene Werte in einer Spalte hast, die als kategorische Variable interpretiert werden sollte, könnte dies auf ein Problem mit den Daten hinweisen (z.B. falsche Datenwerte).
- Besonders nützlich bei der Identifikation von Variablen wie "Ja/Nein", "männlich/weiblich" oder "kategorisierten Altersgruppen", bei denen nur bestimmte Werte erwartet werden.
4. Korrelation der Merkmale
Ein Korrelation Heatmap hilft, die Beziehungen zwischen den numerischen Features zu visualisieren. Starke Korrelationen zwischen den Features können dazu führen, dass das Modell ineffizient wird, und in einigen Fällen ist es besser, einige der stark korrelierten Variablen zu entfernen (Multikollinearität).
Beispiel zur Berechnung der Korrelation und Visualisierung mit einer Heatmap:
import seaborn as sns
import matplotlib.pyplot as plt
# Berechne die Korrelation zwischen den numerischen Features
correlation_matrix = data.corr()
# Visualisierung der Korrelation mit einer Heatmap
plt.figure(figsize=(10, 8))
sns.heatmap(correlation_matrix, annot=True, cmap='coolwarm', fmt='.2f')
plt.title('Korrelation Heatmap')
plt.show()
- Was passiert hier? Die Korrelation gibt an, wie stark zwei Variablen miteinander zusammenhängen:
- Ein Wert nahe 1 oder -1 zeigt eine starke positive oder negative Korrelation.
- Ein Wert nahe 0 zeigt keine lineare Korrelation.
Was lässt sich daraus ableiten?
- Wenn zwei Variablen stark korreliert sind, könnte es sinnvoll sein, eine der beiden zu entfernen, da das Modell andernfalls redundant wird und die Modellgenauigkeit möglicherweise negativ beeinflusst.
- Eine hohe Korrelation zwischen Eingabefeatures kann zu Multikollinearität führen, was zu instabilen Modellparametern führt, besonders bei linearen Modellen.
5. Weitere Analysen
Abhängig von deinem Dataset und Anwendungsfall kannst du auch fortgeschrittene Techniken wie die Analyse der Verteilung von Zielvariablen oder das Überprüfen auf Ausreisser durch Boxplots oder Z-Score-Tests durchführen.
Schritt 2: Datenbereinigung und Vorverarbeitung
Umgang mit fehlenden Werten
Bevor du ein Modell trainierst, musst du sicherstellen, dass deine Daten korrekt und vollständig sind. Der Umgang mit fehlenden Werten ist entscheidend. Hier sind die wichtigsten Optionen:
-
Einfaches Entfernen von Zeilen/Spalten:
- Wann wählen? Wenn nur ein kleiner Teil der Daten fehlt, kann das Entfernen von Zeilen oder Spalten eine einfache Lösung sein.
data = data.dropna() # Entfernt alle Zeilen mit fehlenden Werten
- Wann wählen? Wenn nur ein kleiner Teil der Daten fehlt, kann das Entfernen von Zeilen oder Spalten eine einfache Lösung sein.
-
Imputation mit Durchschnitt/Median:
- Wann wählen? Wenn du die Daten beibehalten möchtest und nur wenige Werte fehlen, kannst du fehlende Werte durch den Mittelwert, Median oder häufigsten Wert ersetzen.
from sklearn.impute import SimpleImputer imputer = SimpleImputer(strategy='mean') # Median oder häufigster Wert sind auch möglich data['column'] = imputer.fit_transform(data[['column']])
- Wann wählen? Wenn du die Daten beibehalten möchtest und nur wenige Werte fehlen, kannst du fehlende Werte durch den Mittelwert, Median oder häufigsten Wert ersetzen.
-
KNN-Imputation:
- Wann wählen? Wenn die Imputation auf Basis der Ähnlichkeit der Daten erfolgen soll, kann
KNNImputer
eine gute Wahl sein, insbesondere wenn die Daten mehrere Variablen enthalten, die miteinander korrelieren.from sklearn.impute import KNNImputer imputer = KNNImputer(n_neighbors=3) data_imputed = imputer.fit_transform(data)
- Wann wählen? Wenn die Imputation auf Basis der Ähnlichkeit der Daten erfolgen soll, kann
-
Iterative Imputation:
- Wann wählen? Wenn du komplexe Muster und Beziehungen zwischen den fehlenden Werten berücksichtigen möchtest, hilft die
IterativeImputer
-Methode, die Modelle iterativ nutzt, um Werte zu schätzen.from sklearn.experimental import enable_iterative_imputer from sklearn.impute import IterativeImputer imputer = IterativeImputer(max_iter=10, random_state=0) data_imputed = imputer.fit_transform(data)
- Wann wählen? Wenn du komplexe Muster und Beziehungen zwischen den fehlenden Werten berücksichtigen möchtest, hilft die
Schritt 3: Feature-Skalierung
Die Skalierung der Daten ist besonders wichtig, wenn du Modelle verwendest, die empfindlich auf die Skala der Eingabedaten reagieren. Hier sind einige Varianten:
-
StandardScaler:
- Wann wählen? Wenn du sicherstellen möchtest, dass die Daten eine Standardnormalverteilung (Mittelwert = 0, Standardabweichung = 1) haben, insbesondere für Modelle wie SVM, KNN oder Logistische Regression.
from sklearn.preprocessing import StandardScaler scaler = StandardScaler() X_scaled = scaler.fit_transform(X)
- Wann wählen? Wenn du sicherstellen möchtest, dass die Daten eine Standardnormalverteilung (Mittelwert = 0, Standardabweichung = 1) haben, insbesondere für Modelle wie SVM, KNN oder Logistische Regression.
-
MinMaxScaler:
- Wann wählen? Wenn du die Daten in einen bestimmten Bereich skalieren möchtest (z.B. [0, 1]), besonders bei Modellen, die empfindlich auf den Bereich der Daten reagieren (wie neuronale Netze).
from sklearn.preprocessing import MinMaxScaler scaler = MinMaxScaler() X_scaled = scaler.fit_transform(X)
- Wann wählen? Wenn du die Daten in einen bestimmten Bereich skalieren möchtest (z.B. [0, 1]), besonders bei Modellen, die empfindlich auf den Bereich der Daten reagieren (wie neuronale Netze).
-
RobustScaler:
- Wann wählen? Wenn deine Daten Ausreisser enthalten, hilft der
RobustScaler
, indem er die Skalierung auf den Median und die Quartile der Daten stützt.from sklearn.preprocessing import RobustScaler scaler = RobustScaler() X_scaled = scaler.fit_transform(X)
- Wann wählen? Wenn deine Daten Ausreisser enthalten, hilft der
-
MaxAbsScaler:
- Wann wählen? Wenn du mit bereits zentrierten Daten arbeitest, hilft der
MaxAbsScaler
, diese auf den Bereich [-1, 1] zu skalieren, ohne die Verteilung zu verändern.from sklearn.preprocessing import MaxAbsScaler scaler = MaxAbsScaler() X_scaled = scaler.fit_transform(X)
- Wann wählen? Wenn du mit bereits zentrierten Daten arbeitest, hilft der
Schritt 4: Kategorische Variablen umwandeln
Viele Modelle benötigen numerische Eingabewerte. Deshalb müssen kategorische Variablen in numerische umgewandelt werden:
-
LabelEncoder:
- Wann wählen? Bei ordinalen Variablen (z.B. „niedrig“, „mittel“, „hoch“).
from sklearn.preprocessing import LabelEncoder encoder = LabelEncoder() data['encoded_column'] = encoder.fit_transform(data['column'])
- Wann wählen? Bei ordinalen Variablen (z.B. „niedrig“, „mittel“, „hoch“).
-
OneHotEncoder:
- Wann wählen? Wenn du nominale Variablen hast (z.B. „rot“, „blau“, „grün“), die keine natürliche Reihenfolge haben.
from sklearn.preprocessing import OneHotEncoder encoder = OneHotEncoder(sparse_output=False) encoded_data = encoder.fit_transform(data[['column']])
- Wann wählen? Wenn du nominale Variablen hast (z.B. „rot“, „blau“, „grün“), die keine natürliche Reihenfolge haben.
-
OrdinalEncoder:
- Wann wählen? Wenn die kategorischen Variablen eine natürliche Reihenfolge haben (ähnlich wie
LabelEncoder
, aber für DataFrames geeignet).from sklearn.preprocessing import OrdinalEncoder encoder = OrdinalEncoder() data['encoded_column'] = encoder.fit_transform(data[['column']])
- Wann wählen? Wenn die kategorischen Variablen eine natürliche Reihenfolge haben (ähnlich wie
Schritt 5: Modellwahl
Die Wahl des richtigen Modells ist entscheidend und hängt von deinem Problem ab. Hier sind die gängigsten Algorithmen:
1. Logistische Regression (LogisticRegression
)
- Wann wählen? Für binäre Klassifikation, wenn die Beziehung zwischen den Features und der Zielvariable linear ist.
from sklearn.linear_model import LogisticRegression model = LogisticRegression() model.fit(X_train, y_train) predictions = model.predict(X_test)
2. Random Forest (RandomForestClassifier
, RandomForestRegressor
)
- Wann wählen? Für Klassifikation oder Regression bei komplexeren, nichtlinearen Beziehungen und grossen Datensätzen.
from sklearn.ensemble import RandomForestClassifier model = RandomForestClassifier(n_estimators=100) model.fit(X_train, y_train) predictions = model.predict(X_test)
3. Support Vector Machine (SVM) (SVC
, SVR
)
- Wann wählen? Bei hochdimensionalen, nichtlinearen Problemen.
from sklearn.svm import SVC model = SVC(kernel='linear') model.fit(X_train, y_train) predictions = model.predict(X_test)
4. K-Nearest Neighbors (KNN) (KNeighborsClassifier
, KNeighborsRegressor
)
- Wann wählen? Für einfache Klassifikation oder Regression, wenn du keine linearen Zusammenhänge erwartest.
from sklearn.neighbors import KNeighborsClassifier model = KNeighborsClassifier(n_neighbors=3) model.fit(X_train, y_train) predictions = model.predict(X_test)
5. Gradient Boosting (GradientBoostingClassifier
, GradientBoostingRegressor
)
- Wann wählen? Bei komplexen, nichtlinearen Daten.
from sklearn.ensemble import GradientBoostingClassifier model = GradientBoostingClassifier(n_estimators=100) model.fit(X_train, y_train) predictions = model.predict(X_test)
6. Naive Bayes (GaussianNB
, MultinomialNB
, BernoulliNB
)
- Wann wählen? Bei Klassifikationsaufgaben, insbesondere bei textbasierten Daten oder wenn die Annahme der bedingten Unabhängigkeit zutrifft.
from sklearn.naive_bayes import GaussianNB model = GaussianNB() model.fit(X_train, y_train) predictions = model.predict(X_test)
7. Decision Trees (DecisionTreeClassifier
, DecisionTreeRegressor
)
- Wann wählen? Wenn du ein einfaches, interpretiertes Modell mit klaren Entscheidungsgrenzen benötigst.
from sklearn.tree import DecisionTreeClassifier model = DecisionTreeClassifier(max_depth=3) model.fit(X_train, y_train) predictions = model.predict(X_test)
8. Linear Regression (LinearRegression
)
- Wann wählen? Bei Regressionsproblemen, wenn die Beziehung zwischen den Features und der Zielvariable linear ist.
from sklearn.linear_model import LinearRegression model = LinearRegression() model.fit(X_train, y_train) predictions = model.predict(X_test)
9. KMeans Clustering (KMeans
)
- Wann wählen? Für unüberwachtes Lernen bei der Gruppierung von Daten in Cluster.
from sklearn.cluster import KMeans model = KMeans(n_clusters=3) model.fit(X_train) predictions = model.predict(X_test)
10. DBSCAN Clustering (DBSCAN
)
- Wann wählen? Wenn du unregelmässig geformte Cluster oder Ausreisser identifizieren möchtest.
from sklearn.cluster import DBSCAN model = DBSCAN(eps=0.5, min_samples=5) model.fit(X_train) predictions = model.labels_
11. Principal Component Analysis (PCA) (PCA
)
- Wann wählen? Wenn du die Dimensionalität der Daten reduzieren möchtest, um die Berechnungen zu beschleunigen oder die Visualisierung zu erleichtern.
from sklearn.decomposition import PCA pca = PCA(n_components=2) X_pca = pca.fit_transform(X_train)
12. Lasso and Ridge Regression (Ridge
, Lasso
)
- Wann wählen? Für Regressionsaufgaben, bei denen Regularisierung wichtig ist, um Überanpassung zu vermeiden.
from sklearn.linear_model import Ridge model = Ridge(alpha=1.0) model.fit(X_train, y_train) predictions = model.predict(X_test)
13. ElasticNet (ElasticNet
)
- Wann wählen? Wenn du sowohl Lasso- als auch Ridge-Regularisierung kombinieren möchtest, um die besten Eigenschaften beider zu nutzen.
from sklearn.linear_model import ElasticNet model = ElasticNet(alpha=1.0, l1_ratio=0.5) model.fit(X_train, y_train) predictions = model.predict(X_test)
Schritt 6: Hyperparameter-Tuning
Um die besten Parameter für dein Modell zu finden, kannst du erweiterte Suchmethoden verwenden. Diese Methoden helfen dabei, die besten Hyperparameter effizienter zu finden, als mit einfachen Techniken wie GridSearch.
1. Bayesian Optimization (BayesSearchCV
)
-
Wann wählen? Wenn du eine effiziente Suche nach den besten Parametern durchführen möchtest, indem du das Wissen aus früheren Versuchen nutzt. Bayesian Optimization berücksichtigt bereits getestete Parameter und fokussiert die Suche auf vielversprechendere Bereiche.
Vorteile:
- Schnellere Konvergenz, da es die Parameterverteilung analysiert und gezielt nach den besten Parametern sucht.
- Besonders nützlich bei teuren oder langwierigen Modelltrainings, bei denen eine grosse Anzahl an Parametern getestet werden muss.
Beispiel:
from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import train_test_split from skopt import BayesSearchCV import numpy as np # Example dataset X = np.random.rand(100, 2) # 100 samples and 2 features (random floats) y = np.random.randint(0, 2, size=100) # Binary target with 100 labels (0 or 1) # Split the dataset into training and testing sets X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.33, random_state=42) # Define the parameter space param_space = { 'n_estimators': (10, 200), # Number of trees in the Random Forest 'max_depth': (1, 20) # Maximum depth of the trees } # Define the model model = RandomForestClassifier() # Perform Bayesian Search with 20 iterations and 3-fold cross-validation opt = BayesSearchCV(model, param_space, n_iter=20, cv=3, n_jobs=-1) # Train the model opt.fit(X_train, y_train) # Print the best parameters found print("Beste Parameter:", opt.best_params_)
2. Evolutionary Computing Search (Optimierung mit Gaussian Process)
-
Wann wählen? Diese Methode ist besonders nützlich bei sehr komplexen Modellen und grossen Parameterbereichen, in denen die Suche nach optimalen Parametern durch zufällige oder vollständige Raster-Suche ineffizient wäre. Der Evolutionary Computing Search-Ansatz nutzt probabilistische Modelle (hier ein Gaussian Process), um die besten Parameter zu "evolutionieren" und die Suche effizienter zu gestalten, indem die Parameterkombinationen schrittweise angepasst werden.
Vorteile:
- Sehr leistungsfähig bei der Modellanpassung von hochdimensionalen Daten.
- Nutzt probabilistische Modellierung, um das Suchgebiet effizient zu erkunden und fundierte Entscheidungen zur Exploration der Parameter zu treffen.
- Reduziert die Notwendigkeit von umfangreichen Berechnungen, indem es die Suche auf vielversprechende Bereiche fokussiert.
Beispiel:
In diesem Beispiel verwenden wir den Gaussian Process-optimierten Evolutionsansatz von
skopt.Optimizer
, um die besten Hyperparameter für ein Support Vector Regression (SVR)-Modell zu finden. Der Parameterraum für die Optimierung umfasst zwei Hyperparameter:C
(Regularisierungsparameter) undepsilon
(Epsilon für den SVR):import numpy as np from sklearn.svm import SVR from skopt import Optimizer from skopt.space import Real from sklearn.model_selection import cross_val_score from sklearn.model_selection import train_test_split # Example dataset X = np.random.rand(100, 2) # 100 samples and 2 features (random floats) y = np.random.randint(0, 2, size=100) # Binary target with 100 labels (0 or 1) # Split the dataset into training and testing sets X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.33, random_state=42) # Define the parameter space for the optimization param_space = [ Real(1e-6, 1e+6, name='C'), Real(0.01, 1, name='epsilon') ] # Set the optimizer with a Gaussian Process-based evolutionary approach optimizer = Optimizer(param_space, base_estimator="GP", acq_func="EI", random_state=42) # Perform optimization over 20 iterations for i in range(20): # Iterate for 20 steps next_params = optimizer.ask() # Suggest next parameters model = SVR(C=next_params[0], epsilon=next_params[1]) # Create model with the suggested parameters score = np.mean(cross_val_score(model, X_train, y_train, cv=3)) # Use training data for cross-validation optimizer.tell(next_params, score) # Provide feedback to the optimizer # Print the best parameters found after optimization best_params = optimizer.Xi[np.argmax(optimizer.yi)] print(f"Beste Parameter: C = {best_params[0]}, epsilon = {best_params[1]}")
Erklärung:
- Der Code nutzt den Gaussian Process als Basis-Estimator für den Optimierungsprozess, um den besten Satz an Hyperparametern (
C
undepsilon
) für den Support Vector Regression (SVR)-Algorithmus zu finden. - In jedem Schritt schlägt der Optimizer neue Parameter vor, basierend auf den vorherigen Ergebnissen, und bewertet sie mit Kreuzvalidierung (
cross_val_score
) auf den Trainingsdaten (X_train
,y_train
). - Nach 20 Iterationen gibt der Optimierer die besten gefundenen Parameter aus, die die Leistung des Modells maximieren.
Dieser Ansatz ist besonders nützlich, wenn der Parameterraum gross oder hochdimensional ist und eine effiziente Suche erforderlich ist.
3. Monte Carlo Search
-
Wann wählen? Für Zufallssuche, wenn du keine systematische oder vollständige Untersuchung der Parameter benötigst. Monte Carlo Search führt die Suche auf Grundlage von zufälligen Samples der Parameter durch, ohne einen festen Raster zu definieren.
Vorteile:
- Sehr nützlich bei Modellen mit vielen Parametern oder sehr grossen Datensätzen, wo eine exhaustive Suche nicht praktikabel ist.
- Effizienter, wenn die Parameterbereiche weit und unklar sind.
Beispiel:
In diesem Beispiel verwenden wir Monte Carlo Search mithilfe von
skopt.Optimizer
und einem Zufallssampling-Ansatz, um die besten Hyperparameter für ein Modell zu finden. Dies wird durch das zufällige Sampling von Parametern aus einem definierten Parameterraum erreicht, ohne einen festen Raster zu verwenden.import numpy as np from sklearn.ensemble import RandomForestClassifier from skopt import Optimizer from skopt.space import Integer from sklearn.model_selection import cross_val_score from sklearn.model_selection import train_test_split # Beispiel-Daten X = np.random.rand(100, 2) # 100 Stichproben und 2 Merkmale (Zufallswerte) y = np.random.randint(0, 2, size=100) # Binäre Zielvariable mit 100 Labels (0 oder 1) # Splitte den Datensatz in Trainings- und Testdaten X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.33, random_state=42) # Definiere den Parameterraum für die Zufallssuche param_space = [ Integer(10, 200, name='n_estimators'), # Anzahl der Bäume im Random Forest Integer(1, 20, name='max_depth') # Maximale Tiefe der Bäume ] # Setze den Optimierer mit Monte Carlo Search optimizer = Optimizer(param_space, random_state=42) # Führe die Zufallssuche über 20 Iterationen durch for i in range(20): # Iteriere für 20 Schritte next_params = optimizer.ask() # Schlage nächste Parameter vor model = RandomForestClassifier(n_estimators=next_params[0], max_depth=next_params[1]) # Modell mit den vorgeschlagenen Parametern erstellen score = np.mean(cross_val_score(model, X_train, y_train, cv=3)) # Nutze Trainingsdaten für Cross-Validation optimizer.tell(next_params, score) # Gib Feedback an den Optimierer # Beste Parameter nach der Optimierung ausgeben best_params = optimizer.Xi[np.argmax(optimizer.yi)] print(f"Beste Parameter: n_estimators = {best_params[0]}, max_depth = {best_params[1]}")
Erklärung:
- Monte Carlo Search: Hier verwenden wir den
Optimizer
ausskopt
, um zufällig Parameter aus dem definierten Parameterraum zu ziehen. Diese zufällige Suche ist besonders nützlich, wenn der Parameterraum gross oder unklar ist und eine vollständige Raster-Suche zu ineffizient wäre. - Zufallssampling: Im Gegensatz zu einer vollständigen Grid-Suche werden die Parameter zufällig aus einem gegebenen Bereich (
Integer
-Werte fürn_estimators
undmax_depth
) ausgewählt. - Kreuzvalidierung: Nach jeder Auswahl von Parametern wird das Modell mit Kreuzvalidierung (
cross_val_score
) auf den Trainingsdaten bewertet. - Optimierer: Der Optimierer gibt nach 20 Iterationen die besten Parameter aus, die die Modellleistung maximiert haben.
Dieser Ansatz ist effizienter, wenn die Parameterbereiche weit und unklar sind, und er ermöglicht eine flexible, zufällige Exploration des Parameterraums.
Schritt 7: Vermeidung von Overfitting und Early Stopping
1. Kreuzvalidierung mit cross_val_score
Kreuzvalidierung ist eine sehr wichtige Technik, um sicherzustellen, dass dein Modell nicht nur auf den Trainingsdaten gut funktioniert, sondern auch auf neuen, ungesehenen Daten. Dabei wird der Datensatz in mehrere Teile unterteilt, und das Modell wird auf verschiedenen Kombinationen dieser Teile trainiert und validiert.
- Wann wählen? Wenn du sicherstellen möchtest, dass dein Modell gut auf ungesehenen Daten funktioniert und nicht überangepasst ist (Overfitting). Kreuzvalidierung hilft, die Leistung des Modells zu stabilisieren und zu überprüfen.
Kreuzvalidierung teilt den Datensatz in k
Teile (Folds) und trainiert das Modell k
-mal, wobei jeweils ein Teil als Testset und der Rest als Trainingsset verwendet wird.
Beispiel für Kreuzvalidierung:
from sklearn.model_selection import cross_val_score
from sklearn.ensemble import RandomForestClassifier
# Beispiel-Daten (X: Features, y: Zielvariable)
X = ... # Dein Trainingsdatensatz
y = ... # Deine Zielvariable
# Modell definieren
model = RandomForestClassifier()
# Kreuzvalidierung mit 5 Folds
scores = cross_val_score(model, X, y, cv=5) # cv=5 bedeutet 5-Fold Kreuzvalidierung
# Ausgabe der Durchschnittlichen Genauigkeit
print("Durchschnittliche Genauigkeit:", scores.mean())
- Was passiert hier?
- Der Datensatz wird in 5 Teile unterteilt.
- Das Modell wird 5-mal trainiert, wobei jeweils 4 Teile als Trainingsdaten und der verbleibende Teil als Testdaten verwendet wird.
- Die Ergebnisse aus den 5 Iterationen werden kombiniert, um eine stabilere Schätzung der Modellleistung zu erhalten.
- Das Mittel der Scores gibt die durchschnittliche Leistung des Modells auf den verschiedenen Teilen des Datensatzes wieder.
2. Early Stopping (bei GradientBoostingClassifier
)
Early Stopping ist eine Technik, die hilft, Überanpassung (Overfitting) zu vermeiden, indem das Training gestoppt wird, wenn das Modell keine signifikante Verbesserung mehr erzielt. Dies ist besonders nützlich bei iterativen Modellen wie Gradient Boosting, bei denen die Leistung des Modells im Laufe der Trainingszeit oft weiter verbessert wird, aber irgendwann zu einer Verschlechterung führt (Overfitting).
- Wann wählen? Wenn du ein iteratives Modell wie
GradientBoosting
oderXGBoost
verwendest und sicherstellen möchtest, dass das Modell nicht zu lange trainiert wird, was zu einer Überanpassung führen könnte. Early Stopping stoppt das Training, wenn die Leistung auf den Validierungsdaten sich nicht mehr verbessert.
Beispiel für Early Stopping:
from sklearn.ensemble import GradientBoostingClassifier
# Modell definieren
model = GradientBoostingClassifier(n_iter_no_change=5) # Stoppt nach 5 Iterationen ohne Verbesserung
# Trainieren des Modells
model.fit(X_train, y_train)
- Was passiert hier?
- Der
GradientBoostingClassifier
hat den Parametern_iter_no_change
, der das Modell anweist, das Training zu stoppen, wenn die Leistung auf den Validierungsdaten über 5 aufeinanderfolgende Iterationen hinweg nicht verbessert wird. - Dies verhindert, dass das Modell zu lange trainiert wird und dadurch anfängt, unnötig an den Trainingsdaten zu "lernen", was zu einer schlechteren Generalisierung führt.
- Der
Weitere Optionen für Early Stopping:
In vielen modernen Implementierungen von Gradient Boosting und anderen iterativen Modellen (wie XGBoost oder LightGBM) kann Early Stopping auch durch die Verwendung einer Validierungsmenge (z.B. durch die Verwendung von validation_fraction
oder early_stopping_rounds
) direkt implementiert werden. Hier ist ein Beispiel für Early Stopping bei XGBoost:
import xgboost as xgb
# Beispiel für Early Stopping in XGBoost
model = xgb.XGBClassifier()
# XGBoost mit Early Stopping
model.fit(X_train, y_train, eval_metric="logloss",
eval_set=[(X_val, y_val)],
early_stopping_rounds=10)
- Was passiert hier?
- In diesem Beispiel wird XGBoost mit einer Validierungsmenge (
eval_set=[(X_val, y_val)]
) trainiert. - Das Training wird gestoppt, wenn die Leistung auf den Validierungsdaten (
eval_metric="logloss"
) über 10 aufeinanderfolgende Runden hinweg nicht verbessert wird (early_stopping_rounds=10
).
- In diesem Beispiel wird XGBoost mit einer Validierungsmenge (
Schritt 8: Modellbewertung und Analyse
Nachdem dein Modell trainiert wurde, ist es wichtig, die Leistung zu bewerten. Dies geht über die reine Genauigkeit hinaus und berücksichtigt verschiedene Metriken und Interpretationsmethoden, die helfen, die Entscheidungsfindung des Modells nachzuvollziehen.
1. Verwendung von Metriken wie Precision, Recall, F1-Score und AUC-ROC
Diese Metriken sind besonders wichtig, wenn du dich auf die Balance zwischen den richtigen und falschen Klassifikationen konzentrieren möchtest. Sie bieten eine detailliertere Einsicht, insbesondere bei unausgeglichenen Klassen.
-
Precision (Präzision): Misst, wie viele der als positiv klassifizierten Beispiele tatsächlich positiv sind. Eine hohe Präzision bedeutet, dass das Modell wenige falsche Positiv-Klassifikationen macht.
-
Recall (Vollständigkeit): Misst, wie viele der tatsächlich positiven Beispiele korrekt als positiv klassifiziert wurden. Ein hoher Recall zeigt, dass das Modell nur wenige positive Fälle übersieht.
-
F1-Score: Der harmonische Mittelwert von Precision und Recall. Ein guter F1-Score zeigt an, dass sowohl Präzision als auch Recall in einem akzeptablen Bereich liegen.
-
AUC-ROC: Die Fläche unter der ROC-Kurve, die die Fähigkeit eines Klassifikators zur Unterscheidung zwischen den Klassen beschreibt. Ein Wert nahe 1 zeigt eine gute Trennfähigkeit des Modells an.
Wann wählen? Diese Metriken sind besonders nützlich, wenn du ein ausgewogenes Verhältnis zwischen der Erkennung positiver und negativer Fälle anstrebst, oder wenn du mit unausgewogenen Klassen arbeitest (z.B. bei Betrugserkennung oder seltenen Krankheitsdiagnosen).
Beispiel zur Berechnung dieser Metriken:
from sklearn.metrics import precision_score, recall_score, f1_score, roc_auc_score
# Vorhersagen des Modells
predictions = model.predict(X_test)
# Berechnung der Metriken
precision = precision_score(y_test, predictions)
recall = recall_score(y_test, predictions)
f1 = f1_score(y_test, predictions)
auc = roc_auc_score(y_test, model.predict_proba(X_test)[:, 1])
print(f"Precision: {precision}")
print(f"Recall: {recall}")
print(f"F1-Score: {f1}")
print(f"AUC-ROC: {auc}")
- Interpretation der Werte:
- Precision gibt an, wie genau das Modell ist, wenn es eine positive Vorhersage trifft. Eine hohe Präzision bedeutet wenige falsch positive Ergebnisse.
- Recall gibt an, wie gut das Modell in der Lage ist, alle positiven Fälle zu erkennen. Ein hoher Recall bedeutet, dass das Modell wenig relevante positive Beispiele übersieht.
- F1-Score ist besonders nützlich, wenn sowohl Precision als auch Recall wichtig sind. Ein Wert nahe 1 zeigt an, dass beide Metriken gut sind.
- AUC-ROC gibt an, wie gut das Modell zwischen den Klassen unterscheidet. Ein Wert nahe 1 zeigt, dass das Modell gut zwischen positiven und negativen Beispielen unterscheidet, während ein Wert nahe 0,5 auf zufällige Vorhersagen hinweist.
2. Feature Importance
Die Feature Importance hilft dir zu verstehen, welche Merkmale den grössten Einfluss auf das Modell haben. Diese Technik ist besonders nützlich bei Modellen wie Random Forest und Gradient Boosting, bei denen die Bedeutung der Eingabefunktionen direkt berechnet werden kann.
- Wann wählen? Wenn du herausfinden möchtest, welche Merkmale (Features) das Modell am meisten beeinflussen. Dies ist wichtig, um das Modell zu verstehen und mögliche Bias-Quellen zu identifizieren.
Beispiel zur Berechnung der Feature Importance:
import matplotlib.pyplot as plt
# Feature Importances aus dem Modell extrahieren
feature_importances = model.feature_importances_
# Visualisierung der Feature Importance
plt.figure(figsize=(12, 6))
plt.bar(range(len(feature_importances)), feature_importances)
plt.xticks(range(len(feature_importances)), feature_names, rotation=90)
plt.xlabel('Feature')
plt.ylabel('Importance')
plt.title('Feature Importance')
plt.show()
- Interpretation der Feature Importance:
- Die Balken in der Visualisierung geben an, wie wichtig jedes Feature für das Modell war. Ein hoher Wert bedeutet, dass das Feature einen grossen Einfluss auf die Vorhersagen hatte.
- Ein Modell mit niedriger Feature Importance für ein bestimmtes Feature könnte darauf hindeuten, dass dieses Feature keine wesentliche Rolle bei der Entscheidungsfindung spielt.
- Wichtig für die Modellanalyse: Die Identifizierung von wichtigen Features hilft, das Modell besser zu verstehen und mögliche Bias-Quellen zu finden, die auf nicht relevanten oder überflüssigen Features basieren.
3. Verstehen des Modells und seiner Entscheidungen
Um die Vorhersagen des Modells besser zu verstehen, kannst du verschiedene Techniken anwenden, die erklären, warum ein Modell eine bestimmte Entscheidung getroffen hat.
a) SHAP (Shapley Additive Explanations) Werte
- Wann wählen? Wenn du ein tieferes Verständnis darüber bekommen möchtest, wie jedes Feature die Vorhersage für ein bestimmtes Beispiel beeinflusst. SHAP-Werte bieten eine explizite Erklärung, wie jedes Feature zur Vorhersage des Modells beigetragen hat.
Beispiel für SHAP-Visualisierung:
import shap
# SHAP-Werte berechnen
explainer = shap.TreeExplainer(model)
shap_values = explainer.shap_values(X_test)
# SHAP-Werte visualisieren
shap.summary_plot(shap_values, X_test)
- Was passiert hier?
- SHAP berechnet den Einfluss jedes Features auf die Vorhersage des Modells. Die Visualisierung zeigt, wie jedes Feature das Modell für jedes Beispiel beeinflusst hat, und hilft, die Entscheidungen des Modells zu erklären.
- Interpretation: Jeder Punkt auf dem Diagramm repräsentiert einen Einfluss des Features auf die Vorhersage. Ein hoher Einfluss (positiv oder negativ) bedeutet, dass das Feature eine starke Rolle in der Entscheidung gespielt hat.
b) Partial Dependence Plots (PDP)
- Wann wählen? Wenn du die Beziehung zwischen einem oder mehreren Features und der Zielvariable untersuchen möchtest, während alle anderen Features konstant gehalten werden.
Beispiel für Partial Dependence Plot:
from sklearn.inspection import PartialDependenceDisplay
import matplotlib.pyplot as plt
# PDP für das Modell erstellen
disp = PartialDependenceDisplay.from_estimator(
model, X_train, features=[0, 1], feature_names=feature_names
)
# Show the plot
plt.title('Partial Dependence Plot (PDP) for Feature 0 and Feature 1')
plt.show()
- Was passiert hier?
- Der Partial Dependence Plot zeigt, wie sich die Vorhersage des Modells in Bezug auf ein oder mehrere Features verändert, während alle anderen Features konstant gehalten werden.
- Interpretation: Wenn der Plot eine klare, lineare oder nichtlineare Beziehung zwischen einem Feature und der Zielvariable zeigt, bedeutet das, dass dieses Feature einen wesentlichen Einfluss auf die Vorhersage hat.
4. Bias und Fairness im Modell
Durch die Analyse von Feature Importance, SHAP-Werten und PDPs kannst du Rückschlüsse darauf ziehen, ob dein Modell potenziell biasbehaftet ist.
- Bias tritt auf, wenn ein Modell systematisch bestimmte Gruppen oder Merkmale benachteiligt. Du kannst Bias erkennen, indem du die Wichtigkeit der Features und die Auswirkungen von spezifischen Merkmalen analysierst.
- Wenn das Modell z.B. bestimmte demografische Merkmale wie Geschlecht oder Ethnie übermässig gewichtet, könnte dies auf Bias hinweisen.
Durch das Testen auf Bias und Fairness kannst du sicherstellen, dass dein Modell gerecht und objektiv bleibt, was besonders in sensiblen Anwendungsbereichen wie Finanzdienstleistungen oder Gesundheitspflege von Bedeutung ist.
Fazit: Eine flexible, skalierbare und nachvollziehbare ML-Pipeline aufbauen
Durch die Wahl der richtigen Schritte und Varianten in der Pipeline kannst du ein robustes, leistungsfähiges Modell entwickeln. Bei der Modellwahl, der Feature-Vorverarbeitung und der Hyperparameter-Optimierung gibt es viele Optionen, die dir helfen können, das beste Modell für deine Daten zu finden.