Предсказание временных рядов с помощью регрессии

Временные ряды. Простые решения

В этой статье мы рассмотрим несколько простых подходов прогнозирования временных рядов.

Материал, изложенный в статье, на мой взгляд, хорошо дополняет первую неделю курса «Прикладные задачи анализа данных» от МФТИ и Яндекс. На обозначенном курсе можно получить теоретические знания, достаточные для решения задач прогнозирования рядов динамики, а в качестве практического закрепления материала предлагается с помощью модели ARIMA библиотеки scipy сформировать прогноз заработной платы в Российской Федерации на год вперед. В статье, мы также будем формировать прогноз заработной платы, но при этом будем использовать не библиотеку scipy, а библиотеку sklearn. Фишка в том, что в scipy уже предусмотрена модель ARIMA, а sklearn не располагает готовой моделью, поэтому нам придется потрудиться ручками. Таким образом, нам для решения задачи, в каком то смысле, необходимо будет разобраться как устроена модель изнутри. Также, в качестве дополнительного материала, в статье, задача прогнозирования решается с помощью однослойной нейронной сети библиотеки pytorch.

Весь код написан на python 3 в jupyter notebook. Более того, notebook устроен таким образом, что вместо данных о заработной плате можно подставить многие другие ряды динамики, например данные о ценах на сахар, поменять период прогнозирования, валидации и обучения, добавить иные внешние факторы и сформировать соответствующий прогноз. Другими словами, в работе используется простенький самописный тренажерчик, с помощью которого можно прогнозировать различные ряды динамики. Код можно посмотреть здесь

План статьи

Краткое описание тренажера


Import the data
Здесь все просто — импортируем данные. Иногда бывает так, что сырых данных достаточно для формирования более-менее внятного прогноза. Именно первый и второй прогнозы в статье моделируются на основании сырых данных, то есть для прогноза заработной платы используются необработанные данные о заработной плате в прошлые периоды.

Aggregate the data
В статье не используется агрегация данных ввиду отсутствия необходимости. Однако зачастую, данные могут быть представлены неравными временными интервалами. В таком случае, просто необходимо их агрегировать. Например, данные с торгов ценными бумагами, валютой и другими финансовыми инструментами необходимо агрегировать. Обычно берут среднее значение в интервале, но можно и максимальное, минимальное, стандартное отклонение и другие статистики.

Preprocessing the data
В нашем случае, речь идет в первую очередь о предобработке данных, благодаря которой, временной ряд приобретает свойство гомоскедастичности (через логарифмирование данных) и становится стационарным (через дифференцирование ряда).

Split to train, test & forecast
В этом блоке кода временные ряды разбиваются на периоды обучения, тестирования и прогнозирования путем добавления нового столбца с соответствующими значениями «train», «test», «forecast». То есть, мы не создаем три отдельных таблицы для каждого периода, а просто добавляем столбец, на основании, которого в дальнейшем будем разделять данные.

Extraction exogenous time-series features
Бывает полезным выделить дополнительные внешние (экзогенные) признаки из временного ряда. Например, указать выходной это день или нет, указать количество дней в месяце (или количество рабочих дней в месяце) и др. Как правило эти признаки «вытаскиваются» из самого временного ряда без какого либо ручного вмешательства.

Create/import exogenous data
Не всю информацию можно «вытащить» из временного ряда. Иногда могут потребоваться дополнительные внешние данные. Например, какие-то эпизодические события, которые оказывают сильное влияние на значения временного ряда. Такими событиями могут быть даты начала военных действий, введения санкций, природные катаклизмы и др. В работе такие факторы не рассматриваются, однако возможность их использования стоит иметь в виду.

Exogenous values
В этом блоке кода мы объединяем все экзогенные данные в одну таблицу.

Union the data (create dataset)
В этом блоке кода мы объединяем значения временного ряда и экзогенных признаков в одну таблицу. Другими словами — готовим датасет, на основании, которого будем обучать модель, тестировать качество и формировать прогноз.

Learning the model
Здесь все понятно — мы просто обучаем модель.

Preprocessing data: predict & forecast
В случае, если мы для обучения модели использовали предобаботанные данные (логарифмированные, обработанные функцией бокса-кокса, стационарный ряд и др.), то качество модели для начала оценивается на предобработанных данных и только потом уже на «сырых» данных. Если, мы данные не предобрабатывали, то данный этап пропускается.

Читайте также:  Предсказания конца света когда он будет

Row data: predict & forecast
Данный этап является заключительным. Если, модель обучалась на предобработанных данных, например, мы их прологарифмировали, то для получения прогноза заработной платы в рублях, а не логарифма рублей, нам следует перевести прогноз обратно в рубли.

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

Решение в лоб

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

Давайте возьмем значения заработной платы в 12-ть прошлых месяцев для предсказания заработной платы в 13-й месяц. Другими словами для каждого целевого значения у нас будет 12 признаков.

Признаки будем подавать на вход Ridge Regression библиотеки sklearn. Параметры модели берем по умолчанию за исключением параметра alpha, его установили на 0, то есть по сути мы используем обычную регрессию.

Это и есть решение в лоб — оно самое простое:) Бывают ситуации, когда нужно очень срочно дать хоть какой-то результат, а времени на какую-либо предобработку просто нет или не хватает опыта, чтобы оперативно обработать или добавить данные. Вот в таких ситуациях, можно в качестве baseline использовать сырые данные для построения прогноза. Забегая вперед, отмечу, что качество модели оказалось сопоставимо с качеством моделей, в которых используется предобработка данных.

Давайте посмотрим, что у нас получилось.

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

В соответствии со значениями коэффициентов регрессии, наибольшее влияние на прогноз заработной платы оказывает значение заработной платы ровно год назад.

Попробуем добавить в модель экзогенные переменные.

Добавление экзогенных переменных

Мы будем использовать 2 внешних признака: количество дней в месяце и номер месяца (от 1 до 12). Признак «Номер месяца» мы бинаризируем, в итоге у нас получится 12 столбцов для каждого месяца со значениями 0 или 1.

Сформируем новый датасет и посмотрим на качество модели.

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

Давайте теперь проведем первую предобработку данных.

Коррекция гетероскедастичности.

Если мы посмотрим на график заработной платы за период с 2010 по 2020 гг, то мы увидим, что ежегодно разброс заработной платы внутри года между месяцами растет.

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

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

Обучим модель на прологарифмированном ряду

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

Приведение ряда к стационарному

Приводить ряд к стационарному будем следующим образом:

  • Определяем разницу между целевым значением заработной платы и значением год назад: t — (t-12) = dif_1
  • Определяем разницу между полученным и смещенным на 1 месяц значением: dif_1 — (dif_1-1) = dif_2

В итоге получаем следующий временной ряд.

Ряд действительно выглядит стационарным, об этом также говорит значение критерия Дики-Фуллера.

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

Давайте посмотрим, что получилось.

Вот так выглядит предсказание стационарного ряда. Как и ожидали — не очень-то и хорошо 🙂

А вот предсказание и прогноз заработной платы.

Качество заметно улучшилось и прогноз визуально стал выглядеть правдоподобным.

Теперь сформируем прогноз без использования экзогенных переменных

Качество еще улучшилось и правдоподобность прогноза сохранилась 🙂

Прогнозирование с помощью однослойной нейронной сети

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

Читайте также:  Предсказание какое будет лето

Для начала посмотрим на саму сеть

Теперь пару слов о том, как будем ее обучать.

  1. Фиксируем random seed для целей воспроизводимости результата
  2. Инициализируем модель
  3. Задаем функцию потерь — MSELoss
  4. Выбираем в качестве оптимизатора Adam optimizer
  5. Указываем начальный шаг обучения и определяем условие, при котором шаг понижается. Отмечу, что правильный выбор шага и его дальнейшее изменение (обычно уменьшение) приносит хорошие плоды
  6. Указываем количество эпох обучения
  7. Запускаем обучение
  8. На вход сети подаем целиком датасет, так как он очень маленький и разбивать его на батчи не имеет смысла
  9. При обучении, каждую тысячу эпох формируем графики значения функции потерь на обучающей и тестовой выборках. Это позволяет нам контролировать переобучение или не дообучение модели.

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

Не будем рассматривать качество предсказаний для каждого датасета отдельно (желающие могут посмотреть подробности на гите). Давайте сравним итоговые результаты.

Качество на тестовой выборке с использованием Ridge Regression

Качество на тестовой выборке с использованием Single layer NN

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

На этом моменте статья подошла к завершению. Надеюсь, материал будет полезен как некий обзор baseline-подходов, применяемых при прогнозировании временных рядов и послужит хорошим практическим дополнением к курсу «Прикладные задачи анализа данных» от МФТИ и Яндекс.

Источник

Прогнозирование временных рядов нейронными сетями. Keras. Часть 1.

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

Разбираю код Дмитрия Романова, ведущего курс по нейронным сетям в «Университете Искуственного Интеллекта». Мой notebook с моделированием. Я немного оптимизировал код Дмитрия и добавил ряд пояснений, позволяющих разобраться в теме.

С временными рядами мы сталкиваемся повседневно. Это может быть описание каких-то природных событий, например, прогноз температуры, который жестко привязан к времени. Поменять местами последовательность нельзя, временной ряд рассыплется, если прогноз на 10-е поставить на 5-е число. Это будут совершенно другие данные. Т.е. делать перемешивание, как в случае, например, с определением авторства текстов, нельзя. К таким данным условно можно отнести многие, даже не представляющие собой зависимость именно от времени. Важно, что отсчеты нельзя менять местами. Например, аудиопоток, цены на акции, даже слова, поскольку изменить порядок букв в слове нельзя без искажения слова.

Обучающая выборка

Рассмотрим простой пример. Например, есть вектор, описывающий среднюю дневную температуру в течение 100 лет. Нужно построить прогноз на день вперед. Подготовим исходные данные для обучения сети.

Подготовка данных для анализа временных рядов на нейронных сетях

  • xTrainCount = 36500 отсчетов — длина вектора описывающего погоду за 100 лет * 365 дней.
  • xLen = 100 — длина вектора xTrain.
  • stepsForward = 1 — длина вектора yTrain или количество шагов (дней) на которое делается прогноз погоды.
  • Shft = 1 — смещение вектора xLen относительно предыдущего. Обычно смещение делают на единицу.
  • xCount = xTrainCount — xLen + 1 – stepsForward — количество строк в матрице, которое получится после «раскусывания» исходного временного ряда на xLen + stepsForward.
  • Выборка xTrain — матрица с размерностью (xTrainCount — xLen + 1 – stepsForward, xLen).
  • Выборка yTrain — матрица с размерностью (xTrainCount — xLen + 1 – stepsForward, stepsForward).
  • Каждое значение yTrain — это значение температуры в некоторый день, но оно определяется температурой за предыдущие xLen дней. Нейронка пытается обобщить, как значение yTrain длиной stepsForward (предсказание на 1 или более дней) зависит от значений xTrain длиной xLen. Т.е. в какой-то степени yTrain = f(xTrain).
  • При таком перемешивании мы как бы ставим задачу нейронной сети найти взаимосвязь последовательности длиной stepsForward (например, в случае прогноза погоды на 1 день) из значений в исходной выборке от предыдущих xLen значений (например, от 50 предыдущих дней).
  • Если мы задаем поиск закономерностей между значением yTrain длиной stepsForward и остальными значениями, то в случае, когда stepsForward > 1, мы тренируем нейронную сеть на предсказание на несколько дней вперед.
  • При наличии нейронной сети, предсказывающей на 1 день вперед, можно сделать предсказание на 2 и более дней, подавая на вход значение, предсказанное нейронной сетью ранее и убирая с начала по одному дню, чтобы длина вектора не изменилась. Однако, для такого варианта точность предсказания нейронкой на один день должна быть очень высокой, иначе прогноз быстро станет случайным.
Читайте также:  Предсказания ванги что уже сбылось

Проверочная выборка

Важный вопрос, как формируется проверочная выборка. В случае с временными рядами все не так просто, как с некоторыми другими данными. В данном случае нельзя случайным образом выбрать, например, 20% данных из xTrain и yTrain.
P.S. Цифры на графике обозначают некоторую общность данных — это не данные выборок. Например, желтые — это вектора относящиеся к xTrain.

  • Проверочная выборка берется из векторов xTrain и yTrain.
  • Для проверочной выборки берется «хвост» данных длиной valLen.
  • Данные для проверочной выборки берутся снизу (с конца), чтобы быть максимально близкими к прогнозным значениям. Скажем, в случае прогноза погоды если взять начало последовательности, то это будут данные столетней давности. За 100 лет ситуация с погодой могла измениться, поэтому использовать их для проверки неправильно.
  • Между проверочной выборкой и обучающей нужно сделать промежуток длиной xLen + stepsForward. В этом промежутке данные xTrain и xVal в значительной степени пересекаются. Нейронка может «заучить» на обучающей выборке общие с проверочной выборкой паттерны. Это может обманчиво улучшить показатели на проверочной выборке. Поэтому приходится пожертвовать данными из этого промежутка — они исключаются из выборок.
  • При коротких выборках, когда данных мало, можно исключать не диапазон xLen + stepsForward , а меньше, например, 80% от этой длины или менее. Это уже в значительной степени уменьшает взаимное перемешивание.

Прогнозирование акций Лукойл

В предыдущей статье по нейронным сетям я уже рассмотрел загрузку данных с ftp/http. В данном случае URL для загрузки данных:

Загрузим данные из .csv с помощью Pandas. Нужно обратить внимание, что в качестве разделителя в csv используется «;» и в явном виде передать sep=’;’.

Что такое OHLC?

Видно, что показатели OHLC берутся с интервалом в 1 минуту.

OHLC – это сокращенное обозначение котировок, которые указываются для элементарной диаграммы ценового графика. В этой аббревиатуре :

  • О обозначает Open – цену открытия интервала.
  • H означает High (в нашем случае Max) — максимум цены интервала.
  • L означает Low (Min) – минимум цены интервала.
  • C означает Close – цену закрытия интервала.
  • Volume — объем операций.

График OHLC

На графике OHLC каждый интервал времени (например, 5 минут) представлен OHLC ценами внутри этого интервала. OHLC — это один из основных показателей фьючерсной торговли на FOREX.

Подготовка данных

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

Файлы с данными по акциям идут по годам. Объединим два года для анализа.

Выведем данные OHLC на график:

Значения OHLC достаточно близки друг к другу, поэтому слились на первом графике.

На втором графике видны два аномально высоких значения операций.

Проверка алгоритма подготовки данных

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

Исходную последователность преобразуем в массив «раскусыванием»:

Для получения yTrain нужно в каждой строке брать вектор длиной stepsForward, начиная с xLen, поскольку последовательность до xLen пошла в xTrain. Соотвественно, range начнется с xLen.

Видно, что значения вектора yTrain начинаются с xLen. Каждый элемент длиной stepsForward = 1. По сути, нейронка ищет закономерность между каждым yTrain и предыдущими xLen значений xTrain.

После получения исходной последовательности нужно разрезать массив на две части: обучающую выборку и валидационную. Кроме того между ними нужно выбросить «прослойку» размером bias записей. Это важно, чтобы гарантированно исключить перемешивание, т.е. чтобы в валидационную выборку не попали значения из обучающей.

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

После запуска «раскусывалки» получим следующее:

  • 3 записи остались для для проверочной выборки.
  • 8 записей были выброшены (bias)
  • 4 записи с начала оставлись для обучающей выборки

итого 15 записей.

Код подготовки данных для временного ряда

Одномерная свертка

Забегая вперед скажу, что на этом временном ряде наилучшие показатели обеспечила одномерная свертка.

Источник

Оцените статью