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

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

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

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

Понимание рекуррентных нейронных сетей (RNN)

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

Например, если вы предоставите нейронной сети с прямой связью последовательность букв типа «N-E-U-R-O-N», к тому времени, когда она дойдет до «R», она, скорее всего, забудет, что просто читала «U». Это серьезная проблема, и она не делает эти нейронные сети слишком полезными.

Однако RNN непрерывно собирают только что переданные входные данные и также сохраняют их. Например, в «N-E-U-R-O-N» они смогут запомнить «R», но также и «U», которое оно только что передало. Это очень полезно для модели, которая лучше предсказывает, что будет дальше в последовательности.

Сети с долгосрочной краткосрочной памятью (LSTM)

Сети LSTM имеют уникальные блоки вместе со стандартными блоками, которые имеют ячейки памяти. Это позволяет сети хранить информацию в памяти в течение длительного периода времени. Модель включает в себя несколько вентилей, которые контролируют время, когда информация поступает в память, когда она выводится и когда эта информация игнорируется.

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

Однако с сетью LSTM:

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

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

Импорт необходимых пакетов

Библиотеки Python, необходимые для этой модели, включают:

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

Pyplot из matplotlib: требуется для визуализации прогнозируемых значений после тестирования.

Панды: чаще всего используется для чтения набора данных.

MinMaxScaler от sklearn: используется для масштабирования данных. Он используется на этапе нормализации данных и, по сути, то, что он делает, берет большие числа и преобразует значения в диапазон от 0 до 1 для обучения модели.

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.preprocessing import MinMaxScaler

Набор данных

Это набор данных, который я использовал для обучения модели. Мы сохраняем контент из набора данных в переменную под названием data. Я также использовал data_parser, чтобы преобразовать последовательность строковых столбцов в массив экземпляров datetime.

data = pd.read_csv('GOOG.csv', date_parser = True)
data.tail()

Разделение данных

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

data_training = data[data['Date'] < '2021-01-01'].copy() data_training

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

Также важно отметить, что функция по умолчанию выполняет поиск по оси Y, поэтому важно установить axis = 1,, чтобы функция просматривала столбцы, а не строки.

data_training = data_training.drop(['Date', 'Adj Close'], axis = 1)

Масштабирование

Значения в наших обучающих данных также находятся в разных диапазонах. Чтобы получить все эти значения в диапазоне от 0 до 1, мы будем использовать MinMaxScaler. Минимальное значение будет 0, а максимальное - 1. Масштабирование сокращает время выполнения, а также уменьшает ошибку. Этот шаг называется нормализацией данных.

scaler = MinMaxScaler()
data_training = scaler.fit_transform(data_training)
data_training

Как упоминалось выше, мы собираемся обучать эту модель на данных за 60 дней за раз. Теперь мы разделим данные на блоки по 60 строк. Во-первых, давайте создадим переменные, которые будут содержать обучающие значения x и y.

Я создал цикл for, чтобы данные начинались через 60 дней, и выполняю следующий код до тех пор, пока не будет data_training.shape [0]. Форма обучающих данных - это в основном длина набора данных или наших входных данных.

Затем «i» будет повторяться от 60 до длины ввода (в этом конкретном наборе данных длина равна 3617). Чтобы добавить, он будет начинаться с «i -60» и идти до тех пор, пока i не станет фактически равным 60. И снова мы разбиваем весь набор данных на части по 60. Точно так же мы сделаем то же самое с данными обучения y. Однако в этом случае параметрами будут «i, 0», что указывает на цену акции на 60-й день.

x_train = []
Y_train = []
for i in range(60, data_training.shape[0]):
    x_train.append(data_training[i-60:i])
    y_train.append(data_training[i, 0])

Затем мы просто сохраняем данные x_train и y_train в 2 разных массивах numpy. Еще раз, это полезно, так как поддерживает многомерные массивы.

x_train, y_train = np.array(x_train), np.array(y_train)

Мы также можем проверить, как выглядят наши данные x_train. Мы видим, как он состоит из 3557 блоков данных (строк), которые разделены на 60 списков, и каждый список имеет 5 элементов, которые соответствуют 5 атрибутам / столбцам в наборе данных.

X_train.shape

Выход:

(3557, 60, 5)

Создание LSTM

Я импортировал эти необходимые слои для построения нашей повторяющейся нейронной сети.

from tensorflow.keras import Sequential
from tensorflow.keras.layers import Dense, LSTM, Dropout

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

Мы будем называть эту модель регрессором. Это потому, что мы будем постоянно прогнозировать значение.

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

Последовательная модель не подходит, когда:

  • Модель имеет несколько входов или несколько выходов.
  • Любой из слоев имеет несколько входов или несколько выходов.

Переходя к слоям:

Первый уровень слоя LSTM, который будет иметь 60 единиц. По сути, это единицы памяти, а не каждый нейрон. В нашем случае у нас будет 60.

В качестве функции активации я использовал Rectified Linear Activation Function (ReLU). Это наиболее применимо, поскольку эта функция активации способна обнаруживать нелинейные тенденции, что, безусловно, будет иметь место для наших входных значений. Кроме того, эта функция будет выводить входные данные напрямую, если они положительные, но в противном случае выдает ноль. Это также самый распространенный тип функции активации.

Затем для reurn_sequences устанавливается значение True, чтобы на выходе возвращалась полная последовательность. В более технической терминологии return_sequence также выводит скрытую последовательность. По умолчанию для return_sequences установлено значение False в слоях Keras RNN, поэтому слой RNN будет просто возвращать только вывод последнего скрытого состояния.

После этого input_shape устанавливается на (X_train.shape[1],5), что составляет [60,5]. Это означает, что для одного слоя размеры будут 60 на 5. Каждая строка имеет форму 60 на 5 для длины обучающего набора данных.

Dropout layer используется для предотвращения переобучения модели, что по сути означает, что модель ограничена только определенным набором данных, что делает модель не очень гибкой для других случайных входных данных. Чтобы предотвратить это, слой выпадения (он реализован для каждого слоя, как показано ниже) случайно игнорирует единицы (нейроны) во время обучения. По сути, он случайным образом устанавливает исходящие края скрытых блоков равными 0 на каждом уровне. Однако они не игнорируются полностью. Каждое отброшенное значение указывает вероятность, с которой выпадают выходы уровня.

Последний слой этой нейронной сети - Dense layer. добавляя этот плотный слой, мы просто заявляем, что нейроны этого слоя полностью связаны с нейронами предыдущего слоя.

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

Это может быть немного сложно представить, но ссылка на эту диаграмму может помочь вам полностью понять процесс построения этой нейронной сети:

regressor = Sequential()

regressor.add(LSTM(units = 60, activation = 'relu', return_sequences = True, input_shape = (X_train.shape[1], 5)))
regressor.add(Dropout(0.2))

regressor.add(LSTM(units = 60, activation = 'relu', return_sequences = True))
regressor.add(Dropout(0.2))

regressor.add(LSTM(units = 80, activation = 'relu', return_sequences = True))
regressor.add(Dropout(0.2))

regressor.add(LSTM(units = 120, activation = 'relu'))
regressor.add(Dropout(0.2))

regressor.add(Dense(units = 1))

Запустите этот код, чтобы увидеть сводку вашей модели:

regressor.summary()

Выход:

Составление модели

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

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

Формула для среднеквадратичных ошибок:

n указывает количество точек данных.

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

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

Подходит для модели

Мы будем использовать 50 эпох для обучения этой модели. Думайте об эпохе как о количестве итераций / проходов обучающих данных, которые модель завершила. Наличие 1 эпохи для всего набора данных, который передается вперед и назад, было бы слишком большим и замедлило бы время выполнения. Вместо этого мы разделяем на batch_size.

batch_size указывает количество обучающих выборок, используемых для каждой итерации нейронной сети. С технической точки зрения, это количество выборок на обновление градиента. Это будет означать, сколько раз веса будут обновляться после каждого примера обучения. Для меньших наборов данных размер пакета может быть эквивалентен эпохе, но это определенно не относится к этому набору данных. Я выбрал размер партии в этой модели 32.

regressor.compile(optimizer='adam', loss = 'mean_squared_error')
regressor.fit(X_train, y_train, epochs=50, batch_size=32)

** Компиляция этого кода займет некоторое время, так как каждая эпоха повторяется в нашей нейронной сети **

Выход:

Мы официально закончили обучение нашей модели на обучающих данных !!

Подготовка тестового набора данных

Поскольку мы прогнозируем цену акций за предыдущие 60 дней, мы сохраним цены акций за эти дни в переменнойpast_60_days. Чтобы спрогнозировать курс акций на первый день в нашем наборе данных тестирования, мы возьмем данные за последние 60 дней из данных обучения. Мы делаем это:

past_60_days = data_training.tail(60)

Затем мы добавим тестовые данные и проигнорируем индекс. Мы также опустим столбцы Date и Adj Close, поскольку они снова не влияют на прогнозируемое значение.

df = past_60_days.append(data_test, ignore_index = True)
df = df.drop(['Date', 'Adj Close'], axis = 1)
df.head()

Выход:

Подобно тому, что мы сделали с данными обучения, нам снова нужно масштабировать дату тестирования, чтобы убедиться, что она находится в диапазоне от 0 до 1.

inputs = scaler.transform(df)
inputs

Выход:

Как видите, все значения находятся в диапазоне от 0 до 1.

Еще раз, мы должны подготовить тестовые данные, разделив их на блоки по 60 дней, как данные обучения. Чтобы понять, что здесь происходит, вернитесь к объяснению, приведенному при подготовке данных для обучения.

X_test = []
y_test = []

for i in range(60, inputs.shape[0]):
    X_test.append(inputs[i-60:i])
    y_test.append(inputs[i, 0])

X_test, y_test = np.array(X_test), np.array(y_test)
X_test.shape, y_test.shape

Выход:

Последние 2 строки приведенного выше кода дают нам форму наших данных x_test и y_test.

Прогнозирование цены открытия

Мы будем использовать predict(), чтобы предсказать цену открытия для данных x_test.

y_pred = regressor.predict(X_test)

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

scaler.scale_ дает нам коэффициент масштабирования.

Выход:

array([8.18605127e-04, 8.17521128e-04, 8.32487534e-04, 8.20673293e-04,
       1.21162775e-08])

Как вы можете видеть выше, 8.186 - это первое значение в списке, которое соответствует коэффициенту масштабирования, примененному к первому столбцу (цена открытия). Поскольку наше прогнозируемое значение будет только из этого столбца, мы проигнорируем остальные масштабные коэффициенты.

Это означает, что мы умножаем y_pred и y_test на обратный коэффициент масштабирования, в нашем случае, обратный 8,186, чтобы вернуть все значения к их исходному значению.

scale = 1/18605127e-04
scale

Выход:

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

y_pred = y_pred*scale
y_test = y_test*scale

Визуализация данных

Pyplot отлично подходит для визуализации данных. Будет намного проще сравнить прогнозируемые значения с фактическими значениями с помощью графика, особенно в случае цен на акции.

plt.figure(figsize=(14,5))
plt.plot(y_test, color = 'red', label = 'Real Google Stock Price')
plt.plot(y_pred, color = 'blue', label = 'Predicted Google Stock Price')
plt.title('Google Stock Price Prediction')
plt.xlabel('Time')
plt.ylabel('Google Stock Price')
plt.legend()
plt.show()

Выход:

Как видно из графика, у нас приличная точность по ценам открытия! Разница между обоими значениями не полностью исключена. Вы также можете увидеть, как между 75–125 ценами акции очень похожи!

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

Если точность модели повысится, это означает, что вы сможете раскрыть поведение будущего рынка, помогая вам точно знать когда и в какие акции инвестировать. в, так что вы на пути к тому, чтобы стать миллионером!

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

Свяжитесь со мной:

🔗 Linkedin

💻 Github

🗓 Подпишитесь на мою ежемесячную рассылку новостей