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

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

Если вы видели то же самое, что я описываю, и настроены скептически, вы правы, думая так. Обычно происходит то, что аналитик передает строку одношаговых прогнозов, которые кажутся точными на проверочном срезе данных. Аналитик заключает, что созданная ими модель достаточно мощна, чтобы точно предсказывать многие периоды в будущем. Это может происходить сознательно или неосознанно — если бы мне пришлось угадывать, я бы сказал, что большинство аналитиков, которые обычно видят примеры моделей, построенных на срезах данных, никогда не задумываются о том, что добавление элемента времени в набор данных может сделать с их моделями. . Временные ряды отличаются от другого машинного обучения, и необходимо соблюдать дополнительные меры предосторожности, иначе результаты будут вводящими в заблуждение.

Роль автокорреляции

Настоящее сериала обычно тесно связано с его прошлым. Это называется авторегрессионным (AR) свойством ряда или автокорреляцией. В частности, любое данное значение, вероятно, довольно близко к значению, которое было непосредственно перед ним.

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

Эта функция говорит, что предыдущее значение (x_{t-1}), умноженное на коэффициент альфа, сезонность ряда (s_t), тренд ряда (t_t) и член ошибки (e_t), определяют следующее значение в ряду. Допустим, вы хотите применить эту модель к реальным данным, в частности, для прогнозирования на 10 месяцев вперед. Для этого вам дается 100 ежемесячных наблюдений:

У нас есть данные, поэтому давайте подготовим модель. Чтобы получить первый член из перечисленного уравнения, авторегрессионный член, мы можем использовать метод shift() из pandas:

data['AR1'] = data['Target'].shift()

Для сезонности вместо использования значения месяца как есть мы можем использовать преобразование Фурье, которое моделирует сезонность с помощью волновых функций:

data['MonthSin'] = data['Month'].apply(
    lambda x: np.sin(np.pi * x/(12/2))
)
data['MonthCos'] = data['Month'].apply(
    lambda x: np.cos(np.pi * x/(12/2))
)

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

Теперь у нас есть все три компонента, которые нам нужны: значение предыдущего ряда (x_{t-1}), его тенденция (t_t) и его сезонность (s_t). Мы предполагаем, что член ошибки (e_t) является случайным и не может быть смоделирован. Итак, давайте напишем нормальный конвейер машинного обучения и посмотрим, что получится.

data_processed = data[
    [
         'Target',
         'AR1',
         'MonthSin',
         'MonthCos',
         'Year'
    ]
].dropna()
data_processed.head()

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

y = data_processed['Target']
X = data_processed.drop('Target',axis=1)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=10, shuffle=False
)

Остальное - кусок пирога. Обучите линейную модель:

lr = LinearRegression()
lr.fit(X_train,y_train)

Делайте прогнозы на невидимых тестовых данных:

pred = lr.predict(X_test)

Наконец, оцените ошибку:

mean_absolute_error(y_test,pred)

Это возвращает значение 3,8. Мы можем даже построить график результатов.

sns.lineplot(
    y='Target',
    x='Date',
    data=data.reset_index(),
    label='actuals',
)
sns.lineplot(
    y=pred,
    x=data.index[-10:],
    label='predictions',
)
plt.rcParams["figure.figsize"] = (8,6)
plt.show()

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

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

В более общем плане мы можем сказать, что:

Где j — любой период в течение горизонта прогноза. Это может быть надежным способом расширить ваш прогноз на будущие периоды. Задача решена. Кроме того, мы сталкиваемся с двумя проблемами:

  1. Требуется много дополнительной работы, чтобы запрограммировать процедуру оценки прогноза, которая делает это.
  2. Наша модель не тестировалась таким образом, поэтому метрика тестового набора может вводить в заблуждение.

Мы можем не думать, что пункт 2 имеет большое значение, но на самом деле, как мы увидим, это очень важно. Поскольку в тестовом наборе мы знали значение AR1 для всех 10 сделанных нами прогнозов, производительность по ошибкам, о которой мы сообщали, сильно завышена (или, можно сказать, занижена, поскольку чем меньше, тем лучше). Наш график также вводит в заблуждение. Модель на самом деле не такая точная, по крайней мере, не для 10 шагов.

Мы можем эффективно продемонстрировать это, используя пакет scalecast. Мы будем использовать те же данные и добавим те же термины к объекту Forecaster:

from scalecast.Forecaster import Forecaster
f = Forecaster(y=data['Target'],current_dates=data.index)
f.set_test_length(10)
f.generate_future_dates(10)
f.add_seasonal_regressors('month',sincos=True,raw=False)
f.add_seasonal_regressors('year')
f.add_ar_terms(1)

Затем мы вызываем модель. Но мы будем называть это двумя разными способами. Первый способ — это метод по умолчанию из Scalecast, где прогнозы тестового набора делаются с помощью процедуры динамического прогнозирования, которую мы уже рассмотрели. Значение для AR1 больше не известно в тестовом наборе, но вместо этого оно прогнозируется для 9 из 10 шагов (на шаге 1 мы используем фактическое значение, поскольку знаем, что это такое). Вторая модель будет точно такой же, как та, которую мы оценивали без масштабирования, без динамической оценки, где мы всегда знали значение AR1 в тестовом наборе:

f.set_estimator('mlr')
f.manual_forecast(call_me='mlr_dynamic')
f.manual_forecast(call_me='mlr_non-dynamic',dynamic_testing=False)

Затем мы наносим результаты:

f.plot_test_set()
plt.rcParams["figure.figsize"] = (8,6)
plt.show()

Красная линия на графике выше — это модель, которую мы оценивали ранее, та же тенденция, те же прогнозируемые значения. Оранжевая линия — это результаты, которые мы получили бы с теми же входными данными, но вместо этого динамически тестируя модель. Совсем небольшая разница! Настолько, что тест MAE падает до 9,1, то есть почти в 2,5 раза хуже!

f.export('model_summaries')[['ModelNickname','TestSetMAE']]

Упс! Теперь нам нужно объяснить, что случилось с нашим руководителем.

Более широкие последствия

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

S&P 500

Вот мои прогнозы для S&P 500, основанные только на данных, полученных чуть позже 2021 года:

Я мог бы предсказать ее стремительный подъем, а также почти точный момент, когда она начала падать.

Биткойн

В декабре 2021 года я мог бы предсказать недавний крах биткойнов:

COVID-19

Используя авиапассажиров из крупного аэропорта в качестве прокси, я мог бы предсказать падение авиаперевозок из-за пандемии COVID-19, а также последующее восстановление еще в 2015 году!!

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

Как избежать создания вводящих в заблуждение моделей

Так что, очевидно, я не мог предсказать ничего из этого. Но как часто мы видим такие же результаты, когда аналитики пытаются выдать высокоточные модели, когда на самом деле они представляют не более чем ряд одношаговых прогнозов? Многие из этих моделей не подвергаются сомнению, потому что они построены с использованием передовых алгоритмов машинного обучения или аналитиками, которые не знают лучше. Какими бы причудливыми они ни казались, как бы красноречиво они ни объяснялись, продвинутые модели, включая рекуррентные нейронные сети и LSTMS, не являются магией. Они не могут предсказать непредсказуемое. Это не означает, что нет очень умных людей, которые делают с ними невероятную работу, это просто означает, что когда они применяются неправильно, они вводят в заблуждение.

Итак, теперь мы знаем, чего делать нельзя. Во части 2 мы исследуем методы проверки, которые могут помочь нам построить высокоэффективные модели временных рядов, которые не будут обманывать. Они не выглядят такими точными на тестовом наборе, но они лучше предсказывают будущее, а это то, о чем мы действительно заботимся при прогнозировании. Так что подпишитесь и подпишитесь на уведомления по электронной почте, если это вас интересует. Все данные, использованные в этом анализе, были выдуманы, получены через общедоступные API или доступны по лицензии MIT. Смотрите здесь блокнот, который я создал.