Conjunto de diferentes tipos de regressores usando o scikit-learn (ou qualquer outra estrutura python)

27

Estou tentando resolver a tarefa de regressão. Descobri que três modelos estão funcionando bem para diferentes subconjuntos de dados: LassoLARS, SVR e Gradient Tree Boosting. Percebi que quando faço previsões usando todos esses três modelos e, em seguida, faço uma tabela de 'saída verdadeira' e saídas dos meus três modelos, vejo que cada vez que pelo menos um dos modelos está realmente próximo da saída real, embora outros dois poderia estar relativamente longe.

Quando eu calculo o erro possível mínimo (se eu usar a previsão do 'melhor' preditor para cada exemplo de teste), recebo um erro muito menor que o erro de qualquer modelo sozinho. Então, pensei em tentar combinar as previsões desses três modelos diferentes em algum tipo de conjunto. A questão é: como fazer isso corretamente? Todos os meus três modelos são construídos e ajustados usando o scikit-learn, ele fornece algum tipo de método que pode ser usado para agrupar modelos em conjunto? O problema aqui é que eu não quero apenas previsões médias dos três modelos. Quero fazer isso com ponderação, em que a ponderação deve ser determinada com base nas propriedades de um exemplo específico.

Mesmo que o scikit-learn não ofereça essa funcionalidade, seria bom se alguém soubesse como resolver essa tarefa - de descobrir o peso de cada modelo para cada exemplo nos dados. Eu acho que isso pode ser feito por um regressor separado, construído sobre todos esses três modelos, que tentará produzir pesos ideais para cada um dos três modelos, mas não tenho certeza se essa é a melhor maneira de fazer isso.

Maksim Khaitovich
fonte

Respostas:

32

Na verdade, scikit-learnfornece essa funcionalidade, embora possa ser um pouco complicado de implementar. Aqui está um exemplo de trabalho completo desse regressor médio, construído sobre três modelos. Primeiro, vamos importar todos os pacotes necessários:

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

Então, precisamos converter nossos três modelos de regressores em transformadores. Isso nos permitirá mesclar suas previsões em um único vetor de recurso usando 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)

Agora, vamos definir uma função de construtor para o nosso modelo de 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

Por fim, vamos ajustar o modelo:

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)

Saída:

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

Por que se preocupar em complicar as coisas dessa maneira? Bem, essa abordagem nos permite otimizar os hiperparâmetros do modelo usando scikit-learnmódulos padrão como GridSearchCVou RandomizedSearchCV. Além disso, agora é possível salvar e carregar facilmente do disco um modelo pré-treinado.

constt
fonte
Ao usar essa abordagem, existe uma maneira simples de extrair qual algo está sendo usado quando / qual fração de cada algo?
David Hagan
Talvez analisar os coeficientes do modelo linear resultante ( model.named_steps['lin_regr'].coef_) forneça algumas idéias sobre quanto cada modelo em um conjunto contribui para a solução final.
Constt
@constt Você não precisaria usar cross_val_predict em seus modelos básicos? Parece que o seu modelo de nível superior receberia um sinal super-otimista dos seus modelos básicos, pois isso está implementado atualmente.
Brian Bien
11
Este é apenas um exemplo de prova de conceito, não abordei uma seleção de modelo aqui. Eu acho que esses modelos devem ser otimizados como um todo, ou seja, otimizando hiperparâmetros de todos os modelos internos simultaneamente usando a abordagem de validação cruzada.
constt 16/05/19
se colocarmos n_targets = 1 X, y = make_regression(n_features=10, n_targets=1), ocorrerá um erro de dimensão. alguém pode explicar o que fazer?
Mohit Yadav
9

Ok, depois de passar algum tempo pesquisando no Google, descobri como poderia fazer o peso em python, mesmo com o scikit-learn. Considere o seguinte:

Treino um conjunto de meus modelos de regressão (como mencionado SVR, LassoLars e GradientBoostingRegressor). Então eu corro todos eles com dados de treinamento (mesmos dados que foram usados ​​para o treinamento de cada um desses três regressores). Recebo previsões de exemplos com cada um dos meus algoritmos e salvo esses três resultados no dataframe do pandas com as colunas 'predictedSVR', 'predictedLASSO' e 'predictedGBR'. E adiciono a coluna final a essa faixa de dados que chamo de 'predito', que é um valor real de previsão.

Depois, apenas treino uma regressão linear neste novo quadro de dados:

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

Portanto, quando quero fazer uma previsão para um novo exemplo, basta executar cada um dos meus três regressores separadamente e, em seguida, faço:

 stacker.predict() 

nas saídas dos meus 3 regressores. E obtenha um resultado.

O problema aqui é que estou encontrando pesos ideais para os regressores em média; os pesos serão os mesmos para cada exemplo no qual tentarei fazer previsões.

Se alguém tiver alguma idéia de como fazer empilhamento (ponderação) usando os recursos do exemplo atual, seria bom ouvi-los.

Maksim Khaitovich
fonte
Uau, eu gosto muito dessa abordagem! Mas por que você usou em LinearRegression()vez de LogisticRegression()modelo?
precisa
11
@ harrison4 porque eu estava fazendo regressão, não tarefa de classificação? Então, eu queria 'ponderar' a saída de cada modelo. De qualquer forma, essa é uma abordagem ruim, e uma boa é descrita aqui: stackoverflow.com/a/35170149/3633250
Maksim Khaitovich 15/17
Sim, desculpe, você está certo! Obrigado por compartilhar o link!
precisa
5

Se seus dados tiverem subconjuntos óbvios, você poderá executar um algoritmo de cluster como k-means e associar cada classificador aos clusters nos quais ele executa bem. Quando um novo ponto de dados chegar, determine em qual cluster ele está e execute o classificador associado.

Você também pode usar as distâncias inversas dos centróides para obter um conjunto de pesos para cada classificador e prever usando uma combinação linear de todos os classificadores.

anthonybell
fonte
Eu encontrei um papel testou este stategy (juntamente com uma comparação de algumas ideias semelhantes): papel
anthonybell
Idéia interessante, embora exija muito trabalho para aplicá-la. Obrigado pelo papel!
Maksim Khaitovich 26/03
1

Realizo um tipo de ponderação fazendo o seguinte, depois que todos os seus modelos estiverem totalmente treinados e com bom desempenho:

  1. Execute todos os seus modelos em um grande conjunto de dados de teste invisíveis
  2. Armazene as pontuações f1 no conjunto de testes para cada classe, para cada modelo
  3. Quando você prediz com o conjunto, cada modelo fornece a classe mais provável; portanto, avalie a confiança ou a probabilidade pela pontuação f1 desse modelo nessa classe. Se você estiver lidando com distância (como no SVM, por exemplo), apenas normalize as distâncias para obter uma confiança geral e prossiga com a ponderação f1 por classe.

Você pode ajustar ainda mais o seu conjunto medindo a porcentagem correta ao longo do tempo. Depois de ter um conjunto de dados novo significativamente grande, você pode plotar o limiar nas etapas 0,1, por exemplo, em relação à porcentagem correta se usar esse limiar para pontuar, para ter uma idéia de qual limiar lhe dará, digamos, 95% correto para a classe 1 e assim por diante. É possível continuar atualizando o conjunto de testes e as pontuações f1 à medida que novos dados são recebidos e acompanhar o desvio, reconstruindo os modelos quando os limites ou a precisão caem.

wwwslinger
fonte
11
Isso é interessante, mas funciona apenas para tarefas de classificação, tanto quanto vejo, enquanto estou tentando resolver tarefas de regressão. Portanto, não consigo calcular a pontuação da F1.
Maksim Khaitovich