
Предсказание цен акций с RNN и LSTM: полный гайд
Как построить RNN/LSTM модель для прогнозирования цен акций: архитектура, нормализация данных, скользящее окно и код на Python.
Введение: почему акции и нейросети — идеальная пара?
Финансовые рынки — это один из самых сложных объектов для прогнозирования. Фондовый рынок крайне волатилен и труден для точного предсказания из-за множества неопределённых факторов, влияющих на цены акций. Тем не менее именно здесь рекуррентные нейросети показали один из наиболее впечатляющих результатов среди всех алгоритмов машинного обучения.
Эта статья — туториал по построению рекуррентной нейронной сети (RNN) на основе TensorFlow для предсказания цен на фондовом рынке. Мы разберём каждый этап: от подготовки данных до обучения полноценной LSTM-модели с нормализацией через скользящее окно — всё по мотивам классической работы Лилиан Вэнг (Lilian Weng).
«Предсказание цен акций — задача непростая. Особенно после нормализации: ценовые тренды выглядят очень зашумлёнными.» — Lilian Weng
Почему RNN и LSTM — правильный инструмент
Прежде чем писать код, важно понять, почему классические методы не справляются с ценами акций.
Традиционные модели — ARIMA и экспоненциальное сглаживание — хорошо работают с линейными временными рядами, но испытывают трудности с долгосрочными зависимостями в данных фондового рынка.
Стандартные рекуррентные нейросети (RNN) с трудом справляются с длинными последовательностями из-за проблемы исчезающего градиента.
Здесь и появляются LSTM (Long Short-Term Memory) — специальный вид RNN:
LSTM — это специализированная архитектура RNN, разработанная для решения конкретной задачи: запоминания информации на длительные периоды, расширяя возможности памяти рекуррентных нейросетей.
LSTM преодолевает эти ограничения, обучаясь долгосрочным зависимостям, что делает их мощным инструментом для финансового прогнозирования.
Сравнение подходов к прогнозированию цен
| Метод | Долгосрочные зависимости | Нелинейность | Адаптивность |
|---|---|---|---|
| ARIMA | ❌ Слабо | ❌ Нет | ❌ Нет |
| Скользящая средняя | ❌ Нет | ❌ Нет | ❌ Нет |
| Простая RNN | ⚠️ Ограниченно | ✅ Да | ✅ Да |
| LSTM | ✅ Отлично | ✅ Да | ✅ Да |
| Transformer | ✅ Отлично | ✅ Да | ✅ Да |
Эффективность LSTM распространяется на различные задачи моделирования последовательностей в нескольких прикладных областях, включая видео, NLP, геопространственные данные и анализ временных рядов.
Данные: откуда брать и как подготавливать
Источник данных
В оригинальном туториале Lilian Weng использовались данные S&P 500. Для работы нужно скачать полные данные S&P 500 с Yahoo Finance (^GSPC), выбрав максимальный временной период, и сохранить файл .csv в директорию data/SP500.csv.
Далее запускается скрипт python data_fetcher.py для загрузки цен отдельных акций из S&P 500, каждая сохраняется в data/{{stock_abbreviation}}.csv.
Данные для каждой акции обычно содержат поля:
Набор данных содержит следующие поля: цена открытия, наивысшая цена, наименьшая цена, цена закрытия, скорректированная цена закрытия и объём торгов.
Ключевая проблема: выход значений за пределы обучающей выборки
Одна из главных трудностей при работе с ценами акций — это постоянный рост рынка. Если модель обучена на данных за 5 лет, цены следующего года могут выйти за пределы диапазона тренировочной выборки, и модель начнёт ошибаться катастрофически.
Для решения проблемы выхода значений за диапазон тренировочных данных применяется нормализация цен в каждом скользящем окне. Задача преобразуется из предсказания абсолютных значений в предсказание относительных изменений.
Скользящее окно (Sliding Window)
Метод скользящего окна — основа подготовки данных для RNN. Вместо подачи всего временного ряда сразу, данные нарезаются на перекрывающиеся последовательности фиксированной длины.
W'_t в момент времени t все значения делятся на последнюю известную цену — последнюю цену из предыдущего окна W_{t-1}. Это позволяет модели работать с относительными изменениями, а не абсолютными числами.Пример кода для формирования датасета со скользящим окном:
import numpy as np
def create_sliding_windows(prices, window_size=30, input_size=5):
"""
prices: массив цен
window_size: длина каждого окна (num_steps)
input_size: сколько шагов предсказываем
"""
X, y = [], []
for i in range(len(prices) - window_size - input_size):
window = prices[i : i + window_size]
target = prices[i + window_size : i + window_size + input_size]
# Нормализация: делим на последнюю цену предыдущего окна
last_price = prices[i + window_size - 1]
norm_window = window / last_price
norm_target = target / last_price
X.append(norm_window)
y.append(norm_target)
return np.array(X), np.array(y)
Нормализация через MinMaxScaler
Альтернативный подход — глобальная нормализация данных:
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler(feature_range=(0, 1))
scaled_data = scaler.fit_transform(df['Close'].values.reshape(-1, 1))
Предобработка включает нормализацию данных с помощью MinMaxScaler; обучающая выборка строится с учётом данных за последние 60 дней как параметров обучения для предсказания следующего дня.
Архитектура модели: строим LSTM на практике
graph TD
A[Исторические цены] --> B[Скользящее окно]
B --> C[Нормализация]
C --> D[LSTM слой 1]
D --> E[Dropout 0.2]
E --> F[LSTM слой 2]
F --> G[Dropout 0.2]
G --> H[LSTM слой 3]
H --> I[Dropout 0.2]
I --> J[Dense выходной слой]
J --> K[Предсказанная цена]
K --> L{Денормализация}
L --> M[Итоговый прогноз]
Описание архитектуры
Модель LSTM содержит num_layers стека LSTM-слоёв, каждый из которых содержит lstm_size ячеек LSTM. После каждой LSTM-ячейки применяется маска дропаута с вероятностью сохранения keep_prob.
Полная реализация на Keras/TensorFlow:
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense, Dropout
def build_lstm_model(input_shape, lstm_size=128, num_layers=2, keep_prob=0.8):
model = Sequential()
# Первый LSTM слой
model.add(LSTM(
units=lstm_size,
return_sequences=True,
input_shape=input_shape
))
model.add(Dropout(1 - keep_prob))
# Средние LSTM слои
for _ in range(num_layers - 2):
model.add(LSTM(units=lstm_size, return_sequences=True))
model.add(Dropout(1 - keep_prob))
# Последний LSTM слой
model.add(LSTM(units=lstm_size))
model.add(Dropout(1 - keep_prob))
# Выходной слой
model.add(Dense(units=1))
return model
model = build_lstm_model(
input_shape=(30, 1), # window_size=30, 1 признак
lstm_size=128,
num_layers=2,
keep_prob=0.8
)
model.compile(
optimizer='adam',
loss='mean_squared_error'
)
model.summary()
Конфигурация гиперпараметров
Автор оригинального туториала использовала следующую конфигурацию в эксперименте: num_layers=1, keep_prob=0.8, batch_size=64, init_learning_rate=0.001, learning_rate_decay=0.99, init_epoch=5, max_epoch=100, num_steps=30.
| Гиперпараметр | Значение | Описание |
|---|---|---|
num_steps | 30 | Длина скользящего окна |
lstm_size | 128 | Размер LSTM-слоя |
num_layers | 1–2 | Кол-во стеков LSTM |
keep_prob | 0.8 | Вероятность сохранения (dropout) |
batch_size | 64 | Размер батча |
learning_rate | 0.001 | Начальная скорость обучения |
max_epoch | 100 | Максимум эпох обучения |
Обучение, оценка и типичные ошибки
Тренировочный процесс
history = model.fit(
X_train, y_train,
epochs=100,
batch_size=64,
validation_split=0.1,
callbacks=[
tf.keras.callbacks.LearningRateScheduler(
lambda epoch, lr: lr * 0.99
)
]
)
В выходном слое используется 1 нейрон для предсказания нормализованной цены акции. В качестве функции потерь применяется MSE, а оптимизатор — Adam (стохастический градиентный спуск).
Метрики качества
Для оценки результатов чаще всего используют:
- MSE (Mean Squared Error) — средняя квадратичная ошибка
- RMSE (Root MSE) — корень из MSE, удобен в единицах цены
- MAPE (Mean Absolute Percentage Error) — относительная ошибка в процентах
from sklearn.metrics import mean_squared_error
import numpy as np
# Предсказание
y_pred_scaled = model.predict(X_test)
# Денормализация
y_pred = scaler.inverse_transform(y_pred_scaled)
y_true = scaler.inverse_transform(y_test.reshape(-1, 1))
# Метрики
rmse = np.sqrt(mean_squared_error(y_true, y_pred))
mape = np.mean(np.abs((y_true - y_pred) / y_true)) * 100
print(f"RMSE: {rmse:.4f}")
print(f"MAPE: {mape:.2f}%")
Типичные проблемы и как их решать
Модель опирается на все исторические точки данных, чтобы предсказать лишь следующие 5 дней (input_size). При небольшом input_size модель не должна беспокоиться о долгосрочной кривой роста.
При увеличении input_size предсказание становится значительно сложнее.
Что дальше: расширение модели
Первая часть туториала закладывает фундамент. В продолжении темы прогнозирования цен акций RNN-сети можно расширить, добавив возможность работать с несколькими акциями одновременно.
Для различения паттернов, связанных с разными ценовыми последовательностями, используются векторы эмбеддингов символов акций в качестве части входных данных.
Также существуют более современные подходы для улучшения базовой модели:
Можно улучшить модель, включив дополнительные признаки — объём торгов, макроэкономические индикаторы (инфляция, процентные ставки) и анализ настроений из новостей и соцсетей. Стоит также поэкспериментировать с моделями на основе Transformer (например, BERT, GPT для финансов) для лучшего понимания последовательностей.
Гибридные модели, комбинирующие LSTM с традиционными моделями временных рядов (ARIMA + LSTM), обеспечивают более устойчивый подход.
Полностью рабочий код доступен в репозитории github.com/lilianweng/stock-rnn. Базовый запуск обучения:
python main.py \
--stock_count=100 \
--train \
--input_size=1 \
--lstm_size=128 \
--max_epoch=50
Заключение
В целом предсказание цен акций — непростая задача. Особенно после нормализации ценовые тренды выглядят очень зашумлёнными.
Но именно в этой сложности — ценность задачи как учебного полигона для RNN/LSTM:
- Вы научились формировать обучающую выборку через скользящие окна
- Вы разобрали нормализацию как ключевой приём борьбы с out-of-scale проблемой
- Вы построили многослойную LSTM-модель с дропаутом и decay learning rate
- Вы поняли ключевые гиперпараметры и их влияние на качество
Прогнозирование цен акций остаётся сложной, но увлекательной задачей. Хотя методы глубокого обучения, такие как LSTM, повышают точность прогнозов, ни одна модель не может полностью предсказать движения рынка из-за внешних неопределённостей.
Мотивация этого туториала — продемонстрировать, как строить и обучать RNN-модель в TensorFlow, а не решить задачу предсказания акций как таковую. Код является отправной точкой: добавляйте собственные идеи, связанные с предсказанием акций, чтобы улучшить результаты.