МАШИННОЕ ОБУЧЕНИЕ
Одна потенциальная причина переобучения, которую я никогда раньше не замечал
Переобучение происходит, когда производительность обучающих данных намного выше, чем производительность данных тестирования. Гиперпараметры по умолчанию в пакетах машинного обучения могут привести к проблеме переобучения.
«Почему моя настроенная модель все еще переоснащена?»
"Вы использовали перекрестную проверку?"
"Конечно да."
«Какую модель вы использовали и какие гиперпараметры настроили?»
«Регрессор случайного леса и я настроили количество деревьев и максимальное количество функций для каждого дерева».
«Мне нужно проверить ваш код».
Это был диалог между мной и моей женой, когда она делала мини-проект.
Честно говоря, судя по ее описанию, я не мог придумать ничего неправильного в этой процедуре. Однако после того, как я проверил ее код построчно, я обнаружил проблему, которая вызвала проблему переобучения, о которой я никогда раньше не думал.
Давайте вместе разберем ее код.
Это блок кода, который настраивал модель в обучающем наборе данных, а также где возникла проблема.
def train_pipeline_rf(X,y): # X are factors # y is output # impute missing X by median X_prepared = pre_pipeline.fit_transform(X) # set cross-validation tscv = TimeSeriesSplit(n_splits=10) data_split = tscv.split(X_prepared) # hyper-parameter space param_grid_RF = { 'n_estimators' : [10,20,50,100,200,500,1000], 'max_features' : [0.6,0.8,"auto","sqrt"] } # build random forest model rf_model = RandomForestRegressor(random_state=42,n_jobs=-1) # gridsearch for the best hyper-parameter gs_rf = GridSearchCV(rf_model, param_grid=param_grid_RF, cv=data_split, scoring='neg_mean_squared_error', n_jobs=-1) # fit dataset gs_rf.fit(X_prepared, y) return gs_rf
Предварительная обработка
pre_pipeline в коде - это конвейер для вменения пропущенных значений и масштабирования признаков, которые являются важными этапами предварительной обработки данных. обработка. Вот как это выглядит:
pre_pipeline = Pipeline([ ('imputer', SimpleImputer(strategy="median")), ('std_scaler', StandardScaler()), ])
Здесь она использовала imputer, чтобы заменить значения NA на медианное значение столбца, а масштабатор - стандартный масштабатор, который нормализует столбцы как:
z = (x — u) / s
где u - среднее значение, а s - стандартное отклонение столбца [1]. Некоторые другие импьютеры и скейлеры в sklearn Также можно использовать. Но эта часть не связана с проблемой переобучения.
Установить разделение данных при перекрестной проверке
# set cross-validation tscv = TimeSeriesSplit(n_splits=10) data_split = tscv.split(X_prepared)
Здесь она использовала метод, специально разработанный для данных временных рядов, TimeSeriesSplit.
Обычно люди используют k-кратную перекрестную проверку для случайного разделения обучающих данных или стратифицированную k-кратную перекрестную проверку, чтобы сохранить процент выборок для каждого класса [2]. Но эти два метода не подходят для данных временных рядов, потому что они не сохраняют необработанный порядок точек данных.
Этот шаг довольно стандартный и не может быть связан с проблемой переобучения.
Пространство гиперпараметров и определение модели
# hyper-parameter space param_grid_RF = { 'n_estimators' : [10,20,50,100,200,500,1000], 'max_features' : [0.6,0.8,"auto","sqrt"] } # build random forest model rf_model = RandomForestRegressor(random_state=42,n_jobs=-1)
Что касается настройки гиперпараметров, следует указать набор кандидатов, которые обычно определяются как формат словаря в пакете sklearn. Имена гиперпараметров взяты из модели, которую нужно построить, например, RandomForestRegressor в коде моей жены.
Так называемый шаг «настройка-гиперпараметр» заключается в выборе наилучшей комбинации гиперпараметров из ваших заданных кандидатов, которая превосходит другие комбинации гиперпараметров в процедуре перекрестной проверки.
Это часть, из-за которой возникла проблема с переоснащением.
Решите проблему переобучения.
Если мы обратимся к странице руководства Random Forest Regressor в sklearn, то увидим длинный список параметров в функции RandomForestRegressor (здесь нет; при необходимости см. веб-страницу) .
Однако в приведенном выше коде только n_estimators и max_features были переданы функции во время процесса настройки, что в результате все остальные параметры приняли значения по умолчанию.
Один из параметров функции называется max_depth, который представляет собой максимальную глубину дерева в вашей модели случайного леса. Значение по умолчанию - Нет, что означает, что дерево решений будет доходить до такой степени, что каждый выход чистый или все листья имеют не более m выборок, где m определяется как min_samples_split ( по умолчанию = 2).
Этот параметр значительно повысил сложность обученной модели. И на основе компромисса между смещением и дисперсией модель, обученная в приведенном выше коде, имела низкое смещение и высокую дисперсию. Конечно, в конечном итоге это привело к проблеме переобучения.
Я решил проблему, добавив max_depth в пространство гиперпараметров.
# hyper-parameter space param_grid_RF = { 'n_estimators' : [10,20,50,100,200,500,1000], 'max_features' : [0.6,0.8,"auto","sqrt"], 'max_depth' : [4,5,6] }
На самом деле есть некоторые другие параметры, которые могут повлиять на сложность модели в RandomForestRegressor, но не рекомендуется помещать их все в пространство гиперпараметров.
Дополнительный гиперпараметр в приведенном выше коде уже увеличил стоимость вычислений в три раза. Таким образом, не стоит увеличивать время работы в геометрической прогрессии, чтобы просто настроить больше параметров.
Вместо этого вы можете установить некоторые из них при запуске модели следующим образом.
# hyper-parameter space param_grid_RF = { 'n_estimators' : [10,20,50,100,200,500,1000] } # build random forest model rf_model = RandomForestRegressor(random_state=42,n_jobs=-1,max_features=0.6,max_depth=5)
В приведенном выше коде единственный настроенный гиперпараметр - это n_estimators и max_features,, а также max_depth, фиксируются в процессе обучения. Настроенные или фиксированные параметры не обязательно должны быть такими.
Обоснование настройки состоит в том, чтобы снизить вычислительные затраты, когда вы уже имеете некоторое представление о выборе некоторых гиперпараметров.
Да, проблема моей жены решена. Однако, честно говоря, я не мог найти его , пока не просмотрел все параметры по умолчанию в пакете. Вот почему я хочу поделиться с вами этим опытом, чтобы вы могли более внимательно относиться к параметрам по умолчанию в процессе обучения модели машинного обучения.
Переоснащение - одна из наиболее распространенных проблем в науке о данных, которая в основном возникает из-за высокой сложности модели и отсутствие точек данных.
Чтобы этого избежать, лучше полностью контролировать используемые пакеты.
Надеюсь, этот совет поможет вам.
Использованная литература:
- Https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.StandardScaler.html#sklearn.preprocessing.StandardScaler
- Https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.StratifiedKFold.html
- Https://en.wikipedia.org/wiki/Random_forest
Исправления:
Я хотел бы поблагодарить Антонио Карлоса за указание на эту проблему в моем исходном посте. Он также предложил хороший пост Предварительная обработка данных с конвейером для предотвращения утечки данных во время перекрестной проверки. к теме. Я действительно ценю это.
Это связано с частью конвейера предварительной обработки. Этого следовало делать не на всем наборе обучающих данных, а на каждой итерации этапа перекрестной проверки, потому что общая нормализация обучающих данных вызовет утечку между обучающим и проверочным набором данных на этапе CV.
Итак, я поместил исправленный код с соответствующей ревизией, как показано ниже:
def train_pipeline_rf(X,y): # X are factors # y is output # set cross-validation tscv = TimeSeriesSplit(n_splits=10) data_split = tscv.split(X) # build a pipeline of pre-processing and random forest model my_pipe = Pipeline([ ('imputer', SimpleImputer(strategy="median")), ('std_scaler', StandardScaler()), ('rf_model', RandomForestRegressor(random_state=42,n_jobs=-1)) ]) # hyper-parameter space param_grid_RF = { 'rf_model__n_estimators' : [10,20,50,100,200,500,1000], 'rf_model__max_features' : [0.6,0.8,"auto","sqrt"], 'rf_model__max_depth' : [4,5,6] } # gridsearch for the best hyper-parameter within the pipeline. gs_rf = GridSearchCV(my_pipe, param_grid=param_grid_RF, cv=data_split, scoring='neg_mean_squared_error', n_jobs=-1) # fit dataset gs_rf.fit(X, y) return gs_rf