使用LSTM预测时间序列的多个未来时间步

23

我希望能够预测那些每周可预测的值(低信噪比)。我需要预测一整个年度形成的时间序列,该年度由一年中的每周组成(52个数值-图1)。

Figure 1: Year time series by week

我的第一个想法是使用Keras over TensorFlow开发一个多对多LSTM模型(图2)。我正在使用52个输入层(上一年的时间序列)和52个预测输出层(下一年的时间序列)来训练模型。train_X的形状为(X_examples,52,1),换句话说,X_examples表示训练样本数,52个时间步长,每个时间步长有1个特征。我知道Keras会将这52个输入视为同一域的时间序列。train_Y的形状也相同(y_examples,52,1)。 我添加了一个TimeDistributed层。我的想法是算法将预测值作为时间序列而不是孤立的值进行预测(我正确吗?)
在Keras中,该模型的代码如下:
y = y.reshape(y.shape[0], 52, 1)
X = X.reshape(X.shape[0], 52, 1)
# design network
model = Sequential()
model.add(LSTM(n_neurons, input_shape=(X.shape[1], X.shape[2]), return_sequences=True))
model.add(TimeDistributed(Dense(1)))
model.compile(loss='mean_squared_error', optimizer='adam')
# fit network
model.fit(X, y, epochs=n_epochs, batch_size=n_batch, verbose=2)

Figure 2: Many-to-many LSTM architecture

问题在于算法没有学习到示例,它正在预测与属性值非常相似的值。我是否正确地建模了问题?
第二个问题: 另一个想法是用1个输入和1个输出来训练算法,但是在测试期间,我如何预测整个2015年的时间序列而不查看“1个输入”?测试数据将具有不同的形状比训练数据。

你有多少个训练样例? - Imran
我有10年的数据。如果我的训练数据集是:使用4周的值来预测第5周,然后不断地向前移动,那么我可以有将近52 X 9个示例来训练模型,并且有52个用于预测(去年的数据)。 - Lucas Brito
当你的预测接近第52周时,你确实希望有大量的误差,对吗?如果使用LSTMs可以如此轻松准确地进行这种类型的预测,我们将永远不会使用任何其他方法。 - DJK
3个回答

33

如果你也关心数据量不足的问题,可以通过以下方式解决。

首先,建议将数值归一化在-1到+1之间,因此应先对其进行规范化。

对于LSTM模型,必须确保您使用了return_sequences=True
您的模型并没有什么“错误”,但为了实现您的目标,可能需要更多或更少的层数或单位数。(虽然这方面没有明确的答案)。

训练模型以预测下一步:

你只需要将Y作为移位后的X传递即可:

entireData = arrayWithShape((samples,52,1))
X = entireData[:,:-1,:]
y = entireData[:,1:,:]

使用这些内容训练模型。

预测未来:

现在,为了预测未来,由于我们需要使用预测的元素作为更多预测元素的输入,我们将使用循环并将模型设置为stateful=True

创建与先前模型相同的模型,但进行以下更改:

  • 所有LSTM层必须具有stateful=True
  • 批量输入形状必须是(batch_size, None, 1) - 这允许可变长度

复制先前训练模型的权重:

newModel.set_weights(oldModel.get_weights())

每次只预测一个样本,开始任何序列之前永远不要忘记调用 model.reset_states()

首先使用已知的序列进行预测(这将确保模型为预测未来做好准备)。

model.reset_states()
predictions = model.predict(entireData)

顺便提一下我们的训练方式,预测中的最后一步将是第一个未来元素:

futureElement = predictions[:,-1:,:]

futureElements = []
futureElements.append(futureElement)

现在我们创建一个循环,其中这个元素是输入。(由于具有状态,模型将理解它是前一个序列的新输入步骤,而不是一个新序列)

for i in range(howManyPredictions):
    futureElement = model.predict(futureElement)
    futureElements.append(futureElement)

这个链接包含一个完整的示例,可以预测两个特征的未来: https://github.com/danmoller/TestRepo/blob/master/TestBookLSTM.ipynb


你还需要为Keras递归层设置batch_size以存储状态内存,或者使用batch_input_size=(x,y,z)(而不是input_size=(x,y),batch=z)。 - Veltzer Doron
@DanielMöller stateful=True 是否会使上一个时间步的输出作为下一个时间步的输入?这种状态性有哪些细节需要我知道吗? - ajaysinghnegi
1
上一步的输出始终是下一步的输入。有状态并不会改变层的工作方式,除了它允许将“一个序列分成多个批次”。您可以手动决定何时结束序列(而不是让系统将每个批次视为包含整个序列)。--- 更多信息:https://dev59.com/zVkT5IYBdhLWcg3wXuTP#50235563 - Daniel Möller
@DanielMöller 很好的总结,唯一缺少的是我们想要重置状态并使用 reset_states(states=[state_h, state_c]) 设置来自先前预测的最后状态,其中 [predictions[1], predictions[2]]=[state_h, state_c] 并且 futureElement = predictions[0][:,-1:,:]。我还没有测试过,但我认为它应该是这样工作的?请评论 :) - GRS
此外,了解如何为每个层单独设置状态(例如,在堆叠LSTM时)将非常有帮助。@DanielMöller - GRS
显示剩余12条评论

3

我想补充一下这个问题。

我添加了一个TimeDistributed层。我的想法是,算法将预测时间序列的值而不是孤立的值(我是正确的吗?)

因为我自己也很难理解Keras TimeDistributed层的功能。

我认为您的动机是正确的,不应该将时间序列预测的计算隔离开来。当预测其未来形状时,您特别希望将整个系列的特征和相互依赖性混合在一起。

然而,这正好与TimeDistributed层的用途相反。它是用于在每个时间步上隔离计算的。你可能会问这有什么用处?对于完全不同的任务,例如序列标记,其中您具有顺序输入(i1、i2、i3、...、i_n)并旨在分别为每个时间步输出标签(label1、label2、label1、...、label2)

在我看来,最好的解释可以在这篇文章Keras文档中找到。

因此,我认为,与所有直觉相反,添加TimeDistributed层可能永远不是时间序列预测的好主意。欢迎听取其他意见!


1
请另发一条新问题。 - GKE

2
我有10年的数据。如果我的训练数据集是:使用4周的值来预测第5周,然后不断滚动,那么我可以有近52 X 9个示例来训练模型并且有52个用于预测(去年)。
实际上,这意味着您只有9个训练示例,每个示例有52个特征(除非您想在高度重叠的输入数据上进行训练)。无论如何,我认为这远远不足以进行LSTM训练。
我建议尝试一个更简单的模型。您的输入和输出数据具有固定的大小,因此您可以尝试 sklearn.linear_model.LinearRegression,它可以处理每个训练示例中的多个输入特征(在您的情况下为52个)和多个目标(也是52个)。
更新:如果您必须使用LSTM,请查看LSTM神经网络用于时间序列预测,这是一个支持同时或迭代地通过将每个预测作为输入反馈的Keras LSTM实现。根据您的评论,这应该正是您想要的。
此实现中网络的架构为:
model = Sequential()

model.add(LSTM(
    input_shape=(layers[1], layers[0]),
    output_dim=layers[1],
    return_sequences=True))
model.add(Dropout(0.2))

model.add(LSTM(
    layers[2],
    return_sequences=False))
model.add(Dropout(0.2))

model.add(Dense(
    output_dim=layers[3]))
model.add(Activation("linear"))

然而,我仍然建议运行线性回归或者一个带有一个隐藏层的简单前馈网络,并将其与LSTM的准确性进行比较。特别是当你一次预测一个输出并将其作为输入反馈时,你的错误可能会累积,从而给你带来更糟糕的预测结果。

好的,但是如果我仍然想使用LSTM。有没有一种方法可以训练模型,使输出向前1步,但预测向前多步呢? - Lucas Brito
抱歉,我不理解。模型的“输出”正是您试图预测的内容。 - Imran
好的,我再仔细想了一下,我认为我明白你的意思了。你可以构建一个预测一步的网络,然后将预测结果与下一个输入一起反馈。在这种情况下,你的输入会移动一个滑动窗口,并且输入会相互重叠。请参考实现。 - Imran
Dropout的直觉是什么? - Lucas Brito
Dropout是一种正则化技术,可以帮助您的模型泛化。除非您过度拟合训练数据,否则不一定需要使用它。 - Imran

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接