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

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

Плюсы

  • Временные зависимости. LSTM специально разработаны для обработки последовательностей, что делает их хорошо подходящими для данных временных рядов.
  • Сложные закономерности. Финансовые данные могут иметь нелинейные закономерности, которые являются сложными для традиционных моделей. LSTM могут фиксировать эти шаблоны.
  • Гибкость.LSTM могут быть разработаны с использованием различных архитектур, например, многоуровневых, двунаправленных или даже в сочетании с другими типами нейронных сетей (например, CNN-LSTM).

Несмотря на множество преимуществ, LSTM также имеет некоторые предостережения, которые вам также следует учитывать:

Минусы

  • Объем данных.LSTM выигрывают от больших объемов данных. Если ваш набор данных ограничен, возможно, вы не сможете полностью использовать возможности LSTM.
  • Затраты на вычисления. Обучение LSTM, особенно глубоких архитектур, может потребовать больших вычислительных ресурсов. Полезно иметь доступ к мощному оборудованию.
  • Переоснащение. LSTM могут переподстраиваться под обучающие данные, особенно если данные зашумлены (как это часто бывает с финансовыми данными). Чтобы противодействовать этому, важно использовать такие методы, как отсев, ранняя остановка или регуляризация.

Сбор и подготовка данных

Давайте перейдем к самой интересной части: мы будем использовать LSTM для прогнозирования данных EUR/USD. Вот некоторые ключевые сведения о данных, о которых вам следует знать:

      Date   Price    Open    High     Low     Vol.   Change %
0  12/30/2022  1.0702  1.0663  1.0714  1.0639   NaN    0.38%
1  12/29/2022  1.0661  1.0609  1.0691  1.0609   NaN    0.50%
2  12/28/2022  1.0608  1.0642  1.0675  1.0606   NaN   -0.28%
3  12/27/2022  1.0638  1.0638  1.0670  1.0611   NaN    0.03%
4  12/26/2022  1.0635  1.0611  1.0638  1.0604   NaN    0.20%

Набор данных содержит исторические данные по EUR/USD со следующими столбцами:

  1. Дата. Дата ввода данных.
  2. Цена. Цена закрытия пары EUR/USD на эту дату.
  3. Открытие. Цена открытия пары EUR/USD на эту дату.
  4. Максимум. Самая высокая цена пары EUR/USD, достигнутая на тот день.
  5. Минимум. Самая низкая цена пары EUR/USD, достигнутая на этот день.
  6. Объем:Объем торгов за день (кажется, значения отсутствуют).
  7. Изменение в %: процентное изменение цены по сравнению с предыдущим днем.

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

Код

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

Сначала мы импортируем все необходимые библиотеки для нашей модели. Наш код будет написан на Python, и хотя я буду использовать Jupyter Notebook в качестве интегрированной среды разработки (IDE), вы можете использовать предпочитаемую вами IDE.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
from keras.models import Sequential
from keras.layers import LSTM, Dense, Dropout
from keras.callbacks import EarlyStopping

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

Давайте импортируем данные CSV для пары EUR/USD, которые вы скачали с сайта Investment.com.

data = pd.read_csv("EUR_USD Historical Data.csv")

Поскольку исторические данные на сайтеinvesting.com по умолчанию располагаются в порядке от самых последних к самым ранним, нам необходимо извлечь столбец «Цена», а затем перевернуть его, чтобы расположить в хронологическом порядке.

# Reverse the entire DataFrame to ensure it's in chronological order
data = data.iloc[::-1].reset_index(drop=True)

# Extract the 'Price' column without reversing
prices = data['Price'].values
prices = prices.reshape(-1, 1)

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

# Normalize the data
scaler = MinMaxScaler(feature_range=(0, 1))
normalized_prices = scaler.fit_transform(prices)

Далее давайте создадим последовательности заданной длины для подачи в LSTM, в случае, если мы будем использовать sequence_length=60.

# Create sequences of a given length (60 days) to feed into the LSTM
sequence_length = 60
X, y = [], []
for i in range(sequence_length, len(normalized_prices)):
    X.append(normalized_prices[i-sequence_length:i, 0])
    y.append(normalized_prices[i, 0])

Обучение данных и создание моделей

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

X, y = np.array(X), np.array(y)
X = np.reshape(X, (X.shape[0], X.shape[1], 1))
X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, shuffle=False)

Теперь давайте определим нашу модель LSTM. Эту часть может быть немного сложно понять, поэтому я постараюсь подробно ее объяснить:

model = Sequential()

#This is the first LSTM layer with 50 units (or cells).
model.add(LSTM(units=50, return_sequences=True, input_shape=(X_train.shape[1], 1)))
#First Dropout Layer with a rate of 20% to prevent overfitting
model.add(Dropout(0.2))

#This is the second LSTM layer, also with 50 units.
model.add(LSTM(units=50, return_sequences=True))
#Second Dropout Layer with a rate of 20% to prevent overfitting
model.add(Dropout(0.2))

#The third LSTM layer, also with 50 units.
model.add(LSTM(units=50))
#Third Dropout Layer with a rate of 20% to prevent overfitting
model.add(Dropout(0.2))

#This is a fully connected layer that outputs aggregates of features learned by the LSTM layers and produces a single output value
model.add(Dense(units=1))
#The model is compiled using the Adam optimization algorithm that combines the advantages of two other extensions of stochastic gradient descent: AdaGrad and RMSProp.
model.compile(optimizer='adam', loss='mean_squared_error')

Итак, используемая нами модель определяется с помощью класса Sequential из Keras, который позволяет нам строить модель слой за слоем в линейном стеке. В модели используются три слоя LSTM, а также уровни исключения для уменьшения переобучения, которое может стать проблемой в глубоких сетях и ограниченном объеме данных.

Для первого и второго уровня LSTM:

  • Аргумент return_sequences=True означает, что эти уровни LSTM вернут полную последовательность на следующий уровень. Это необходимо при объединении слоев LSTM, чтобы следующий уровень LSTM получал последовательности в качестве входных данных.
  • input_shape определяет форму входных данных. В этом случае требуются последовательности длиной X_train.shape[1], и каждая последовательность имеет 1 признак.

Для третьего слоя LSTM для return_sequences установлено значение False, поэтому этот слой будет возвращать только выходные данные последнего временного шага. Мы установили это значение на False , потому что после обработки всей входной последовательности мы собираемся использовать модель для создания одной прогноз (Цена). Следовательно, на плотный слой передается только конечный результат последовательности.

Для всех выпадающих слоев:

  • Dropout — это метод регуляризации, при котором случайно выбранные нейроны игнорируются во время обучения, что помогает предотвратить переобучение. Уровень 0.2 означает, что примерно 20 % входных единиц будут исключены на каждом этапе обучения.

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

early_stop = EarlyStopping(monitor='val_loss', patience=10, restore_best_weights=True)
history = model.fit(X_train, y_train, epochs=100, batch_size=32, validation_data=(X_val, y_val), callbacks=[early_stop], shuffle=False)

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

plt.figure(figsize=(10, 6))
plt.plot(history.history['loss'], label='Training Loss')
plt.plot(history.history['val_loss'], label='Validation Loss')
plt.title('Training and Validation Loss')
plt.xlabel('Epoch')
plt.ylabel('Mean Squared Error')
plt.legend()
plt.show()

Результатом будет следующий график:

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

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

Давайте посмотрим, о чём нам расскажет этот сюжет.

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

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

Прогноз производительности: прогнозируемый и фактический

Давайте воспользуемся matplotlib, чтобы увидеть, как прогнозируемая цена работает по сравнению с фактической ценой:

# Use the trained model to generate predictions on the validation dataset.
# `X_val` contains the input features for the validation set.
# The resulting predictions are stored in `y_val_pred` for further evaluation against actual values.
y_val_pred = model.predict(X_val)

# Assuming val_dates is the list/array of dates corresponding to your validation set
val_dates = data['Date'][-len(y_val):].values
val_dates = pd.to_datetime(val_dates)  # Convert to datetime format

plt.figure(figsize=(14,5))
plt.plot(val_dates, y_val, color='blue', label='Actual EUR/USD Price')
plt.plot(val_dates, y_val_pred, color='red', linestyle='dashed', label='Predicted EUR/USD Price')
plt.title('EUR/USD Price Prediction')
plt.xlabel('Date')
plt.ylabel('EUR/USD Price')
plt.legend()
plt.grid(True)
plt.show()

Глядя на этот сюжет, мы можем понять:

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

Мы также можем создать гистограмму ошибок прогнозирования, чтобы увидеть распределение ошибок прогнозирования (остатков) для модели LSTM:

# Calculate prediction errors
errors = y_val - y_val_pred.flatten()


# Plot histogram of errors
plt.figure(figsize=(10, 6))
plt.hist(errors, bins=50, color='blue', alpha=0.7)
plt.title('Histogram of Prediction Errors')
plt.xlabel('Error Value')
plt.ylabel('Frequency')
plt.show()

Код сгенерирует следующую диаграмму:

Глядя на гистограмму выше, мы можем понять, что:

  1. Центрировано вокруг нуля. Большинство ошибок сосредоточено вокруг нуля, что указывает на то, что прогнозы модели в целом близки к фактическим значениям.
  2. Колоколообразная кривая. Распределение ошибок чем-то напоминает колоколообразную кривую (хотя и не совсем нормальное). Это говорит о том, что крупные ошибки (как положительные, так и отрицательные) встречаются реже.
  3. Асимметрия.Гистограмма имеет небольшой наклон вправо, что указывает на то, что в некоторых случаях модель занижает прогноз (предсказывает значение ниже фактического значения) с большим отрывом.

Заключение

В нашем прогнозе обменного курса EUR/USD с использованием LSTM результаты были многообещающими. Прогнозы модели тесно согласуются с фактическими наблюдаемыми темпами. Это подтверждается среднеквадратической ошибкой (RMSE) для прогнозов нашей модели LSTM по данным EUR/USD, равной 0,02860742615917037. Это означает, что в среднем прогнозы модели отклоняются от фактических значений примерно на 2,860%.

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

Не стесняйтесь поделиться своими мыслями об использовании LSTM для прогнозирования данных Форекс в разделе комментариев ниже!