- Вопрос или проблема
- Ответ или решение
- Проблемы с фитингом модели временного ряда: Путеводитель по повышению точности прогнозирования с использованием LSTM и Bidirectional LSTM
- Введение
- Проблемы, возникшие в процессе работы с моделями
- 1. Модель LSTM
- 2. Модель Bidirectional LSTM
- Дополнительные рекомендации
- Заключение
Вопрос или проблема
Я пытаюсь спрогнозировать цены акций Google. Я создал две модели: одну с LSTM и другую с двунаправленным LSTM, но прогнозируемые значения не очень хорошо совпадают с тестовыми значениями. Я пробовал разные параметры, но почти не увидел улучшения.
Сначала мне пришлось установить эти библиотеки:
!pip install yfinance
!pip install yahoofinancials
Затем я импортирую необходимые библиотеки:
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import os
import pandas as pd
import yfinance as yf
from yahoofinancials import YahooFinancials
import datetime
from datetime import date
from datetime import timedelta
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Input, Dense, GRU, LSTM, Embedding, SimpleRNN, Activation, Dropout
from tensorflow.keras.optimizers import RMSprop
from tensorflow import keras
from sklearn.metrics import r2_score
Далее я устанавливаю параметры для данных, которые собираюсь скачать:
start_date = date(2004, 8, 1)
end_date = date.today() - datetime.timedelta(days=1)
Я скачиваю данные:
In [5]: df = yf.download('GOOG', start = start_date, end = end_date, interval="1d")
Out[5]: [*********************100%***********************] 1 из 1 завершено
Я визуализирую df:
In [6]: df.head()
Out[6]: Open High Low Close Adj Close Volume
Дата
2004-08-19 49.813290 51.835709 47.800831 49.982655 49.982655 44871361
2004-08-20 50.316402 54.336334 50.062355 53.952770 53.952770 22942874
2004-08-23 55.168217 56.528118 54.321388 54.495735 54.495735 18342897
2004-08-24 55.412300 55.591629 51.591621 52.239197 52.239197 15319808
2004-08-25 52.284027 53.798351 51.746044 52.802086 52.802086 9232276
Затем я начинаю преобразовывать данные для их использования:
In [7]: uni_data = np.array(df['Adj Close'])[1:].reshape(1,-1)
In [7]: uni_data
Out[7]: array([[ 53.95277023, 54.49573517, 52.23919678, ..., 2143.87988281,
2207.81005859, 2132.7199707 ]])
In [7]: data_transpose = uni_data.T
Я продолжаю добавлять регуляризацию к данным:
In [8]: tset = (uni_data.T-np.mean(uni_data.T))/np.std(uni_data.T)
In [9]: data_mean = np.mean(uni_data)
In [9]: data_std = np.std(uni_data)
In [10]: tset = tset.T
Я генерирую пакетный процесс:
In [11]: n_steps = 15
In [11]: predict_ahead = 1
In [12]: def generate_batches(uni_data , n_steps, predict_ahead):
_ , lenght = uni_data.shape
batchs = lenght - horizonte - predict_ahead + 1
X = np.zeros((batchs, n_steps + predict_ahead ))
print('Начальная форма данных : ' , uni_data.shape)
print('Конечная форма данных : ' , X.shape)
for el in range(batchs):
data_slice = uni_data[0, el : el + n_steps + predict_ahead]
X[el , :] = data_slice
return X , batchs
In [13]: X , batchs = generate_batches(tset , n_steps, predict_ahead)
Out[13]: Начальная форма данных : (1, 4488)
Out[13]: Конечная форма данных : (4473, 16)
Разделение на обучающую/тестовую выборку:
In [14]: tf.random.set_seed(42)
In [14]: porcentaje_train = 0.8
In [14]: train_range = int(round(len(df)) * porcentaje_train)
In [14]: train = range(train_range)
In [14]: valid = set(range(batchs)) - set(train)
In [14]: valid = np.array(list(valid))
In [15]: X_train = X[train , :- predict_ahead ]
In [15]: X_valid = X[valid , :- predict_ahead ]
In [15]: Y_train = X[train , - predict_ahead :]
In [15]: Y_valid = X[valid , - predict_ahead :]
In [16]: print('Форма X Train : ' , X_train.shape)
In [16]: print('Форма Y Train : ' , Y_train.shape)
In [16]: print('Форма X Valid : ' , X_valid.shape)
In [16]: print('Форма Y Valid : ' , Y_valid.shape)
Out [16]: Форма Train : (3591, 15)
Out [16]: Форма Y Train : (3591, 1)
Out [16]: Форма X Valid : (882, 15)
Out [16]: Форма Y Valid : (882, 1)
Затем я создаю модель LSTM
In [17]: epochs = 25
In [18]: def lstm_fit(X_train, Y_train , X_valid , Y_valid, predict_ahead, num_hidden_layers , activ, num_units, loss_type):
_ , _ , num_vars = X_train.shape
model1 = lstm(predict_ahead, num_hidden_layers , activ, num_units, loss_type, num_vars)
hist = model1.fit(x = X_train , y = Y_train, epochs = 20 ,validation_data=(X_valid , Y_valid) , verbose=0)
return model1
In [19]: tf.random.set_seed(42)
def lstm(predict_ahead, num_hidden_layers , activ, num_units, loss_type, num_vars):
model1 = keras.models.Sequential()
model1.add(keras.layers.InputLayer(input_shape = (n_steps,num_vars)))
for el in range(num_hidden_layers):
model1.add(keras.layers.LSTM(activation = activ, units = num_units , return_sequences = True))
model1.add(keras.layers.LSTM(units = num_units))
model1.add(keras.layers.Dense(predict_ahead))
model1.compile(optimizer="adam", loss=loss_type, metrics=['mae' , 'mse', 'mape'])
return model1
In [20]: num_hidden_layers = 3
activ = keras.activations.relu
loss_type = keras.losses.mean_squared_error
num_units = 64
dropout_rate = 0.3
Затем я создаю модель Bidirectional LSTM:
Сначала я создал функцию обратного вызова, так как модель, похоже, переобучалась.
In [21]: class myCallback(tf.keras.callbacks.Callback):
def on_epoch_end(self, epoch, logs={}):
if(logs.get('loss')) <0.4:
print("\nДостигнуто значение потерь ниже 0.4, поэтому отменяем обучение!")
self.model.stop_training = True
In [21]: callbacks = myCallback()
In [22]: def bidir_fit(X_train, Y_train , X_valid , Y_valid, predict_ahead, num_hidden_layers , activ, num_units, loss_type, callbacks):
_ , _ , num_vars = X_train.shape
model2 = bidir(predict_ahead, num_hidden_layers , activ, num_units, loss_type, num_vars)
hist = model2.fit(x = X_train , y = Y_train, epochs = epochs ,validation_data=(X_valid , Y_valid) , verbose=0 , callbacks=[callbacks])
return model2
In [23]: tf.random.set_seed(42)
def bidir(predict_ahead, num_hidden_layers , activ, num_units_bidir, loss_type, num_vars):
model2 = tf.keras.models.Sequential()
model2.add(tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(32)
, input_shape=X_train.shape[-2:]
))
model2.add(tf.keras.layers.Dense(32, activation='relu'))
model2.add(Dropout(0.2))
model2.add(tf.keras.layers.Dense(6, activation='relu'))
model2.add(tf.keras.layers.Dense(1, activation='relu'))
model2.compile(optimizer = tf.keras.optimizers.Adam()
, loss="mse",metrics=['mae' , 'mse', 'mape'])
return model2
Затем я начал обучать модели.
In [24]: X_train = np.expand_dims(X_train ,2)
In [24]: X_valid = np.expand_dims(X_valid ,2)
In [24]: Y_train = np.expand_dims(Y_train ,2)
In [24]: Y_valid = np.expand_dims(Y_valid ,2)
In [25]: X_train.shape
Out [25]: (3591, 15, 1)
In [26]: lstm = lstm_fit(X_train, Y_train , X_valid , Y_valid , predict_ahead, num_hidden_layers , activ, num_units, loss_type)
In [26]: bidir = bidir_fit(X_train, Y_train , X_valid , Y_valid , predict_ahead, num_hidden_layers , activ, num_units, loss_type, callbacks)
Out [26]: Достигнуто значение потерь ниже 0.4, поэтому отменяем обучение!
Затем я начал оценивать и строить графики результатов.
In [27]: print(lstm.evaluate(X_train, Y_train))
In [27]: print(bidir.evaluate(X_train, Y_train))
Out [27]: 13/113 [==============================] - 1s 8ms/step - loss: 0.0011 - mae: 0.0215 - mse: 0.0011 - mape: 23.0664
[0.0011325260857120156, 0.02146766521036625, 0.0011325260857120156, 23.0664119720459]
Out [27]: 113/113 [==============================] - 0s 2ms/step - loss: 0.3158 - mae: 0.4743 - mse: 0.3158 - mape: 85.1597
[0.31578168272972107, 0.47427594661712646, 0.31578168272972107, 85.15969848632812]
LSTM вернул MAPE 23.0664, тогда как Bidirectional LSTM вернул MAPE 85.1597, что довольно высоко. Это проблема, с которой я столкнулся, потому что, независимо от того, сколько изменений я внес в эту модель, ее MAPE всегда была намного выше, чем я хотел.
In [28]: plt.figure(figsize=(10,10))
In [28]: plt.plot(data_mean + data_std * Y_valid.reshape(-1,1), label="Реальные значения")
In [28]: plt.plot(data_mean + data_std * lstm(X_valid), label="LSTM")
In [28]: plt.plot(data_mean + data_std * bidir(X_valid), label="Двунаправленный")
In [28]: plt.title('Реальные и Прогнозируемые Значения')
In [28]: plt.grid()
In [28]: plt.legend()
In [28]: plt.show()
Как вы можете видеть, модели не очень хорошо подходят, по крайней мере, не в первой половине, и я уже внес несколько изменений в обе модели, но первая половина никогда не подходила хорошо.
In [29]: historylstm = lstm.fit(X_train,
Y_train,
validation_data = (X_valid, Y_valid),
verbose = 0)
In [29]: historybidir = bidir.fit(X_train,
Y_train,
validation_data = (X_valid, Y_valid),
verbose = 0)
In [30]: lstm_error = pd.DataFrame.from_dict(historylstm.history).iloc[0:1, 7:8].to_string(index=False, header=False)
In [30]: bidir_error = pd.DataFrame.from_dict(historybidir.history).iloc[0:1, 7:8].to_string(index=False, header=False)
In [31]: print(f"Модель LSTM - Вал MAPE:{lstm_error}")
In [31]: print(f"Модель Двунаправленная - Вал MAPE:{bidir_error }")
Out [31]: Модель LSTM - Вал MAPE:23.216141
Out [31]: Модель Двунаправленная - Вал MAPE:14.745646
И с этой последней частью я оказался в текущей ситуации. Для модели LSTM обучающий MAPE составил 23.0664, а MAPE для проверки составил 23.21641, что кажется довольно хорошим соответствием, но для модели Двунаправленной обучающий MAPE составил 85.1597, тогда как проверка составила 14.745646, что не является хорошим соответствием, и это не значит, что она переобучена или недообучена, это просто странно. Вы могли бы подумать, что низкие результаты проверки были связаны с функцией обратного вызова и дроп-аутом, но MAPE проверки была даже ниже, прежде чем я применил их!
Короче говоря, у меня есть две основные проблемы:
1. Модель LSTM кажется идеальным соответствием, если смотреть на ее MAPE, но когда она отображается по сравнению с реальными значениями, соответствие не кажется особенно хорошим. На самом деле, судя по графику, модель Двунаправленная выглядит как лучшее соответствие, и все же не так хорошо, как должно быть.
2. У модели Двунаправленной есть странная проблема, когда ее обучающий MAPE значительно выше, чем проверка, и, тем не менее, когда она отображается, кажется, что она не так плохо соответствует.
По сути, я в замешательстве и буду очень признателен за любую помощь. Спасибо заранее!
LSTM имеет пределы, и использование необработанных данных в качестве входных данных может не сработать в некоторых случаях, потому что он не может легко усваивать значения из диапазонов, которые слишком далеко друг от друга (в вашем случае 3000 далеко от 1000). Поэтому вы можете сделать 2 вещи:
- Нормализовать значения (например, minmax), чтобы LSTM обучался только на диапазоне между 0 и 1.
- Преобразовать ваши данные в относительные значения (т.е. +X пунктов, если значение увеличивается, 0, если стабильно, -X в противном случае).
Вы также должны учитывать, что LSTM обучается на значениях от 250 до 500 и что он довольно чувствителен к шуму. В фондовых рынках много шума, поэтому вы можете получить даже более точные прогнозы, если сгладите свои данные.
Ответ или решение
Проблемы с фитингом модели временного ряда: Путеводитель по повышению точности прогнозирования с использованием LSTM и Bidirectional LSTM
Введение
Прогнозирование цен акций, таких как акции Google, представляет собой сложную задачу, особенно когда используются модели глубокого обучения, такие как LSTM и Bidirectional LSTM. Ваша ситуация, когда модель не достигает удовлетворительных результатов, несмотря на применение различных параметров, является распространенной проблемой. Этот текст поможет вам понять возможные причины низкой точности модели и предложит решения для улучшения результатов.
Проблемы, возникшие в процессе работы с моделями
1. Модель LSTM
Логика того, что модель LSTM имеет хорошую метрику MAPE (Mean Absolute Percentage Error) на тренировочных данных, но плохое согласование с реальными значениями, может быть связана с несколькими важными моментами:
-
Нормализация данных: Использование нормализованных данных (например, Min-Max Scaling) имеет важное значение. Если входные данные имеют большой разброс значений, то LSTM может иметь трудности в обучении. Преобразование данных в диапазон [0, 1] позволит модели учиться более последовательно и эффективно.
-
Подбор параметров: Текущая конфигурация с 3 скрытыми слоями LSTM и 64 юнитами может быть перегруженной для вашего набора данных. Попробуйте уменьшить количество слоев или юнитов, чтобы избежать переобучения.
2. Модель Bidirectional LSTM
Учитывая, что в вашей модели Bidirectional LSTM наблюдается высокая MAPE на тренировочных данных и низкая на валидационных, имеет смысл рассмотреть несколько моментов:
-
Выбор веса и регуляризация: Высокая ошибка на тренировочных данных указывает на возможное переобучение. Использование регуляризации (например, Dropout) может помочь улучшить обобщающую способность модели. Убедитесь, что вы правильно настроили параметры регуляризации.
-
Адаптация структуры: Вы можете рассмотреть возможность добавления других слоев или использование различных функций активации. Например, применение функции активации
tanh
может привести к более стабильным результатам, так как она значительно используется в LSTM.
Дополнительные рекомендации
-
Сглаживание данных: Учитывая, что финансовые данные подвержены значительным колебаниям, применение методов сглаживания (например, скользящее среднее) может помочь очистить данные и устранить шум, что позволит модели лучше уловить тренды.
-
Трансформация данных: Попробуйте создать новые признаки, основанные на разностях между текущими и предыдущими значениями. Например, изменения: «больше», «меньше» или «такое же» по сравнению с предыдущим значением, могут помочь модели сосредоточиться на более важных закономерностях.
-
Тестирование различных конфигураций: Проектирование нескольких моделей с различным количеством слоев, юнитов и функций активации (например, ReLU, tanh) может выявить лучшую конфигурацию для вашей задачи.
-
Увеличение объема данных для обучения: Если возможно, рассмотрите добавление дополнительных временных рядов, которые могут улучшить обобщение модели на новых данных. Использование других переменных (например, рынков, акций конкурентов или экономических индикаторов) может значительно улучшить точность прогнозирования.
Заключение
Проблемы с качеством фитинга моделей временных рядов при прогнозировании цен акций часто требуют многогранного подхода. Нормализация данных, регуляризация, использование методов сглаживания и добавление новых признаков могут существенно повлиять на качество прогнозов. Внимательное создание и тестирование различных моделей позволят вам найти наилучшее решение для вашей задачи.