Практический пример использования моделей BATS и TBATS в Python
При работе с временными рядами мы часто сталкиваемся с сезонностью. Сезонность определяется как периодическое изменение в нашей серии. Это цикл, который происходит в течение фиксированного периода в нашей серии. Например, давайте взглянем на набор данных популярных авиакомпаний, показанный ниже.
Здесь мы можем четко видеть сезонный цикл, так как каждый год количество авиапассажиров достигает пика примерно в июле и снова падает.
Чтобы спрогнозировать этот ряд, мы можем просто использовать модель SARIMA, так как существует только один сезонный период продолжительностью в один год.
Теперь все усложняется, когда мы работаем с высокочастотными данными. Например, почасовой временной ряд может демонстрировать дневную, недельную, месячную и годовую сезонность, а это означает, что теперь у нас есть несколько сезонных периодов. Взгляните на почасовой объем трафика на межштатной автомагистрали 94, показанный ниже.
Глядя на данные выше, мы видим, что у нас есть два сезонных периода! Во-первых, у нас есть суточная сезонность, так как мы видим, что днем по дороге проезжает больше автомобилей, чем ночью. Во-вторых, у нас есть еженедельная сезонность, так как в будние дни объем трафика выше, чем в выходные.
В этом случае нельзя использовать модель SARIMA, потому что мы можем указать только один сезонный период, тогда как в наших данных точно есть два сезонных периода: дневная сезонность и недельная сезонность.
Поэтому мы обращаем наше внимание на модели BATS и TBATS. Используя эти модели, мы можем подобрать и спрогнозировать временные ряды, которые имеют более одного сезонного периода.
В этой статье мы сначала исследуем теорию, лежащую в основе BATS и TBATS, а затем применим их для прогнозирования почасового объема трафика на следующие семь дней. Давайте начнем!
Изучите новейшие методы анализа временных рядов с помощью Прикладного анализа временных рядов в Python. Курс охватывает как статистические модели, так и модели глубокого обучения, и вы будете работать с Python и TensorFlow!
Интуиция, стоящая за BATS и TBATS
Прежде чем мы погрузимся в проект, давайте сначала разберемся, как BATS и TBATS работают за кулисами.
летучие мыши
Аббревиатура BATS относится к методу: модель экспоненциального сглаживания в пространстве состояний с преобразованием Box-Cox, Aошибками RMA, Trend и S сезонные компоненты. Здесь есть что разобрать, так что давайте пошагово.
- Экспоненциальное сглаживание — это семейство методов прогнозирования. Общая идея, лежащая в основе этих методов прогнозирования, заключается в том, что будущие значения представляют собой средневзвешенное значение прошлых значений, причем веса экспоненциально уменьшаются по мере того, как мы возвращаемся назад во времени. Методы прогнозирования включают простое экспоненциальное сглаживание, двойное экспоненциальное сглаживание или метод Холта (для временных рядов с трендом) и тройное экспоненциальное сглаживание или метод Холта-Винтера (для временных рядов с трендом и сезонностью).
- Моделирование в пространстве состояний — это структура, в которой временной ряд рассматривается как набор наблюдаемых данных, на который влияет набор ненаблюдаемых факторов. Затем модель в пространстве состояний выражает отношения между двумя наборами. Опять же, это следует рассматривать как основу, поскольку модель ARMA может быть выражена как модель в пространстве состояний.
- Преобразование Бокса-Кокса — это степенное преобразование, которое помогает сделать ряд стационарным за счет стабилизации дисперсии и среднего значения во времени.
- Ошибки ARMA — это процесс, в котором мы применяем модель ARMA к остаткам временного ряда, чтобы найти любую необъяснимую связь. Обычно остатки модели должны быть полностью случайными, если модель не захватила какую-то информацию. Здесь мы используем модель ARMA для сбора оставшейся информации в остатках.
- Тренд — это компонент временного ряда, который объясняет долгосрочное изменение среднего значения ряда. Когда у нас есть положительная динамика, то наша серия со временем увеличивается. При отрицательном тренде ряд уменьшается со временем.
- Сезонная составляющая объясняет периодические изменения ряда.
Подводя итог, можно сказать, что BATS является расширением методов экспоненциального сглаживания, которое сочетает в себе преобразование Бокса-Кокса для обработки нелинейных данных и использует модель ARMA для фиксации автокорреляции в остатках.
Преимущество использования BATS заключается в том, что он может обрабатывать нелинейные данные, решать проблему автокорреляции в остатках, поскольку он использует модель ARMA, и может учитывать несколько сезонных периодов.
Однако сезонные периоды должны быть целыми числами, в противном случае BATS не могут быть применены. Например, предположим, что у вас есть недельные данные с годовой сезонностью, тогда ваш период равен 365,25/7, что примерно равно 52,2. В этом случае BATS исключается.
Кроме того, BATS может занять много времени, если сезонный период очень большой, а это означает, что он не подходит, если у вас есть почасовые данные с месячными (период будет 730).
Таким образом, модель TBATS была разработана для решения этой ситуации.
ТБАТ
Аббревиатура TBATS расшифровывается как Tригонометрическая сезонность, Bпреобразование Окса-Кокса, Aошибки RMA, Trend, и S сезонные компоненты.
В ней используются те же компоненты, что и в модели BATS, однако она представляет каждый сезонный период как тригонометрическое представление, основанное на рядах Фурье. Это позволяет модели соответствовать большим сезонным периодам и нецелочисленным сезонным периодам.
Таким образом, это лучший выбор при работе с высокочастотными данными, и он обычно подходит быстрее, чем BATS.
Здесь я намеренно избегал математики, чтобы избежать путаницы. Для подробного математического объяснения как BATS, так и TBATS я предлагаю вам прочитать эту статью.
Теперь, когда у нас есть интуитивное представление о том, как работают обе модели, давайте применим их для прогнозирования почасового объема трафика на следующие семь дней.
Применение BATS и TBATS в Python
Давайте посмотрим на обе модели в действии при прогнозировании почасового объема трафика. Вы можете обратиться ко всему исходному коду на GitHub.
Исследование
Во-первых, мы импортируем необходимые библиотеки для этого проекта.
import pandas as pd import numpy as np import matplotlib.pyplot as plt
Затем мы читаем файл CSV, содержащий наши данные. Обратите внимание, что вы также можете скачать его с GitHub.
data = pd.read_csv(‘daily_traffic.csv’) data = data.dropna()
Большой! После этого мы можем визуализировать наши данные.
fig, ax = plt.subplots(figsize=(14, 8)) ax.plot(data['traffic_volume']) ax.set_xlabel('Time') ax.set_ylabel('Traffic volume') fig.autofmt_xdate() plt.tight_layout() plt.show()
Из рисунка выше ясно видно, что у нас есть два сезонных периода. Давайте увеличим масштаб и обозначим дни недели, чтобы идентифицировать оба периода.
fig, ax = plt.subplots(figsize=(14, 8)) ax.plot(data['traffic_volume']) ax.set_xlabel('Time') ax.set_ylabel('Traffic volume') plt.xticks(np.arange(7, 400, 24), ['Friday', 'Saturday', 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']) plt.xlim(0, 400) fig.autofmt_xdate() plt.tight_layout() plt.show()
Конечно, мы знаем сюжет с самого начала этой статьи и замечаем, что объем трафика действительно ниже в выходные, чем в будние дни. Кроме того, мы видим ежедневную сезонность, когда трафик днем больше, чем ночью.
Таким образом, у нас есть два периода: дневной период имеет длину 24 часа, а недельный период имеет длину 168 часов. Имейте это в виду, когда мы перейдем к моделированию.
Моделирование
Теперь мы готовы начать моделирование наших данных. Здесь мы используем пакет sktime. Я только что обнаружил эту структуру, которая предлагает множество статистических и машинных методов обучения для временных рядов. Он также использует синтаксис, аналогичный scikit-learn, что упрощает его использование.
Первым шагом является определение нашей цели и определение горизонта прогноза. Здесь целью является сам объем трафика. Для горизонта прогноза мы хотим предсказать одну неделю данных. Поскольку у нас есть почасовые данные, мы должны предсказать 168 временных шагов (7 * 24) в будущее.
y = data['traffic_volume'] fh = np.arange(1, 168)
Затем мы разделяем наши данные на обучающий набор и тестовый набор. Мы сохраним данные за последнюю неделю в качестве тестового набора, чтобы оценить наши прогнозы.
Здесь мы используем функцию temporal_train_test_split из sktime.
from sktime.forecasting.model_selection import temporal_train_test_split y_train, y_test = temporal_train_test_split(y, test_size=168)
При желании мы можем визуализировать наш тестовый набор.
fig, ax = plt.subplots(figsize=(14, 8)) ax.plot(y_train, ls='-', label='Train') ax.plot(y_test, ls='--', label='Test') ax.set_xlabel('time') ax.set_ylabel('Daily traffic volu,e') ax.legend(loc='best') fig.autofmt_xdate() plt.tight_layout() plt.show()
Базовая модель
Прежде чем мы реализуем наши более сложные модели BATS и TBATS, всегда полезно иметь базовую модель. Таким образом, мы можем определить, действительно ли наши более сложные методы прогнозирования эффективны.
Здесь самая простая базовая линия, которую я могу придумать, — это просто повторение данных за последнюю неделю из тренировочного набора в будущее.
y_pred_baseline = y_train[-168:].values
Применение BATS
Теперь, когда у нас есть базовый уровень, давайте перейдем к реализации модели BATS.
Сначала мы импортируем модель BATS из sktime. Затем мы указываем параметры модели для обучения. Здесь мы хотим использовать преобразование Бокса-Кокса, поскольку имеем дело с нелинейными данными. Затем, поскольку в нашем наборе данных нет явной тенденции, мы удаляем эти компоненты из модели. Наконец, мы указываем сезонные периоды: 24 (для ежедневной сезонности) и 168 (для недельной сезонности).
Как только модель определена, мы просто подгоняем ее к обучающему набору и генерируем прогнозы на горизонте прогноза.
Все шаги, описанные выше, преобразуются в приведенный ниже код.
from sktime.forecasting.bats import BATS forecaster = BATS(use_box_cox=True, use_trend=False, use_damped_trend=False, sp=[24, 168]) forecaster.fit(y_train) y_pred_BATS = forecaster.predict(fh)
Применение TBATS
Прогнозирование с помощью TBATS оказывается точно таким же, как с использованием BATS, только теперь, ну… мы используем TBATS!
from sktime.forecasting.tbats import TBATS forecaster = TBATS(use_box_cox=True, use_trend=False, use_damped_trend=False, sp=[24, 168]) forecaster.fit(y_train) y_pred_TBATS = forecaster.predict(fh)
Оценка производительности
На данный момент у нас есть прогнозы на основе нашей базовой модели, BATS и TBATS. Затем мы готовы визуализировать прогнозы и посмотреть, какая модель работает лучше всего.
Визуализация прогнозов дает следующий график.
fig, ax = plt.subplots(figsize=(14, 8)) ax.plot(y_train, ls='-', label='Train') ax.plot(y_test, ls='-', label='Test') ax.plot(y_test.index, y_pred_baseline, ls=':', label='Baseline') ax.plot(y_pred_BATS, ls='--', label='BATS') ax.plot(y_pred_TBATS, ls='-.', label='TBATS') ax.set_xlabel('time') ax.set_ylabel('Daily traffic volume') ax.legend(loc='best') fig.autofmt_xdate() plt.tight_layout() plt.show()
Глядя на рисунок выше, кажется, что все наши модели дают очень похожие прогнозы, поскольку линии перекрываются. Очень сложно определить, какая модель работает лучше всего, просто взглянув на график.
При желании мы можем увеличить тестовый набор, чтобы лучше визуализировать прогнозы.
Глядя на рисунок выше, мы сначала замечаем, что обе модели действительно моделируют двойную сезонность, что само по себе здорово! Кроме того, кажется, что BATS лучше справляется с предсказанием будущего, поскольку TBATS, кажется, иногда переоценивает или недооценивает. Обратите также внимание на то, что базовая модель точно следует кривой фактических значений.
Теперь мы вычисляем метрику ошибок, чтобы определить лучшую модель и сравнить их производительность. В этом случае мы используем среднюю абсолютную процентную ошибку (MAPE) для простоты ее интерпретации. Напомним, что чем ближе MAPE к 0, тем выше производительность.
MAPE еще не реализован в scikit-learn, поэтому мы сами определяем эту функцию.
def mape(y_true, y_pred): return round(np.mean(np.abs((y_true - y_pred) / y_true)) * 100,2)
Затем мы просто вычисляем производительность каждой модели и визуализируем ее в виде гистограммы.
mape_baseline = mape(y_test, y_pred_baseline) mape_BATS = mape(y_test, y_pred_BATS) mape_TBATS = mape(y_test, y_pred_TBATS) print(f'MAPE from baseline: {mape_baseline}') print(f'MAPE from BATS: {mape_BATS}') print(f'MAPE from TBATS: {mape_TBATS}') fig, ax = plt.subplots() x = ['Baseline', 'BATS', 'TBATS'] y = [mape_baseline, mape_BATS, mape_TBATS] ax.bar(x, y, width=0.4) ax.set_xlabel('Models') ax.set_ylabel('MAPE (%)') ax.set_ylim(0, 35) for index, value in enumerate(y): plt.text(x=index, y=value + 1, s=str(round(value,2)), ha='center') plt.tight_layout()
Из рисунка выше видно, что BATS работает лучше, чем TBATS, чего и следовало ожидать, как мы видели на графике. Однако мы видим, что базовая модель является наиболее эффективной моделью, достигающей MAPE 11,97%.
Это немного антиклиматически, но давайте поймем, почему это произошло.
Возможно, наш набор данных слишком мал. Возможно, образец, который мы использовали для тестирования, оказался более предпочтительным для базовой модели. Один из способов проверки — спрогнозировать несколько 168-часовых горизонтов, чтобы увидеть, превосходит ли базовая модель остальные.
Также может быть, что мы были слишком строги с параметрами моделей. Здесь мы заставили обе модели использовать преобразования Бокса-Кокса и удалить компонент тренда. Однако мы могли бы не указывать эти параметры, и модель попробовала бы обе возможности для каждого параметра и выбрала бы ту, у которой наименьший AIC (информационный критерий Акаике). Хотя это удлиняет тренировочный процесс, это также может привести к повышению производительности BATS и TBATS.
Тем не менее, ключевым выводом является то, что построение базовой модели очень важно для любого проекта прогнозирования.
Заключение
В этой статье мы узнали о моделях BATS и TBATS и о том, как их можно использовать для прогнозирования временных рядов, имеющих более одного сезонного периода, и в этом случае модель SARIMA использовать нельзя.
Мы применили обе модели для прогнозирования почасового объема трафика, но оказалось, что наша базовая модель остается самой эффективной.
Тем не менее, мы увидели, что BATS и TBATS действительно могут моделировать временные ряды со сложными сезонными факторами.
Возможные улучшения
- Спрогнозируйте несколько 168-часовых горизонтов и посмотрите, действительно ли базовая модель является наиболее эффективной моделью. Вы можете использовать исходный набор данных, который содержит гораздо больше данных, чем то, с чем мы работали.
- Не указывайте параметры use_box_cox, use_trend и use_damped_trend и позвольте модели сделать лучший выбор на основе AIC.
Основные выводы
- Всегда создавайте базовую модель при прогнозировании
- BATS и TBATS можно использовать для моделирования временных рядов со сложной сезонностью.
- BATS хорошо работает, когда периоды короткие и целые числа
- TBATS обучается быстрее, чем BATS, и работает с сезонными периодами, которые не являются целыми числами.
Спасибо за чтение, и я надеюсь, что узнали что-то полезное! Если вы хотите узнать больше о прогнозировании временных рядов в Python с использованием статистических моделей и моделей глубокого обучения, ознакомьтесь с моим курсом временных рядов!
Ваше здоровье! 🍺