Dlaczego sieć neuronowa przewiduje błędy na podstawie własnych danych treningowych?

13

Zrobiłem sieć neuronową LSTM (RNN) z nadzorowanym uczeniem się do prognozowania zapasów danych. Problem polega na tym, dlaczego źle prognozuje na podstawie własnych danych treningowych? (uwaga: odtwarzalny przykład poniżej)

Stworzyłem prosty model do przewidywania ceny akcji na najbliższe 5 dni:

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])

Prawidłowe wyniki podano w y_test(5 wartości), więc modeluj pociągi, patrząc wstecz na 90 poprzednich dni, a następnie przywróć wagi od najlepszego ( val_loss=0.0030) wyniku 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

Wynik prognozy jest niesamowity, prawda?

wprowadź opis zdjęcia tutaj

Jest tak, ponieważ algorytm przywrócił najlepsze wagi z epoki # 5. Okej, zapiszmy teraz ten model do .h5pliku , cofnijmy się o 10 dni i przewidujmy ostatnie 5 dni (w pierwszym przykładzie wykonaliśmy model i sprawdziliśmy go w dniach 17-23 kwietnia, w tym weekendy wolne od pracy, teraz przetestujmy w dniach 2-8 kwietnia). Wynik:

wprowadź opis zdjęcia tutaj

Pokazuje absolutnie zły kierunek. Jak widzimy, dzieje się tak, ponieważ model został przeszkolony i najlepiej wykorzystał epokę nr 5 do walidacji ustawionej na 17–23 kwietnia, ale nie na 2-8. Jeśli spróbuję więcej trenować, bawiąc się epoką, którą wybiorę, cokolwiek zrobię, w przeszłości zawsze istnieje wiele przedziałów czasowych, które mają błędne prognozy.

Dlaczego model pokazuje błędne wyniki na własnych przeszkolonych danych? Przeszkoliłem dane, musi pamiętać, jak przewidywać dane na tym zestawie, ale źle przewiduje. Co też próbowałem:

  • Używaj dużych zestawów danych z ponad 50 tys. Wierszy, 20-letnimi cenami akcji, dodając więcej lub mniej funkcji
  • Twórz różne typy modeli, np. Dodawaj więcej ukrytych warstw, różne rozmiary partii, różne aktywacje warstw, rezygnacje, normalizacja partii
  • Utwórz niestandardowe wywołanie zwrotne EarlyStopping, uzyskaj średnią wartość val_loss z wielu zestawów danych sprawdzania poprawności i wybierz najlepszy

Może coś mi umknęło? Co mogę poprawić?

Oto bardzo prosty i powtarzalny przykład. yfinancepobiera dane giełdowe 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()
Sirjay
źródło
3
Powtarzalne przykłady są obecnie tak rzadkie (w przeciwieństwie do gazzilionów podobnych pytań bez), co jest prawdopodobnie dobrym pomysłem na reklamowanie ich istnienia na początku twojego wpisu (dodano);)
desertnaut
7
Problemem może być po prostu to, że oczekujesz zbyt dużej przewidywalności na giełdzie. Jeśli wytrenowałeś model na sekwencji 1 miliona rzutów monetą, a następnie próbowałeś przekonać go, aby przewidywał rzut monetą, nie byłoby zaskoczeniem, że model pomylił się, nawet jeśli obroty pochodziły z danych treningowych - model nie oczekuje się, że zapamięta dane treningowe i zwróci je.
user2357112 obsługuje Monikę
2
Oprócz tego, co powiedział @ user2357112supportsMonica, twój model ma średnią rację, co jest naprawdę wszystkim, czego spodziewałbym się, że taki model naprawdę dostanie (przynajmniej z dowolną konsekwencją), a oczekujesz zbyt wiele z 5 dni dane. Naprawdę potrzebujesz znacznie więcej danych, aby móc bez znaczenia określić, jaki jest błąd w twoim modelu.
Aaron
Istnieje o wiele więcej parametrów, aby dostroić model. Próbowałem kilka z nich, takich jak wczesne zatrzymanie (cierpliwość = 20), większa liczba epok, zwiększona liczba jednostek lstm z 32 do 64 itd. Wyniki były znacznie lepsze. sprawdź tutaj github.com/jvishnuvardhan/Stackoverflow_Questions/blob/master/… . Jak wspomniano w @sirjay, dodając więcej funkcji (obecnie tylko 4), dodając więcej warstw (lstm, batchnorm, dropout itp.), Uruchomienie optymalizacji hiperparametrów spowodowałoby znacznie lepszą wydajność.
Vishnuvardhan Janapati
@VishnuvardhanJanapati dziękuję za sprawdzenie. Skompilowałem twój kod, zapisałem model, a potem ustawiłem df.drop(df.tail(10).index, inplace=True), pokazał ten sam zły wynik, co miałem.
sirjay

Odpowiedzi:

3

Dlaczego model pokazuje błędne wyniki na własnych przeszkolonych danych? Przeszkoliłem dane, musi pamiętać, jak przewidywać dane na tym zestawie, ale źle przewiduje.

Chcesz, aby model nauczył się związku między wejściem a wyjściem zamiast zapamiętywania. Jeśli model zapamiętuje poprawne dane wyjściowe dla każdego wejścia, możemy powiedzieć, że są one niezgodne z danymi treningowymi. Często możesz zmusić model do przeregulowania, używając małego podzbioru danych, więc jeśli takie zachowanie chcesz zobaczyć, możesz to wypróbować.

tensordude
źródło
2

PO postuluje interesujące odkrycie. Pozwolę sobie uprościć pierwotne pytanie w następujący sposób.

Jeśli model jest trenowany w określonym szeregu czasowym, dlaczego model nie może zrekonstruować danych z poprzednich szeregów czasowych, na których już był trenowany?

Odpowiedź jest osadzona w samym postępie szkolenia. Ponieważ EarlyStoppingzastosowano go tutaj, aby uniknąć przeuczenia, najlepszy model zapisano w miejscu epoch=5, o którym val_loss=0.0030wspomniało PO. W tym przypadku utrata treningu jest równa 0.0343RMSE treningu 0.185. Ponieważ zestaw danych jest skalowany przy użyciu MinMaxScalar, musimy cofnąć skalowanie RMSE, aby zrozumieć, co się dzieje.

Stwierdzono, że minimalne i maksymalne wartości sekwencji czasowej to 2290i 3380. Dlatego posiadanie 0.185jako RMSE treningu oznacza, że ​​nawet dla zestawu treningowego przewidywane wartości mogą różnić się od podstawowych wartości prawdy o około 0.185*(3380-2290), czyli ~200średnio o jednostki.

To wyjaśnia, dlaczego istnieje duża różnica w przewidywaniu samych danych treningowych na poprzednim etapie czasowym.

Co powinienem zrobić, aby idealnie emulować dane treningowe?

Zadałem sobie to pytanie. Prosta odpowiedź brzmi: spraw, aby strata treningowa zbliżała się 0, a więc przewyższa model.

Po pewnym treningu zdałem sobie sprawę, że model z tylko 1 warstwą LSTM z 32komórkami nie jest wystarczająco skomplikowany, aby zrekonstruować dane treningowe. Dlatego dodałem kolejną warstwę LSTM w następujący sposób.

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')

Model jest szkolony dla 1000epok bez uwzględnienia EarlyStopping.

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

Pod koniec 1000tej epoki utrata treningu 0.00047jest znacznie niższa niż utrata treningu w twoim przypadku. Oczekujemy więc, że model lepiej zrekonstruuje dane treningowe. Poniżej znajduje się wykres prognoz dla 2-8 kwietnia.

Prognoza

Ostatnia uwaga:

Szkolenie na konkretnej bazie danych niekoniecznie oznacza, że ​​model powinien być w stanie idealnie zrekonstruować dane szkoleniowe. W szczególności, gdy metody takie jak wczesne zatrzymanie, regularyzacja i rezygnacja są wprowadzane w celu uniknięcia nadmiernego dopasowania, model jest bardziej uogólniony niż zapamiętywanie danych treningowych.

Achintha Ihalage
źródło
1

Zasadniczo Jeśli chcesz uzyskać lepszy wynik dla danych treningowych, dokładność treningu powinna być jak najwyższa. Powinieneś użyć lepszego modelu w odniesieniu do posiadanych danych. Zasadniczo powinieneś sprawdzić, czy Twoja dokładność treningu w tym celu jest niezależna od dokładności testu. Nazywa się to również nadmiernym dopasowaniem, które zapewnia większą dokładność danych treningowych niż danych testowych.

Wcześniejsze zatrzymanie może mieć wpływ na ten scenariusz, w którym wybierana jest najlepsza dokładność testu / walidacji, a nie dokładność szkolenia.

Demotte
źródło
1

Krótka odpowiedź:

Zestaw:

batch_size = 1
epochs = 200
shuffle = False

Intuicja: Opisujesz priorytet wysokiej dokładności w danych treningowych. To opisuje nadmierne dopasowanie. Aby to zrobić, ustaw rozmiar partii na 1, wysokie epoki i przemieszaj.

Daniel Scott
źródło
0

Dlaczego model pokazuje błędne wyniki na własnych przeszkolonych danych? Przeszkoliłem dane, musi pamiętać, jak przewidywać dane na tym zestawie, ale źle przewiduje.

Zobacz, co robisz:

  1. Budowanie modelu z kilkoma warstwami
  2. Model treningowy z danymi treningowymi
  3. Gdy trenujesz model, trenowane są wszystkie parametry, które można trenować (tzn. Zapisano wagi modelu)
  4. Te wagi reprezentują teraz związek między wejściem a wyjściem.
  5. Kiedy ponownie przewidujesz te same dane treningowe, tym razem wytrenowany model używa ciężarów, aby uzyskać wynik.
  6. Jakość twojego modelu decyduje teraz o przewidywaniach, a zatem różnią się od oryginalnych wyników, nawet jeśli dane są takie same.
Piyush Gupta
źródło
0

To jest niedopasowane i poprawiam to, że potrzebuję dodać neurony do twoich ukrytych warstw. !! Kolejnym punktem jest próba aktywacji funkcji „relu”. Sigmoid nie daje dobrych wyników. Musisz także zdefiniować „softmax” w swojej warstwie wyjściowej.!

Rahul Anand
źródło
Wygląda na to, że kryjesz tajemnice przewidywania rynku. Co jeszcze powinien zrobić?
Daniel Scott
1
softmax służy do klasyfikacji, jest to problem regresji.
ShmulikA
0

Po zmianie architektury modelu i optymalizatora na Adagrad mogłem w pewnym stopniu poprawić wyniki.

Powodem korzystania z optymalizatora Adagrad tutaj jest:

Dostosowuje szybkość uczenia się do parametrów, wykonując mniejsze aktualizacje (tj. Niskie wskaźniki uczenia się) dla parametrów związanych z często występującymi funkcjami oraz większe aktualizacje (tj. Wysokie wskaźniki uczenia się) dla parametrów związanych z rzadkimi funkcjami. Z tego powodu dobrze nadaje się do radzenia sobie z rzadkimi danymi.

Proszę odnieść się do kodu poniżej:

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()

Prognozowanie zapasów jest bardzo trudnym zadaniem, więc zamiast trzymać się prognozy jednego modelu, możemy mieć kilka modeli współpracujących ze sobą, aby dokonać prognozy, a następnie w oparciu o maksymalny głosowany wynik podjąć wyzwanie, podobnie jak podejście do uczenia się zespołowego. Ponadto możemy zestawić ze sobą kilka modeli, takich jak:

  1. Automatyczna enkoder z głębokim sprzężeniem zwrotnym w celu zmniejszenia wymiarów + Głęboka, rekurencyjna sieć neuronowa + ARIMA + Ekstremalne zwiększenie gradientu regresora

  2. Adaboost + Bagging + Dodatkowe drzewa + Gradient Boost + Random Forest + XGB

Agenci uczący się o wzmocnieniach radzą sobie całkiem dobrze w Prognozie giełdowej:

  1. Agent handlu żółwiami
  2. Agent średniej ruchomej
  3. Sygnał kroczący
  4. Agent gradientowy
  5. Agent Q-learningu
  6. Agent strategii ewolucji

Proszę znaleźć bardzo pomysłowy odnośnik tutaj .

Rishab P.
źródło
adam ma również te właściwości, właściwie adam jest ewolucją adagrada
ShmulikA
0

Podejrzany nr 1 - Regularyzacja

Sieci neuronowe świetnie nadają się do dopasowania danych treningowych, w rzeczywistości istnieje eksperyment zastępujący etykiety CIFAR10 (zadanie klasyfikacji obrazu) (wartości y) losowymi etykietami w zbiorze danych szkoleniowych, a sieć dopasowuje losowe etykiety, co powoduje prawie zerową utratę.

wprowadź opis zdjęcia tutaj

po lewej stronie widać, że przy wystarczającej liczbie epok losowe etykiety osiągają około 0 strat - doskonały wynik (od zrozumienia głębokiego uczenia się wymaga ponownego przemyślenia uogólnienia przez zhang i in. 2016 )

Dlaczego więc nie dzieje się cały czas? regularyzacja .

regularyzacja (z grubsza) próbuje rozwiązać trudniejszy problem niż problem optymalizacji (strata), który zdefiniowaliśmy dla modelu.

niektóre typowe metody regularyzacji w sieciach neuronowych:

  • wczesne zatrzymanie
  • spadkowicz
  • normalizacja partii
  • rozpad masy ciała (np. normy l1 l2)
  • powiększanie danych
  • dodawanie szumu losowego / gaussowskiego

metody te pomagają zmniejszyć nadmierne dopasowanie i zwykle skutkują lepszą walidacją i wydajnością testu, ale skutkują niższą wydajnością pociągu (co nie ma znaczenia, jak wyjaśniono w ostatnim akapicie).

wydajność danych pociągu zwykle nie jest tak ważna i do tego używamy zestawu walidacyjnego.

Podejrzany # 2 - Rozmiar modelu

używasz pojedynczej warstwy LSTM z 32 jednostkami. to jest dość małe. spróbuj zwiększyć rozmiar, a nawet umieść dwie warstwy LSTM (lub dwukierunkową) i jestem pewien, że model i optymalizator prześcigną twoje dane tak długo, jak im na to pozwolisz - tj. usuniesz wczesne zatrzymanie, restore_last_weights i wszelkie inne regularyzacje określone powyżej.

Uwaga na temat złożoności problemu

próba przewidzenia przyszłych cen akcji na podstawie samej historii nie jest łatwym zadaniem, a nawet jeśli model (idealnie) pasuje idealnie do zestawu treningowego, prawdopodobnie nie zrobi nic użytecznego na zestawie testowym ani w prawdziwym świecie.

ML nie jest czarną magią, próbki x muszą być w jakiś sposób skorelowane z próbkami y, zwykle zakładamy, że (x, y) są pobierane z pewnego rozkładu razem.

Bardziej intuicyjny sposób myślenia o tym, gdy trzeba ręcznie oznaczyć obraz dla klasy pies / kot - to całkiem proste. ale czy możesz ręcznie „otagować” cenę akcji, patrząc tylko na historię tej akcji?

To intuicja, jak trudny jest ten problem.

Uwaga na temat przeuczenia

Nie należy ścigać się z wyższą wydajnością treningu, ponieważ prawie bezużyteczne jest próbowanie przewyższenia danych treningowych, ponieważ zwykle staramy się osiągać dobre wyniki z modelem na nowych niewidzialnych danych o właściwościach podobnych do danych pociągu. wszystko polega na próbie uogólnienia i poznania właściwości danych i korelacji z celem, czyli uczenia się :)

ShmulikA
źródło