如何使用深度学习模型进行时间序列预测?

7

我有从机器(m1,m2等)记录的信号,共28天。 (注意:每天的每个信号长度为360)。

machine_num, day1, day2, ..., day28
m1, [12, 10, 5, 6, ...], [78, 85, 32, 12, ...], ..., [12, 12, 12, 12, ...]
m2, [2, 0, 5, 6, ...], [8, 5, 32, 12, ...], ..., [1, 1, 12, 12, ...]
...
m2000, [1, 1, 5, 6, ...], [79, 86, 3, 1, ...], ..., [1, 1, 12, 12, ...]

我希望能够预测每台机器未来三天的信号序列,即在day29day30day31。然而,我没有day29day30day31的数值。所以,我的计划是使用LSTM模型。第一步是获取day1的信号并预测day2的信号,然后在下一步中获取day1day2的信号并预测day3的信号,以此类推,直到达到day28时,网络拥有所有信号并被要求预测day29等的信号。我尝试了如下的单变量LSTM模型。
# univariate lstm example
from numpy import array
from keras.models import Sequential
from keras.layers import LSTM
from keras.layers import Dense
# define dataset
X = array([[10, 20, 30], [20, 30, 40], [30, 40, 50], [40, 50, 60]])
y = array([40, 50, 60, 70])
# reshape from [samples, timesteps] into [samples, timesteps, features]
X = X.reshape((X.shape[0], X.shape[1], 1))
# define model
model = Sequential()
model.add(LSTM(50, activation='relu', input_shape=(3, 1)))
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')
# fit model
model.fit(X, y, epochs=1000, verbose=0)
# demonstrate prediction
x_input = array([50, 60, 70])
x_input = x_input.reshape((1, 3, 1))
yhat = model.predict(x_input, verbose=0)
print(yhat)

然而,这个示例非常简单,因为它没有像我的一样的长序列。例如,对于m1的数据如下所示。
m1 = [[12, 10, 5, 6, ...], [78, 85, 32, 12, ...], ..., [12, 12, 12, 12, ...]]

此外,我需要第29、30、31天的预测。在这种情况下,我不确定如何更改此示例以满足我的需求。我想具体知道我选择的方向是否正确。如果是的话,该如何操作。
如果需要,我很乐意提供更多细节。
编辑:我已经提到了model.summary()。

enter image description here


2
也许这个答案有用?https://dev59.com/kqbja4cB1Zd3GeqPcTD6#46934799 --- 以后我可能会写一个具体的回答来回答你的问题。 - Daniel Möller
1
因为我现在无法保存此链接,而且非常感兴趣这个主题,所以进行评论。 - Celius Stingher
1
谢谢提醒。只是为了确保我正确理解尺寸,我们有2000台机器,每天记录一个包含360个值的数组。因此,所有数组的尺寸都相同,我们可以说该矩阵是2000 * 28 * 360,对吗? - Celius Stingher
1
@CeliusStingher 非常感谢您的评论。是的,您说得对。我有2000台机器的28天数据。每天我都有一个包含360个元素的数组。因此,矩阵的大小为200028360。如果您需要更多详细信息,请告诉我。期待您的建议。谢谢 :) - EmJ
1
测试之前的最后一个问题是,360个观察值与每天之间的关系是什么?我们可以说每个观察值(对于每一天)都是独立的吗?对于第1天的观察1和第2天的观察1,这些值之间是否存在关联?就像多元方差分析(MANOVA)一样... 简而言之:是否应该考虑值之间的任何关系? - Celius Stingher
显示剩余12条评论
2个回答

4

模型和形状

由于这些是序列中的序列,您需要以不同的格式使用数据。

虽然您可以像这样简单地进行处理(machines, days, 360)并将360仅作为特征处理(在某种程度上可能有效),但对于强大的模型(也许存在速度问题),您需要将两个因素都视为序列。

那么我会选择像这样的数据(machines, days, 360, 1)和两个级别的循环。

我们模型的input_shape将为(None, 360, 1)

模型案例1-仅天数循环

数据形状:(machines, days, 360)
对数据进行一些归一化处理。

这里有一个示例,但是模型可以灵活变通,例如添加更多层,尝试卷积等:

inputs = Input((None, 360)) #(m, d, 360)
outs = LSTM(some_units, return_sequences=False, 
            stateful=depends_on_training_approach)(inputs)  #(m, some_units)
outs = Dense(360, activation=depends_on_your_normalization)(outs) #(m, 360)
outs = Reshape((1,360)) #(m, 1, 360) 
    #this reshape is not necessary if using the "shifted" approach - see time windows below
    #it would then be (m, d, 360)

model = Model(inputs, outs)

根据日内序列的复杂性,使用此方法可以很好地进行预测,但如果它们以复杂的方式发展,那么下一个模型会更好一些。
请记住,您可以创建更多的层并探索事物,以增强此模型的能力,这只是一个微小的例子。
模型案例2-双级递归
数据形状:(机器,天数,360,1)应用一些规范化到数据中。
有许多许多实验可尝试如何实现这一点,但这里提供了一个简单的方法。
inputs = Input((None, 360, 1)) #(m, d, 360, 1)

#branch 1
inner_average = TimeDistributed(
                    Bidirectional(
                        LSTM(units1, return_sequences=True, stateful=False),
                        merge_mode='ave'
                    )
                )(inputs) #(m, d, 360, units1)
inner_average = Lambda(lambda x: K.mean(x, axis=1))(inner_average) #(m, 360, units1)


#branch 2
inner_seq = TimeDistributed(
                LSTM(some_units, return_sequences=False, stateful=False)
            )(inputs) #may be Bidirectional too
            #shape (m, d, some_units)

outer_seq = LSTM(other_units, return_sequences = False, 
                 stateful=depends_on_training_approach)(inner_seq) #(m, other_units)

outer_seq = Dense(few_units * 360, activation = 'tanh')(outer_seq) #(m, few_units * 360)
    #activation = same as inner_average 


outer_seq = Reshape((360,few_units))(outer_seq) #(m, 360, few_units)


#join branches

outputs = Concatenate()([inner_average, outer_seq]) #(m, 360, units1+few_units)
outputs = LSTM(units, return_sequences=True, stateful= False)(outputs) #(m, 360,units)
outputs = Dense(1, activation=depends_on_your_normalization)(outputs) #(m, 360, 1)
outputs = Reshape((1,360))(outputs) #(m, 1, 360) for training purposes

model = Model(inputs, outputs)

这是一个尝试,我对每天的平均值进行了计算,但我也可以使用类似于 inner_average 的东西:

#branch 1
daily_minutes = Permute((2,1,3))(inputs) #(m, 360, d, 1)
daily_minutes = TimeDistributed(
                    LSTM(units1, return_sequences=False, 
                         stateful=depends_on_training_approach)
                )(daily_minutes) #(m, 360, units1)

有许多其他探索数据的方法,这是一个高度创造性的领域。例如,在 inner_average 之后使用每日分钟数方法,排除 K.mean lambda 层......你有想法了吧。
时间窗口法:您的方法听起来不错。给出一个步骤来预测下一个,给出两个步骤来预测第三个,给出三个步骤来预测第四个。
上述模型适用于此方法。
请记住,非常短的输入可能是无用的,并且可能会使您的模型变得更糟。(尝试想象一下什么样的步骤足够合理,可以开始预测下一步)
预处理数据并将其分组:
- 长度为4的组(例如) - 长度为5的组 - … - 长度为28的组
您将需要一个手动的训练循环,在每个 epoch 中,您会将这些组中的每一个馈送到模型中(您不能同时馈送不同长度)。
另一种方法是,给出所有步骤,使模型预测平移序列,如:
- inputs = original_inputs [:, :-1] #排除最后一个训练天 - outputs = original_inputs [:, 1:] #排除第一个训练天
要使上述模型适用于此方法,您需要在使用天数维度作为步骤的每个 LSTM 中使用 return_sequences=True(而不是 inner_seq)。 (inner_average 方法将失败,您将不得不采用返回序列和另一个 Permute((2,1,3))daily_minutes 方法。
形状将是:
- branch1:(m,d,360,units1) - branch2:(m,d,360,few_units) - 需要调整此reshape - 使用1个时间步长的reshape将是不必要的,days 维度将替换它。 - 如果需要详细信息,可能需要使用 Lambda 层来重新调整考虑批量大小和可变数量的天数。
训练和预测:(抱歉现在没有时间详细说明)
你可以按照这里这里所提到的方法进行操作(注意输出形状,即使时间步骤可能为1,在你的问题中也要一直保留时间步骤维度)。
重点是:
如果你选择 stateful=False:
- 这意味着可以使用 fit 进行简单的训练(只要你没有使用“不同长度”方法); - 这还意味着你需要建立一个新的带有 stateful=True 的模型,并复制已训练模型的权重; - 然后进行逐步手动预测。
如果你从一开始就选择了 stateful=True:
- 这必然意味着要使用手动训练循环(例如使用 train_on_batch); - 这必然意味着你需要在呈现不是上一个批次序列的批次时(如果你的批次包含整个序列,则每个批次),调用 model.reset_states()。 - 不需要建立新模型以手动预测,但手动预测仍然相同。

1
你选择了哪个时间窗口?我回答中的第二个链接展示了如何进行训练。 - Daniel Möller
1
只有当我在家的时候,抱歉。:( - Daniel Möller
我成功运行了代码的一半。然而,在代码中间出现了错误,错误信息为“RuntimeError: You must compile a model before training/testing. Use model.compile(optimizer, loss).” 我在文档中标明了出错的位置:https://docs.google.com/document/d/1v6kA2Y8fzTPMVl-JvZgAe-UZHKze2jcLE3HNK8bEzhk/edit#heading=h.p1v7iulqho0l 我在思考是否需要像这样提到一些内容 model.fit(X, y, epochs=50, verbose=0)。但是,由于我们正在使用批处理,我不确定是否正确。请您告知您的想法 :) - EmJ
1
如果您在最后使用了“线性”激活函数,那么损失可能是“mse”。我喜欢“adam”优化器,通常效果不错。除了“mae”之外,我们可能没有好的度量标准。您可能已经使用了sigmoid(从0到1),可以尝试使用“binary_crossentropy”。 - Daniel Möller
1
你的模型输出形状必须与“目标”(batch_y)的形状相匹配。你的模型当前输出2D数据 (None, something),你在某个地方使时间维度丢失了。请查看模型摘要。 - Daniel Möller
显示剩余12条评论

2
我认为你正在朝着正确的方向前进,如果想要增加每天的时间步数,你需要在数据中添加填充。这个例子可以帮助你: https://github.com/keras-team/keras/blob/master/examples/imdb_lstm.py#L46
然而,我也建议尝试另一种方法,比如固定时间步数,例如3天、4天、5天... 然后,在评估训练结果时,你可以选择最适合你模型的时间步数。
也许你最初的方法增加天数会更好,但在这种类型的问题中,找到LSTM的最佳时间步数非常重要。

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