Фондовый рынок — это мост между экономикой страны и гражданами конкретной страны. Состояние фондового рынка связано со здоровьем экономики страны, но в то же время позволяет простому человеку принять участие в этом рынке. Фондовый рынок содержит акции больших и малых компаний, которые находятся в определенной стране, и граждане могут вести бизнес, покупая акции по низкой цене и продавая их с прибылью, когда цена акций растет. Однако вот в чем загвоздка: огромные убытки возникают, когда цены на акции резко падают. Поэтому неудивительно, что люди, инвестирующие в фондовый рынок, проводят десятки расчетов, прежде чем потратить хоть копейку. Однако, к счастью, появление ИИ привело к решению этой проблемы, поскольку ИИ работает исключительно хорошо с меньшим участием человека. Мало того, методы искусственного интеллекта, такие как рекуррентные нейронные сети (RNN), могут делать прогнозы, используя данные за десятилетия. Чтобы проиллюстрировать это, в этой статье RNN используются для прогнозирования акций Schlumberger (нефтегазовой сервисной компании) с 2010 по 2019 год.

Отказ от ответственности: это статья об искусственном интеллекте от среднего до продвинутого уровня. Обязательно знание нейронных сетей, RNN и языка Python.

Что такое RNN?

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

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

Реализация

Первым шагом является сбор данных. Я собрал данные фондового рынка за 10 лет с официального сайта Nasdaq. Данные представлены в формате csv, а код для визуализации показан ниже:

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
import csv
time_step = []
temps = []
with open('HistoricalQuotes(1).csv') as csvfile:
  reader = csv.reader(csvfile, delimiter=',')
  next(reader)
  step=0
  for row in reader:
    val=row[1]
    temps.append(float(val[2:]))
    time_step.append(step)
    step = step + 1
series = np.array(temps)
time = np.array(time_step)
plt.figure(figsize=(10, 6))
plot_series(time, series)

HistoryQuotes(1).csv содержит данные фондового рынка. Читатель разделяет каждую строку везде, где есть запятая. Второе значение (то есть первый индекс) — это цена закрытия акции. Первый символ, который является знаком доллара, опускается при использовании val[2:]. Функция plot_series показана ниже:

def plot_series(time, series, format="-", start=0, end=None):
    plt.plot(time[start:end], series[start:end], format)
    plt.xlabel("Time")
    plt.ylabel("Value")
    plt.grid(True)
    plt.show()

Данные показаны ниже:

Данные разделены на набор для обучения и проверки. Данные разделены на отметке 1800-го временного шага.

split_time = 1800
time_train = time[:split_time]
x_train = series[:split_time]
time_valid = time[split_time:]
x_valid = series[split_time:]

Входные данные для RNN представлены в виде оконных рядов. Это необходимо для того, чтобы сеть видела часть данных, а не только одну точку данных.

def windowed_dataset(series, window_size, batch_size, shuffle_buffer):
    series = tf.expand_dims(series, axis=-1)
    ds = tf.data.Dataset.from_tensor_slices(series)
    ds = ds.window(window_size + 1, shift=1, drop_remainder=True)
    ds = ds.flat_map(lambda w: w.batch(window_size + 1))
    ds = ds.shuffle(shuffle_buffer)
    ds = ds.map(lambda w: (w[:-1], w[1:]))
    return ds.batch(batch_size).prefetch(1)
def model_forecast(model, series, window_size):
    ds = tf.data.Dataset.from_tensor_slices(series)
    ds = ds.window(window_size, shift=1, drop_remainder=True)
    ds = ds.flat_map(lambda w: w.batch(window_size))
    ds = ds.batch(32).prefetch(1)
    forecast = model.predict(ds)
    return forecast
tf.keras.backend.clear_session()
np.random.seed(51)
window_size = 64
batch_size = 256
shuffle_buffer_size=1000
train_set = windowed_dataset(x_train, window_size, batch_size, shuffle_buffer_size)

До сих пор мы подготовили данные. Следующим шагом является подготовка модели. Используемая нами модель имеет следующую последовательность:

Conv2d -> LSTM -> LSTM -> Плотный -> Плотный -> Плотный -> Лямбда

model = tf.keras.models.Sequential([
  tf.keras.layers.Conv1D(filters=60, kernel_size=5,
                      strides=1, padding="causal",
                      activation="relu",
                      input_shape=[None, 1]),
  tf.keras.layers.LSTM(60, return_sequences=True),
  tf.keras.layers.LSTM(60, return_sequences=True),
  tf.keras.layers.Dense(30, activation="relu"),
  tf.keras.layers.Dense(10, activation="relu"),
  tf.keras.layers.Dense(1),
  tf.keras.layers.Lambda(lambda x: x * 400)
])

Мы будем использовать стохастический градиентный спуск (SGD) с импульсом в качестве нашего оптимизатора и Хубера в качестве нашей функции потерь, обученной на 100 эпохах. Метрикой является средняя абсолютная ошибка (MAE).

optimizer = tf.keras.optimizers.SGD(lr=1e-6, momentum=0.9)
model.compile(loss=tf.keras.losses.Huber(),
              optimizer=optimizer,
              metrics=["mae"])
history = model.fit(train_set,epochs=100)

Модель обучается в течение 100 эпох, а затем на проверочном наборе делаются прогнозы. Истинные значения (синяя линия) и прогнозируемые значения (оранжевая линия) показаны на одном графике для сравнения.

MAE составляет всего 1,49, что является отличным результатом, учитывая спорадический и непостоянный характер нефтегазовой отрасли.

Этот код был вдохновлен специализацией Tensorflow от Deeplearning.ai