Конвейер с XGBoost — Imputer и Scaler предотвращают обучение модели

Я пытаюсь построить конвейер для предварительной обработки данных для моей модели XGBoost. Данные содержат NaN и должны быть масштабированы. Это соответствующий код:

xgb_pipe = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='mean')),
    ('scaler', preprocessing.StandardScaler()),
    ('regressor', xgboost.XGBRegressor(n_estimators=100, eta=0.1, objective = "reg:squarederror"))])

xgb_pipe.fit(train_x.values, train_y.values, 
            regressor__early_stopping_rounds=20, 
            regressor__eval_metric = "rmse", 
            regressor__eval_set = [[train_x.values, train_y.values],[test_x.values, test_y.values]])

Потери сразу увеличиваются, и обучение останавливается после 20 итераций.

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

Тогда это должно быть связано с вашим regressor__eval_set параметром соответствия. Для начала оставьте это и посмотрите, работает ли XGBoost 100 итераций или нет.

user1808924 10.01.2023 12:27

@user1808924 user1808924 Если я удалю только эту строку, я получу ошибку IndexError: list index out of range. Если я удаляю все три параметра регрессора для ранней остановки, он тренируется в течение всего времени (и потери улучшаются, как и предполагалось). Каков был бы правильный способ реализовать раннюю остановку?

Jonas 10.01.2023 13:12

Обратите внимание, что деревья (с усилением градиента) не заботятся о масштабе входных данных, поэтому StandardScaler здесь строго не требуется. И xgboost будет обрабатывать отсутствующие значения (но если вы этого не хотите, то вменение вызовет разницу.)

Ben Reiniger 10.01.2023 16:46
Типы данных JavaScript
Типы данных JavaScript
В JavaScript существует несколько типов данных, включая примитивные типы данных и ссылочные типы данных. Вот краткое объяснение различных типов данных...
Как сделать движок для футбольного матча? (простой вариант)
Как сделать движок для футбольного матча? (простой вариант)
Футбол. Для многих людей, живущих на земле, эта игра - больше, чем просто спорт. И эти люди всегда мечтают стать футболистом или менеджером. Но, к...
Знайте свои исключения!
Знайте свои исключения!
В Java исключение - это событие, возникающее во время выполнения программы, которое нарушает нормальный ход выполнения инструкций программы. Когда...
CSS Flex: что должен знать каждый разработчик
CSS Flex: что должен знать каждый разработчик
CSS Flex: что должен знать каждый разработчик Модуль flexbox, также известный как гибкий модуль разметки box, помогает эффективно проектировать и...
Введение в раздел "Заголовок" в HTML
Введение в раздел "Заголовок" в HTML
Говорят, что лучшее о человеке можно увидеть только изнутри, и это относится и к веб-страницам HTML! Причина, по которой некоторые веб-страницы не...
1
3
55
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Проблема в том, что предварительная обработка не применяется к вашим наборам eval, поэтому модель работает на них довольно плохо, и ранняя остановка срабатывает очень рано.

Я не уверен, что есть простой способ сделать это, к сожалению, чтобы все было в одном конвейере. Вам необходимо применить этапы предварительной обработки конвейера к наборам eval, поэтому их необходимо настроить перед установкой этого параметра.

Отдельная предварительная обработка

Как два объекта это не проблема:

preproc = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='mean')),
    ('scaler', preprocessing.StandardScaler()),
])

reg = xgboost.XGBRegressor(n_estimators=100, eta=0.1, objective = "reg:squarederror")

train_x_preproc = preproc.fit_transform(train_x.values, train_y.values)
test_x_preproc = preproc.transform(test_x)

reg.fit(train_x.values, train_y.values, 
    regressor__early_stopping_rounds=20, 
    regressor__eval_metric = "rmse", 
    regressor__eval_set = [[train_x_preproc, train_y.values], [test_x_preproc, test_y.values]],
)

После подгонки вы можете объединить эти уже подобранные оценки в конвейер (конвейеры не клонируют свои оценки) для предсказания, если хотите.

Пользовательский оценщик

Есть много способов сделать это, но наследование от Pipeline означает, что вы можете инициализировать так же, как вы делаете текущую настройку, и мы просто предполагаем, что последний шаг — это модель xgboost, а остальные — предварительная обработка, которую необходимо применить к наборы eval, а также наборы для подгонки и прогнозирования. Думаю, все остальное можно оставить унаследованным от Pipeline методам?

class PreprocEarlyStoppingXGB(Pipeline):
    def fit(self, X, y, eval_set):
        preproc = self.steps[:-1]
        X_preproc = preproc.fit_transform(X, y)
        eval_preproc = []
        for eval in eval_set:
            eval_preproc.append([preproc.transform(eval[0]), eval[1]])
        self.steps[-1].fit(X_preproc, y, eval_set=eval_preproc)
        return self

Что касается вашего варианта использования из комментариев, что происходит, когда вы выполняете перекрестную проверку с этим объектом? На каждой тренировочной складке установлены этапы предварительной обработки. Затем они применяются к обучающей выборке и ко всем оценочным наборам (всему обучающему набору, а также внешнему тестовому набору) и, наконец, при подсчете тестовой выборки. Модель xgboost тренируется на предварительно обработанной обучающей выборке и отслеживает результаты всего обучающего набора и внешнего тестового набора (оба были предварительно обработаны), причем последний используется для ранней остановки.

В этом есть смысл. Как это будет сочетаться с перекрестной проверкой kfold? Я реализовал конвейер, чтобы иметь простую оценку gridsearch/kfold, где масштабирование/вменение настраивается отдельно для каждого из 5 разбиений train/eval, чтобы избежать утечек данных. Следующим шагом должно было быть что-то вроде: CV = GridSearchCV(xgb_pipe, param_grid)

Jonas 10.01.2023 16:15

@Jonas Да, это главный недостаток отсутствия всего этого в одном объекте. Я добавил пользовательский оценщик, который, как мне кажется, должен выполнять эту работу; может быть что-то и можно сделать без этого, но я этого не вижу.

Ben Reiniger 11.01.2023 16:01

Другие вопросы по теме

Python/Seaborn — как отображать имена каждого значения на диаграмме рассеяния
Как я могу добавить новый столбец в фрейм данных (df1), который представляет собой сумму нескольких значений поиска из df1 в другом фрейме данных (df2)
При извлечении текстовых данных из файлов в разных подкаталогах возникает ошибка «ValueError: подстрока не найдена».
Извлечение текстовых данных между ключевыми словами в строке
Значение истинности массива с более чем одним элементом неоднозначно. Используйте a.any() или a.all()
Как заполнить нулевые значения в агрегированной таблице с помощью Pandas?
Найден массив с 0 образцами (форма = (0, 1)), хотя требуется минимум 1
Python — печать нескольких массивов случайно выбранных элементов из исходного массива
Как очистить таблицу со страницы и создать фрейм данных с несколькими столбцами с помощью python?
Как извлечь текст одной страницы с помощью PyPDF2?