Чому нейронна мережа прогнозує неправильні власні дані тренувань?


13

Я створив нейронну мережу 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 попередніх днів, а потім відновити ваги з найкращого ( 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. Якщо я спробую більше тренуватися, граючи з якою епохою обрати, що б я не робив, в минулому завжди є багато часових інтервалів, які мають неправильне передбачення.

Чому модель показує неправильні результати за власними підготовленими даними? Я тренував дані, він повинен пам’ятати, як прогнозувати дані на цьому наборі, але прогнозує неправильне. Що я також спробував:

  • Використовуйте великі набори даних із 50-кратними + рядками, 20-річні ціни на акції, додаючи більше або менше функцій
  • Створюйте різні типи моделі, як-от додавання більше прихованих шарів, різних batch_size, активації різних шарів, випадання, batchnormalization
  • Створіть спеціальний зворотний виклик 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
Приклади, що відтворюються, сьогодні такі рідкісні (на відміну від gazzilions подібних питань без), що, мабуть, хороша ідея рекламувати своє існування на початку вашого допису (додано);)
пустельний

7
Проблема може бути просто в тому, що ви очікуєте занадто багато передбачуваності поза фондовим ринком. Якщо ви тренували модель на послідовності мільйонів мільйонів монет, а потім намагалися змусити її передбачити відвернення монети, не було б дивно, що модель помилилася, навіть якщо фліп походив із даних про навчання - модель не очікується, що вони запам’ятовують свої дані тренувань та відтворюють їх.
user2357112 підтримує Моніку

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

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

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

Відповіді:


3

Чому модель показує неправильні результати за власними підготовленими даними? Я тренував дані, він повинен пам’ятати, як прогнозувати дані на цьому наборі, але прогнозує неправильне.

Ви хочете, щоб модель вивчила взаємозв'язок між входом і виходом замість запам'ятовування. Якщо модель запам'ятовує правильний вихід для кожного входу, ми можемо сказати, що це більше, ніж відповідати навчальним даним. Часто ви можете змусити модель переобладнати за допомогою невеликого підмножини даних, тому, якщо це поведінка, яку ви хочете побачити, ви можете спробувати це.


2

ОП постулює з цікавою знахідкою. Дозвольте мені спростити початкове запитання наступним чином.

Якщо модель готується до певного часового ряду, чому модель не може реконструювати дані попередніх часових рядів, на яких вона вже була навчена?

Ну, відповідь закладена в самому навчальному процесі. Оскільки EarlyStoppingтут використовується для уникнення переобладнання, найкраща модель зберігається epoch=5там, де val_loss=0.0030зазначено в ОП. У цьому випадку втрата навчання дорівнює 0.0343, тобто RMSE навчання 0.185. Оскільки набір даних масштабується за допомогою MinMaxScalar, нам потрібно скасувати масштабування RMSE, щоб зрозуміти, що відбувається.

Знаходяться мінімальні та максимальні значення часової послідовності 2290та 3380. Таким чином, наявність 0.185RMSE тренінгу означає, що навіть для навчального набору прогнозовані значення можуть відрізнятися від основних значень істини приблизно 0.185*(3380-2290), тобто ~200в середньому на одиниці.

Це пояснює, чому існує велика різниця при прогнозуванні самих даних тренувань на попередньому етапі часу.

Що мені робити, щоб ідеально імітувати дані тренувань?

Я задав це питання собі. Проста відповідь полягає в тому, щоб наблизити втрати до тренінгу 0, тобто перевершити модель.

Після деякого тренінгу я зрозумів, що модель із лише 1 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 квітня.

передбачення

Заключна примітка:

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


1

В основному Якщо ви хочете отримати кращі результати для тренувальних даних, точність тренувань повинна бути максимально високою. Вам слід використовувати кращу модель стосовно даних, які у вас є. В основному ви повинні перевірити, чи відповідає ваша ціль точності тренувань для цієї мети незалежно від точності тесту. Це також називається перевиконанням, що забезпечує кращу точність у навчальних даних, а не тестових даних.

Рання зупинка може вплинути на цей сценарій, коли найкраща точність тесту / перевірки приймається, а не точність тренувань.


1

Коротка відповідь:

Набір:

batch_size = 1
epochs = 200
shuffle = False

Інтуїція: Ви описуєте пріоритет високої точності у навчальних даних. Це опис надягання. Для цього встановіть розмір партії 1, епохи високі, і перетасуйте.


0

Чому модель показує неправильні результати за власними підготовленими даними? Я тренував дані, він повинен пам’ятати, як прогнозувати дані на цьому наборі, але прогнозує неправильне.

Подивіться, що ви робите:

  1. Побудова моделі з деякими шарами
  2. Навчальна модель з навчальними_даними
  3. Коли ви тренували модель, всі треновані параметри навчаються (тобто ваги моделі були збережені)
  4. Ці ваги тепер представляють залежність між входом і виходом.
  5. Коли ви знову прогнозуєте ті самі навчальні дані, на цей раз навчена модель використовує ваги, щоб отримати результат.
  6. Якість вашої моделі тепер визначає прогнози, а отже, вони відрізняються від оригінальних результатів, навіть якщо дані однакові.

0

Це недостатньо підходить і для того, щоб вдосконалити те, що мені потрібно, щоб додати нейрони до своїх прихованих шарів. !! Ще один момент - спробувати функцію активації 'relu'. Сигмоїд не дає хороших результатів. Також вам потрібно визначити "softmax" у вашому вихідному шарі.!


Схоже, ви зберігаєте секрети передбачення ринку. Що ще йому робити?
Даніель Скотт

1
softmax - це класифікація, її проблема регресії.
ShmulikA

0

Змінивши архітектуру моделі та оптимізатор на 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 + Baging + Додаткові дерева + Підвищення градієнта + Випадковий ліс + XGB

Підсилюючі агенти навчання роблять досить добре в прогнозі запасів, як:

  1. Черепаховий агент
  2. Ковзний середній агент
  3. Агент прокатки сигналів
  4. Агент-градієнт політики
  5. Q-агент для навчання
  6. Еволюційно-стратегічний агент

Ви можете знайти дуже винахідливий посилання тут .


Адам також має ці властивості, насправді Адам - ​​це якась еволюція адаграду
ShmulikA

0

Підозрюваний №1 - Регуляризація

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

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

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

Так чому ж це не відбувається постійно? регуляризація .

регуляризація намагається вирішити важче завдання, ніж задачу оптимізації (втрати), яку ми визначили для моделі.

деякі поширені методи регуляризації в нейронних мережах:

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

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

Ефективність даних поїздів, як правило, не так важлива, і для цього ми використовуємо набір перевірки.

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

ви використовуєте один рівень LSTM з 32 одиницями. це досить мало. спробуйте збільшити розмір і навіть покладіть два шари LSTM (або двонаправлений), і я впевнений, що модель та оптимізатор будуть переповнювати ваші дані до тих пір, поки ви дозволите їх, тобто видаліть ранню зупинку, Resto_last_weights та будь-яку іншу регуляризацію, вказану вище.

Примітка про складність проблеми

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

ML не є чорною магією, зразки x потрібно якось співвідносити з тегами y, ми зазвичай припускаємо, що (x, y) виведені з деякого розподілу разом.

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

Ось яка інтуїція щодо того, наскільки важка ця проблема.

Примітка про надмірну обробку

Не слід гнатися за результатами вищої підготовки, майже марними, щоб намагатися переоцінити дані тренувань, оскільки ми зазвичай намагаємося добре працювати з моделлю на нових небачених даних, що мають властивості, подібні до даних поїздів. вся ідея полягає у тому, щоб спробувати узагальнити та вивчити властивості даних та кореляцію із цільовою, ось що таке навчання :)


0

Як уже говорили інші, від цього не слід багато чекати.

Тим не менш, я знайшов у вашому коді таке:

  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. Встановити shuffle=False. Як вам потрібно дотримуватися порядку вашого набору даних.

  3. Встановити batch_size=1. Оскільки це буде менш схильним до перенапруження, а навчання буде шумніше, а помилка - усереднена.

  4. Встановити epochs=50чи більше.


За допомогою вищезазначених налаштувань модель досягнута loss: 0.0037 - val_loss: 3.7329e-04.

Перевірте такі зразки прогнозування:

З 17.04.2020 -> 23.04.2020:

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

З 04.04.2020 -> 04.04.2020:

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

З 25.03.2020 -> 31.03.2020:

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.