В этой истории мы собираемся сделать прогнозирование временных рядов, используя RNN в наборе данных Quandl в Google Collaboratory.

Quandl (https://www.quandl.com) — набор данных, используемый для финансово-экономического анализа.

Чтобы получить доступ к данным Quandl в коде Python, нам нужно установить пакет Python Quandl:

!pip install quandl

После импорта пакета в код нам нужно установить ключ API, который можно получить на сайте Quandl, создав бесплатную учетную запись.

import quandl
quandl.ApiConfig.api_key = 'YOUR_API_KEY'

Мы также будем использовать Keras из пакета TensorFlow. Версия TensorFlow по умолчанию, установленная в Google Colaboratory, — это версия 1.15. Мы можем обновить его до версии 2.0, используя следующий код.

!pip install --upgrade tensorflow
import tensorflow as tf
from tensorflow import keras
Tf.__version__
>  '2.0.0'

В этой истории мы будем использовать Quandl API для получения набора данных о ценах на акции Microsoft и попытаемся предсказать высокое значение.

mydata = quandl.get("WIKI/MSFT")
mydata.head()

data = mydata['High'].values
data.shape
>  (8076,)

Используя три модели — Simple RNN, Deep RNN и LSTM — мы выполняем одни и те же шаги для каждой:

  1. Разделение данных
  2. Определение модели
  3. Компиляция модели (указание функции потерь, алгоритма оптимизации и показателей)
  4. Подгонка модели
  5. Оценка модели
  6. Делать прогнозы

Использование простой модели RNN

Чтобы применить методы прогнозирования временных рядов, данные необходимо разделить на пакеты:

def split_in_batches(data, n_steps):
   return data.reshape(-1)[:(data.size // n_steps * n_steps)].reshape(-1,n_steps,1)

Используя наш метод split_in_batches, мы разделили наши данные на партии по 60 (50 шагов обучения/независимая переменная/X и 10 следующих шагов для прогнозирования/зависимая переменная/Y):

n_steps = 50
data = split_in_batches(data, n_steps + 10)
data.shape
>  (134, 60, 1)

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

X_size = data.shape[0]
X_train_size = round(X_size * 0.8)
X_valid_size = round((X_size - X_train_size)/2)
X_test_size = X_size - X_train_size - X_valid_size
X_train, y_train = data[:X_train_size, :n_steps], data[:X_train_size, -10:]
X_valid, y_valid = data[X_train_size:X_train_size+X_valid_size, :n_steps], data[X_train_size:X_train_size+X_valid_size, -10:]
X_test, y_test = data[X_train_size+X_valid_size:, :n_steps], data[X_train_size+X_valid_size:, -10:]
print('train:', X_train.shape, y_train.shape)
>  train: (107, 50, 1) (107, 10, 1)
print('valid:', X_valid.shape, y_valid.shape)
>  valid: (14, 50, 1) (14, 10, 1)
print('test:', X_test.shape, y_test.shape)
>  test: (13, 50, 1) (13, 10, 1)

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

y_pred = np.repeat(X_valid[:,-1], 10, axis=1)[:,:,np.newaxis]
np.mean(keras.losses.mean_squared_error(y_valid, y_pred))
>  1.1452930265000008

Мы используем последовательный API Keras для построения простой модели RNN с двумя SimpleRNN и плотным слоем из 10 нейронов в качестве выходных данных.

model_simple_rnn = keras.models.Sequential([
  keras.layers.SimpleRNN(20, return_sequences=True, input_shape=[None, 1]),
  keras.layers.SimpleRNN(20),
  keras.layers.Dense(10)
])

На этапе компиляции нам нужно указать функцию потерь и оптимизатор. Здесь мы используем «Среднеквадратичную ошибку» в качестве функции потерь и «Адам» в качестве алгоритма оптимизации.

model_simple_rnn.compile(loss='mse', optimizer='adam')

Метод summary дает текстовое представление модели:

model_simple_rnn.summary()
>  Model: "sequential" _________________________________________________________________ Layer (type)                 Output Shape              Param #    ================================================================= simple_rnn (SimpleRNN)       (None, None, 20)          440        _________________________________________________________________ simple_rnn_1 (SimpleRNN)     (None, 20)                820        _________________________________________________________________ dense (Dense)                (None, 10)                210        ================================================================= Total params: 1,470
Trainable params: 1,470
Non-trainable params: 0 _________________________________________________________________

Затем мы будем обучать нашу модель по данным поезда. Эпоха — это количество раз, когда наша модель получает данные из всего набора данных. Мы используем %%timeit для измерения времени обучения. Это одна из магических команд Python, которая используется для измерения времени выполнения команды.

%%timeit
model_simple_rnn.fit(X_train, y_train, epochs=200, validation_data=(X_valid, y_valid), verbose=0)
>  1 loop, best of 3: 13.3 s per loop

Последним шагом является оценка модели с использованием тестовых данных. Метод evaluate возвращает значение потерь и значение метрик для модели.

model_simple_rnn.evaluate(X_test, y_test)
> 230.00787353515625

Мы можем использовать pyplot, чтобы нарисовать историю обучения нашей модели.

def plot_training_history(history):
  plt.plot(history.history['val_loss'], 'r', label='Validation loss')
  plt.plot(history.history['loss'], 'b', label='Training loss')
  plt.legend()
plot_training_history(model_simple_rnn.history)

Использование модели Deep RNN

Попробуем более сложную модель. В этом разделе вместо того, чтобы пытаться предсказать следующие 10 шагов, основываясь только на последнем шаге (из 50), мы попытаемся предсказать следующие 10 шагов на каждом шаге (из 50). Для этого мы должны определить новый Y.

Y = np.empty((X_size, n_steps, 10))
for step_ahead in range(1, 10 + 1):
  Y[..., step_ahead - 1] = data[..., step_ahead:step_ahead+n_steps, 0]
Y_train = Y[:X_train_size]
Y_valid = Y[X_train_size:X_train_size+X_valid_size]
Y_test = Y[X_train_size+X_valid_size:]

Мы снова используем последовательный API Keras, чтобы построить модель RNN с двумя SimpleRNN и плотным слоем из 10 нейронов в качестве выходных данных с оболочкой TimeDistributed, чтобы применить слой ко всем дополнительным слоям, которые мы добавили к Y.

model_deep_rnn = keras.models.Sequential([
  keras.layers.SimpleRNN(20, return_sequences=True, input_shape=[None, 1]),
  keras.layers.SimpleRNN(20, return_sequences=True),
  keras.layers.TimeDistributed(keras.layers.Dense(10))
])
model_deep_rnn.summary()
> Model: "sequential_1" _________________________________________________________________ Layer (type)                 Output Shape              Param #    ================================================================= simple_rnn_2 (SimpleRNN)     (None, None, 20)          440        _________________________________________________________________ simple_rnn_3 (SimpleRNN)     (None, None, 20)          820        _________________________________________________________________ time_distributed (TimeDistri (None, None, 10)          210        ================================================================= Total params: 1,470 Trainable params: 1,470 Non-trainable params: 0

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

def last_time_step_mse(Y_true, Y_pred):
  return keras.metrics.mean_squared_error(Y_true[:, -1], Y_pred[:, -1])
optimizer = keras.optimizers.Adam(lr=0.01)
model_deep_rnn.compile(loss="mse", optimizer=optimizer, metrics=[last_time_step_mse])

Затем мы будем обучать нашу модель по данным поезда.

%%timeit
model_deep_rnn.fit(X_train, Y_train, epochs=200, validation_data=(X_valid, Y_valid), verbose=0)
> 1 loop, best of 3: 14.8 s per loop

И последний шаг — оценка модели с использованием тестовых данных. Метод evaluate возвращает значение потерь, а также значение пользовательской метрики для модели.

model_deep_rnn.evaluate(X_test, Y_test)
> [7.594574928283691, 4.924448]

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

plot_training_history(model_deep_rnn.history)

Использование LSTM

Давайте попробуем модель LSTM, чтобы сделать прогноз.

model_lstm = tf.keras.models.Sequential([
  tf.keras.layers.LSTM(20, return_sequences=True, input_shape=[None, 1]),
  tf.keras.layers.LSTM(20, return_sequences=True),
  tf.keras.layers.TimeDistributed(tf.keras.layers.Dense(10))
])
model_lstm.compile(loss="mse", optimizer="adam")
model_lstm.summary()
> Model: "sequential_2" _________________________________________________________________ Layer (type)                 Output Shape              Param #    ================================================================= lstm (LSTM)                  (None, None, 20)          1760       _________________________________________________________________ lstm_1 (LSTM)                (None, None, 20)          3280       _________________________________________________________________ time_distributed_1 (TimeDist (None, None, 10)          210        ================================================================= Total params: 5,250 Trainable params: 5,250 Non-trainable params: 0 _________________________________________________________________

Затем мы обучим нашу модель LSTM.

%%timeit
model_lstm.fit(X_train, Y_train, epochs=200, validation_data=(X_valid, Y_valid),verbose=0)
> 1 loop, best of 3: 25.3 s per loop

А затем оцените его, используя тестовые данные.

model_lstm.evaluate(X_test, Y_test)
> 162.5330047607422

И, наконец, нарисуйте историю обучения нашей модели LSTM.

plot_training_history(model_lstm.history)