Insieme di diversi tipi di regressori che usano scikit-learn (o qualsiasi altro framework python)


27

Sto cercando di risolvere l'attività di regressione. Ho scoperto che 3 modelli funzionano bene per diversi sottoinsiemi di dati: LassoLARS, SVR e Gradient Tree Boosting. Ho notato che quando faccio previsioni usando tutti questi 3 modelli e poi faccio una tabella di "output reale" e output dei miei 3 modelli, vedo che ogni volta almeno uno dei modelli è davvero vicino all'output vero, anche se altri 2 potrebbe essere relativamente lontano.

Quando calcolo l'errore minimo possibile (se prendo la previsione dal predittore "migliore" per ciascun esempio di test), ricevo un errore che è molto più piccolo dell'errore di qualsiasi modello da solo. Quindi ho pensato di provare a combinare le previsioni di questi 3 diversi modelli in una sorta di ensemble. La domanda è: come farlo correttamente? Tutti i miei 3 modelli sono costruiti e messi a punto utilizzando scikit-learn, fornisce un metodo che potrebbe essere usato per impacchettare i modelli in un ensemble? Il problema qui è che non voglio solo previsioni medie di tutti e tre i modelli, voglio farlo con la ponderazione, dove la ponderazione dovrebbe essere determinata sulla base di proprietà di esempio specifico.

Anche se scikit-learn non fornisce tale funzionalità, sarebbe bello se qualcuno sapesse come indirizzare la proprietà a questo compito - di capire la ponderazione di ciascun modello per ogni esempio nei dati. Penso che potrebbe essere fatto da un regressore separato costruito su tutti e 3 questi modelli, che proverà a produrre pesi ottimali per ciascuno dei 3 modelli, ma non sono sicuro che questo sia il modo migliore per farlo.

Risposte:


32

In realtà, scikit-learnfornisce una tale funzionalità, anche se potrebbe essere un po 'complicato da implementare. Ecco un esempio di lavoro completo di un regressore così medio costruito su tre modelli. Prima di tutto, importiamo tutti i pacchetti richiesti:

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

Quindi, dobbiamo convertire i nostri tre modelli di regressore in trasformatori. Questo ci consentirà di unire le loro previsioni in un singolo vettore di funzionalità utilizzando 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)

Ora, definiamo una funzione di costruzione per il nostro modello 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

Infine, adattiamo il modello:

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)

Produzione:

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

Perché preoccuparsi di complicare le cose in questo modo? Bene, questo approccio ci consente di ottimizzare i iperparametri del modello utilizzando scikit-learnmoduli standard come GridSearchCVo RandomizedSearchCV. Inoltre, ora è possibile salvare e caricare facilmente dal disco un modello pre-addestrato.


Quando si utilizza questo approccio, esiste un modo semplice per estrarre quale algo viene utilizzato quando / quale frazione di ciascun algo?
David Hagan,

Forse guardare i coefficienti del modello lineare risultante ( model.named_steps['lin_regr'].coef_) ti darà alcuni spunti su quanto ciascun modello in un insieme contribuisce alla soluzione finale.
const

@constt Non avresti bisogno di usare cross_val_predict nei tuoi modelli base? Sembra che il tuo modello di livello superiore riceverebbe un segnale troppo ottimistico dai tuoi modelli di base poiché questo è attualmente implementato.
Brian Bien,

1
Questo è solo un esempio di prova del concetto, non ho affrontato una selezione di modelli qui. Penso che tali modelli debbano essere ottimizzati nel loro insieme, cioè ottimizzando gli iperparametri di tutti i modelli integrati contemporaneamente usando l'approccio di cross-validation.
const

se mettiamo n_targets = 1, X, y = make_regression(n_features=10, n_targets=1)si ottiene un errore di dimensione. qualcuno può spiegare cosa fare?
Mohit Yadav,

9

Ok, dopo aver trascorso un po 'di tempo a cercare su google ho scoperto come poter ponderare Python anche con Scikit-Learn. Considera quanto segue:

Alleno una serie dei miei modelli di regressione (come menzionato SVR, LassoLars e GradientBoostingRegressor). Quindi li eseguo tutti sui dati di allenamento (stessi dati utilizzati per l'addestramento di ciascuno di questi 3 regressori). Ricevo previsioni per esempi con ciascuno dei miei algoritmi e salvo questi 3 risultati in un frame di dati Panda con le colonne "predictedSVR", "predictedLASSO" e "predictedGBR". E aggiungo la colonna finale in questo frame di dati che chiamo "predetto", che è un vero valore di predizione.

Quindi alleno solo una regressione lineare su questo nuovo frame di dati:

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

Quindi, quando voglio fare una previsione per un nuovo esempio, eseguo ciascuno dei miei 3 regressori separatamente e quindi faccio:

 stacker.predict() 

sulle uscite dei miei 3 regressori. E ottieni un risultato.

Il problema qui è che sto trovando pesi ottimali per i regressori in media, i pesi saranno gli stessi per ogni esempio su cui proverò a fare una previsione.

Se qualcuno ha qualche idea su come fare accatastamento (ponderazione) usando le funzionalità dell'esempio corrente, sarebbe bello ascoltarli.


Wow, mi piace molto questo approccio! Ma perché hai usato al LinearRegression()posto del LogisticRegression()modello?
harrison4,

1
@ harrison4 perché stavo facendo regressione, non compito di classificazione? Quindi volevo "ponderare" l'output di ciascun modello. In ogni caso, questo è un cattivo approccio, buona è descritto qui: stackoverflow.com/a/35170149/3633250
Maksim Khaitovich

Sì, scusa hai ragione! grazie per aver condiviso il link!
harrison4,

5

Se i tuoi dati hanno sottoinsiemi evidenti, potresti eseguire un algoritmo di clustering come k-medie e quindi associare ciascun classificatore ai cluster su cui funziona bene. Quando arriva un nuovo punto dati, quindi determinare in quale cluster si trova ed eseguire il classificatore associato.

È inoltre possibile utilizzare le distanze inverse dai centroidi per ottenere un insieme di pesi per ciascun classificatore e prevedere utilizzando una combinazione lineare di tutti i classificatori.


Ho trovato un articolo testato questa strategia (insieme a un confronto di alcune idee simili): articolo
anthonybell

Un'idea interessante, anche se richiede molto lavoro per poterla appellare. Grazie per la carta!
Maksim Khaitovich,

1

Compito un tipo di ponderazione procedendo come segue, una volta che tutti i modelli sono stati completamente allenati e funzionano bene:

  1. Esegui tutti i tuoi modelli su un ampio set di dati di test invisibili
  2. Conservare i punteggi f1 sul set di test per ciascuna classe, per ciascun modello
  3. Quando prevedi con l'ensemble, ogni modello ti darà la classe più probabile, quindi valuta la confidenza o la probabilità in base al punteggio f1 per quel modello su quella classe. Se hai a che fare con la distanza (come in SVM, ad esempio), normalizza le distanze per ottenere una sicurezza generale, quindi procedi con la ponderazione per classe f1.

Puoi perfezionare ulteriormente il tuo ensemble prendendo la misura della percentuale corretta nel tempo. Una volta ottenuto un punteggio significativamente nuovo, un nuovo set di dati, è possibile tracciare la soglia con incrementi di 0,1, ad esempio contro la percentuale di correzione se si utilizza quella soglia per ottenere il punteggio, per avere un'idea di quale soglia fornirà, diciamo, corretta al 95% per la classe 1 e così via. È possibile continuare ad aggiornare il set di test e i punteggi f1 man mano che arrivano nuovi dati e tenere traccia della deriva, ricostruendo i modelli quando soglie o precisione cadono.


1
È interessante, ma funziona solo per le attività di classificazione, per quanto vedo, mentre sto cercando di risolvere l'attività di regressione. Quindi non posso calcolare il punteggio di F1.
Maksim Khaitovich,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.