Я создал нейронную сеть LSTM (RNN) с контролируемым обучением для прогнозирования запасов данных. Проблема в том, почему он предсказывает неверные данные о тренировках? (примечание: воспроизводимый пример ниже)
Я создал простую модель для прогнозирования цены акций на следующие 5 дней:
model = Sequential()
model.add(LSTM(32, activation='sigmoid', input_shape=(x_train.shape[1], x_train.shape[2])))
model.add(Dense(y_train.shape[1]))
model.compile(optimizer='adam', loss='mse')
es = EarlyStopping(monitor='val_loss', patience=3, restore_best_weights=True)
model.fit(x_train, y_train, batch_size=64, epochs=25, validation_data=(x_test, y_test), callbacks=[es])
Правильные результаты представлены в y_test
(5 значений), поэтому моделируйте поезда, оглядываясь на 90 предыдущих дней, а затем восстанавливая веса из best ( val_loss=0.0030
), получая с помощью patience=3
:
Train on 396 samples, validate on 1 samples
Epoch 1/25
396/396 [==============================] - 1s 2ms/step - loss: 0.1322 - val_loss: 0.0299
Epoch 2/25
396/396 [==============================] - 0s 402us/step - loss: 0.0478 - val_loss: 0.0129
Epoch 3/25
396/396 [==============================] - 0s 397us/step - loss: 0.0385 - val_loss: 0.0178
Epoch 4/25
396/396 [==============================] - 0s 399us/step - loss: 0.0398 - val_loss: 0.0078
Epoch 5/25
396/396 [==============================] - 0s 391us/step - loss: 0.0343 - val_loss: 0.0030
Epoch 6/25
396/396 [==============================] - 0s 391us/step - loss: 0.0318 - val_loss: 0.0047
Epoch 7/25
396/396 [==============================] - 0s 389us/step - loss: 0.0308 - val_loss: 0.0043
Epoch 8/25
396/396 [==============================] - 0s 393us/step - loss: 0.0292 - val_loss: 0.0056
Результат прогноза довольно удивительный, не правда ли?
Это потому, что алгоритм восстановил лучшие веса с эпохи # 5. Ладно, давайте теперь сохраним эту модель в .h5
файл, вернемся на -10 дней и прогнозируем последние 5 дней (в первом примере мы сделали модель и подтвердим ее 17-23 апреля, включая выходные дни, теперь давайте проверим 2-8 апреля). Результат:
Это показывает абсолютно неправильное направление. Как мы видим, это потому, что модель была обучена и заняла 5-е время лучше всего для проверки, установленной 17-23 апреля, а не 2-8. Если я стараюсь больше тренироваться, играть с какой эпохой выбрать, что бы я ни делал, в прошлом всегда будет много временных интервалов, которые имеют неправильный прогноз.
Почему модель показывает неверные результаты на своих собственных обученных данных? Я тренировал данные, он должен помнить, как прогнозировать данные на этом фрагменте, но предсказывает неправильно. Что я тоже пробовал:
- Используйте большие наборы данных с 50k + строками, 20-летними ценами на акции, добавляя больше или меньше функций
- Создавайте разные типы моделей, такие как добавление дополнительных скрытых слоев, разные размеры batch_s, активация разных слоев, выпадение, пакетная нормализация
- Создайте собственный обратный вызов EarlyStopping, получите среднее значение val_loss из множества наборов проверочных данных и выберите лучший
Может я что то пропустил? Что я могу улучшить?
Вот очень простой и воспроизводимый пример. yfinance
загружает данные о запасах S & P 500
"""python 3.7.7
tensorflow 2.1.0
keras 2.3.1"""
import numpy as np
import pandas as pd
from keras.callbacks import EarlyStopping, Callback
from keras.models import Model, Sequential, load_model
from keras.layers import Dense, Dropout, LSTM, BatchNormalization
from sklearn.preprocessing import MinMaxScaler
import plotly.graph_objects as go
import yfinance as yf
np.random.seed(4)
num_prediction = 5
look_back = 90
new_s_h5 = True # change it to False when you created model and want test on other past dates
df = yf.download(tickers="^GSPC", start='2018-05-06', end='2020-04-24', interval="1d")
data = df.filter(['Close', 'High', 'Low', 'Volume'])
# drop last N days to validate saved model on past
df.drop(df.tail(0).index, inplace=True)
print(df)
class EarlyStoppingCust(Callback):
def __init__(self, patience=0, verbose=0, validation_sets=None, restore_best_weights=False):
super(EarlyStoppingCust, self).__init__()
self.patience = patience
self.verbose = verbose
self.wait = 0
self.stopped_epoch = 0
self.restore_best_weights = restore_best_weights
self.best_weights = None
self.validation_sets = validation_sets
def on_train_begin(self, logs=None):
self.wait = 0
self.stopped_epoch = 0
self.best_avg_loss = (np.Inf, 0)
def on_epoch_end(self, epoch, logs=None):
loss_ = 0
for i, validation_set in enumerate(self.validation_sets):
predicted = self.model.predict(validation_set[0])
loss = self.model.evaluate(validation_set[0], validation_set[1], verbose = 0)
loss_ += loss
if self.verbose > 0:
print('val' + str(i + 1) + '_loss: %.5f' % loss)
avg_loss = loss_ / len(self.validation_sets)
print('avg_loss: %.5f' % avg_loss)
if self.best_avg_loss[0] > avg_loss:
self.best_avg_loss = (avg_loss, epoch + 1)
self.wait = 0
if self.restore_best_weights:
print('new best epoch = %d' % (epoch + 1))
self.best_weights = self.model.get_weights()
else:
self.wait += 1
if self.wait >= self.patience or self.params['epochs'] == epoch + 1:
self.stopped_epoch = epoch
self.model.stop_training = True
if self.restore_best_weights:
if self.verbose > 0:
print('Restoring model weights from the end of the best epoch')
self.model.set_weights(self.best_weights)
def on_train_end(self, logs=None):
print('best_avg_loss: %.5f (#%d)' % (self.best_avg_loss[0], self.best_avg_loss[1]))
def multivariate_data(dataset, target, start_index, end_index, history_size, target_size, step, single_step=False):
data = []
labels = []
start_index = start_index + history_size
if end_index is None:
end_index = len(dataset) - target_size
for i in range(start_index, end_index):
indices = range(i-history_size, i, step)
data.append(dataset[indices])
if single_step:
labels.append(target[i+target_size])
else:
labels.append(target[i:i+target_size])
return np.array(data), np.array(labels)
def transform_predicted(pr):
pr = pr.reshape(pr.shape[1], -1)
z = np.zeros((pr.shape[0], x_train.shape[2] - 1), dtype=pr.dtype)
pr = np.append(pr, z, axis=1)
pr = scaler.inverse_transform(pr)
pr = pr[:, 0]
return pr
step = 1
# creating datasets with look back
scaler = MinMaxScaler()
df_normalized = scaler.fit_transform(df.values)
dataset = df_normalized[:-num_prediction]
x_train, y_train = multivariate_data(dataset, dataset[:, 0], 0,len(dataset) - num_prediction + 1, look_back, num_prediction, step)
indices = range(len(dataset)-look_back, len(dataset), step)
x_test = np.array(dataset[indices])
x_test = np.expand_dims(x_test, axis=0)
y_test = np.expand_dims(df_normalized[-num_prediction:, 0], axis=0)
# creating past datasets to validate with EarlyStoppingCust
number_validates = 50
step_past = 5
validation_sets = [(x_test, y_test)]
for i in range(1, number_validates * step_past + 1, step_past):
indices = range(len(dataset)-look_back-i, len(dataset)-i, step)
x_t = np.array(dataset[indices])
x_t = np.expand_dims(x_t, axis=0)
y_t = np.expand_dims(df_normalized[-num_prediction-i:len(df_normalized)-i, 0], axis=0)
validation_sets.append((x_t, y_t))
if new_s_h5:
model = Sequential()
model.add(LSTM(32, return_sequences=False, activation = 'sigmoid', input_shape=(x_train.shape[1], x_train.shape[2])))
# model.add(Dropout(0.2))
# model.add(BatchNormalization())
# model.add(LSTM(units = 16))
model.add(Dense(y_train.shape[1]))
model.compile(optimizer = 'adam', loss = 'mse')
# EarlyStoppingCust is custom callback to validate each validation_sets and get average
# it takes epoch with best "best_avg" value
# es = EarlyStoppingCust(patience = 3, restore_best_weights = True, validation_sets = validation_sets, verbose = 1)
# or there is keras extension with built-in EarlyStopping, but it validates only 1 set that you pass through fit()
es = EarlyStopping(monitor = 'val_loss', patience = 3, restore_best_weights = True)
model.fit(x_train, y_train, batch_size = 64, epochs = 25, shuffle = True, validation_data = (x_test, y_test), callbacks = [es])
model.save('s.h5')
else:
model = load_model('s.h5')
predicted = model.predict(x_test)
predicted = transform_predicted(predicted)
print('predicted', predicted)
print('real', df.iloc[-num_prediction:, 0].values)
print('val_loss: %.5f' % (model.evaluate(x_test, y_test, verbose=0)))
fig = go.Figure()
fig.add_trace(go.Scatter(
x = df.index[-60:],
y = df.iloc[-60:,0],
mode='lines+markers',
name='real',
line=dict(color='#ff9800', width=1)
))
fig.add_trace(go.Scatter(
x = df.index[-num_prediction:],
y = predicted,
mode='lines+markers',
name='predict',
line=dict(color='#2196f3', width=1)
))
fig.update_layout(template='plotly_dark', hovermode='x', spikedistance=-1, hoverlabel=dict(font_size=16))
fig.update_xaxes(showspikes=True)
fig.update_yaxes(showspikes=True)
fig.show()
df.drop(df.tail(10).index, inplace=True)
, он показал тот же плохой результат, что и я.Ответы:
ОП постулирует интересную находку. Позвольте мне упростить исходный вопрос следующим образом.
Если модель обучается по определенному временному ряду, почему модель не может восстановить данные предыдущих временных рядов, на которых она уже была обучена?
Ну, ответ заложен в самом процессе обучения. Поскольку
EarlyStopping
здесь используется, чтобы избежать переоснащения, лучшая модель сохраняется в том местеepoch=5
, гдеval_loss=0.0030
указано в OP. В этом случае потеря обучения равна0.0343
, то есть RMSE обучения равна0.185
. Поскольку набор данных масштабируется с использованиемMinMaxScalar
, нам нужно отменить масштабирование RMSE, чтобы понять, что происходит.Найдены минимальные и максимальные значения временной последовательности
2290
и3380
. Следовательно, имея0.185
в качестве СКО обучения, означает, что даже для обучающего набора прогнозируемые значения могут приблизительно отличаться от базовых истинных значений0.185*(3380-2290)
, то есть~200
единиц в среднем.Это объясняет, почему существует большая разница при прогнозировании самих данных обучения на предыдущем шаге по времени.
Что я должен сделать, чтобы идеально подражать тренировочным данным?
Я задал этот вопрос от себя. Простой ответ - приблизить потерю тренировок
0
, что соответствует модели.После некоторого обучения я понял, что модель с одним слоем LSTM, имеющим
32
ячейки, недостаточно сложна для восстановления данных обучения. Поэтому я добавил еще один слой LSTM следующим образом.И модель подготовлена к
1000
эпохам без учетаEarlyStopping
.В конце
1000
эпохи у нас потеря тренировок0.00047
намного ниже, чем потеря тренировок в вашем случае. Таким образом, мы ожидаем, что модель лучше восстановит данные обучения. Ниже приведен прогноз на 2-8 апреля.Заключительная записка:
Обучение по конкретной базе данных не обязательно означает, что модель должна быть в состоянии идеально реконструировать данные обучения. Особенно, когда такие методы, как ранняя остановка, регуляризация и отсев вводятся, чтобы избежать переобучения, модель имеет тенденцию быть более обобщенной, чем запоминание данных тренировки.
источник
batch_size=1
очень медленная. И да,shuffle=False
потребовалось бы меньше времени для переобучения, потому что данные последовательны.Вы хотите, чтобы модель изучала отношения между входом и выходом вместо запоминания. Если модель запоминает правильные выходные данные для каждого входа, мы можем сказать, что это перебор данных тренировки. Зачастую вы можете принудительно изменить модель, используя небольшое подмножество данных, поэтому, если вы хотите увидеть такое поведение, вы можете попробовать это.
источник
Подозреваемый № 1 - Регуляризация
Нейронные сети отлично справляются с переобучением обучающих данных, на самом деле есть эксперимент, заменяющий метки CIFAR10 (задача классификации изображений) (значения y) случайными метками в наборе обучающих данных, и сеть соответствует случайным меткам, что приводит к почти нулевым потерям.
Так почему же это не происходит все время? регуляризация .
регуляризация (грубо) пытается решить более сложную проблему, чем проблема оптимизации (потери), которую мы определили для модели.
Некоторые распространенные методы регуляризации в нейронных сетях:
Эти методы помогают уменьшить переоснащение и, как правило, приводят к лучшей валидации и тестированию производительности, но приводят к снижению производительности поезда (что на самом деле не имеет значения, как описано в предыдущем абзаце)
Производительность данных поезда, как правило, не так важна, и для этого мы используем набор валидации.
Подозреваемый № 2 - Размер модели
Вы используете один слой LSTM с 32 единицами. это довольно мало. попробуйте увеличить размер и даже поместить два слоя LSTM (или двунаправленный), и я уверен, что модель и оптимизатор будут соответствовать вашим данным до тех пор, пока вы позволите им - т.е. удалите раннюю остановку, restore_last_weights и любую другую регуляризацию, указанную выше.
Примечание о сложности проблемы
Попытка предсказать будущие цены на акции, просто взглянув на историю, - непростая задача, и даже если модель может (пере) идеально соответствовать обучающему набору, она, вероятно, не будет ничего полезного на тестовом наборе или в реальном мире.
ML - не черная магия, образцы x должны быть каким-то образом соотнесены с тегами y, обычно мы предполагаем, что (x, y) взяты из некоторого распределения вместе.
Более интуитивный способ думать об этом, когда вам нужно пометить изображение вручную для класса собака / кошка - это довольно просто. но можете ли вы вручную «пометить» цену акции, взглянув только на историю этой акции?
Вот какая-то интуиция о том, насколько сложна эта проблема.
Примечание по переоснащению
Не следует гоняться за более высокой тренировочной эффективностью, почти бесполезно пытаться подобрать тренировочные данные, так как мы обычно стараемся хорошо работать с моделью на новых невидимых данных, имеющих свойства, аналогичные данным поезда. вся идея состоит в том, чтобы попытаться обобщить и изучить свойства данных и корреляцию с целью, вот что такое обучение :)
источник
В основном, если вы хотите получить лучший результат для тренировочных данных, ваша точность обучения должна быть максимально высокой. Вы должны использовать лучшую модель в отношении данных, которые у вас есть. В основном вы должны проверить, соответствует ли ваша точность обучения для этой цели независимо от точности теста. Это также называется переоснащением, которое дает лучшую точность данных обучения, а не данных испытаний.
Ранняя остановка может повлиять на этот сценарий, когда берется лучшая точность теста / валидации, а не точность обучения.
источник
Краткий ответ:
Набор:
Интуиция: Вы описываете приоритет высокой точности в данных обучения. Это описывает переоснащение. Чтобы сделать это, установите размер партии на 1, эпохи высокие и перемешайте.
источник
После изменения архитектуры модели и оптимизатора на Adagrad мне удалось улучшить результаты в некоторой степени.
Причина использования оптимизатора Adagrad здесь:
Он адаптирует скорость обучения к параметрам, выполняя меньшие обновления (например, низкие скорости обучения) для параметров, связанных с часто встречающимися функциями, и большие обновления (например, высокие скорости обучения) для параметров, связанных с нечастыми функциями. По этой причине он хорошо подходит для работы с разреженными данными.
Пожалуйста, обратитесь к коду ниже:
Прогнозирование запасов является очень сложной задачей, поэтому вместо того, чтобы придерживаться прогноза одной модели, у нас может быть несколько моделей, работающих вместе, чтобы сделать прогноз, а затем, основываясь на максимальном результате голосования, принять вызов, аналогично ансамблевому подходу к обучению. Кроме того, мы можем сложить несколько моделей вместе, как:
Нейронная сеть с глубокой прямой связью с автоматическим кодировщиком для уменьшения размеров + Глубокая рекуррентная нейронная сеть + ARIMA + Экстремальный ускоряющий градиент-регрессор
Adaboost + Bagging + Дополнительные деревья + Повышение градиента + Случайный лес + XGB
Агенты по обучению подкреплению очень хорошо справляются с прогнозированием запасов:
Пожалуйста, найдите очень изобретательную ссылку здесь .
источник
Как уже говорили другие, от этого многого не стоит ожидать.
Тем не менее, я нашел следующее в вашем коде:
Вы повторно устанавливаете скалер каждый раз во время обучения и тестирования. Вам нужно сохранить саклер и только преобразовывать данные во время тестирования, иначе результаты будут немного отличаться:
Set
shuffle=False
. Поскольку вам нужно сохранить порядок вашего набора данных.Set
batch_size=1
. Так как это будет менее подвержено переобучению, и обучение будет более шумным, а ошибка - менее усредненной.Установить
epochs=50
или больше.С вышеупомянутыми настройками модель достигнута
loss: 0.0037 - val_loss: 3.7329e-04
.Проверьте следующие образцы прогноза:
С 17/04/2020 -> 23/04/2020:
С 02/04/2020 -> 08/04/2020:
С 25/03/2020 -> 31/03/2020:
источник
Это не совсем подходит, и чтобы улучшить это, я думаю, вам нужно добавить нейроны в ваши скрытые слои. !! Еще один момент - попробуйте активировать функцию «relu». Сигмоид не дает хороших результатов. Также вам нужно определить «softmax» в вашем выходном слое.!
источник
Посмотри, что ты делаешь:
источник