Ich habe ein neuronales LSTM (RNN) -Netzwerk mit überwachtem Lernen zur Vorhersage des Datenbestands erstellt. Das Problem ist, warum es auf seinen eigenen Trainingsdaten falsch vorhersagt? (Hinweis: reproduzierbares Beispiel unten)
Ich habe ein einfaches Modell erstellt, um den Aktienkurs der nächsten 5 Tage vorherzusagen:
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])
Die korrekten Ergebnisse sind in y_test
(5 Werte), also Modelleisenbahnen, die 90 Tage zurückblicken und dann die Gewichte aus dem besten ( val_loss=0.0030
) Ergebnis wiederherstellen mit 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
Das Vorhersageergebnis ist ziemlich beeindruckend, nicht wahr?
Das liegt daran, dass der Algorithmus die besten Gewichte aus der 5. Epoche wiederhergestellt hat. Okey, speichern wir dieses Modell jetzt in einer .h5
Datei, verschieben uns -10 Tage zurück und sagen die letzten 5 Tage voraus (im ersten Beispiel haben wir das Modell erstellt und am 17. und 23. April einschließlich der freien Wochenenden validiert, jetzt testen wir am 2. bis 8. April). Ergebnis:
Es zeigt absolut falsche Richtung. Wie wir sehen, liegt das daran, dass das Modell trainiert wurde und am 17. und 23. April die beste Epoche Nr. 5 für die Validierung erreichte, jedoch nicht am 2. und 8. April. Wenn ich versuche, mehr zu trainieren und mit der Epoche zu spielen, die ich wählen möchte, was auch immer ich tue, gibt es in der Vergangenheit immer viele Zeitintervalle, die falsche Vorhersagen haben.
Warum zeigt das Modell anhand seiner eigenen trainierten Daten falsche Ergebnisse? Ich habe Daten trainiert, es muss sich daran erinnern, wie man Daten auf diesem Satz vorhersagt, sagt aber falsch voraus. Was ich auch versucht habe:
- Verwenden Sie große Datenmengen mit mehr als 50.000 Zeilen und Aktienkursen für 20 Jahre, und fügen Sie mehr oder weniger Funktionen hinzu
- Erstellen Sie verschiedene Modelltypen, z. B. das Hinzufügen weiterer versteckter Ebenen, verschiedener Batch-Größen, verschiedener Ebenenaktivierungen, Aussetzer und Batch-Normalisierung
- Erstellen Sie einen benutzerdefinierten EarlyStopping-Rückruf, erhalten Sie den durchschnittlichen Wertverlust aus vielen Validierungsdatensätzen und wählen Sie den besten aus
Vielleicht vermisse ich etwas? Was kann ich verbessern?
Hier ist ein sehr einfaches und reproduzierbares Beispiel. yfinance
lädt S & P 500 Bestandsdaten herunter.
"""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)
, es zeigte das gleiche schlechte Ergebnis wie ich.Antworten:
Das OP postuliert einen interessanten Befund. Lassen Sie mich die ursprüngliche Frage wie folgt vereinfachen.
Wenn das Modell für eine bestimmte Zeitreihe trainiert wird, warum kann das Modell dann keine früheren Zeitreihendaten rekonstruieren, für die es bereits trainiert wurde?
Nun, die Antwort ist in den Trainingsfortschritt selbst eingebettet. Da
EarlyStopping
hier verwendet wird, um eine Überanpassung zu vermeiden, wird das beste Modell dort gespeichertepoch=5
, wo diesval_loss=0.0030
vom OP erwähnt wird. In diesem Fall ist der Trainingsverlust gleich0.0343
dem RMSE des Trainings0.185
. Da das Dataset mit skaliert wirdMinMaxScalar
, müssen wir die Skalierung von RMSE rückgängig machen, um zu verstehen, was los ist.Die Minimal- und Maximalwerte der Zeitfolge sind
2290
und3380
. Daher0.185
bedeutet RMSE des Trainings, dass selbst für den Trainingssatz die vorhergesagten Werte von den Grundwahrheitswerten um ungefähr0.185*(3380-2290)
, dh~200
Einheiten im Durchschnitt, abweichen können .Dies erklärt, warum es einen großen Unterschied gibt, wenn die Trainingsdaten selbst zu einem früheren Zeitpunkt vorhergesagt werden.
Was muss ich tun, um Trainingsdaten perfekt zu emulieren?
Ich habe mir diese Frage gestellt. Die einfache Antwort lautet: Machen Sie den Trainingsverlust näher
0
, das passt zum Modell.Nach einigem Training stellte ich fest, dass ein Modell mit nur 1 LSTM-Schicht mit
32
Zellen nicht komplex genug ist, um die Trainingsdaten zu rekonstruieren. Daher habe ich eine weitere LSTM-Schicht wie folgt hinzugefügt.Und das Modell ist für
1000
Epochen ohne Rücksichtnahme trainiertEarlyStopping
.Am Ende der
1000
Epoche haben wir einen Trainingsverlust, der0.00047
viel geringer ist als der Trainingsverlust in Ihrem Fall. Wir würden also erwarten, dass das Modell die Trainingsdaten besser rekonstruiert. Es folgt das Vorhersage-Diagramm für den 2. bis 8. April.Ein letzter Hinweis:
Das Training in einer bestimmten Datenbank bedeutet nicht unbedingt, dass das Modell die Trainingsdaten perfekt rekonstruieren kann. Insbesondere wenn Methoden wie frühes Stoppen, Regularisieren und Abbrechen eingeführt werden, um eine Überanpassung zu vermeiden, ist das Modell eher verallgemeinerbar als das Speichern von Trainingsdaten.
quelle
Sie möchten, dass das Modell die Beziehung zwischen Eingabe und Ausgabe lernt, anstatt sich zu merken. Wenn sich ein Modell für jede Eingabe die richtige Ausgabe merkt, können wir sagen, dass die Trainingsdaten zu hoch sind. Oft können Sie die Überanpassung des Modells erzwingen, indem Sie eine kleine Teilmenge der Daten verwenden. Wenn dies also das Verhalten ist, das Sie sehen möchten, können Sie dies versuchen.
quelle
Grundsätzlich Wenn Sie bessere Ergebnisse für Trainingsdaten erzielen möchten, sollte Ihre Trainingsgenauigkeit so hoch wie möglich sein. Sie sollten ein besseres Modell in Bezug auf Ihre Daten verwenden. Grundsätzlich sollten Sie unabhängig von der Testgenauigkeit prüfen, ob Ihre Trainingsgenauigkeit für diesen Zweck. Dies wird auch als Überanpassung bezeichnet, die eine bessere Genauigkeit der Trainingsdaten als der Testdaten bietet.
Ein vorzeitiges Anhalten kann sich auf dieses Szenario auswirken, in dem die beste Test- / Validierungsgenauigkeit anstelle der Trainingsgenauigkeit verwendet wird.
quelle
Die kurze Antwort:
Einstellen:
Intuition: Sie beschreiben die Priorität einer hohen Genauigkeit in den Trainingsdaten. Dies beschreibt eine Überanpassung. Stellen Sie dazu die Stapelgröße auf 1, die Epochen hoch und mischen Sie ab.
quelle
Verdächtiger Nr. 1 - Regularisierung
Neuronale Netze passen die Trainingsdaten hervorragend an. Tatsächlich gibt es ein Experiment, bei dem CIFAR10-Beschriftungen (y-Werte) durch zufällige Beschriftungen im Trainingsdatensatz ersetzt werden, und das Netzwerk passt zu den zufälligen Beschriftungen, was zu einem Verlust von nahezu Null führt.
Warum passiert es nicht die ganze Zeit? Regularisierung .
Die Regularisierung versucht (grob), ein schwierigeres Problem zu lösen als das Optimierungsproblem (den Verlust), das wir für das Modell definiert haben.
Einige gängige Regularisierungsmethoden in neuronalen Netzen:
Diese Methoden tragen zur Reduzierung der Überanpassung bei und führen normalerweise zu einer besseren Validierung und Testleistung, jedoch zu einer geringeren Zugleistung (was eigentlich nicht wichtig ist, wie im letzten Absatz erläutert).
Die Leistung der Zugdaten ist normalerweise nicht so wichtig, und dafür verwenden wir den Validierungssatz.
Verdächtiger Nr. 2 - Modellgröße
Sie verwenden eine einzelne LSTM-Schicht mit 32 Einheiten. das ist ziemlich klein. Versuchen Sie, die Größe zu erhöhen, und fügen Sie sogar zwei LSTM-Ebenen (oder eine bidirektionale) ein. Ich bin sicher, dass das Modell und der Optimierer Ihre Daten überanpassen, solange Sie dies zulassen - dh entfernen Sie das vorzeitige Anhalten, restore_last_weights und alle anderen oben angegebenen Regularisierungen.
Hinweis zur Problemkomplexität
Der Versuch, zukünftige Aktienkurse nur anhand der Historie vorherzusagen, ist keine leichte Aufgabe, und selbst wenn das Modell (über) perfekt zum Trainingssatz passt, wird es auf dem Testsatz oder in der realen Welt wahrscheinlich nichts Nützliches bewirken.
ML ist keine schwarze Magie, die x-Samples müssen in irgendeiner Weise mit den y-Tags korreliert werden. Wir gehen normalerweise davon aus, dass (x, y) aus einer Verteilung zusammen gezogen werden.
Eine intuitivere Art, darüber nachzudenken, wenn Sie ein Bild manuell für die Hunde- / Katzenklasse markieren müssen - das ist ziemlich einfach. Aber können Sie den Aktienkurs manuell "markieren", indem Sie nur die Geschichte dieser Aktie betrachten?
Das ist eine gewisse Intuition darüber, wie schwer dieses Problem ist.
Hinweis zur Überanpassung
Man sollte keine höhere Trainingsleistung verfolgen, es ist fast nutzlos, zu versuchen, die Trainingsdaten zu überanpassen, da wir normalerweise versuchen, mit einem Modell für neue unsichtbare Daten mit ähnlichen Eigenschaften wie die Zugdaten eine gute Leistung zu erzielen. Die Idee ist, zu versuchen, die Eigenschaften der Daten und die Korrelation mit dem Ziel zu verallgemeinern und zu lernen. Das ist Lernen :)
quelle
Wie andere bereits gesagt haben, sollte man davon nicht viel erwarten.
Trotzdem habe ich in Ihrem Code Folgendes gefunden:
Sie passen den Scaler jedes Mal während des Trainings und Testens neu an. Sie müssen den Sacler speichern und nur während des Tests Daten transformieren. Andernfalls unterscheiden sich die Ergebnisse geringfügig:
Stellen Sie ein
shuffle=False
. Da müssen Sie die Reihenfolge Ihres Datensatzes beibehalten.Stellen Sie ein
batch_size=1
. Da es weniger anfällig für Überanpassungen ist und das Lernen lauter und der Fehler weniger gemittelt wird.Set
epochs=50
oder mehr.Mit den oben genannten Einstellungen wurde das Modell erreicht
loss: 0.0037 - val_loss: 3.7329e-04
.Überprüfen Sie die folgenden Vorhersagebeispiele:
Vom 17/04/2020 -> 23/04/2020:
Vom 02/04/2020 -> 08/04/2020:
Vom 25/03/2020 -> 31/03/2020:
quelle
Schau was machst du:
quelle
Es ist unterpassend und um das zu verbessern, müssen Sie Neuronen in Ihre verborgenen Schichten einfügen. !! Ein weiterer Punkt ist die Aktivierungsfunktion 'relu'. Sigmoid liefert keine guten Ergebnisse. Außerdem müssen Sie 'softmax' in Ihrer Ausgabeebene definieren.!
quelle
Nachdem ich die Modellarchitektur und den Optimierer auf Adagrad geändert hatte, konnte ich die Ergebnisse in gewissem Maße verbessern.
Grund für die Verwendung des Adagrad-Optimierers ist hier:
Es passt die Lernrate an die Parameter an und führt kleinere Aktualisierungen (dh niedrige Lernraten) für Parameter durch, die häufig auftretenden Merkmalen zugeordnet sind, und größere Aktualisierungen (dh hohe Lernraten) für Parameter, die seltenen Merkmalen zugeordnet sind. Aus diesem Grund eignet es sich gut für den Umgang mit spärlichen Daten.
Bitte beachten Sie den folgenden Code :
Die Aktienvorhersage ist eine sehr herausfordernde Aufgabe. Anstatt an der Vorhersage eines einzelnen Modells festzuhalten, können mehrere Modelle zusammenarbeiten, um eine Vorhersage zu erstellen, und dann basierend auf dem maximal abgestimmten Ergebnis den Anruf annehmen, ähnlich wie bei einem Ensemble-Lernansatz. Außerdem können wir einige Modelle wie folgt stapeln:
Deep Feed-Forward Auto-Encoder Neuronales Netzwerk zur Reduzierung der Dimension + Deep Recurrent Neural Network + ARIMA + Extreme Boosting Gradient Regressor
Adaboost + Absacken + Extra Bäume + Gradient Boosting + Random Forest + XGB
Verstärkungslernagenten schneiden in der Aktienprognose ziemlich gut ab, wie:
Hier finden Sie eine sehr einfalls Link hier .
quelle