Регуляризация, упаковка, укладка и многое другое

Данные временных рядов обычно состоят из трех компонентов:

  • Сезонность
  • Тренд
  • Остаток

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

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

Все это говорит о том, что прогнозирование любого заданного ряда часто не так просто, как использование линейной регрессии. Нелинейные оценки и ансамблевые методы можно комбинировать с линейными методами, чтобы найти наилучший подход для любого ряда. В этом посте я расскажу об образцах этих моделей из библиотеки Scitkit-learn и о том, как их можно использовать для максимальной точности. Эти методы применяются к набору данных о ежедневных посетителях с немногим более 2000 наблюдений, которые можно найти на Kaggle или RegressIt. Отдельное спасибо Professor Bob Nau за предоставленный материал!

Структура этого сообщения в блоге довольно повторяющаяся, с одними и теми же графиками и показателями оценки для каждой прикладной модели. Если вы уже знакомы с моделями и API-интерфейсами Scikit-learn, такими как MLR, XGBoost и другими, вы можете перейти к разделам Бэггинг и Стэкинг, чтобы ознакомиться с тем, что может немного отличаться. Полный блокнот вы можете найти на GitHub.

Подготовьте модели

Все модели запускаются с использованием пакета Scalecast, который содержит результаты и объединяет Scikit-learn и другие модели с данными временных рядов. Все его прогнозы по умолчанию используют динамический многоэтапный прогноз, который возвращает более разумные показатели точности/ошибки по всему горизонту прогноза, чем среднее значение одноэтапных прогнозов.

pip install scalecast

Поставьте пакету звезду на GitHub, если он покажется вам интересным.

Мы будем использовать горизонт прогноза в 60 дней, а также использовать 60-дневный набор для проверки каждой модели и настройки ее гиперпараметров. Все модели будут тестироваться на 20% исходных данных:

f=Forecaster(y=data['First.Time.Visits'],current_dates=data['Date'])
f.generate_future_dates(60)
f.set_test_length(.2)
f.set_validation_length(60)

Из EDA (здесь не показано) видно, что существует автокорреляция до 4 недель в прошлом и что первые 7 отставаний зависимой переменной могут быть значительными.

f.add_ar_terms(7) # 7 auto-regressive terms
f.add_AR_terms((4,7)) # 4 seasonal terms spaced 7 apart

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

f.add_seasonal_regressors(
    'month',
    'quarter',
    'week',
    'dayofyear',
    raw=False,
    sincos=True
) # fourier transformation
f.add_seasonal_regressors(
    'dayofweek',
    'is_leap_year',
    'week',
    raw=False,
    dummy=True,
    drop_first=True
) # dummy vars

Наконец, мы можем смоделировать тренд ряда, добавив переменную года:

f.add_seasonal_regressors('year')

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

МЛР

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

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

Где j — количество добавленных регрессоров (в нашем случае AR, сезонных и трендовых компонентов), а alpha — соответствующий коэффициент. В нашем коде вызов этой функции выглядит так:

f.set_estimator('mlr')
f.manual_forecast()

Стоит отметить, что более распространенным применением линейного метода для временных рядов является ARIMA, который также использует ошибки ряда в качестве регрессора. MLR предполагает, что ошибки ряда некоррелированы, что является ложным во временном ряду. При этом в нашем анализе средняя абсолютная ошибка в процентах набора тестов составляет 13%, а R2 составляет 76%, что достигается с помощью MLR. Давайте посмотрим, можно ли это исправить, добавив сложности.

Лассо

Следующие три рассмотренные модели, Lasso, Ridge и ElasticNet, используют одну и ту же базовую функцию MLR для прогнозирования, но способы оценки коэффициентов различаются. Существуют параметры регуляризации L1 и L2, которые служат для уменьшения величин коэффициентов, что, в свою очередь, уменьшает переобучение и может привести к более точным прогнозам вне выборки. Это может быть хорошим методом, чтобы попробовать в нашем случае, поскольку показатель R2 MLR в выборке составляет 95%, что значительно больше, чем R2 вне выборки, равное 76%, что указывает на переобучение.

Есть один параметр для оценки с помощью лассо — размер штрафного параметра L1, или альфа. Мы можем сделать это с помощью поиска по сетке из 100 альфа-значений в проверочном наборе данных. Это выглядит так:

f.add_sklearn_estimator(Lasso,'lasso')
f.set_estimator('lasso')
lasso_grid = {'alpha':np.linspace(0,2,100)}
f.ingest_grid(lasso_grid)
f.tune()
f.auto_forecast()

Важно, что Lasso (а также Ridge и ElasticNet) использует масштабированные входные данные, чтобы параметр штрафа был сбалансирован по всем коэффициентам. Scalecast по умолчанию использует масштабатор MinMax.

Выбранное оптимальное значение альфа составило 0,081. Эта модель не улучшила точность вне выборки по сравнению с моделью MLR и не уменьшила переобучение. Мы можем попробовать еще раз с моделью Ridge.

хребет

Ridge похож на Lasso, за исключением того, что он использует штраф L2. Разница здесь в том, что штраф L1 может снизить некоторые коэффициенты до нуля, тогда как Ридж может уменьшить коэффициенты только почти до нуля. Обычно обе модели дают одинаковые результаты. Мы можем настроить модель Ridge с той же сеткой, которую мы создали для модели Lasso.

f.set_estimator('ridge')
f.ingest_grid(lasso_grid)
f.tune()
f.auto_forecast()

Оптимальная альфа, выбранная для модели Риджа, составила 0,384, и ее результаты были аналогичны, если не чуть хуже, модели лассо. У нас есть еще одна модель, которая может применять регуляризацию к MLR: ElasticNet.

Эластичная сеть

Модель ElasticNet, предлагаемая Scikit-learn, будет прогнозировать выходные данные с использованием линейной модели, но теперь она будет смешивать штрафы L1 и L2. Ключевые параметры для настройки сейчас:

  • Соотношение штрафов L1/L2 (l1_ratio )
  • Значение штрафа (alpha )

Scalecast предлагает хорошую сетку проверки по умолчанию для ElasticNet, поэтому нам не нужно ее создавать.

f.set_estimator('elasticnet')
f.tune()
f.auto_forecast()

Оптимальными параметрами для ElasticNet были an l1_ratio из 1, что делает его эквивалентным модели лассо, и альфа 0,3. Он работал примерно так же хорошо, как модели Lasso и Ridge, и снова не уменьшал переобучение.

Случайный лес

Исчерпав несколько линейных методов оценки этого ряда, мы можем перейти к нелинейным методам. Одним из самых популярных из них является Random Forest, основанный на древовидном методе ансамбля. Он функционирует путем агрегирования нескольких оценщиков дерева с выборками начальной загрузки (выборка с заменой) исходного набора данных. В каждой выборке могут использоваться разные строки и столбцы исходного набора данных, а окончательные результаты представляют собой среднее значение заданного числа базовых оценок дерева решений, примененных к каждой выборке.

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

rf_grid = {
     'max_depth':[2,3,4,5],
     'n_estimators':[100,200,500],
     'max_features':['auto','sqrt','log2'],
     'max_samples':[.75,.9,1], 
}

А потом запустить прогноз.

f.set_estimator('rf')
f.ingest_grid(rf_grid) 
f.tune() 
f.auto_forecast()

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

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

XGBoost

XGBoost — сложная модель для краткого объяснения, поэтому я полагаюсь на статьи здесь (для начинающих) и здесь (для более глубокого погружения), если читателю интересно. Основная идея заключается в том, что, как и в случае со случайным лесом, оценки выполняются с помощью ряда деревьев решений, где окончательные результаты представляют собой тип средневзвешенного значения каждой базовой оценки. В отличие от случайного леса, деревья строятся последовательно, где каждое последующее дерево моделирует остатки дерева, которое было до него, в надежде максимально минимизировать окончательные остатки. Выборка не случайна, а основана на слабых сторонах предыдущего дерева. Таким образом, результаты получаются путем бустинга выборок, тогда как в Random Forest используется агрегация с начальной загрузкой, где каждое дерево и выборка независимы от других. Это упрощение того, что на самом деле происходит под капотом, поэтому, если это объяснение вас не удовлетворило, прочитайте статьи по ссылкам. Есть много гиперпараметров для настройки такой модели, и мы можем построить такую ​​сетку:

xgboost_grid = {
     'n_estimators':[150,200,250],
     'scale_pos_weight':[5,10],
     'learning_rate':[0.1,0.2],
     'gamma':[0,3,5], 
     'subsample':[0.8,0.9]
}

Оценка модели:

f.set_estimator('xgboost')
f.ingest_grid(xgboost_grid)
f.tune()
f.auto_forecast()

XGBoost превзошел MLR с тестовым набором MAPE 12%. Тем не менее, он кажется даже более подходящим, с оценкой R2 в выборке, близкой к 100%.

К настоящему времени, надеюсь, вы получили представление о том, как эти модели были построены и оценены. В предоставленном блокноте вы также можете увидеть примеры для LightGBM (усиленная древовидная модель Microsoft, похожая на XGBoost), стохастического градиентного спуска и K-ближайших соседей. Для краткости я перейду к моделям Bagging и Stacking, чтобы закончить пост.

Бэгинг

BaggingRegressor от Scikit-learn использует ту же концепцию выборки, которая была представлена ​​в разделе Случайный лес этой статьи, но вместо того, чтобы каждая базовая оценка была деревом решений, мы можем использовать любую модель, которую захотим. В данном случае я указал 10 моделей нейросетей Multi-Level Perceptron с тремя слоями по 100 единиц в каждом и решатель LBFGS. Каждому подмножеству данных разрешено использовать 90% размера исходного набора данных для выборки наблюдений, а также случайным образом использовать 50% функций исходного набора данных. В коде это выглядит так:

f.add_sklearn_estimator(BaggingRegressor,'bagging')
f.set_estimator('bagging')
f.manual_forecast(
     base_estimator = MLPRegressor(
         hidden_layer_sizes=(100,100,100),
         solver='lbfgs'
     ),
     max_samples = 0.9,
     max_features = 0.5,
)

Эта модель показала наилучшие результаты с тестовым набором MAPE 11% и тестовым набором R2 с показателем 79%. Это значительно лучше, чем у следующих лучших моделей по тем же показателям, XGBoost и MLR.

Укладка

Последней рассмотренной моделью является StackingRegressor. Он создает новый оценщик, который использует прогнозы из других указанных моделей для создания окончательного прогноза. Он делает этот окончательный прогноз с оценкой, переданной параметру final_estimator .

В нашем случае мы объединяем четыре модели, которые оценивали лучшее вне выборки в этом наборе данных:

  • K-ближайшие соседи (KNN)
  • XGBoost
  • СветGBM
  • Стохастический градиентный спуск (SGD)

Окончательная оценка — это оценка бэггинга, которую мы определили в предыдущем разделе.

Преимущество использования BaggingRegressor в качестве окончательной оценки заключается в том, что даже несмотря на то, что модели KNN и XGBoost были сильно переобучены, а модели MLP должны концептуально взвешивать свои прогнозы соответственно искусственно завышенными, потому что эта модель обучается только с половиной функций, входных для любого заданного модели MLP, она также должна научиться доверять прогнозам двух моделей, которые не были настолько переобученными: LightGBM и SGD. Таким образом, модель оценивается как таковая:

Усложнение моделей не всегда приводит к улучшению результатов, но, похоже, именно это и происходит. Наша модель стекирования явно превзошла другие: показатель MAPE для тестового набора составил 10%, а показатель R2 для тестового набора — 83%. Он также имеет метрики в выборке, сопоставимые с оцененным MLR.

Тестирование на истории

Для дальнейшей проверки наших моделей мы можем протестировать их на исторических данных. Это процесс итеративной оценки их точности за последние n-множество горизонтов прогнозирования, чтобы увидеть, какие результаты были бы фактически достигнуты, если бы эта модель была реализована, обученная только на наблюдениях, предшествующих каждому горизонту прогноза. По умолчанию Scalecast выбирает 10 горизонтов прогноза с длиной, определяемой количеством будущих дат, сгенерированных в объекте (в нашем случае 60). Вот как это выглядит в коде:

f.backtest('stacking')
f.export_backtest_metrics('stacking')

Это говорит нам о том, что в среднем, используя продолжительность прогноза в 60 дней, наша модель получила бы оценку MAPE, равную 7%, и R2, равную 76%. Это хороший способ убедиться, что модели не просто повезло с конкретным тестовым набором, переданным ей, и что она действительно может обобщать невидимые данные.

Заключение

В этом посте представлены несколько концепций моделирования для прогнозирования, включая линейные методы (MLR, Lasso, Ridge), ансамбли деревьев (Random Forest, XGBoost, LightGBM), бэггинг и суммирование. Увеличивая сложность нелинейных методов, применяемых к нему, и используя умные методы выборки, мы получили показатель MAPE тестового набора 10% на тестовом наборе с примерно 400 наблюдениями. Я надеюсь, что этот пример смог вдохновить вас на некоторые собственные идеи, и я ценю, что вы последовали за мной! Если вы нашли этот обзор полезным, поставьте звезду пакету scalecast на GitHub.