Ensemble verschiedener Arten von Regressoren, die Scikit-Learn (oder ein anderes Python-Framework) verwenden

27

Ich versuche die Regressionsaufgabe zu lösen. Ich habe herausgefunden, dass 3 Modelle für verschiedene Teilmengen von Daten gut funktionieren: LassoLARS, SVR und Gradient Tree Boosting. Mir ist aufgefallen, dass ich, wenn ich mit all diesen drei Modellen Vorhersagen mache und dann eine Tabelle mit den tatsächlichen Ergebnissen meiner drei Modelle erstelle, sehe, dass jedes Mal mindestens eines der Modelle den tatsächlichen Ergebnissen sehr nahe kommt, obwohl es zwei andere sind könnte relativ weit weg sein.

Wenn ich den minimal möglichen Fehler berechne (wenn ich für jedes Testbeispiel die Vorhersage vom 'besten' Prädiktor nehme), erhalte ich einen Fehler, der viel kleiner ist als der Fehler eines Modells allein. Also dachte ich darüber nach, Vorhersagen aus diesen drei verschiedenen Modellen zu einer Art Ensemble zusammenzufassen. Die Frage ist, wie man das richtig macht? Alle meine 3 Modelle werden mit Hilfe von Scikit-Learn erstellt und optimiert. Bietet es eine Methode, um Modelle in ein Ensemble zu packen? Das Problem hierbei ist, dass ich nicht nur die Vorhersagen aller drei Modelle mitteln möchte, sondern dies mit Gewichtung tun möchte, wobei die Gewichtung auf der Grundlage von Eigenschaften eines bestimmten Beispiels bestimmt werden sollte.

Auch wenn scikit-learn diese Funktionalität nicht bietet, wäre es schön, wenn jemand weiß, wie man diese Aufgabe löst - die Gewichtung jedes Modells für jedes Beispiel in Daten herauszufinden. Ich denke, dass es durch einen separaten Regressor möglich ist, der auf all diesen 3 Modellen aufbaut und versucht, für jedes der 3 Modelle ein optimales Gewicht auszugeben, aber ich bin mir nicht sicher, ob dies der beste Weg ist, dies zu tun.

Maksim Khaitovich
quelle

Antworten:

32

Tatsächlich scikit-learnbietet eine solche Funktionalität, obwohl es ein bisschen schwierig zu implementieren sein könnte. Hier ist ein vollständiges Arbeitsbeispiel eines solchen durchschnittlichen Regressors, der auf drei Modellen aufbaut. Importieren wir zunächst alle benötigten Pakete:

from sklearn.base import TransformerMixin
from sklearn.datasets import make_regression
from sklearn.pipeline import Pipeline, FeatureUnion
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestRegressor
from sklearn.neighbors import KNeighborsRegressor
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
from sklearn.linear_model import LinearRegression, Ridge

Dann müssen wir unsere drei Regressormodelle in Transformatoren umwandeln. Auf diese Weise können wir ihre Vorhersagen zu einem einzigen Merkmalsvektor zusammenführen, indem wir Folgendes verwenden FeatureUnion:

class RidgeTransformer(Ridge, TransformerMixin):

    def transform(self, X, *_):
        return self.predict(X)


class RandomForestTransformer(RandomForestRegressor, TransformerMixin):

    def transform(self, X, *_):
        return self.predict(X)


class KNeighborsTransformer(KNeighborsRegressor, TransformerMixin):

    def transform(self, X, *_):
        return self.predict(X)

Definieren wir nun eine Builder-Funktion für unser Frankenstein-Modell:

def build_model():
    ridge_transformer = Pipeline(steps=[
        ('scaler', StandardScaler()),
        ('poly_feats', PolynomialFeatures()),
        ('ridge', RidgeTransformer())
    ])

    pred_union = FeatureUnion(
        transformer_list=[
            ('ridge', ridge_transformer),
            ('rand_forest', RandomForestTransformer()),
            ('knn', KNeighborsTransformer())
        ],
        n_jobs=2
    )

    model = Pipeline(steps=[
        ('pred_union', pred_union),
        ('lin_regr', LinearRegression())
    ])

    return model

Zum Schluss passen wir das Modell an:

print('Build and fit a model...')

model = build_model()

X, y = make_regression(n_features=10, n_targets=2)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

model.fit(X_train, y_train)
score = model.score(X_test, y_test)

print('Done. Score:', score)

Ausgabe:

Build and fit a model...
Done. Score: 0.9600413867438636

Warum sich die Mühe machen, die Dinge so zu komplizieren? Nun, dieser Ansatz ermöglicht es uns, Modell-Hyperparameter mit Standardmodulen scikit-learnwie GridSearchCVoder zu optimieren RandomizedSearchCV. Außerdem ist es jetzt möglich, ein vorab trainiertes Modell auf einfache Weise zu speichern und von der Festplatte zu laden.

Konst
quelle
Gibt es bei diesem Ansatz eine einfache Möglichkeit, zu extrahieren, welches Algo wann verwendet wird / welcher Anteil von jedem Algo?
David Hagan
Wenn Sie sich die Koeffizienten des resultierenden linearen Modells ( model.named_steps['lin_regr'].coef_) ansehen, erhalten Sie möglicherweise einige Einblicke, inwieweit jedes Modell in einem Ensemble zur endgültigen Lösung beiträgt.
07.
@constt Müssen Sie cross_val_predict nicht in Ihren Basismodellen verwenden? Es scheint, als würde Ihr Top-Level-Modell ein überoptimistisches Signal von Ihren Basismodellen erhalten, da dies derzeit implementiert ist.
Brian Bien
1
Dies ist nur ein Proof-of-Concept-Beispiel, ich habe hier keine Modellauswahl angesprochen. Ich denke, dass solche Modelle als Ganzes optimiert werden sollten, dh die Hyperparameter aller eingebauten Modelle gleichzeitig unter Verwendung des Cross-Validation-Ansatzes zu optimieren.
Konst.
Wenn wir n_targets = 1 setzen X, y = make_regression(n_features=10, n_targets=1), ergibt sich ein Dimensionsfehler. kann mir bitte jemand erklären was zu tun ist
Mohit Yadav
9

Ok, nachdem ich einige Zeit mit googeln verbracht hatte, fand ich heraus, wie ich das Gewichten in Python auch mit Scikit-Learn machen konnte. Betrachten Sie das Folgende:

Ich trainiere eine Reihe meiner Regressionsmodelle (wie erwähnt SVR, LassoLars und GradientBoostingRegressor). Dann führe ich sie alle mit Trainingsdaten durch (dieselben Daten, die für das Training jedes dieser 3 Regressoren verwendet wurden). Ich erhalte Vorhersagen für Beispiele mit jedem meiner Algorithmen und speichere diese 3 Ergebnisse in einem Pandadatenrahmen mit den Spalten "predictedSVR", "predictedLASSO" und "predictedGBR". Und ich füge die letzte Spalte in diesen Datenbereich ein, den ich "vorhergesagt" nenne, was ein echter Vorhersagewert ist.

Dann trainiere ich einfach eine lineare Regression für diesen neuen Datenrahmen:

 #df - dataframe with results of 3 regressors and true output

 from sklearn linear_model
 stacker= linear_model.LinearRegression()
 stacker.fit(df[['predictedSVR', 'predictedLASSO', 'predictedGBR']], df['predicted'])

Wenn ich also eine Vorhersage für ein neues Beispiel machen möchte, führe ich einfach jeden meiner 3 Regressoren separat aus und dann mache ich Folgendes:

 stacker.predict() 

auf den Ausgängen meiner 3 Regressoren. Und ein Ergebnis bekommen.

Das Problem hierbei ist, dass ich im Durchschnitt optimale Gewichte für Regressoren finde. Die Gewichte sind für jedes Beispiel, für das ich versuchen werde, Vorhersagen zu treffen, gleich.

Wenn jemand eine Idee hat, wie man mit den Funktionen des aktuellen Beispiels stapelt (gewichtet), wäre es schön, sie zu hören.

Maksim Khaitovich
quelle
Wow, ich mag diesen Ansatz sehr! Aber warum hast du LinearRegression()anstatt LogisticRegression()Modell benutzt?
Harrison4
1
@ Harrison4, weil ich Regression machte, keine Klassifizierungsaufgabe? Deshalb wollte ich die Ausgabe jedes Modells gewichten. Wie auch immer, dies ist ein schlechter Ansatz, ein guter wird hier beschrieben: stackoverflow.com/a/35170149/3633250
Maksim Khaitovich
Ja, tut mir leid, du hast recht! Vielen Dank für den Link!
Harrison4
5

Wenn Ihre Daten offensichtliche Teilmengen haben, können Sie einen Cluster-Algorithmus wie k-means ausführen und dann jeden Klassifikator den Clustern zuordnen, für die er eine gute Leistung erbringt. Wenn ein neuer Datenpunkt eintrifft, bestimmen Sie, in welchem ​​Cluster er sich befindet, und führen Sie den zugehörigen Klassifizierer aus.

Sie können auch die umgekehrten Abstände von den Schwerpunkten verwenden, um eine Reihe von Gewichten für jeden Klassifikator zu erhalten und eine lineare Kombination aller Klassifikatoren vorauszusagen.

anthonybell
quelle
Ich habe ein Papier gefunden, das diese Strategie getestet hat (zusammen mit einem Vergleich einiger ähnlicher Ideen): paper
anthonybell
Interessante Idee, erfordert aber viel Arbeit, um sie anzuwenden. Danke für das Papier!
Maksim Khaitovich
1

Ich bewerkstellige eine Art Gewichtung, indem ich Folgendes tue, sobald alle Ihre Modelle vollständig trainiert sind und eine gute Leistung erbringen:

  1. Führen Sie alle Ihre Modelle mit einer Vielzahl von unsichtbaren Testdaten aus
  2. Speichern Sie die f1-Ergebnisse für jede Klasse und für jedes Modell im Testset
  3. Wenn Sie mit dem Ensemble vorhersagen, gibt Ihnen jedes Modell die wahrscheinlichste Klasse. Gewichten Sie also das Vertrauen oder die Wahrscheinlichkeit mit der f1-Punktzahl für dieses Modell in dieser Klasse. Wenn Sie sich mit Distanz befassen (wie zum Beispiel in SVM), normalisieren Sie einfach die Distanzen, um ein allgemeines Vertrauen zu erhalten, und fahren Sie dann mit der Gewichtung pro Klasse f1 fort.

Sie können Ihr Ensemble weiter stimmen, indem Sie im Laufe der Zeit den korrekten Prozentsatz messen. Sobald Sie einen signifikant großen, neuen Datensatz erhalten haben, können Sie den Schwellenwert in Schritten von 0,1 darstellen, beispielsweise gegen den korrekten Prozentsatz, wenn Sie diesen Schwellenwert für die Bewertung verwenden, um eine Vorstellung davon zu erhalten, welcher Schwellenwert zu 95% korrekt ist für Klasse 1 und so weiter. Sie können den Testsatz und die f1-Ergebnisse aktualisieren, sobald neue Daten eingehen, und die Drift verfolgen und die Modelle neu erstellen, wenn die Schwellenwerte oder die Genauigkeit sinken.

wwwslinger
quelle
1
Das ist interessant, funktioniert aber meines Erachtens nur für Klassifizierungsaufgaben, während ich versuche, die Regressionsaufgabe zu lösen. Daher kann ich keine F1-Punkte berechnen.
Maksim Khaitovich