ニューラルネットワークが独自のトレーニングデータで誤った予測をする理由


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日前を振り返って、次のようにして最良(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日間を予測してみましょう(最初の例では、モデルを作成し、週末の休日を含めて4月17日から23日まで検証します。今度は4月2日から8日までテストします)。結果:

ここに画像の説明を入力してください

まったく間違った方向を示しています。これは、モデルがトレーニングされ、4月17日から23日の検証セットで5エポックを最もよく使用したためであり、2〜8日ではないためです。もっとトレーニングして、どのエポックを選択するかを試してみると、何をするにしても、過去には常に予測が間違っている時間間隔がたくさんあります。

モデルが独自のトレーニング済みデータで誤った結果を表示するのはなぜですか?私はデータをトレーニングしました。このセットのデータを予測する方法を覚えておく必要がありますが、間違って予測します。私も試しました:

  • 5万行以上、20年の株価の大規模なデータセットを使用し、多かれ少なかれ機能を追加する
  • 非表示レイヤーの追加、異なるbatch_sizes、異なるレイヤーのアクティブ化、ドロップアウト、batchnormalizationなど、さまざまなタイプのモデルを作成します
  • カスタムEarlyStoppingコールバックを作成し、多くの検証データセットから平均val_lossを取得して、最適なものを選択します

多分私は何かを逃す?何を改善できますか?

これは非常にシンプルで再現可能な例です。yfinanceS&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
問題は、株式市場からの予測可能性が高すぎることだけです。100万回のコインフリップのシーケンスでモデルをトレーニングしてから、コインフリップを予測させようとした場合、トレーニングデータからフリップが発生したとしても、モデルが間違っていることは驚くに値しません-モデルトレーニングデータを記憶して逆流することは想定されていません。
user2357112は、モニカ

2
@ user2357112supportsMonicaが言ったことに加えて、あなたのモデルは平均が正しいです、これは本当に私がこのようなモデルが本当に得ることを期待するすべてです(少なくとも一定の一貫性で)、そしてあなたは5日のうちの多くを期待していますデータ。モデルのエラーが何であるかを何らかの意味をもって言えるようにするには、本当にもっと多くのデータが必要です。
アーロン

モデルを調整するためのパラメーターは他にもたくさんあります。私は、早期停止(忍耐力= 20)、エポック数の増加、lstmユニットの増加(32から64など)のようなものをいくつか試しました。結果ははるかに優れていました。ここgithub.com/jvishnuvardhan/Stackoverflow_Questions/blob/master/…を確認してください。@sirjayが述べたように、機能の追加(現在は4つのみ)、レイヤーの追加(lstm、batchnorm、ドロップアウトなど)、ハイパーパラメーターの最適化を実行すると、パフォーマンスが大幅に向上します。
Vishnuvardhan Janapati

@VishnuvardhanJanapatiご確認ありがとうございます。私はあなたのコードをコンパイルし、モデルを保存してから設定しましたがdf.drop(df.tail(10).index, inplace=True)、それは私が持っていたのと同じ悪い結果を示しました。
サージェイ

回答:


4

OPは興味深い発見を仮定しています。元の質問を次のように簡略化しましょう。

モデルが特定の時系列でトレーニングされている場合、モデルはすでにトレーニングされている以前の時系列データを再構築できないのはなぜですか?

まあ、答えはトレーニングの進捗状況自体に埋め込まれています。以来EarlyStoppingオーバーフィッティングを避けるために、ここで使用され、最良のモデルがで保存されているepoch=5場合は、val_loss=0.0030OPで述べたように。この場合、トレーニング損失はに等しくなります0.0343。つまり、トレーニングのRMSEはになり0.185ます。データセットはを使用しMinMaxScalarてスケーリングされているため、何が起こっているのかを理解するために、RMSEのスケーリングを元に戻す必要があります。

時系列の最小値と最大値があることが判明している22903380。したがって、0.185トレーニングのRMSEとして持つことは、トレーニングセットの場合でも、予測値がグラウンドトゥルース値と約0.185*(3380-2290)、つまり~200平均単位で異なる場合があることを意味します。

これは、前のタイムステップでトレーニングデータ自体を予測するときに大きな違いがある理由を説明しています。

トレーニングデータを完全にエミュレートするにはどうすればよいですか?

私はこの質問を自分からしました。簡単な答えは、トレーニング損失をに近づける0、つまりモデルに適合しすぎていることです。

いくつかのトレーニングの後、32セルを含むLSTMレイヤーが1つだけのモデルは、トレーニングデータを再構築するほど複雑ではないことに気付きました。したがって、次のように別の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、あなたの場合の訓練損失よりもはるかに低い訓練損失があります。したがって、モデルがトレーニングデータをよりよく再構築することを期待します。以下は、4月2〜8日の予測プロットです。

予測

最後のメモ:

特定のデータベースでのトレーニングは、モデルがトレーニングデータを完全に再構築できる必要があることを必ずしも意味しません。特に、早期適合、正則化、ドロップアウトなどの方法を導入して過剰適合を回避する場合、モデルはトレーニングデータを記憶するよりも一般化する傾向があります。


2

モデルが独自のトレーニング済みデータで誤った結果を表示するのはなぜですか?私はデータをトレーニングしました。このセットのデータを予測する方法を覚えておく必要がありますが、間違って予測します。

記憶ではなく、モデルが入力と出力の関係を学習するようにします。モデルが各入力の正しい出力を記憶している場合、トレーニングデータの適合が過剰であると言えます。多くの場合、データの小さなサブセットを使用してモデルをオーバーフィットさせることができます。そのため、それが見たい動作であれば、それを試すことができます。


1

基本的に、トレーニングデータの結果を改善したい場合は、トレーニングの精度をできるだけ高くする必要があります。あなたはあなたが持っているデータに関してより良いモデルを使うべきです。基本的には、テストの精度に関係なく、この目的のためのトレーニングの精度を確認する必要があります。これはオーバーフィッティングとも呼ばれ、テストデータよりもトレーニングデータの精度が向上します。

このシナリオでは、トレーニングの精度ではなく、テスト/検証の精度が最も高くなるため、早期停止が影響する場合があります。


1

短い答え:

セットする:

batch_size = 1
epochs = 200
shuffle = False

直観:トレーニングデータの高精度の優先度について説明しています。これは過剰適合を説明しています。これを行うには、バッチサイズを1に設定し、エポックを高くして、シャッフルをオフにします。


1

容疑者#1-正則化

ニューラルネットワークはトレーニングデータの過剰適合に優れています。実際には、CIFAR10(画像分類タスク)ラベル(y値)をトレーニングデータセットのランダムラベルで置き換える実験があり、ネットワークはランダムラベルに適合して、ほぼゼロの損失をもたらします。

ここに画像の説明を入力してください

左側では、十分なエポックが与えられると、ランダムラベルがほぼ0の損失を得ることがわかります-完全なスコア(ディープラーニングの理解から、zhang et al 2016による一般化の再考が必要です

では、なぜそれが常に起こらないのでしょうか? 正則

正則化は、(大まかに)モデルに対して定義した最適化問題(損失)よりも難しい問題を解決しようとすることです。

ニューラルネットワークのいくつかの一般的な正則化方法:

  • 早期停止
  • 脱落
  • バッチ正規化
  • 重量減衰(例:l1 l2ノルム)
  • データ増強
  • ランダム/ガウスノイズを追加する

これらの方法は、過剰適合を減らし、通常は検証とテストのパフォーマンスを向上させますが、列車のパフォーマンスを低下させます(これは実際には前の段落で説明したように問題ではありません)。

通常、トレーニングデータのパフォーマンスはそれほど重要ではないため、検証セットを使用します。

容疑者#2-モデルのサイズ

32ユニットの単一LSTMレイヤーを使用しています。それはかなり小さいです。サイズを増やして、2つのLSTMレイヤー(または双方向レイヤー)を配置してみてください。そうすれば、モデルとオプティマイザーはデータをオーバーフィットさせます。つまり、早期停止、restore_last_weights、および上記で指定されたその他の正則化を削除します。

問題の複雑さに関するメモ

履歴を見ただけで将来の株価を予測しようとするのは簡単な作業ではありません。モデルがトレーニングセットに完全に(過剰に)適合できたとしても、テストセットや現実の世界では何も役に立たないでしょう。

MLは黒魔術ではありません。xサンプルは何らかの方法でyタグに関連付ける必要があります。通常、(x、y)は何らかの分布から一緒に抽出されていると想定しています。

直感的な方法で考えると、dog / catクラスの画像に手動でタグを付ける必要がある場合-非常に簡単です。しかし、その株価の履歴だけを見て、株価を手動で「タグ付け」できるでしょうか。

これは、この問題がいかに難しいかについての直感です。

オーバーフィッティングに関する注意

私たちは通常、トレーニングデータと同様のプロパティを持つ新しい目に見えないデータのモデルを適切に実行しようとするため、トレーニングデータのオーバーフィットを試すのにほとんど役に立たない、より高いトレーニングパフォーマンスを追跡するべきではありません。すべてのアイデアは、データのプロパティとターゲットとの相関関係を一般化して学習しようとすることです。


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. セットshuffle=False。データセットの順序を維持する必要があるので。

  3. セットbatch_size=1。過剰適合が少なくなり、学習がより騒々しくなり、エラーの平均が少なくなるためです。

  4. セットepochs=50以上。


上記の設定により、モデルは loss: 0.0037 - val_loss: 3.7329e-04

以下の予測サンプルを確認してください。

2020年4月17日-> 2020年4月23日:

ここに画像の説明を入力してください

2020年2月4日-> 2020年4月8日:

ここに画像の説明を入力してください

2020年3月25日-> 2020年3月31日から:

ここに画像の説明を入力してください


0

モデルが独自のトレーニング済みデータで誤った結果を表示するのはなぜですか?私はデータをトレーニングしました。このセットのデータを予測する方法を覚えておく必要がありますが、間違って予測します。

何してるの?

  1. いくつかのレイヤーを持つモデルを構築する
  2. training_dataを使用したトレーニングモデル
  3. モデルをトレーニングすると、すべてのトレーニング可能なパラメーターがトレーニングされます(つまり、モデルの重みが保存されます)。
  4. これらの重みは、入力と出力の関係を表します。
  5. 同じtraining_dataを再度予測すると、今回のトレーニング済みモデルは、重みを使用して出力を取得します。
  6. モデルの品質によって予測が決定されるため、データが同じであっても元の結果とは異なります。

0

それは不十分であり、あなたが隠れ層にニューロンを追加する必要があることを改善するために!! もう一つのポイントは、アクティベーション機能「relu」を試してみることです。シグモイドは良い結果を与えません。また、出力層で「softmax」を定義する必要があります。


市場を予測する秘訣があるようですね。彼は他に何をすべきですか?
ダニエルスコット

2
softmaxは分類用であり、回帰問題です。
ShmulikA

@DanielScottわかりませんか?真下(数十億のレイヤー)は、利益か損失かを決定する分類問題です。時系列の予測を気にするのはなぜですか?
Sowmya

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 + Bagging + Extra Trees + Gradient Boosting + Random Forest + XGB

強化学習エージェントは、在庫予測で次のように非常に優れています。

  1. カメの取引業者
  2. 移動平均エージェント
  3. 信号ローリングエージェント
  4. ポリシー勾配エージェント
  5. Qラーニングエージェント
  6. 進化戦略エージェント

ここで非常に機知に富んだリンクを見つけてください。


adamにはこれらの特性もありますが、実際にはadamはadagradのある種の進化です
ShmulikA
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.