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

Временной ряд (Time Series)

in

Временной ряд – совокупность Наблюдений (Observation), собранных за определенный временной интервал. Этот тип данных используется для поиска долгосрочного тренда, прогнозирования будущего и прочих видов анализа. В отличие от Датасетов (Dataset) без временных рядов в качестве Признаков (Feature), наборы с временными рядами не выполняют основное требование линейной регрессии о независимости наблюдений.

Временной ряд – стоимость акции компании LG на моменты открытия и закрытия биржи

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

Такие свойства временного ряда коренным образом меняют характер взаимодействия при создании Модели (Model) Машинного обучения (ML). Прежде чем использовать такие данные для обучения Нейронной сети (Neural Network), стоит добиться так называемой Стационарности (Stationarity).

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

Стационарность – это свойство временного ряда, постоянство его статистических свойств: Cреднего значения (Average) и Дисперсии (Variance). Это важно, поскольку большинство моделей работают, исходя из предположения о стационарности. Предполагается, что, если временной ряд ведет себя определенным образом, очень высока вероятность того, что он повторит те же паттерны в будущем. Стационарные ряды к тому же более просты в обработке.

Временные ряды и statsmodels

Поработаем с временным рядом – хронологией стоимости акции компании LG. Для начала протестируем данные на стационарность, то есть пригодность к использованию в качестве обучающих данных модели машинного обучения и предскажем стоимость акции с помощью Модели Бокса — Дженкинса (ARIMA). Для начала импортируем все необходимые библиотеки:

import matplotlib.pyplot as plt
from numpy import log
import pandas as pd

from sklearn.metrics import mean_squared_error

from statsmodels.tsa.stattools import adfuller
from statsmodels.tsa.arima_model import ARIMA

Помимо классических библиотек NumPy и Pandas нам понадобятся также классы Среднеквадратической ошибки (Mean Squared Error), теста Дики-Фуллера и непосредственно модели ARIMA. Теперь загрузим в ноутбук хронологию стоимости акции за текущий торговый год, то есть за период 01.01.2021 по 13.03.2021:

df = pd.read_csv('https://www.dropbox.com/s/j04e6thkqmk02z1/LPL.csv?dl=1')
df.head()

Мы получили такой игрушечный датасет с помощью сервиса Yahoo. Finance, и набор отображаемых признаков обусловлен политикой сервиса:

Отобразим с помощью Линейной диаграммы (Line Plot) цену акции на момент открытия биржи:

plt.plot(df['Open'])
plt.figure(figsize = (25, 10)) # Размер графика в дюймах
plt.show()

За два с небольшим месяца стоимость акции колебалась в пределах 8.5 – 11.5 долларов за штуку:

Именно с такими данными мы и будем создавать модель машинного обучения. Прежде чем приступить к построению модели, стоит выяснить, являются ли такие данные стационарными? В этом поможет тест Дики-Фуллера. Мы выберем целевой признак (Target Feature), значение которого впоследствии и будем предсказывать, – это цена открытия (Open) и сравним коэффициент такого теста с критическими значениями уровней Статистической значимости (Statistical Significance). Чтобы нейтрализовать восходящий тренд, мы логарифмизируем данные с помощью np.log, тем самым приведем более высокие поздние значения к масштабу более ранних:

Y = df['Open'].values # Выделим целевой признак 
Y = log(Y)
result = adfuller(Y)
print('Коэффициент расширенного теста Дики-Фуллера: %f' % result[0])
print('Критические значения: %f' % result[1])
for key, value in result[4].items():
	print('\t%s: %.3f' % (key, value))

Итак, метрика теста равна 0,220931, и само по себе это значение не очень показательно. Оно работает в сравнении с критическими значениями Статистической значимости. Итак, Статистика (Statistics) теста равна 0.220931, и это значение больше всех трех значениями уровней статистической значимости, что означает стационарность.

Коэффициент расширенного теста Дики-Фуллера: -2.160400
Критические значения: 0.220931
	1%: -3.578
	5%: -2.925
	10%: -2.601

Настало время самой задорной части: предскажем стоимость акции. Для этого разделим датасет на Тренировочную (Train Data) и Тестовую части (Test Data) в пропорции 80 на 20. Для наглядности построим график, где эти части будут окрашены синим и красным соответственно:

train_data, test_data = df[0:int(len(df) * 0.8)], df[int(len(df) * 0.8):]
plt.figure(figsize = (25, 10))
plt.title('Стоимость акций LG') # Название графика
plt.xlabel('Даты') # Название оси X
plt.ylabel('Цены') # Название оси Y
plt.plot(df['Open'], 'blue', label = 'Тренировочные данные') # Левая синяя часть линейного графика
plt.plot(test_data['Open'], 'red', label = 'Тестовые данные') # Правая красная часть линейного графика
plt.legend()

Мы получили вот такое разделение:

Определим функцию smape_kun(), которая поможет в дальнейшем охарактеризовать эффективность предсказаний модели – Симметричная средняя абсолютная ошибка в процентах (SMAPE):

def smape_kun(y_true, y_pred):
    return np.mean((np.abs(y_pred - y_true) * 200/ (np.abs(y_pred) + np.abs(y_true))))

Создадим пустые списки predictions и history, которые будем пополнять предсказаниями, и запустим цикл длительностью в тестовую часть датасета:

train_ar = train_data['Open'].values
test_ar = test_data['Open'].values

history = [x for x in train_ar]
predictions = list()
for t in range(len(test_ar)):
    model = ARIMA(history, order = (5, 1, 0)) # Параметризуем модель
    model_fit = model.fit(disp = 0) # Передадим обучающие данные
    output = model_fit.forecast() # Сгенерируем предсказания
    yhat = output[0] # Сделаем первое предсказание отдельной переменной
    predictions.append(yhat) # Добавим в список предсказаний первое значение
    obs = test_ar[t] # Создадим служебную переменную obs (observation)
    history.append(obs) # Пополним служебной переменной список history 

# Определим эффективность модели    
error = mean_squared_error(test_ar, predictions)
print('Тестовая среднеквадратическая ошибка: %.3f' % error)
error2 = smape_kun(test_ar, predictions)
print('Симметричная средняя абсолютная ошибка в процентах: %.3f' % error2)

Данных было немного, однако ошибка небольшая (измеряется в процентах):

Тестовая среднеквадратическая ошибка: 0.030
Симметричная средняя абсолютная ошибка в процентах: 2.269

Изучим сгенерированный список предсказаний, вызвав наполненный список predictions:

predictions

Мир неидеален, потому наш список наполнился предсказаниями – массивами в один элемент, что может сгененировать новичку дополнительную работу в будущем. Но, к счастью, не в нашем случае:

[array([9.93664905]),
 array([10.25150669]),
 array([10.31789186]),
 array([10.13737506]),
 array([10.07325307]),
 array([9.96248479]),
 array([9.79555676]),
 array([9.83781138]),
 array([9.63763933]),
 array([9.89237211])]

Для пущей наглядности визуализируем тестовые (реальные) значения стоимости акции и предсказанные:

plt.figure(figsize = (25, 10))
plt.plot(df['Open'], 'green', color = 'blue', label = 'Тренировочные данные') # Обучающие данные
plt.plot(test_data.index, predictions, color = 'green', marker = 'o', 
         linestyle = 'dashed', label = 'Предказанная цена') # Предсказания модели
plt.plot(test_data.index, test_data['Open'], color = 'red', label = 'Реальная цена') # Реальные тестовые данные
plt.title('Предсказанные цены акции LG') 
plt.xlabel('Даты')
plt.ylabel('Цены')
plt.xticks(np.arange(0, 60, 10), df['Date'][0:60:10]) # Зададим даты как подписи оси X
plt.legend() # Отобразим легенду диаграммы

Сейчас гораздо лучше видно, что модель не очень точна, вероятно, из-за малого объема тренировочных данных, но сейчас это не главное. Мы научились готовить данные с временными рядами к загрузке в модель.

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

Фото: @diegojimenez