data transformation

Pipelines mit sklearn

Martin Albers Blog, Data Science

Es wird häufig angenommen, dass die meiste Arbeit von Data Scientisten darin besteht Algorithmen auszuwählen und zu tunen. Eine Umfrage von Crowdflower aus dem Jahr 2016 hat jedoch ergeben, dass die meiste Zeit dafür aufgewendet wird, Daten zu bereinigen und zu transformieren.

Auch bei modernen Algorithmen im Bereich des Deep Learning ist dies nötig, um entweder die Performance des Modells zu erhöhen, oder die Rechenzeit zu verkürzen. Bei der Datenvorbereitung werden zum Beispiel fehlende Werte ersetzt, Ausreißer erkannt oder Daten logarithmiert. Auch bei STATWORX macht dies einen großen Teil der Arbeit bei einem Projekt aus, weshalb sich dieser Beitrag in frühere Blog Beiträge zur Datenvorbereitung, wie zum Beispiel dem Vergleich der Funktionen zur Datenvorbereitung der verschiedenen Softwarepakete, einreiht. Durch die hier vorgestellten Pipelines wird es ermöglicht, viele Transformationsschritte übersichtlich und reproduzierbar zu halten.

Die übliche Bearbeitung mit sklearn

In dem wohl wichtigsten Modul für Machine Learning in Python „skLearn“ wurde ein eigenes Modul mit speziellen Funktionen zur Verarbeitung der Daten implementiert. Die verschiedenen Funktionen werden dabei über fit_transform() auf den Trainings- sowie Testdatensatz angewandt. Die Anwendung muss dabei für beide Datensätze einzeln erfolgen. Dies ist nötig, um einen Einfluss der Testdaten auf den Trainingsdatensatz zu verhindern („Data Leakage“). Der folgende Code imputiert beispielsweise fehlende Werte und zentriert die Daten:

# Einfügen des Durchschnitts in fehlende Werte
impute=Imputer(strategy="mean")
X_train_impute=impute.fit_transform(X_train)
X_test_impute=impute.fit_transform(X_test)

# Skalieren der Variablen
scaler=StandardScaler()
X_train_scaled=scaler.fit_transform(X_train_impute)
X_test_scaled=scaler.fit_transform(X_test_impute)

Bereits hier ist zu erkennen, dass bei aufwendigeren Transformationen die jeweilige Anwendung auf Trainings- und Testdaten sehr unübersichtlich wird. Hier kann die Nutzung von Pipelines einen großen Vorteil bieten.

Die Nutzung von Pipelines

Um diese Schritte zu vereinfachen, wurde in sklearn die Funktion Pipeline eingeführt. In der Funktion können die einzelnen Transformation einfach übergeben werden und Python übernimmt die Übergabe an die nächste Funktion.

Nun ersetzen wir die fehlenden Werte und zentrieren die Daten mithilfe einer Pipeline:

# Definition der Transformationen
impute = Imputer(strategy="mean")
scaler = StandardScaler()

# Definition der Pipeline
pipe = Pipeline(steps=[('impute',impute),('scaler',scaler)])

# Anwenden auf Trainings- und Testdaten:
X_train = pipe.fit_transform(X_train)
X_test = pipe.fit_transform(X_test)

Der Pipeline Funktion wird dabei eine Liste von Tupeln übergeben, in denen jeweils der Name sowie die Funktion angegeben ist. Über die Funktion make_pipeline entfällt die Namensgebung der Funktionen wodurch wir pipe = make_pipeline(impute, scaler) erhalten.

Auch die direkte Anwendung von Algorithmen ist möglich. Dazu kann der entsprechende Algorithmus einfach als weiteres Tupel in der Pipeline aufgenommen werden. Als Beispiel wird hier die Nutzung einer Random Forest Regression gezeigt:

# Transformation
imputer = Imputer(strategy="mean")
scaler = StandardScaler()

# Random Forest Regression
regressor = RandomForestRegressor(n_estimators=100)

# Bilden und ausführen der Pipeline
pipe = Pipeline(steps= [('imp',imputer),('scaler',scaler),('Regressor',regressor)])
pipe.fit(X_train)

Die große Flexilibität von Pipelines und sklearn zeigt sich vor allem durch die Nutzung der in sklearn implementierten Funktion FunctionTransformer(). Diese ermöglich es aus jeder nutzerdefinierten Funktion eine in der Pipeline nutzbare Funktion zu erstellen, auf die die Methoden fit und transform angewendet werden können. So können als Teil der Pipeline Variablen durch die Funktion square = FunctionTransformer(np.square) quadriert werden.

Kompliziertere Funktionen können durch TransformerMixin erstellt werden. Dabei wird eine Klasse erstellt sowie die zugehörigen Methoden fit und transform definiert. Beispielhaft wird hier eine Funktion gezeigt, die bei kategorialen Variablen den häufigsten Wert für fehlende Werte einsetzt:

# Impute des häufigsten Wertes:
class Imputer_Most_Frequent(TransformerMixin):

         def fit(self, X, y=None):
             return self

         def transform(self, X, y=None):
             self.fill = X.mode().iloc[0]
             return X.fillna(self.fill)

Zusammenfassung

Ein Modell funktioniert nur, wenn Daten für den Algorithmus vernünftig aufbereitet werden. Eine sorgfältige Datenpräparation kann auch sehr viel Zeit bei der Modellierung sparen, da man Werte bspw. durch Skalierung vereinfacht. Außerdem kann man mit einem sauber präparierten Datensatz leicht
neue Features generieren. Wir bei Statworx führen bei unseren Kundenprojekten daher komplizierte und lange Vorbereitungsschritte in unseren Projekten durch. Die Nutzung von Pipelines hilft uns dabei den Code übersichtlicher, einfacher und damit besser verständlich zu gestalten.

Über den Autor

Martin Albers