Evalml: Сложный ансамбль работает плохо

Созданный на 6 апр. 2021  ·  11Комментарии  ·  Источник: alteryx/evalml

Действия по воспроизведению:

  1. Загрузите набор данных Happiness в evalml
  2. Выполнить достаточно долго, чтобы включить ансамбль
  3. Базовый регрессор имеет более высокий рейтинг, чем накопленный регрессор.
    Полный набор данных счастья.csv.zip
bug performance

Все 11 Комментарий

@dancuarini Я пытался воспроизвести это локально, но не смог; может быть из-за дополнительных шагов перед запуском AutoMLSearch (например, размер разбиения данных, удаление столбцов). Поговорим о проблемной настройке!

Вот что я пытался запустить локально:

from evalml.automl import AutoMLSearch
import pandas as pd
import woodwork as ww
from evalml.automl.callbacks import raise_error_callback

happiness_data_set = pd.read_csv("Happiness Data Full Set.csv")
y = happiness_data_set['Happiness']
X = happiness_data_set.drop(['Happiness'], axis=1)
# display(X.head())

X = ww.DataTable(X)
X_train, X_holdout, y_train, y_holdout = evalml.preprocessing.split_data(X, y, problem_type='regression', test_size=0.2, random_seed=0)
# print(X.types)

automl = AutoMLSearch(X, y, problem_type="regression", objective="MAE", error_callback=raise_error_callback, max_batches=20, ensembling=True)
automl.search()

Это приводит к следующим рейтингам:

image

Текущий прогресс: обсудил с @dancuarini невозможность локального воспроизведения , будет поддерживать связь с воспроизведения и следующих шагов.

@ angela97lin, подожди, ты уверен, что не смог повторить это? Здесь сложенный ансамбль оказывается в середине рейтинга - я бы ожидал, что он будет на вершине!

Спасибо, что поделились репродуктором :)

@dsherry Хотя это немного подозрительно, что

@ angela97lin ах да понял! Я отправил вам несколько заметок.

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

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

  1. Набор данных действительно невелик, и наша текущая стратегия разделения данных означает, что ансамблер предоставляется и проверяется на очень небольшом подмножестве данных. Прямо сейчас, если мы хотим обучить составной ансамбль, мы разделяем некоторые данные (обозначенные ensembling_indices ) для обучения ансамблеру. Это сделано для предотвращения переобучения ансамблера путем обучения метаузелщика на тех же данных, на которых уже были обучены входные конвейеры. Затем мы делаем одно разделение резюме, дополнительно разделяя данные из ensembling_indices . Для этого набора данных из 128 строк мы обучаем и проверяем 17 и 8 строк соответственно. Я подал № 2144, чтобы обсудить, хотим ли мы сделать это дополнительное разделение резюме.

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

Например, это итоговая таблица рейтинга:
image

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

Вот репро-код для этого:

Сверху:

import pandas as pd
import woodwork as ww
happiness_data_set = pd.read_csv("Happiness Data Full Set.csv")
y = happiness_data_set['Happiness']
X = happiness_data_set.drop(['Happiness'], axis=1)

X = ww.DataTable(X)
X_train, X_holdout, y_train, y_holdout = evalml.preprocessing.split_data(X, y, problem_type='regression', test_size=0.25, random_seed=0)

automl = AutoMLSearch(X, y, problem_type="regression", objective="MAE", error_callback=raise_error_callback, max_batches=10, ensembling=True)
automl.search()

import woodwork as ww
from evalml.automl.engine import train_and_score_pipeline
from evalml.automl.engine.engine_base import JobLogger

# Get the pipelines fed into the ensemble but only use the ones better than the stacked ensemble
input_pipelines = []
input_info = automl._automl_algorithm._best_pipeline_info
from evalml.model_family import ModelFamily

trimmed = dict()
trimmed.update({ModelFamily.RANDOM_FOREST: input_info[ModelFamily.RANDOM_FOREST]})
trimmed.update({ModelFamily.XGBOOST: input_info[ModelFamily.XGBOOST]})
trimmed.update({ModelFamily.DECISION_TREE: input_info[ModelFamily.EXTRA_TREES]})

for pipeline_dict in trimmed.values():
    pipeline_class = pipeline_dict['pipeline_class']
    pipeline_params = pipeline_dict['parameters']
    input_pipelines.append(pipeline_class(parameters=automl._automl_algorithm._transform_parameters(pipeline_class, pipeline_params),
                                                      random_seed=automl._automl_algorithm.random_seed))
ensemble_pipeline = _make_stacked_ensemble_pipeline(input_pipelines, "regression")
X_train = X.iloc[automl.ensembling_indices]
y_train = ww.DataColumn(y.iloc[automl.ensembling_indices])
train_and_score_pipeline(ensemble_pipeline, automl.automl_config, X_train, y_train, JobLogger())

Просто используя эти три семейства моделей, мы получаем оценку MAE ~ 0,22, что намного лучше, чем у любого отдельного конвейера.

#output of train_and_score_pipeline(ensemble_pipeline, automl.automl_config, X_train, y_train, JobLogger())
{'scores': {'cv_data': [{'all_objective_scores': OrderedDict([('MAE',
                  0.22281276417465426),
                 ('ExpVariance', 0.9578811127332543),
                 ('MaxError', 0.3858477236606914),
                 ('MedianAE', 0.2790362808260225),
                 ('MSE', 0.0642654425375983),
                 ('R2', 0.9152119239698017),
                 ('Root Mean Squared Error', 0.2535062968401343),
                 ('# Training', 17),
                 ('# Validation', 9)]),
    'mean_cv_score': 0.22281276417465426,
    'binary_classification_threshold': None}],
  'training_time': 9.944366216659546,
  'cv_scores': 0    0.222813
  dtype: float64,
  'cv_score_mean': 0.22281276417465426},
 'pipeline': TemplatedPipeline(parameters={'Stacked Ensemble Regressor':{'input_pipelines': [GeneratedPipeline(parameters={'Imputer':{'categorical_impute_strategy': 'most_frequent', 'numeric_impute_strategy': 'most_frequent', 'categorical_fill_value': None, 'numeric_fill_value': None}, 'One Hot Encoder':{'top_n': 10, 'features_to_encode': None, 'categories': None, 'drop': 'if_binary', 'handle_unknown': 'ignore', 'handle_missing': 'error'}, 'Random Forest Regressor':{'n_estimators': 184, 'max_depth': 25, 'n_jobs': -1},}), GeneratedPipeline(parameters={'Imputer':{'categorical_impute_strategy': 'most_frequent', 'numeric_impute_strategy': 'mean', 'categorical_fill_value': None, 'numeric_fill_value': None}, 'One Hot Encoder':{'top_n': 10, 'features_to_encode': None, 'categories': None, 'drop': 'if_binary', 'handle_unknown': 'ignore', 'handle_missing': 'error'}, 'XGBoost Regressor':{'eta': 0.1, 'max_depth': 6, 'min_child_weight': 1, 'n_estimators': 100},}), GeneratedPipeline(parameters={'Imputer':{'categorical_impute_strategy': 'most_frequent', 'numeric_impute_strategy': 'mean', 'categorical_fill_value': None, 'numeric_fill_value': None}, 'One Hot Encoder':{'top_n': 10, 'features_to_encode': None, 'categories': None, 'drop': 'if_binary', 'handle_unknown': 'ignore', 'handle_missing': 'error'}, 'Extra Trees Regressor':{'n_estimators': 100, 'max_features': 'auto', 'max_depth': 6, 'min_samples_split': 2, 'min_weight_fraction_leaf': 0.0, 'n_jobs': -1},})], 'final_estimator': None, 'cv': None, 'n_jobs': -1},}),

Это заставляет меня задуматься, нужно ли нам переосмыслить, какие входные конвейеры мы должны передавать нашему составному ансамблеру.

  1. Используемый нами метаузел (LinearRegressor) не самый лучший. Я протестировал это через созданную мной ветку stacking_test где я обновил метаузел по умолчанию до RidgeCV (scikit-learn по умолчанию, но у нас нет в EvalML), и ансамблер работает намного лучше:
    image

Следующие шаги после обсуждения с @dsherry :

Попробуйте №1 и №3 (с использованием Elastic Net) для других наборов данных, запустите тесты производительности и посмотрите, сможем ли мы повысить производительность в целом.

@ angela97lin Твои

Я также согласен с тем, что метеорологу необходимо использовать строгую регуляризацию. Я использовал Elastic Net в H2O-3 StackedEnsemble, и только один раз помню, что этот ансамбль занял второе место в таблице лидеров. Каждый раз, когда я тестировал, он был первым. Регуляризация никогда не должна позволять плохим моделям снижать производительность ансамбля.

И это скармливало всю таблицу лидеров даже из 50 моделей в мета-поиск. :-)

Просто разместите несколько дополнительных обновлений по этому поводу:

Протестировано локально с использованием всех наборов данных регрессии. Результаты можно найти здесь или только графики здесь .

Из этого:

  • Согласен @rpeck! Мы должны обновить метаузел, чтобы обязательно использовать сильную регуляризацию. Похоже, что ElasticNetCV работает лучше, чем наш LinearRegressor на многих наборах данных. Эта проблема отслеживает это: https://github.com/alteryx/evalml/issues/1739
  • @dsherry и я повторно обсудили нашу стратегию разделения данных: прямо сейчас мы разделяем данные для ансамбля. Однако это сделано в предположении, что мы хотим, чтобы StackedEnsembler на этом разбиении индексов ансамбля, мы в конечном итоге обучаем как входные конвейеры, так и метаузел на этом небольшом наборе данных. Вероятно, это может быть причиной того, что мы не очень хорошо работаем. Хотя параметры для наших входных конвейеров взяты из настройки с использованием других данных, эти конвейеры не подходят. В долгосрочной перспективе развертывание нашей собственной реализации может позволить нам передавать обученные конвейеры ансамблеру, и в этом случае мы будем иметь желаемое поведение. На данный момент это не так.

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

Результаты экспериментов выглядят неплохо: https://alteryx.quip.com/4hEyAaTBZDap/Ensembly-Performance-Using-More-Data

Следующие шаги:

После некоторого покопания мы пришли к выводу, что проблема не в том, как ансамбль выступает, а в том, как мы сообщаем об исполнении ансамбля. В настоящее время мы делаем отдельное разбиение ансамбля, которое составляет 20% данных, а затем делаем еще одно разбиение с проверкой на поезд и сообщаем оценку ансамбля в качестве данных проверки. Это означает, что в некоторых случаях оценка ансамбля рассчитывается с использованием очень небольшого количества строк (как в наборе данных о счастье выше).

Удалив разделение индексов ансамбля и используя наш старый метод расчета оценки обучения CV для ансамбля (предоставить ему все данные, обучить и проверить в один раз), мы видим, что ансамбль получает более высокий рейтинг почти во всех случаях и подходит к концу. как №1 во многих других случаях. Между тем, оценка валидации такая же или немного лучше.

Обратите внимание, что, поскольку мы не выполняем настройку гиперпараметров, входные конвейеры не обучаются, а ансамбль получает только прогнозы входных конвейеров в качестве входных данных, переобучение не является проблемой. Мы можем вернуться к реализации нашего собственного ансамбля и обновить стратегию разделения, но пока мы можем увидеть улучшения, просто изменив стратегию разделения данных и реализацию scikit-learn.

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

Результаты представлены в таблице: https://alteryx.quip.com/jI2mArnWZfTU/Ensembly-vs-Best-Pipeline-Validation-Scores#MKWACADlCDt

Была ли эта страница полезной?
0 / 5 - 0 рейтинги