Вы подписаны на Машинное обучение доступно
Отлично! завершите оплату для полного доступа к Машинное обучение доступно
Снова приветствуем Вас! Вы успешно авторизовались
Успех! Аккаунт активирован, и Вы имеете полный доступ к контенту.
Модель Бокса — Дженкинса (ARIMA)

Модель Бокса — Дженкинса (ARIMA)

in

Модель Бокса — Дженкинса (Авторегрессионная интегрированная скользящая средняя, англ. Autoregressive Integrated Moving Average, ARIMA) – Алгоритм (Algorithm) Машинного обучения (ML), позволяющий делать прогнозы на основе Временных рядов (Time Series), т.е. исторических наблюдений.

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

В области машинного обучения существует определенный набор методов и приемов, которые особенно хорошо подходят для прогнозирования значения зависимой переменной в зависимости от времени. В этой статье мы рассмотрим Авторегрессионная интегрированная скользящая средняя (ARIMA).

Мы называем ряд точек данных, проиндексированных (т.е. нанесенных на график) в хронологическом порядке временными рядами. Временной ряд можно разбить на 3 компонента.

  • Тенденция (Trend): движение данных вверх и вниз в течение длительного периода времени (например, повышение стоимости дома).
  • Сезонность (Seasonality): сезонные скачки (например, рост спроса на мороженое летом).
  • Шум (Noise): всплески и спады через случайные промежутки времени
Сверху вниз: исходник, тенденция, сезонность и шум

Прежде чем применять какую-либо статистическую модель к временному ряду, мы хотим убедиться, что в нем есть Стацинарность (Stationarity):

Чтобы некоторый временной ряд был классифицирован как стационарный, он должен удовлетворять трем условиям:

  • Постоянное Среднее арифметическое (Mean) выборок
  • Постоянная Дисперсия (Variance)
  • Постоянная ковариация между периодами одинакового расстояния. То есть мера линейной зависимости между периодами времени одинаковой длины (скажем, 10 дней / часов / минут) должна быть идентична ковариации некоторого другого периода такой же длины.
Только крайний левый ряд обладает одинаковыми средним и дисперсией

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

Стационарность

Библиотека statsmodels предоставляет набор функций для работы с временными рядами. Для начала импортируем необходимые библиотеки:

import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
from statsmodels.tsa.stattools import adfuller
from statsmodels.tsa.seasonal import seasonal_decompose
from statsmodels.tsa.arima_model import ARIMA
from pandas.plotting import register_matplotlib_converters
register_matplotlib_converters()

Мы будем работать с набором данных, который содержит количество пассажиров самолета в определенный день:

df = pd.read_csv('https://www.dropbox.com/s/iw6ucqq3rhifb7r/AirPassengers.csv?dl=1', parse_dates = ['Month'], index_col = ['Month'])
df.head()

Отобразим динамику на графике:

plt.xlabel('Date')
plt.ylabel('Number of air passengers')
plt.plot(df)

Убедимся, что временной ряд стационарен. Есть два основных способа определить, является ли данный временной ряд таковым:

  • Скользящие среднее и Стандартное отклонение (Stabdard Deviation): временные ряды являются стационарными, если эти метрики остаются постоянными во времени (невооруженным глазом видно, являются ли линии прямыми и параллельными оси x).
  • Расширенный тест Дики-Фуллера (ADF): временной ряд считается стационарным, если P-значение (P-Value) низкое в соответствии с Нулевой гипотезой (Null Hypothesis), а критические значения Доверительными интервалами (Confidence Intervals) 1%, 5%, 10% максимально близки к статистике ADF.

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

rolling_mean = df.rolling(window = 12).mean()
rolling_std = df.rolling(window = 12).std()
plt.plot(df, color = 'blue', label = 'Original')
plt.plot(rolling_mean, color = 'red', label = 'Rolling Mean')
plt.plot(rolling_std, color = 'black', label = 'Rolling Std')
plt.legend(loc = 'best')
plt.title('Rolling Mean & Rolling Standard Deviation')
plt.show()

Как видите, скользящие среднее и стандартное отклонение со временем растут. Таким образом, можно сделать вывод, что временной ряд не является стационарным.

result = adfuller(df['#Passengers'])
print('ADF Statistic: {}'.format(result[0]))
print('p-value: {}'.format(result[1]))
print('Critical Values:')
for key, value in result[4].items():
    print('\t{}: {}'.format(key, value))
ADF Statistic: 0.8153688792060418
p-value: 0.9918802434376409
Critical Values:
	1%: -3.4816817173418295
	5%: -2.8840418343195267
	10%: -2.578770059171598

Статистика ADF далека от критических значений, а P-значение превышает пороговое значение (0,05). Таким образом, можно сделать вывод, что временной ряд не является стационарным.

Взятие логарифма – это простой способ снизить скорость увеличения скользящего среднего.

df_log = np.log(df)
plt.plot(df_log)

Давайте создадим функцию для запуска двух тестов, которые определяют, является ли данный временной ряд стационарным.

def get_stationarity(timeseries):
    
    # rolling statistics
    rolling_mean = timeseries.rolling(window = 12).mean()
    rolling_std = timeseries.rolling(window = 12).std()
    
    # rolling statistics plot
    original = plt.plot(timeseries, color = 'blue', label = 'Original')
    mean = plt.plot(rolling_mean, color = 'red', label = 'Rolling Mean')
    std = plt.plot(rolling_std, color = 'black', label = 'Rolling Std')
    plt.legend(loc = 'best')
    plt.title('Rolling Mean & Standard Deviation')
    plt.show(block = False)
    
    # Dickey–Fuller test:
    result = adfuller(timeseries['#Passengers'])
    print('ADF Statistic: {}'.format(result[0]))
    print('p-value: {}'.format(result[1]))
    print('Critical Values:')
    for key, value in result[4].items():
        print('\t{}: {}'.format(key, value))

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

rolling_mean = df_log.rolling(window = 12).mean()
df_log_minus_mean = df_log - rolling_mean
df_log_minus_mean.dropna(inplace = True)
get_stationarity(df_log_minus_mean)
ADF Statistic: -3.1629079913008504
p-value: 0.022234630001244333
Critical Values:
	1%: -3.4865346059036564
	5%: -2.8861509858476264
	10%: -2.579896092790057

Как мы видим, после вычитания среднего скользящее среднее и стандартное отклонение стали приблизительно горизонтальны. P-значение ниже порога 0,05, а статистика ADF близка к критическим значениям. Следовательно, временной ряд стал стационарным.

Модель авторегрессии (AR)

Модели авторегрессии основываются на предположении, что прошлые значения влияют на текущие. Такие методы обычно используются при анализе природы, экономики и других изменяющихся во времени процессов. Пока справедливо предположение, мы можем построить модель Линейной регрессии (Linear Regression), которая пытается предсказать значение зависимой переменной сегодня, учитывая значения, которые она "узнала" о предыдущих днях.

Модель скользящих средних (MA)

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

Модель ARIMA, как Вы уже догададись, – это комбинация AR и MA.

ARIMA:

Модель ARIMA использует дифференцирование к модели ARMA. При дифференцировании текущее значение вычитается из предыдущего, и полученная разность используется для преобразования временного ряда в стационарный.

Данные до (справа) и после дифференцирования со смещением на единицу

Три целых числа (p, d, q) обычно используются для параметризации ARIMA:

  • p: количество членов авторегрессии
  • d: количество несезонных различий
  • q: количество условий скользящей средней

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

Функция автокорреляции (ACF) – корреляция между наблюдениями в текущий момент времени и наблюдениями во все предыдущие моменты времени. Мы можем использовать ACF для определения оптимального количества условий скользящей средней. Количество элементов определяет порядок модели.

Функция частичной автокорреляции (PACF): как следует из названия, PACF является подмножеством ACF. PACF выражает корреляцию между наблюдениями, сделанными в два момента времени, с учетом любого влияния со стороны других точек данных. Мы можем использовать PACF, чтобы определить оптимальное количество элементов для использования в модели AR. Количество элементов определяет порядок модели.

Давайте посмотрим на пример. Горизонтальные фиолетовые пунктирные линии представляют уровни значимости. Вертикальные линии представляют значения ACF и PACF в определенный момент времени. Только вертикальные линии, которые превышают горизонтальные, считаются значимыми.

Таким образом, мы использовали предыдущие два дня в уравнении авторегрессии.

ACF можно использовать для определения наилучших параметров модели MA.

Таким образом, в уравнении скользящего среднего мы будем использовать только предыдущий день.

Возвращаясь к нашему примеру, мы можем создать и подогнать модель ARIMA:

decomposition = seasonal_decompose(df_log) 
model = ARIMA(df_log, order = (2, 1, 2))
results = model.fit(disp = -1)
plt.plot(df_log_shift)
plt.plot(results.fittedvalues, color = 'red')

Посмотрим, как модель сравнивается с исходным временным рядом:

predictions_ARIMA_diff = pd.Series(results.fittedvalues, copy=True)
predictions_ARIMA_diff_cumsum = predictions_ARIMA_diff.cumsum()
predictions_ARIMA_log = pd.Series(df_log['#Passengers'].iloc[0], index = df_log.index)
predictions_ARIMA_log = predictions_ARIMA_log.add(predictions_ARIMA_diff_cumsum, fill_value = 0)
predictions_ARIMA = np.exp(predictions_ARIMA_log)
plt.plot(df)
plt.plot(predictions_ARIMA)

Учитывая, что у нас есть данные за каждый месяц за 12 лет и мы хотим спрогнозировать количество пассажиров на следующие 10, мы используем параметр 264 – (12 * 12) + (12 * 10) = 264.

results.plot_predict(1, 264)


Ноутбук, не требующий дополнительной настройки на момент написания статьи, можно скачать здесь.

Автор оригинальной статьи: Cory Maklin