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

EmJ*_*EmJ 7 python time-series forecasting deep-learning lstm

我从机器上记录(m1, m2, so on)了 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, ...]
Run Code Online (Sandbox Code Playgroud)

我想预测未来3天每台机器的信号序列。即在day29, day30, day31. 不过,我没有为天的值293031。所以,我的计划如下使用LSTM模型。

第一步是获取信号day 1并要求预测信号day 2,然后在下一步中获取信号days 1, 2并要求预测信号day 3等,所以当我到达day 28,网络时,所有信号都高达 28 并被要求预测用于信号day 29

我尝试做一个单变量 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)
Run Code Online (Sandbox Code Playgroud)

然而,这个例子非常简单,因为它没有像我这样的长序列。例如,我的数据m1如下所示。

m1 = [[12, 10, 5, 6, ...], [78, 85, 32, 12, ...], ..., [12, 12, 12, 12, ...]]
Run Code Online (Sandbox Code Playgroud)

此外,我需要预测 day 29, 30, 31。在这种情况下,我不确定如何更改此示例以满足我的需要。我想特别知道我选择的方向是否正确。如果是这样,该怎么做。

如果需要,我很乐意提供更多详细信息。

编辑:

我已经提到了model.summary().

在此处输入图片说明

Dan*_*ler 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)
Run Code Online (Sandbox Code Playgroud)

根据每日内序列的复杂性,可以很好地预测它们,但如果它们以复杂的方式进化,那么下一个模​​型会更好一点。

永远记住,您可以创建更多层并探索事物来增加该模型的功能,这只是一个很小的例子

模型案例 2 - 两级循环

数据形状: (machines, days, 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)
Run Code Online (Sandbox Code Playgroud)

这是一次尝试,我对这些天进行了平均,但我可以做出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)
Run Code Online (Sandbox Code Playgroud)

探索数据的许多其他方式都是可能的,这是一个高度创造性的领域。例如,您可以在排除lambda 层daily_minutes之后立即使用该方法......您明白了。inner_averageK.mean

时间窗口方法

你的方法听起来不错。给出一个步骤来预测下一步,给出两个步骤来预测第三个,给出三个步骤来预测第四个。

上述模型适合这种方法。

请记住,非常短的输入可能毫无用处,并且可能会使您的模型变得更糟。(尝试想象有多少步骤足以让您开始预测接下来的步骤)

预处理数据并将其分组:

  • 长度= 4的组(例如)
  • 长度= 5的组
  • ...
  • 长度 = 28 的组

您将需要一个手动训练循环,在每个时期,您为每个组提供数据(您不能同时提供不同长度的数据)。


另一种方法是,给出所有步骤,使模型预测移位序列,例如:

  • inputs = original_inputs[:, :-1]#排除最后一天的训练
  • outputs = original_inputs[:, 1:]#排除第一天训练

为了使上述模型适合这种方法,您需要return_sequences=True在每个 LSTM 中使用日期维度作为步骤(而不是inner_seq)。(该inner_average方法将会失败,您将不得不立即采用该daily_minutes方法return_sequences=True和另一个方法Permute((2,1,3))

形状将是:

  • 分支1:(m, d, 360, units1)
  • 分支2: -需要为此 (m, d, 360, few_units)调整Reshape
    • 使用 1 个时间步长的重塑将是不必要的,days尺寸将取代 1。
    • Lambda考虑到批量大小和可变天数, 您可能需要使用图层来重塑(如果需要详细信息,请告诉我)

训练和预测

(抱歉现在没时间详细介绍)

然后,您也可以按照此处和此处提到的方法进行操作,通过一些链接更完整。(不过,请注意输出形状,在您的问题中,我们始终保持时间步长维度,即使它可能是 1)

重点是:

  • 如果您选择stateful=False
    • 这意味着轻松训练fit(只要您不使用“不同长度”方法);
    • 这也意味着您需要使用 构建一个新模型stateful=True,复制训练模型的权重;
    • 然后你进行手动逐步预测
  • stateful=True如果您从一开始 就选择:
    • 这必然意味着手动训练循环(train_on_batch例如使用);
    • model.reset_states()这必然意味着每当您要呈现一个批次,其序列不是上一个批次的续集时,您都 将需要(如果您的批次包含整个序列,则每个批次)。
    • 不需要建立新的模型来手动预测,但手动预测保持不变