Почему нейронная сеть предсказывает неверные данные о своих тренировках?


17

Я создал нейронную сеть 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()

3
Воспроизводимые примеры в наше время настолько редки (в отличие от газилионов с аналогичными вопросами без), что, пожалуй, хорошая идея рекламировать свое существование в начале вашего поста (добавлено);)
desertnaut

7
Возможно, проблема в том, что вы ожидаете слишком много предсказуемости на фондовом рынке. Если вы натренировали модель на последовательности бросков в 1 миллион монет, а затем попытались заставить ее предсказать броски монет, для модели было бы не удивительно ошибиться, даже если броски были получены из данных обучения - модель не ожидается, чтобы запомнить его данные обучения и извергнуть его.
user2357112 поддерживает Монику

2
В дополнение к тому, что сказал @ user2357112supportsMonica, ваша модель получила правильное среднее значение, и это действительно все, чего я ожидаю от такой модели (по крайней мере, с любой последовательностью), и вы ожидаете слишком многого из 5 дней данные. Вам действительно нужно гораздо больше данных, чтобы иметь возможность с какой-либо значимостью сказать, в чем заключается ошибка вашей модели.
Аарон

Есть намного больше параметров для настройки модели. Я попробовал пару из них, как ранняя остановка (терпение = 20), увеличение количества эпох, увеличение единиц измерения lstm с 32 до 64 и т. Д. Результаты были намного лучше. проверьте здесь github.com/jvishnuvardhan/Stackoverflow_Questions/blob/master/… . Как упомянуто @sirjay, добавив больше функций (в настоящее время только 4), добавив больше слоев (lstm, batchnorm, dropout и т. Д.), Выполнение гиперпараметрической оптимизации приведет к гораздо большей производительности.
Вишнувардхан Джанапати

@VishnuvardhanJanapati спасибо за проверку. Я скомпилировал ваш код, сохранил модель, а затем установил df.drop(df.tail(10).index, inplace=True), он показал тот же плохой результат, что и я.
Сирджай

Ответы:


5

ОП постулирует интересную находку. Позвольте мне упростить исходный вопрос следующим образом.

Если модель обучается по определенному временному ряду, почему модель не может восстановить данные предыдущих временных рядов, на которых она уже была обучена?

Ну, ответ заложен в самом процессе обучения. Поскольку 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 следующим образом.

model = Sequential()
model.add(LSTM(32, return_sequences=True, activation = 'sigmoid', input_shape=(x_train.shape[1], x_train.shape[2])))
# model.add(Dropout(0.2))
# model.add(BatchNormalization())
model.add(LSTM(units = 64, return_sequences=False,))
model.add(Dense(y_train.shape[1]))
model.compile(optimizer = 'adam', loss = 'mse')

И модель подготовлена ​​к 1000эпохам без учета EarlyStopping.

model.fit(x_train, y_train, batch_size = 64, epochs = 1000, shuffle = True, validation_data = (x_test, y_test))

В конце 1000эпохи у нас потеря тренировок 0.00047намного ниже, чем потеря тренировок в вашем случае. Таким образом, мы ожидаем, что модель лучше восстановит данные обучения. Ниже приведен прогноз на 2-8 апреля.

прогнозирование

Заключительная записка:

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


Мне любопытно. Почему вы рекомендовали размер партии 64 для переоснащения (по сравнению с 1)? И почему shuffle = True? Разве это не займет больше времени, чтобы приблизиться к решению?
Дэниел Скотт

Я считаю, что модель становится более склонной к переоснащению по мере увеличения размера партии. Пожалуйста, посмотрите здесь и здесь . Кроме того, тренировка batch_size=1очень медленная. И да, shuffle=Falseпотребовалось бы меньше времени для переобучения, потому что данные последовательны.
Achintha Ihalage

2

Почему модель показывает неверные результаты на своих собственных обученных данных? Я тренировал данные, он должен помнить, как прогнозировать данные на этом фрагменте, но предсказывает неправильно.

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


2

Подозреваемый № 1 - Регуляризация

Нейронные сети отлично справляются с переобучением обучающих данных, на самом деле есть эксперимент, заменяющий метки CIFAR10 (задача классификации изображений) (значения y) случайными метками в наборе обучающих данных, и сеть соответствует случайным меткам, что приводит к почти нулевым потерям.

введите описание изображения здесь

с левой стороны мы видим, что при достаточном количестве случайных меток эпох получается около 0 потерь - идеальный результат (для понимания глубокого обучения требуется переосмысление обобщений в zhang et al 2016 )

Так почему же это не происходит все время? регуляризация .

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

Некоторые распространенные методы регуляризации в нейронных сетях:

  • ранняя остановка
  • выбывать
  • нормализация партии
  • снижение веса (например, l1 l2 норм)
  • увеличение данных
  • добавление случайного / гауссовского шума

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

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

Подозреваемый № 2 - Размер модели

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

Примечание о сложности проблемы

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

ML - не черная магия, образцы x должны быть каким-то образом соотнесены с тегами y, обычно мы предполагаем, что (x, y) взяты из некоторого распределения вместе.

Более интуитивный способ думать об этом, когда вам нужно пометить изображение вручную для класса собака / кошка - это довольно просто. но можете ли вы вручную «пометить» цену акции, взглянув только на историю этой акции?

Вот какая-то интуиция о том, насколько сложна эта проблема.

Примечание по переоснащению

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


1

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

Ранняя остановка может повлиять на этот сценарий, когда берется лучшая точность теста / валидации, а не точность обучения.


1

Краткий ответ:

Набор:

batch_size = 1
epochs = 200
shuffle = False

Интуиция: Вы описываете приоритет высокой точности в данных обучения. Это описывает переоснащение. Чтобы сделать это, установите размер партии на 1, эпохи высокие и перемешайте.


1

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

Причина использования оптимизатора Adagrad здесь:

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

Пожалуйста, обратитесь к коду ниже:

model = Sequential()
model.add(LSTM(units=100,return_sequences=True, kernel_initializer='random_uniform', input_shape=(x_train.shape[1], x_train.shape[2])))
model.add(Dropout(0.2))
model.add(LSTM(units=100,return_sequences=True, kernel_initializer='random_uniform'))
model.add(LSTM(units=100,return_sequences=True, kernel_initializer='random_uniform'))
model.add(Dropout(0.20))
model.add(Dense(units=25, activation='relu'))
model.add(Dense(y_train.shape[1]))

# compile model
model.compile(loss="mse", optimizer='adagrad', metrics=['accuracy'])
model.summary()

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

  1. Нейронная сеть с глубокой прямой связью с автоматическим кодировщиком для уменьшения размеров + Глубокая рекуррентная нейронная сеть + ARIMA + Экстремальный ускоряющий градиент-регрессор

  2. Adaboost + Bagging + Дополнительные деревья + Повышение градиента + Случайный лес + XGB

Агенты по обучению подкреплению очень хорошо справляются с прогнозированием запасов:

  1. Turtle-торговый агент
  2. Агент скользящей средней
  3. Агент прокатки сигнала
  4. Агент политики-градиента
  5. Q-Learning Agent
  6. Эволюционно-стратегический агент

Пожалуйста, найдите очень изобретательную ссылку здесь .


Адам также обладает этими свойствами, на самом деле Адам - ​​это своего рода эволюция Адаграда
Шмулик

1

Как уже говорили другие, от этого многого не стоит ожидать.

Тем не менее, я нашел следующее в вашем коде:

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

    from sklearn.externals import joblib
    scaler_filename = "scaler.save"
    if new_s_h5:
        scaler = MinMaxScaler()
        df_normalized = scaler.fit_transform(df.values)
        joblib.dump(scaler, scaler_filename)
    
    else:
        scaler = joblib.load(scaler_filename)
        df_normalized = scaler.transform(df.values)
  2. Set shuffle=False. Поскольку вам нужно сохранить порядок вашего набора данных.

  3. Set batch_size=1. Так как это будет менее подвержено переобучению, и обучение будет более шумным, а ошибка - менее усредненной.

  4. Установить 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:

введите описание изображения здесь


0

Это не совсем подходит, и чтобы улучшить это, я думаю, вам нужно добавить нейроны в ваши скрытые слои. !! Еще один момент - попробуйте активировать функцию «relu». Сигмоид не дает хороших результатов. Также вам нужно определить «softmax» в вашем выходном слое.!


Похоже, у вас есть секреты предсказания рынка. Что еще он должен сделать?
Дэниел Скотт

2
Softmax для классификации, это проблема регрессии.
ШмуликА

2
@ Даниэль Скотт, ты не понял? Глубоко (миллиард слоев ниже) это проблема классификации, которая решает вопрос о прибыли или убытке. Зачем вообще заботиться о прогнозировании временных рядов?
Соумя

@ Совмья, мне нравится твой юмор. ;)
Дэниел Скотт

-1

Почему модель показывает неверные результаты на своих собственных обученных данных? Я тренировал данные, он должен помнить, как прогнозировать данные на этом фрагменте, но предсказывает неправильно.

Посмотри, что ты делаешь:

  1. Построение модели с несколькими слоями
  2. Тренировочная модель с training_data
  3. Когда вы тренировали модель, все обучаемые параметры обучаются (т.е. веса модели сохраняются)
  4. Эти веса теперь представляют отношения между входом и выходом.
  5. Когда вы снова прогнозируете те же данные training_data, на этот раз обученная модель использует веса для получения выходных данных.
  6. Качество вашей модели теперь определяет прогнозы и, следовательно, они отличаются от исходных результатов, даже если данные совпадают.
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.