Ансамбль различных видов регрессоров, использующий scikit-learn (или любую другую среду Python)

27

Я пытаюсь решить регрессионную задачу. Я обнаружил, что 3 модели прекрасно работают для разных подмножеств данных: LassoLARS, SVR и Gradient Tree Boosting. Я заметил, что когда я делаю прогнозы, используя все эти 3 модели, а затем составляю таблицу «истинного результата» и выходных данных моих 3 моделей, я вижу, что каждый раз, по крайней мере, одна из моделей действительно близка к истинному результату, хотя 2 другие может быть относительно далеко.

Когда я вычисляю минимально возможную ошибку (если я беру прогноз от «лучшего» предиктора для каждого тестового примера), я получаю ошибку, которая намного меньше, чем ошибка любой отдельной модели. Поэтому я подумал о том, чтобы попытаться объединить предсказания этих трех разных моделей в какой-то ансамбль. Вопрос в том, как это сделать правильно? Все мои 3 модели построены и настроены с использованием scikit-learn. Предоставляет ли он какой-то метод, который можно использовать для упаковки моделей в ансамбль? Проблема в том, что я не хочу просто усреднять прогнозы по всем трем моделям, я хочу сделать это с взвешиванием, где взвешивание должно определяться на основе свойств конкретного примера.

Даже если scikit-learn не предоставляет такую ​​функциональность, было бы неплохо, если бы кто-то знал, как правильно решить эту задачу - вычислить вес каждой модели для каждого примера в данных. Я думаю, что это может быть сделано с помощью отдельного регрессора, построенного поверх всех этих 3 моделей, который попытается вывести оптимальные веса для каждой из 3 моделей, но я не уверен, что это лучший способ сделать это.

Максим Хаитович
источник

Ответы:

32

На самом деле, scikit-learnпредоставляет такую ​​функциональность, хотя это может быть немного сложно реализовать. Вот полный рабочий пример такого среднего регрессора, построенного на основе трех моделей. Прежде всего, давайте импортируем все необходимые пакеты:

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

Затем нам нужно преобразовать наши три модели регрессоров в трансформаторы. Это позволит нам объединить их предсказания в один вектор признаков, используя 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)

Теперь давайте определим функцию построителя для нашей модели Франкенштейна:

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

Наконец, давайте подгоним модель:

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)

Выход:

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

Зачем усложнять вещи таким образом? Что ж, этот подход позволяет нам оптимизировать гиперпараметры модели, используя стандартные scikit-learnмодули, такие как GridSearchCVили RandomizedSearchCV. Кроме того, теперь можно легко сохранять и загружать с диска предварительно обученную модель.

constt
источник
При использовании этого подхода существует ли простой способ извлечь, какой алгоритм используется, когда / какая доля каждого алгоритма?
Дэвид Хаган
Возможно, просмотр коэффициентов полученной линейной модели ( model.named_steps['lin_regr'].coef_) даст вам некоторое представление о том, насколько каждая модель в ансамбле вносит свой вклад в окончательное решение.
constt
@constt Разве вам не нужно использовать cross_val_predict в ваших базовых моделях? Похоже, что ваша модель верхнего уровня получит сверхоптимистический сигнал от ваших базовых моделей, поскольку это в настоящее время реализовано.
Брайан Бьен,
1
Это всего лишь пример подтверждения концепции, здесь я не рассматривал выбор модели. Я думаю, что такие модели следует оптимизировать в целом, т. Е. Оптимизировать гиперпараметры всех встроенных моделей одновременно, используя метод перекрестной проверки.
Constt
если мы введем n_targets = 1, X, y = make_regression(n_features=10, n_targets=1)это даст ошибку измерения. Может кто-нибудь объяснить, пожалуйста, что делать?
Мохит Ядав
9

Хорошо, потратив некоторое время на поиск в Google, я узнал, как я могу выполнить взвешивание в python даже с помощью scikit-learn. Рассмотрим ниже:

Я тренирую набор своих регрессионных моделей (как уже упоминалось SVR, LassoLars и GradientBoostingRegressor). Затем я запускаю их все на тренировочных данных (те же данные, которые использовались для обучения каждого из этих 3-х регрессоров). Я получаю прогнозы для примеров с каждым из моих алгоритмов и сохраняю эти 3 результата в фрейм данных pandas со столбцами «предсказанныйSVR», «предсказанный ЛАССО» и «предсказанный ГБР». И я добавляю последний столбец в это поле данных, которое я называю «прогнозируемым», что является реальным значением прогнозирования.

Затем я просто тренирую линейную регрессию на этом новом фрейме данных:

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

Поэтому, когда я хочу сделать прогноз для нового примера, я просто запускаю каждый из 3 моих регрессоров по отдельности, а затем я делаю:

 stacker.predict() 

на выходах моих 3 регрессоров. И получить результат.

Проблема в том, что я нахожу оптимальные веса для регрессоров в среднем, веса будут одинаковыми для каждого примера, на котором я попытаюсь сделать прогноз.

Если у кого-то есть идеи о том, как сделать суммирование (взвешивание) с использованием функций текущего примера, было бы приятно услышать их.

Максим Хаитович
источник
Вау, мне очень нравится этот подход! Но почему вы использовали LinearRegression()вместо LogisticRegression()модели?
harrison4
1
@ harrison4 потому что я занимался регрессией, а не классификацией? Поэтому я хотел «весить» вывод каждой модели. В любом случае, это плохой подход, хороший описан здесь: stackoverflow.com/a/35170149/3633250
Максим Хаитович
Да, прости, что ты прав! спасибо, что поделились ссылкой!
harrison4
5

Если ваши данные имеют очевидные подмножества, вы можете запустить алгоритм кластеризации, такой как k-means, а затем связать каждый классификатор с кластерами, с которыми он хорошо работает. Когда появится новая точка данных, определите, в каком кластере она находится, и запустите соответствующий классификатор.

Вы также можете использовать обратные расстояния от центроидов, чтобы получить набор весов для каждого классификатора и прогнозировать, используя линейную комбинацию всех классификаторов.

anthonybell
источник
Я обнаружил, что бумага проверила эту формулировку (вместе со сравнением некоторых похожих идей): paper
anthonybell
Интересная идея, хотя и требует много работы, чтобы ее применить. Спасибо за бумагу!
Максим Хаитович
1

Я выполняю определенный тип взвешивания, выполняя следующие действия после того, как все ваши модели полностью обучены и работают хорошо:

  1. Запустите все свои модели на большом наборе невидимых данных тестирования
  2. Сохраните оценки f1 в тестовом наборе для каждого класса, для каждой модели
  3. Когда вы прогнозируете с помощью ансамбля, каждая модель даст вам наиболее вероятный класс, поэтому взвесьте достоверность или вероятность на оценку f1 для этой модели в этом классе. Если вы имеете дело с расстоянием (как, например, в SVM), просто нормализуйте расстояния, чтобы получить общую уверенность, а затем переходите к взвешиванию для каждого класса f1.

Вы можете дополнительно настроить свой ансамбль, приняв правильное значение процента в течение некоторого времени. После того, как вы набрали значительно больший, новый набор данных, вы можете построить порог с шагом 0,1, например, против процента правильного, если использовать этот порог для оценки, чтобы получить представление о том, какой порог даст вам, скажем, 95% правильного для класса 1 и так далее. Вы можете продолжать обновлять набор тестов и оценки f1 по мере поступления новых данных и отслеживать отклонения, перестраивая модели при падении порогов или точности.

wwwslinger
источник
1
Это интересно, но, насколько я вижу, это работает только для задач классификации, когда я пытаюсь решить регрессионную задачу. Таким образом, я не могу вычислить счет Ф1.
Максим Хаитович