3

我正在使用 TensorFlow 对时间序列回归问题进行有状态 LSTM 试验。抱歉,我无法共享数据集。下面是我的代码。

train_feature = train_feature.reshape((train_feature.shape[0], 1, train_feature.shape[1]))
val_feature = val_feature.reshape((val_feature.shape[0], 1, val_feature.shape[1]))

batch_size = 64

model = tf.keras.Sequential()
model.add(tf.keras.layers.LSTM(50, batch_input_shape=(batch_size, train_feature.shape[1], train_feature.shape[2]), stateful=True))
model.add(tf.keras.layers.Dense(1))

model.compile(optimizer='adam',
              loss='mse',
              metrics=[tf.keras.metrics.RootMeanSquaredError()])

model.fit(train_feature, train_label, 
          epochs=10,
          batch_size=batch_size)

当我运行上面的代码时,在第一个 epoch 结束后,我会收到如下错误。

InvalidArgumentError:  [_Derived_]  Invalid input_h shape: [1,64,50] [1,49,50]
     [[{{node CudnnRNN}}]]
     [[sequential_1/lstm_1/StatefulPartitionedCall]] [Op:__inference_train_function_1152847]

Function call stack:
train_function -> train_function -> train_function

但是,如果我将batch_size 更改为 1 ,模型将被成功训练,并将模型训练的代码更改为以下内容。

total_epochs = 10

for i in range(total_epochs):
    model.fit(train_feature, train_label, 
              epochs=1,
              validation_data=(val_feature, val_label),
              batch_size=batch_size,
              shuffle=False)

    model.reset_states()

然而,对于非常大的数据(100 万行),由于 batch_size 为 1,模型训练需要很长时间。

所以,我想知道,如何训练批量大小大于 1(例如 64)的有状态 LSTM,而不会出现无效的 input_h 形状错误?

感谢您的回答。

4

1 回答 1

7

解决方法是确保批次大小永远不会在批次之间发生变化。它们的大小必须相同。

方法一

一种方法是使用将数据集完美划分为大小相等的批次的批次大小。例如,如果数据的总大小为 1500 个示例,则使用 50 或 100 的批量大小或 1500 的其他适当除数。

batch_size = len(data)/proper_divisor

方法二

另一种方法是忽略任何小于指定大小的批次,这可以使用 TensorFlow Dataset API 并将 设置为drop_remainder来完成True

batch_size = 64

train_data = tf.data.Dataset.from_tensor_slices((train_feature, train_label))

train_data = train_data.repeat().batch(batch_size, drop_remainder=True)

steps_per_epoch = len(train_feature) // batch_size 

model.fit(train_data, 
          epochs=10, steps_per_epoch = steps_per_epoch)

使用上述数据集 API 时,您还需要指定多少轮训练计为一个 epoch(本质上是多少批次计为 1 个 epoch)。一个tf.data.Dataset实例(来自 的结果tf.data.Dataset.from_tensor_slices)不知道它流向模型的数据的大小,因此必须手动指定一个 epoch 的构成steps_per_epoch

您的新代码将如下所示:

train_feature = train_feature.reshape((train_feature.shape[0], 1, train_feature.shape[1]))
val_feature = val_feature.reshape((val_feature.shape[0], 1, val_feature.shape[1]))

batch_size = 64
train_data = tf.data.Dataset.from_tensor_slices((train_feature, train_label))
train_data = train_data.repeat().batch(batch_size, drop_remainder=True)

model = tf.keras.Sequential()
model.add(tf.keras.layers.LSTM(50, batch_input_shape=(batch_size, train_feature.shape[1], train_feature.shape[2]), stateful=True))
model.add(tf.keras.layers.Dense(1))

model.compile(optimizer='adam',
              loss='mse',
              metrics=[tf.keras.metrics.RootMeanSquaredError()])

steps_per_epoch = len(train_feature) // batch_size 
model.fit(train_data, 
          epochs=10, steps_per_epoch = steps_per_epoch)

您还可以包括验证集,如下所示(不显示其他代码):


batch_size = 64
val_data = tf.data.Dataset.from_tensor_slices((val_feature, val_label))
val_data = val_data.repeat().batch(batch_size, drop_remainder=True)

validation_steps = len(val_feature) // batch_size 
model.fit(train_data, epochs=10, 
          steps_per_epoch=steps_per_epoch,
          validation_steps=validation_steps)

警告:这意味着模型永远不会看到一些数据点。为了解决这个问题,您可以在每轮训练中打乱数据集,以便每个时期留下的数据点发生变化,让每个人都有机会被模型看到。

buffer_size = 1000 # the bigger the slower but more effective shuffling.

train_data = tf.data.Dataset.from_tensor_slices((train_feature, train_label))
train_data = train_data.shuffle(buffer_size=buffer_size, reshuffle_each_iteration=True)
train_data = train_data.repeat().batch(batch_size, drop_remainder=True)

为什么会发生错误

有状态的 RNN 及其变体(LSTM、GRU 等)需要固定的批量大小。原因很简单,因为有状态是实现截断反向传播的一种方式,通过将批次的最终隐藏状态作为下一批的初始隐藏状态传递。第一批的最终隐藏状态必须与下一批的初始隐藏状态具有完全相同的形状,这要求批次大小在各个批次之间保持相同。

当您将批处理大小设置为 64 时,model.fit将使用一个 epoch 结束时的剩余数据作为批处理,这可能没有多达 64 个数据点。因此,您会收到这样的错误,因为批量大小与有状态 LSTM 所期望的不同。批量大小为 1 没有问题,因为在一个时期结束时任何剩余的数据将始终包含恰好 1 个数据点,因此没有错误。更一般地说,1 始终是任何整数的除数。因此,如果您选择了数据大小的任何其他除数,则不应出现错误。

在您发布的错误消息中,最后一批的大小似乎是 49 而不是 64。附带说明:形状看起来与输入不同的原因是,在引擎盖下,keras 使用 time_major 中的张量(即第一个轴用于序列步骤)。当您传递一个表示 (batch_size, steps_per_sequence, num_features) 的形状 (10, 15, 2) 的张量时,keras 会将其重塑为 (15, 10, 2) 在引擎盖下。

于 2020-10-20T00:15:29.770 回答