为啥用于预测的 Keras LSTM 批量大小必须与拟合批量大小相同?

Posted

技术标签:

【中文标题】为啥用于预测的 Keras LSTM 批量大小必须与拟合批量大小相同?【英文标题】:Why does Keras LSTM batch size used for prediction have to be the same as fitting batch size?为什么用于预测的 Keras LSTM 批量大小必须与拟合批量大小相同? 【发布时间】:2017-09-27 21:17:41 【问题描述】:

当我使用 Keras LSTM 预测时间序列数据时,我在尝试使用 50 的批量大小训练模型时遇到错误,然后尝试使用 50 的批量大小对同一模型进行预测1(即只是预测下一个值)。

为什么我不能一次用多个批次训练和拟合模型,然后使用该模型预测除相同批次大小之外的任何内容。这似乎没有意义,但我很容易错过一些关于此的内容。

编辑:这是模型。 batch_size为50,sl为序列长度,目前设置为20。

    model = Sequential()
    model.add(LSTM(1, batch_input_shape=(batch_size, 1, sl), stateful=True))
    model.add(Dense(1))
    model.compile(loss='mean_squared_error', optimizer='adam')
    model.fit(trainX, trainY, epochs=epochs, batch_size=batch_size, verbose=2)

这是用于预测 RMSE 训练集的行

    # make predictions
    trainPredict = model.predict(trainX, batch_size=batch_size)

这是看不见的时间步长的实际预测

for i in range(test_len):
    print('Prediction %s: ' % str(pred_count))

    next_pred_res = np.reshape(next_pred, (next_pred.shape[1], 1, next_pred.shape[0]))
    # make predictions
    forecastPredict = model.predict(next_pred_res, batch_size=1)
    forecastPredictInv = scaler.inverse_transform(forecastPredict)
    forecasts.append(forecastPredictInv)
    next_pred = next_pred[1:]
    next_pred = np.concatenate([next_pred, forecastPredict])

    pred_count += 1

此问题与以下行有关:

forecastPredict = model.predict(next_pred_res, batch_size=batch_size)

这里batch_size设置为1时的错误是:

ValueError: Cannot feed value of shape (1, 1, 2) for Tensor 'lstm_1_input:0', which has shape '(10, 1, 2)' 与此处的 batch_size 设置为 50 时抛出的错误相同,就像其他批量大小一样。

总误差为:

    forecastPredict = model.predict(next_pred_res, batch_size=1)
  File "/home/entelechy/tf_keras/lib/python3.5/site-packages/keras/models.py", line 899, in predict
    return self.model.predict(x, batch_size=batch_size, verbose=verbose)
  File "/home/entelechy/tf_keras/lib/python3.5/site-packages/keras/engine/training.py", line 1573, in predict
    batch_size=batch_size, verbose=verbose)
   File "/home/entelechy/tf_keras/lib/python3.5/site-packages/keras/engine/training.py", line 1203, in _predict_loop
    batch_outs = f(ins_batch)
  File "/home/entelechy/tf_keras/lib/python3.5/site-packages/keras/backend/tensorflow_backend.py", line 2103, in __call__
    feed_dict=feed_dict)
  File "/home/entelechy/tf_keras/lib/python3.5/site-packages/tensorflow/python/client/session.py", line 767, in run
    run_metadata_ptr)
  File "/home/entelechy/tf_keras/lib/python3.5/site-packages/tensorflow/python/client/session.py", line 944, in _run
    % (np_val.shape, subfeed_t.name, str(subfeed_t.get_shape())))
ValueError: Cannot feed value of shape (1, 1, 2) for Tensor 'lstm_1_input:0', which has shape '(10, 1, 2)'

编辑:一旦我将模型设置为stateful=False,我就可以使用不同的批量大小进行拟合/训练和预测。这是什么原因?

【问题讨论】:

批量大小与您描述的不一样,请添加详细信息和代码。 添加错误及相关代码 如果您要传递大小为 1 的批次,请确保它仍然是一个与训练数组具有完全相同形状的数组,除了 50 变为 1。确保您没有丢失一维仅将 (50, 1, sl) 转换为 (1,sl)。应该保留 (1,1,sl)。 @Daniel 嘿,谢谢您的回复。是的,我已经通过它检查了正确的维度,据我所知,我认为这一切都很好。错误消息是 ValueError: Cannot feed value of shape (1, 1, 2) for Tensor 'lstm_1_input:0',其形状为 '(10, 1, 2)' 无论如何 我也遇到过类似的问题。但我发现这个链接:machinelearningmastery.com/… 真的很有帮助。救命.... 【参考方案1】:

不幸的是,Keras 无法实现您想要做的事情......我也在这个问题上挣扎了很多时间,唯一的方法是潜入兔子洞并直接使用 Tensorflow 进行 LSTM 滚动预测。

首先,要明确术语,batch_size 通常表示一起训练的序列数,num_steps 表示一起训练的时间步数。当您的意思是 batch_size=1 和“只是预测下一个值”时,我认为您的意思是使用 num_steps=1 进行预测。

否则,应该可以使用batch_size=50 进行训练和预测,这意味着您正在训练 50 个序列并在每个时间步进行 50 个预测,每个序列一个(意味着训练/预测 num_steps=1)。

但是,我认为您的意思是您想使用有状态 LSTM 来训练 num_steps=50 并使用 num_steps=1 进行预测。从理论上讲,这是有道理的,应该是可能的,而且使用 Tensorflow 是可能的,只是 Keras 不行。

问题:Keras 需要有状态 RNN 的明确批量大小。您必须指定 batch_input_shape (batch_size, num_steps, features)。

原因:Keras 必须在计算图中分配一个固定大小的隐藏状态向量,形状为 (batch_size, num_units),以便在训练批次之间保持值。另一方面,当stateful=False 时,隐藏状态向量可以在每批开始时用零动态初始化,因此它不需要是固定大小。更多细节在这里:http://philipperemy.github.io/keras-stateful-lstm/

可能的解决方法:使用num_steps=1 进行训练和预测。示例:https://github.com/keras-team/keras/blob/master/examples/lstm_stateful.py。这可能对您的问题完全有效,也可能根本无效,因为反向传播的梯度将仅在一个时间步上计算。见:https://github.com/fchollet/keras/issues/3669

我的解决方案:使用 Tensorflow:在 Tensorflow 中,您可以使用 batch_size=50, num_steps=100 进行训练,然后使用 batch_size=1, num_steps=1 进行预测。这可以通过为训练和预测创建不同的模型图来共享相同的 RNN 权重矩阵来实现。有关下一个字符预测,请参阅此示例:https://github.com/sherjilozair/char-rnn-tensorflow/blob/master/model.py#L11 和博客文章 http://karpathy.github.io/2015/05/21/rnn-effectiveness/。请注意,一张图仍然只能与一个指定的 batch_size 一起使用,但您可以在 Tensorflow 中设置多个共享权重的模型图。

【讨论】:

您好,非常感谢您的回复。你能再解释一下batch_sizenum_steps之间的区别吗?我从未真正使用或见过num_step,我认为batch_size 就是同时训练了多少个窗口。序列和时间步有什么区别? 对于 RNN 模型,输入通常是 3D 张量(batch_size, num_steps, num_features),这意味着您在同一个批次中训练多个序列,每个序列的长度为 num_steps,每个 num 步的长度为 num_features。跨度> 这是一个完整的short example,介绍了在使用与训练时不同的batch_sizenum_steps 时如何使用LSTM 单元恢复张量流模型。 是什么让你认为作者的意思是num_steps 而不是batch_size?虽然您的答案是相关的并且有些有用,但我认为您做出了错误的假设。如果我没有误读的话,他们清楚地谈到了各种batch_size,而不是num_step(他们称之为sl)。其他答案似乎为作者描述的特定问题提供了更好的解决方案。不过,感谢您努力写出如此详细的答案! @Hai-AnhTrinh 你有使用张量流计算图实现 rnn 的博客链接吗?【参考方案2】:

遗憾的是,您希望的结果是不可能的,因为您在定义模型时指定了 batch_size... 但是,我找到了解决这个问题的简单方法:创建 2 个模型!第一个用于训练,第二个用于预测,并让它们共享权重:

train_model = Sequential([Input(batch_input_shape=(batch_size,...),
<continue specifying your model>])

predict_model = Sequential([Input(batch_input_shape=(1,...),
<continue specifying exact same model>])

train_model.compile(loss='sparse_categorical_crossentropy', optimizer=Adam())
predict_model.compile(loss='sparse_categorical_crossentropy', optimizer=Adam())

现在您可以使用任何您想要的批量大小。在适合您的 train_model 后,只需保存它的权重并使用 predict_model 加载它们:

train_model.save_weights('lstm_model.h5')
predict_model.load_weights('lstm_model.h5')

请注意,您只想保存和加载权重,而不是整个模型(包括架构、优化器等...)。这样你就可以得到权重,但你可以一次输入一批...... 更多关于 keras 保存/加载模型: https://keras.io/getting-started/faq/#how-can-i-save-a-keras-model

请注意,您需要安装 h5py 才能使用“保存权重”。

【讨论】:

Oren 我要试试这个,看起来是个不错的解决方案! 谢谢你。 对我不起作用。 ValueError: Tensor("Placeholder:0", shape=(4, 24), dtype=float32) 必须与 Tensor("l1_1/kernel:0", shape=(4, 24), dtype=float32_ref 来自同一图表)。【参考方案3】:

另一个简单的解决方法是:

def create_model(batch_size):
    model = Sequential()
    model.add(LSTM(1, batch_input_shape=(batch_size, 1, sl), stateful=True))
    model.add(Dense(1))
    return model

model_train = create_model(batch_size=50)

model_train.compile(loss='mean_squared_error', optimizer='adam')
model_train.fit(trainX, trainY, epochs=epochs, batch_size=batch_size)

model_predict = create_model(batch_size=1)

weights = model_train.get_weights()
model_predict.set_weights(weights)

【讨论】:

【参考方案4】:

我发现以下内容很有帮助(并且完全符合以上内容)。 “解决方案 3:复制权重”部分对我有用:

How to use Different Batch Sizes when Training and Predicting with LSTMs, by Jason Brownlee

n_neurons = 10
# design network
model = Sequential()
model.add(LSTM(n_neurons, batch_input_shape=(n_batch, X.shape[1], X.shape[2]), stateful=True))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
# fit network
for i in range(n_epoch):
    model.fit(X, y, epochs=1, batch_size=n_batch, verbose=1, shuffle=False)
    model.reset_states()
# re-define the batch size
n_batch = 1
# re-define model
new_model = Sequential()
new_model.add(LSTM(n_neurons, batch_input_shape=(n_batch, X.shape[1], X.shape[2]), stateful=True))
new_model.add(Dense(1))
# copy weights
old_weights = model.get_weights()
new_model.set_weights(old_weights)
# compile model
new_model.compile(loss='mean_squared_error', optimizer='adam')

【讨论】:

【参考方案5】:

我也有同样的问题并解决了

另一种方式,你可以保存你的权重,当你测试你的结果时,你可以用相同的架构重新加载你的模型并设置batch_size=1如下:

 n_neurons = 10
 # design network
 model = Sequential()
 model.add(LSTM(n_neurons, batch_size=1, batch_input_shape=(n_batch,X.shape[1], X.shape[2]), statefull=True))
 model.add(Dense(1))
 model.compile(loss='mean_squared_error', optimizer='adam')
 model.load_weights("w.h5")

会很好用的希望对你有帮助

【讨论】:

【参考方案6】:

这个问题的最佳解决方案是“复制权重”。如果您想使用具有不同批量大小的 LSTM 模型进行训练和预测,这将非常有帮助。

例如,一旦您使用 'n' 批大小训练了模型,如下所示:

# configure network
n_batch = len(X)
n_epoch = 1000
n_neurons = 10
# design network
model = Sequential()
model.add(LSTM(n_neurons, batch_input_shape=(n_batch, X.shape[1], X.shape[2]), stateful=True))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')

现在您希望预测值小于您的批量大小,其中 n=1。

您可以做的是,复制您的拟合模型的权重并重新初始化具有相同架构的新模型 LSTM 模型并将批量大小设置为 1。

# re-define the batch size
n_batch = 1
# re-define model
new_model = Sequential()
new_model.add(LSTM(n_neurons, batch_input_shape=(n_batch, X.shape[1], X.shape[2]),       stateful=True))
new_model.add(Dense(1))
# copy weights
old_weights = model.get_weights()
new_model.set_weights(old_weights)

现在您可以轻松预测和训练具有不同批量大小的 LSTM。

更多信息请阅读:https://machinelearningmastery.com/use-different-batch-sizes-training-predicting-python-keras/

【讨论】:

【参考方案7】:

如果您对创建模型的代码没有访问权,或者如果您只是不希望您的预测/验证代码依赖您的模型创建和训练代码还有另一种方法

您可以像这样从已加载模型配置的修改版本创建一个新模型:

loaded_model = tf.keras.models.load_model('model_file.h5')
config = loaded_model.get_config()
old_batch_input_shape = config['layers'][0]['config']['batch_input_shape']
config['layers'][0]['config']['batch_input_shape'] = (new_batch_size, old_batch_input_shape[1])
new_model = loaded_model.__class__.from_config(config)
new_model.set_weights(loaded_model.get_weights())

这对我来说很有效,因为我有几个不同的模型,它们的状态全 RNN 层在图网络中协同工作,但使用不同的网络单独训练,导致不同的批量大小。它允许我试验模型结构和训练批次,而无需更改我的验证脚本中的任何内容。

【讨论】:

以上是关于为啥用于预测的 Keras LSTM 批量大小必须与拟合批量大小相同?的主要内容,如果未能解决你的问题,请参考以下文章

带有 LSTM 单元的 Keras RNN 用于基于多个输入时间序列预测多个输出时间序列

如何使用Keras LSTM与word嵌入来预测单词id

用于时间序列预测的 Keras LSTM 神经网络在模型拟合期间显示 nan

预测取决于 Keras 中的批量大小

使用 LSTM 预测一个简单的合成时间序列。为啥这么糟糕?

keras lstm 区别 stateless 和 stateful