为什么神经网络会根据自己的训练数据预测错误?


12

我制作了带有监督学习的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时代恢复了最佳权重。Okey,让我们现在将该模型保存到.h5文件中,移回-10天并预测最后5天(在第一个示例中,我们在4月17日至23日(包括休息日)制作了模型并进行了验证,现在让我们在4月2日至8日进行测试)。结果:

在此处输入图片说明

它显示了绝对错误的方向。如我们所见,这是因为模型经过训练,最适合验证的时间是#5纪元,时间是4月17日至23日,而不是2月8日。如果我尝试进行更多的训练,尝试选择哪个纪元,无论我做什么,过去总是会有很多错误的预测时间间隔。

为什么模型在其训练有素的数据上显示错误的结果?我训练数据时,必须记住如何在这组数据上预测数据,但是预测错误。我还尝试了什么:

  • 使用行数超过5万,股价20年的大型数据集,添加或多或少的功能
  • 创建不同类型的模型,例如添加更多的隐藏层,不同的batch_size,不同的层激活,退出,批量归一化
  • 创建自定义的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
如今,可重现的示例如此罕见(与没有类似问题的公告相比),可以说是在您的帖子开始时宣传其存在的好主意(已添加;);
Desertnaut

7
问题可能只是您期望股票市场有太多可预测性。如果您以一百万次硬币翻转的序列训练模型,然后尝试使其预测硬币翻转,那么即使模型翻转来自训练数据,该模型也会出错,这并不奇怪。不应记住其训练数据并反省它。
user2357112支持Monica

2
除了@ user2357112supportsMonica所说的之外,您的模型还算是对的,这实际上是我希望这样的模型能够真正实现的所有结果(至少具有任何一致性),并且您期望在5天内数据。您确实需要更多的数据才能有意义地说明模型中的错误是什么。
亚伦

还有更多参数可以调整模型。我尝试了其中的一些操作,例如提前停止(耐心= 20),增加次数,将lstm单位从32增加到64等。结果要好得多。在这里检查github.com/jvishnuvardhan/Stackoverflow_Questions/blob/master/…。正如@sirjay提到的,添加更多功能(当前仅4个),添加更多层(lstm,batchnorm,dropout等),运行超参数优化将带来更好的性能。
Vishnuvardhan Janapati

@VishnuvardhanJanapati感谢您的检查。我编译了代码,保存了模型,然后设置df.drop(df.tail(10).index, inplace=True),结果显示出与我相同的不良结果。
sirjay,

Answers:


3

为什么模型在其训练有素的数据上显示错误的结果?我训练数据时,必须记住如何在这组数据上预测数据,但是预测错误。

您希望模型学习输入和输出之间的关系,而不是记忆。如果模型能够记住每个输入的正确输出,那么我们可以说它过度适合训练数据。通常,您可以使用一小部分数据来强制模型过度拟合,因此如果您希望看到这种行为,可以尝试一下。


1

基本上,如果您想获得更好的训练数据结果,则训练精度应尽可能高。您应该针对所拥有的数据使用更好的模型。基本上,无论测试精度如何,您都应检查为此目的的训练精度。这也称为过拟合,它在训练数据而非测试数据中提供了更高的准确性。

对于采用最佳测试/验证准确性而非培训准确性的情况,提前停止可能会受到影响。


1

简短的答案:

组:

batch_size = 1
epochs = 200
shuffle = False

直觉:您正在描述训练数据中高精度的优先级。这是描述过度拟合。为此,将批处理大小设置为1,历时较大,然后改组。


1

OP提出了一个有趣的发现。让我将原始问题简化如下。

如果模型是在特定时间序列上进行训练的,那么为什么模型不能重建已经对其进行训练的先前时间序列数据?

好吧,答案就在于培训进度本身。由于EarlyStopping此处使用它来避免过度拟合,因此最佳模型保存在epoch=5val_loss=0.0030如OP所述。在这种情况下,训练损失等于0.0343,即训练的RMSE为0.185。由于使用来缩放数据集MinMaxScalar,因此我们需要撤消RMSE的缩放,以了解发生了什么。

发现时间序列的最小值和最大值为22903380。因此,具有0.185训练的RMSE意味着即使对于训练集,预测值也可能与地面真实值相差大约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远低于您的情况下的培训损失。因此,我们希望模型能够更好地重建训练数据。以下是4月2日至8日的预测图。

预测

最后说明:

在特定数据库上进行训练并不一定意味着该模型应该能够完美地重建训练数据。特别是,当引入诸如提前停止,正则化和辍学之类的方法以避免过度拟合时,该模型倾向于更具通用性,而不是记住训练数据。


0

为什么模型在其训练有素的数据上显示错误的结果?我训练数据时,必须记住如何在这组数据上预测数据,但是预测错误。

看你在做什么:

  1. 建立具有某些层的模型
  2. 使用training_data的训练模型
  3. 训练模型时,所有可训练的参数都会被训练(即,模型的权重已保存)
  4. 这些权重现在表示输入和输出之间的关系。
  5. 当您再次预测相同的training_data时,这次经过训练的模型将使用权重来获取输出。
  6. 现在,模型的质量决定了预测,因此即使数据相同,预测也不同于原始结果。

0

这很不合适,要改善这一点,我需要将神经元添加到隐藏层中!!另一点是尝试激活功能“ relu”。Sigmoid效果不佳。另外,您需要在输出层中定义“ softmax”。


听起来您掌握了预测市场的秘密。他还应该做什么?
Daniel Scott
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.