Ensemble de différents types de régresseurs utilisant scikit-learn (ou tout autre framework python)

27

J'essaie de résoudre la tâche de régression. J'ai découvert que 3 modèles fonctionnent parfaitement pour différents sous-ensembles de données: LassoLARS, SVR et Gradient Tree Boosting. J'ai remarqué que lorsque je fais des prédictions en utilisant tous ces 3 modèles, puis que je fais un tableau de la «sortie réelle» et des sorties de mes 3 modèles, je vois que chaque fois au moins un des modèles est vraiment proche de la sortie réelle, bien que 2 autres pourrait être relativement loin.

Lorsque je calcule une erreur minimale possible (si je prends la prédiction du «meilleur» prédicteur pour chaque exemple de test), j'obtiens une erreur qui est beaucoup plus petite que l'erreur de n'importe quel modèle seul. J'ai donc pensé à combiner les prédictions de ces 3 modèles différents dans une sorte d'ensemble. La question est, comment faire cela correctement? Tous mes 3 modèles sont construits et ajustés à l'aide de scikit-learn, fournit-il une sorte de méthode qui pourrait être utilisée pour emballer des modèles dans un ensemble? Le problème ici est que je ne veux pas simplement faire la moyenne des prévisions des trois modèles, je veux le faire avec la pondération, où la pondération doit être déterminée en fonction des propriétés d'un exemple spécifique.

Même si scikit-learn ne fournit pas une telle fonctionnalité, il serait bien que quelqu'un sache comment gérer cette tâche - de déterminer la pondération de chaque modèle pour chaque exemple dans les données. Je pense que cela pourrait être fait par un régresseur séparé construit au-dessus de tous ces 3 modèles, qui essaiera de produire des poids optimaux pour chacun des 3 modèles, mais je ne suis pas sûr que ce soit la meilleure façon de le faire.

Maksim Khaitovich
la source

Réponses:

32

En fait, scikit-learnfournit une telle fonctionnalité, même si elle peut être un peu délicate à mettre en œuvre. Voici un exemple de travail complet d'un tel régresseur moyen construit sur trois modèles. Tout d'abord, importons tous les packages requis:

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

Ensuite, nous devons convertir nos trois modèles de régresseurs en transformateurs. Cela nous permettra de fusionner leurs prédictions en un seul vecteur d'entités en utilisant 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)

Maintenant, définissons une fonction de construction pour notre modèle frankenstein:

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

Enfin, ajustons le modèle:

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)

Sortie:

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

Pourquoi se donner la peine de compliquer les choses de cette façon? Eh bien, cette approche nous permet d'optimiser les hyperparamètres du modèle à l'aide de scikit-learnmodules standard tels que GridSearchCVou RandomizedSearchCV. De plus, il est désormais possible d'enregistrer et de charger facilement à partir du disque un modèle pré-formé.

constt
la source
En utilisant cette approche, existe-t-il un moyen simple d'extraire quel algo est utilisé quand / quelle fraction de chaque algo?
David Hagan
Peut-être que l'examen des coefficients du modèle linéaire résultant ( model.named_steps['lin_regr'].coef_) vous donnera un aperçu de la contribution de chaque modèle d'un ensemble à la solution finale.
Constt
@constt N'auriez-vous pas besoin d'utiliser cross_val_predict dans vos modèles de base? Il semble que votre modèle de niveau supérieur obtiendrait un signal trop optimiste de vos modèles de base car il est actuellement implémenté.
Brian Bien
1
Ceci est juste un peu un exemple de preuve de concept, je n'ai pas abordé une sélection de modèle ici. Je pense que ces modèles devraient être optimisés dans leur ensemble, c'est-à-dire en optimisant simultanément les hyper-paramètres de tous les modèles intégrés en utilisant l'approche de validation croisée.
constt
si nous mettons n_targets = 1, X, y = make_regression(n_features=10, n_targets=1)cela donne une erreur de dimension. quelqu'un peut-il expliquer quoi faire?
Mohit Yadav
9

Ok, après avoir passé un peu de temps sur Google, j'ai découvert comment je pouvais faire la pondération en python même avec scikit-learn. Considérez ce qui suit:

Je forme un ensemble de mes modèles de régression (comme mentionné SVR, LassoLars et GradientBoostingRegressor). Ensuite, je les exécute tous sur des données d'entraînement (mêmes données qui ont été utilisées pour l'entraînement de chacun de ces 3 régresseurs). J'obtiens des prédictions pour des exemples avec chacun de mes algorithmes et j'enregistre ces 3 résultats dans une trame de données pandas avec les colonnes 'PrededSVR', 'PrededLASSO' et 'PrededGBR'. Et j'ajoute la dernière colonne dans cette datafrane que j'appelle «prédit» qui est une vraie valeur de prédiction.

Ensuite, je forme juste une régression linéaire sur cette nouvelle trame de données:

 #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'])

Donc, quand je veux faire une prédiction pour un nouvel exemple, je lance simplement chacun de mes 3 régresseurs séparément, puis je fais:

 stacker.predict() 

sur les sorties de mes 3 régresseurs. Et obtenez un résultat.

Le problème ici est que je trouve en moyenne des poids optimaux pour les régresseurs, les poids seront les mêmes pour chaque exemple sur lequel j'essaierai de faire des prédictions.

Si quelqu'un a des idées sur la façon d'empiler (pondérer) en utilisant les fonctionnalités de l'exemple actuel, ce serait bien de les entendre.

Maksim Khaitovich
la source
Wow, j'aime beaucoup cette approche! Mais pourquoi avez-vous utilisé à la LinearRegression()place du LogisticRegression()modèle?
harrison4
1
@ harrison4 parce que je faisais une régression, pas une tâche de classification? Je voulais donc «pondérer» la sortie de chaque modèle. Quoi qu'il en soit, c'est une mauvaise approche, une bonne est décrite ici: stackoverflow.com/a/35170149/3633250
Maksim Khaitovich
Ouais, désolé tu as raison! Merci d'avoir partagé le lien!
harrison4
5

Si vos données ont des sous-ensembles évidents, vous pouvez exécuter un algorithme de clustering comme k-means, puis associer chaque classificateur aux clusters sur lesquels il fonctionne bien. Lorsqu'un nouveau point de données arrive, déterminez dans quel cluster il se trouve et exécutez le classificateur associé.

Vous pouvez également utiliser les distances inverses des centroïdes pour obtenir un ensemble de poids pour chaque classificateur et prédire en utilisant une combinaison linéaire de tous les classificateurs.

anthonybell
la source
J'ai trouvé un article testé cette stratégie (avec une comparaison d'idées similaires): papier
anthonybell
Idée intéressante, mais nécessite beaucoup de travail pour l'appliquer. Merci pour le papier!
Maksim Khaitovich
1

J'accomplis un type de pondération en procédant comme suit, une fois que tous vos modèles sont parfaitement formés et fonctionnent bien:

  1. Exécutez tous vos modèles sur un grand ensemble de données de test invisibles
  2. Stocker les scores f1 sur le test pour chaque classe, pour chaque modèle
  3. Lorsque vous prédisez avec l'ensemble, chaque modèle vous donnera la classe la plus probable, alors pondérez la confiance ou la probabilité par le score f1 pour ce modèle sur cette classe. Si vous avez affaire à la distance (comme dans SVM, par exemple), normalisez simplement les distances pour obtenir une confiance générale, puis procédez à la pondération f1 par classe.

Vous pouvez affiner votre ensemble en prenant la mesure du pourcentage correct sur une certaine période. Une fois que vous avez un nouvel ensemble de données significativement important, vous pouvez tracer le seuil par étapes de 0,1, par exemple, contre un pourcentage correct si vous utilisez ce seuil pour marquer, pour avoir une idée de ce seuil qui vous donnera, disons, 95% de correct pour la classe 1, etc. Vous pouvez continuer à mettre à jour l'ensemble de tests et les scores f1 à mesure que de nouvelles données arrivent et suivre la dérive, reconstruire les modèles lorsque les seuils ou la précision tombent.

wwwslinger
la source
1
C'est intéressant, mais cela ne fonctionne que pour les tâches de classification, pour autant que je sache, pendant que j'essaie de résoudre la tâche de régression. Je ne peux donc pas calculer le score F1.
Maksim Khaitovich