web-dev-qa-db-fra.com

Pourquoi le réseau de neurones prédit-il mal sur ses propres données d'entraînement?

J'ai créé un réseau neuronal LSTM (RNN) avec apprentissage supervisé pour la prédiction des stocks de données. Le problème est pourquoi il prédit mal sur ses propres données d'entraînement? (note: exemple reproductible ci-dessous)

J'ai créé un modèle simple pour prédire le cours de l'action dans les 5 prochains jours:

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

Les résultats corrects sont dans y_test (5 valeurs), donc modélisez les trains, en regardant en arrière 90 jours précédents, puis restaurez les poids à partir du meilleur résultat (val_loss=0.0030) Avec 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

Le résultat de la prédiction est plutôt génial, n'est-ce pas?

enter image description here

C'est parce que l'algorithme a restauré les meilleurs poids de l'époque n ° 5. Okey, sauvegardons maintenant ce modèle dans le fichier .h5, Reculons de -10 jours et prédisons les 5 derniers jours (dans le premier exemple, nous avons créé un modèle et validé les 17-23 avril y compris les jours de repos, testons maintenant le 2 -8 avril). Résultat:

enter image description here

Cela montre une direction absolument fausse. Comme nous le voyons, c'est parce que le modèle a été formé et a pris le meilleur temps pour la validation n ° 5 du 17 au 23 avril, mais pas du 2 au 8. Si j'essaie de m'entraîner davantage, en jouant avec quelle époque choisir, quoi que je fasse, il y a toujours beaucoup d'intervalles de temps dans le passé qui ont de mauvaises prédictions.

Pourquoi le modèle affiche-t-il des résultats erronés sur ses propres données entraînées? J'ai formé des données, il faut se souvenir comment prédire les données sur cet ensemble, mais prédit mal. Ce que j'ai aussi essayé:

  • Utilisez de grands ensembles de données avec plus de 50 000 lignes, des cours boursiers sur 20 ans, en ajoutant plus ou moins de fonctionnalités
  • Créez différents types de modèles, comme l'ajout de couches cachées, différentes tailles de lots, différentes activations de couches, abandons, normalisation par lots
  • Créez un rappel EarlyStopping personnalisé, obtenez une val_loss moyenne à partir de nombreux ensembles de données de validation et choisissez le meilleur

Peut-être que je rate quelque chose? Que puis-je améliorer?

Voici un exemple très simple et reproductible . yfinance télécharge les données boursières du 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()
23
sirjay

Après avoir changé l'architecture du modèle et l'optimiseur en Adagrad, j'ai pu améliorer les résultats dans une certaine mesure.

La raison d'utiliser l'optimiseur Adagrad ici est:

Il adapte le taux d'apprentissage aux paramètres, en effectuant des mises à jour plus petites (c'est-à-dire des taux d'apprentissage faibles) pour les paramètres associés à des fonctionnalités fréquentes, et des mises à jour plus importantes (c'est-à-dire des taux d'apprentissage élevés) pour les paramètres associés à des fonctionnalités peu fréquentes. Pour cette raison, il est bien adapté pour traiter des données rares.

Veuillez vous référer au code ci-dessous:

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

La prédiction de stock est une tâche très difficile, donc plutôt que de nous en tenir à la prédiction d'un seul modèle, nous pouvons avoir plusieurs modèles travaillant ensemble pour faire une prédiction, puis en fonction du résultat maximal voté, prendre l'appel, similaire à une approche d'apprentissage d'ensemble. En outre, nous pouvons empiler quelques modèles ensemble comme:

  1. Réseau neuronal à codeur automatique à anticipation profonde pour réduire la dimension + Réseau neuronal récurrent profond + ARIMA + Régresseur de gradient à amplification extrême

  2. Adaboost + Ensachage + Arbres supplémentaires + Boosting de gradient + Forêt aléatoire + XGB

Les agents d'apprentissage par renforcement se débrouillent plutôt bien dans la prévision des actions comme:

  1. Agent commercial de tortues
  2. Agent de moyenne mobile
  3. Agent d'enroulement de signal
  4. Agent de gradient de politique
  5. Agent Q-learning
  6. Agent de stratégie d'évolution

Veuillez trouver un lien très ingénieux ici .

2
Rishab P.

Pourquoi le modèle affiche-t-il des résultats erronés sur ses propres données entraînées? J'ai formé des données, il faut se rappeler comment prédire les données sur cet ensemble, mais prédit mal.

Vous voulez que le modèle apprenne la relation entre l'entrée et la sortie au lieu de la mémorisation. Si un modèle mémorise la sortie correcte pour chaque entrée, nous pouvons dire qu'il est sur-ajusté les données d'apprentissage. Souvent, vous pouvez forcer le modèle à se surajuster en utilisant un petit sous-ensemble de données, donc si c'est le comportement que vous voulez voir, vous pouvez essayer cela.

2
tensordude

La réponse courte:

Ensemble:

batch_size = 1
epochs = 200
shuffle = False

Intuition: vous décrivez la priorité de la haute précision dans les données d'entraînement. Ceci décrit le surajustement. Pour ce faire, définissez la taille du lot sur 1, les époques élevées et la lecture aléatoire.

1
Daniel Scott

Fondamentalement, si vous souhaitez obtenir de meilleurs résultats pour les données d'entraînement, la précision de votre entraînement doit être aussi élevée que possible. Vous devriez utiliser un meilleur modèle en ce qui concerne les données dont vous disposez. Fondamentalement, vous devez vérifier si la précision de votre entraînement à cette fin, quelle que soit la précision du test. Ceci est également appelé surajustement, ce qui donne une meilleure précision des données d'entraînement plutôt que des données de test.

Un arrêt prématuré peut avoir une incidence sur ce scénario où la meilleure précision de test/validation est prise plutôt que la précision d'entraînement.

1
Demotte

C'est mal ajusté et pour améliorer cela, je pense que vous devez ajouter des neurones dans vos couches cachées. !! Un autre point est d'essayer la fonction d'activation "relu". Sigmoid ne donne pas de bons résultats. Vous devez également définir "softmax" dans votre couche de sortie.!

0
Rahul Anand