多类型的LSTM 模型进行时间序列预测(Python实现)

长短期记忆网络(简称LSTM)可应用于时间序列预测。

有许多类型的 LSTM 模型可用于每种特定类型的时间序列预测问题。

在本教程中,您将了解如何为一系列标准时间序列预测问题开发一套 LSTM 模型。

本教程的目标是为每种时间序列问题的每个模型提供独立示例,作为您可以复制和调整的模板,以适应您的特定时间序列预测问题。

完成本教程后,您将了解:

如何开发用于单变量时间序列预测的 LSTM 模型。

如何开发用于多元时间序列预测的 LSTM 模型。

如何开发用于多步时间序列预测的 LSTM 模型。

所有示例的Python 源代码文件。

教程概述

在本教程中,我们将探讨如何开发一套用于时间序列预测的不同类型的 LSTM 模型。

这些模型是在小型人为的时间序列问题上进行演示的,旨在展示所要解决的时间序列问题的类型。所选的模型配置是任意的,并未针对每个问题进行优化;这不是我们的目标。

本教程分为四个部分:

  1. 单变量 LSTM 模型
    1. 数据准备
    2. 香草 LSTM
    3. 堆叠式 LSTM
    4. 双向 LSTM
    5. CNN 长短期记忆
    6. 卷积LSTM
  2. 多元 LSTM 模型
    1. 多输入系列。
    2. 多个并行系列。
  3. 多步骤 LSTM 模型
    1. 数据准备
    2. 矢量输出模型
    3. 编码器-解码器模型
  4. 多元多步骤 LSTM 模型

    1. 多输入多步骤输出。
    2. 多个并行输入和多步输出

 一. 单变量 LSTM 模型

 LSTM 可用于建模单变量时间序列预测问题。

这些问题由一系列观察组成,需要一个模型从一系列过去的观察中学习来预测序列中的下一个值。

我们将展示用于单变量时间序列预测的 LSTM 模型的多种变体。

本节分为六个部分,分别是:

  1. 数据准备
  2. 标准LSTM
  3. 堆叠式 LSTM
  4. 双向 LSTM
  5. CNN 长短期记忆
  6. 卷积LSTM

这些模型中的每一个都适用于单步单变量时间序列预测,但可以轻松地进行调整并用作其他类型的时间序列预测问题模型的输入部分。

1. 数据准备

在对单变量序列进行建模之前,必须做好准备工作。

LSTM 模型将学习一个函数,该函数将过去观察序列作为输入映射到输出观察。因此,观察序列必须转换为 LSTM 可以学习的多个示例。

考虑给定的单变量序列:

[10, 20, 30, 40, 50, 60, 70, 80, 90]

我们可以将序列划分为多个输入/输出模式(称为样本),其中三个时间步骤用作输入,一个时间步骤用作正在学习的一步预测的输出。

10, 20, 30 40
20, 30, 40 50
30, 40, 50 60
...

下面的split_sequence()函数实现了这一行为,并将给定的单变量序列分成多个样本,其中每个样本具有指定数量的时间步骤,并且输出是单个时间步骤。

# split a univariate sequence into samples
def split_sequence(sequence, n_steps):
 X, y = list(), list()
 for i in range(len(sequence)):
 # find the end of this pattern
 end_ix = i + n_steps
 # check if we are beyond the sequence
 if end_ix > len(sequence)-1:
 break
 # gather input and output parts of the pattern
 seq_x, seq_y = sequence[i:end_ix], sequence[end_ix]
 X.append(seq_x)
 y.append(seq_y)
 return array(X), array(y)

我们可以在上面我们设计的小型数据集上演示此功能。

完整的示例如下。

# univariate data preparation
from numpy import array
 
# split a univariate sequence into samples
def split_sequence(sequence, n_steps):
 X, y = list(), list()
 for i in range(len(sequence)):
 # find the end of this pattern
 end_ix = i + n_steps
 # check if we are beyond the sequence
 if end_ix > len(sequence)-1:
 break
 # gather input and output parts of the pattern
 seq_x, seq_y = sequence[i:end_ix], sequence[end_ix]
 X.append(seq_x)
 y.append(seq_y)
 return array(X), array(y)
 
# define input sequence
raw_seq = [10, 20, 30, 40, 50, 60, 70, 80, 90]
# choose a number of time steps
n_steps = 3
# split into samples
X, y = split_sequence(raw_seq, n_steps)
# summarize the data
for i in range(len(X)):
 print(X[i], y[i])

运行该示例将单变量序列分成六个样本,每个样本有三个输入时间步骤和一个输出时间步骤。

[10 20 30] 40
[20 30 40] 50
[30 40 50] 60
[40 50 60] 70
[50 60 70] 80
[60 70 80] 90

现在我们知道如何准备一个单变量序列进行建模,让我们看看如何开发可以学习输入到输出映射的 LSTM 模型,从 Vanilla LSTM 开始。

2. 标准LSTM

标准LSTM(Vanilla LSTM) 是一种 LSTM 模型,它具有一个由 LSTM 单元组成的隐藏层和一个用于进行预测的输出层。

我们可以定义一个用于单变量时间序列预测的 Vanilla LSTM,如下所示。

...
# define model
model = Sequential()
model.add(LSTM(50, activation='relu', input_shape=(n_steps, n_features)))
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')

定义中的关键是输入的形状;即模型在时间步数和特征数量方面对每个样本的输入期望。

我们正在处理单变量系列,因此对于一个变量,特征数量为 1。

作为输入的时间步数是我们在准备数据集作为split_sequence()函数的参数时选择的数字。

每个样本的输入形状在第一个隐藏层的定义中的input_shape参数中指定。

我们几乎总是有多个样本,因此,模型将期望训练数据的输入部分具有以下维度或形状:

[samples, timesteps, features]

上一节中的split_sequence()函数输出形状为 [ samples, timesteps ] 的 X,因此我们可以轻松地将其重塑,为一个特征增加一个维度。

...
# reshape from [samples, timesteps] into [samples, timesteps, features]
n_features = 1
X = X.reshape((X.shape[0], X.shape[1], n_features))

在本例中,我们定义一个模型,其隐藏层有 50 个 LSTM 单元,输出层可以预测单个数值。

该模型使用高效的Adam 版本随机梯度下降进行拟合,并使用均方误差或“ mse ”损失函数进行优化。

一旦定义了模型,我们就可以将其适合训练数据集。

...
# fit model
model.fit(X, y, epochs=200, verbose=0)

模型拟合好之后,我们就可以用它来进行预测。

我们可以通过提供输入来预测序列中的下一个值:

[70, 80, 90]

并期望模型预测如下:

[100]

该模型期望输入形状是三维的,具有 [样本、时间步长、特征],因此,我们必须在进行预测之前重塑单个输入样本。

...
# demonstrate prediction
x_input = array([70, 80, 90])
x_input = x_input.reshape((1, n_steps, n_features))
yhat = model.predict(x_input, verbose=0)

我们可以将所有这些结合在一起,并演示如何开发用于单变量时间序列预测的 Vanilla LSTM 并做出单一预测。

# univariate lstm example
from numpy import array
from keras.models import Sequential
from keras.layers import LSTM
from keras.layers import Dense

# split a univariate sequence into samples
def split_sequence(sequence, n_steps):
	X, y = list(), list()
	for i in range(len(sequence)):
		# find the end of this pattern
		end_ix = i + n_steps
		# check if we are beyond the sequence
		if end_ix > len(sequence)-1:
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequence[i:end_ix], sequence[end_ix]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
raw_seq = [10, 20, 30, 40, 50, 60, 70, 80, 90]
# choose a number of time steps
n_steps = 3
# split into samples
X, y = split_sequence(raw_seq, n_steps)
# reshape from [samples, timesteps] into [samples, timesteps, features]
n_features = 1
X = X.reshape((X.shape[0], X.shape[1], n_features))
# define model
model = Sequential()
model.add(LSTM(50, activation='relu', input_shape=(n_steps, n_features)))
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')
# fit model
model.fit(X, y, epochs=200, verbose=0)
# demonstrate prediction
x_input = array([70, 80, 90])
x_input = x_input.reshape((1, n_steps, n_features))
yhat = model.predict(x_input, verbose=0)
print(yhat)

运行示例来准备数据、拟合模型并做出预测。

注意:由于算法或评估程序的随机性,或数值精度的差异,您的结果可能会有所不同。考虑运行示例几次并比较平均结果。

我们可以看到模型预测了序列中的下一个值。

[[102.09213]]

3. 堆叠式 LSTM

多个隐藏的 LSTM 层可以堆叠在一起,这就是所谓的 Stacked LSTM 模型。

LSTM 层需要三维输入,并且 LSTM 默认会产生二维输出作为序列末尾的解释。

通过在层上设置return_sequences=True参数,我们可以让 LSTM 为输入数据中的每个时间步骤输出一个值,从而解决这个问题。这使我们能够将隐藏 LSTM 层的 3D 输出作为下一层的输入。

因此,我们可以按如下方式定义 Stacked LSTM。

...
# define model
model = Sequential()
model.add(LSTM(50, activation='relu', return_sequences=True, input_shape=(n_steps, n_features)))
model.add(LSTM(50, activation='relu'))
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')

我们可以将它们联系在一起;完整的代码示例如下所示。

# univariate stacked lstm example
from numpy import array
from keras.models import Sequential
from keras.layers import LSTM
from keras.layers import Dense

# split a univariate sequence
def split_sequence(sequence, n_steps):
	X, y = list(), list()
	for i in range(len(sequence)):
		# find the end of this pattern
		end_ix = i + n_steps
		# check if we are beyond the sequence
		if end_ix > len(sequence)-1:
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequence[i:end_ix], sequence[end_ix]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
raw_seq = [10, 20, 30, 40, 50, 60, 70, 80, 90]
# choose a number of time steps
n_steps = 3
# split into samples
X, y = split_sequence(raw_seq, n_steps)
# reshape from [samples, timesteps] into [samples, timesteps, features]
n_features = 1
X = X.reshape((X.shape[0], X.shape[1], n_features))
# define model
model = Sequential()
model.add(LSTM(50, activation='relu', return_sequences=True, input_shape=(n_steps, n_features)))
model.add(LSTM(50, activation='relu'))
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')
# fit model
model.fit(X, y, epochs=200, verbose=0)
# demonstrate prediction
x_input = array([70, 80, 90])
x_input = x_input.reshape((1, n_steps, n_features))
yhat = model.predict(x_input, verbose=0)
print(yhat)

注意:由于算法或评估程序的随机性,或数值精度的差异,您的结果可能会有所不同。考虑运行示例几次并比较平均结果。

运行该示例将预测序列中的下一个值,我们预计该值是 100。

[[102.47341]]

4. 双向 LSTM

在某些序列预测问题中,允许 LSTM 模型正向和反向学习输入序列并连接两种解释会很有帮助。

这被称为双向 LSTM。

我们可以通过将第一个隐藏层包装在名为双向的包装层中来实现用于单变量时间序列预测的双向 LSTM。

定义双向 LSTM 来读取前向和后向输入的示例如下。

...
# define model
model = Sequential()
model.add(Bidirectional(LSTM(50, activation='relu'), input_shape=(n_steps, n_features)))
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')

下面列出了用于单变量时间序列预测的双向 LSTM 的完整示例。

# univariate bidirectional lstm example
from numpy import array
from keras.models import Sequential
from keras.layers import LSTM
from keras.layers import Dense
from keras.layers import Bidirectional

# split a univariate sequence
def split_sequence(sequence, n_steps):
	X, y = list(), list()
	for i in range(len(sequence)):
		# find the end of this pattern
		end_ix = i + n_steps
		# check if we are beyond the sequence
		if end_ix > len(sequence)-1:
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequence[i:end_ix], sequence[end_ix]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
raw_seq = [10, 20, 30, 40, 50, 60, 70, 80, 90]
# choose a number of time steps
n_steps = 3
# split into samples
X, y = split_sequence(raw_seq, n_steps)
# reshape from [samples, timesteps] into [samples, timesteps, features]
n_features = 1
X = X.reshape((X.shape[0], X.shape[1], n_features))
# define model
model = Sequential()
model.add(Bidirectional(LSTM(50, activation='relu'), input_shape=(n_steps, n_features)))
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')
# fit model
model.fit(X, y, epochs=200, verbose=0)
# demonstrate prediction
x_input = array([70, 80, 90])
x_input = x_input.reshape((1, n_steps, n_features))
yhat = model.predict(x_input, verbose=0)
print(yhat)

注意:由于算法或评估程序的随机性,或数值精度的差异,您的结果可能会有所不同。考虑运行示例几次并比较平均结果。

运行该示例将预测序列中的下一个值,我们预计该值是 100。

[[101.48093]]

 5. CNN 长短期记忆

卷积神经网络(简称 CNN)是一种为处理二维图像数据而开发的神经网络。

CNN 可以非常有效地从一维序列数据(例如单变量时间序列数据)中自动提取和学习特征。

CNN 模型可以与 LSTM 后端一起用于混合模型,其中 CNN 用于解释输入的子序列,这些子序列一起作为序列提供给 LSTM 模型进行解释。这种混合模型称为 CNN-LSTM。

第一步是将输入序列拆分为 CNN 模型可以处理的子序列。例如,我们可以先将单变量时间序列数据拆分为输入/输出样本,其中四个步骤作为输入,一个步骤作为输出。然后可以将每个样本拆分为两个子样本,每个子样本有两个时间步骤。CNN 可以解释两个时间步骤的每个子序列,并将子序列的解释时间序列提供给 LSTM 模型作为输入进行处理。

我们可以将其参数化,将子序列的数量定义为n_seq,将每个子序列的时间步数定义为n_steps。然后可以重塑输入数据以具有所需的结构:

[samples, subsequences, timesteps, features]

例如:

...
# choose a number of time steps
n_steps = 4
# split into samples
X, y = split_sequence(raw_seq, n_steps)
# reshape from [samples, timesteps] into [samples, subsequences, timesteps, features]
n_features = 1
n_seq = 2
n_steps = 2
X = X.reshape((X.shape[0], n_seq, n_steps, n_features))

我们希望在分别读取每个数据子序列时重用相同的 CNN 模型。

这可以通过将整个 CNN 模型包装在TimeDistributed 包装器中来实现,该包装器将对每个输入应用一次整个模型,在本例中为每个输入子序列应用一次。

CNN 模型首先有一个卷积层,用于读取子序列,这需要指定一定数量的过滤器和内核大小。过滤器的数量是输入序列的读取或解释次数。内核大小是输入序列的每个“读取”操作所包含的时间步数。

卷积层之后是最大池化层,该层将滤波器映射精简为包含最显著特征的 1/2 大小。然后,这些结构被扁平化为单个一维向量,用作 LSTM 层的单个输入时间步长。

...
model.add(TimeDistributed(Conv1D(filters=64, kernel_size=1, activation='relu'), input_shape=(None, n_steps, n_features)))
model.add(TimeDistributed(MaxPooling1D(pool_size=2)))
model.add(TimeDistributed(Flatten()))

接下来,我们可以定义模型的 LSTM 部分,它解释 CNN 模型对输入序列的读取并做出预测。

...
model.add(LSTM(50, activation='relu'))
model.add(Dense(1))

我们可以将所有这些联系在一起;下面列出了单变量时间序列预测的 CNN-LSTM 模型的完整示例。

# univariate cnn lstm example
from numpy import array
from keras.models import Sequential
from keras.layers import LSTM
from keras.layers import Dense
from keras.layers import Flatten
from keras.layers import TimeDistributed
from keras.layers.convolutional import Conv1D
from keras.layers.convolutional import MaxPooling1D

# split a univariate sequence into samples
def split_sequence(sequence, n_steps):
	X, y = list(), list()
	for i in range(len(sequence)):
		# find the end of this pattern
		end_ix = i + n_steps
		# check if we are beyond the sequence
		if end_ix > len(sequence)-1:
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequence[i:end_ix], sequence[end_ix]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
raw_seq = [10, 20, 30, 40, 50, 60, 70, 80, 90]
# choose a number of time steps
n_steps = 4
# split into samples
X, y = split_sequence(raw_seq, n_steps)
# reshape from [samples, timesteps] into [samples, subsequences, timesteps, features]
n_features = 1
n_seq = 2
n_steps = 2
X = X.reshape((X.shape[0], n_seq, n_steps, n_features))
# define model
model = Sequential()
model.add(TimeDistributed(Conv1D(filters=64, kernel_size=1, activation='relu'), input_shape=(None, n_steps, n_features)))
model.add(TimeDistributed(MaxPooling1D(pool_size=2)))
model.add(TimeDistributed(Flatten()))
model.add(LSTM(50, activation='relu'))
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')
# fit model
model.fit(X, y, epochs=500, verbose=0)
# demonstrate prediction
x_input = array([60, 70, 80, 90])
x_input = x_input.reshape((1, n_seq, n_steps, n_features))
yhat = model.predict(x_input, verbose=0)
print(yhat)

注意:由于算法或评估程序的随机性,或数值精度的差异,您的结果可能会有所不同。考虑运行示例几次并比较平均结果。

运行该示例将预测序列中的下一个值,我们预计该值是 100。

[[101.69263]]

6. 卷积LSTM

与 CNN-LSTM 相关的一种 LSTM 类型是 ConvLSTM,其中输入的卷积读取直接构建到每个 LSTM 单元中。

ConvLSTM 是为读取二维时空数据而开发的,但可以适用于单变量时间序列预测。

该层期望输入为二维图像序列,因此输入数据的形状必须是:

[samples, timesteps, rows, columns, features]

为了达到我们的目的,我们可以将每个样本拆分为子序列,其中时间步长将成为子序列的数量,即n_seq,列将成为每个子序列的时间步长数量,即n_steps。由于我们处理的是一维数据,因此行数固定为 1。

我们现在可以将准备好的样品重塑为所需的结构。

...
# choose a number of time steps
n_steps = 4
# split into samples
X, y = split_sequence(raw_seq, n_steps)
# reshape from [samples, timesteps] into [samples, timesteps, rows, columns, features]
n_features = 1
n_seq = 2
n_steps = 2
X = X.reshape((X.shape[0], n_seq, 1, n_steps, n_features))

我们可以将 ConvLSTM 定义为单层(就过滤器数量而言)和二维内核大小(就行、列而言)。由于我们处理的是一维序列,因此内核中的行数始终固定为 1。

然后必须将模型的输出平坦化,然后才能对其进行解释和做出预测。

...
model.add(ConvLSTM2D(filters=64, kernel_size=(1,2), activation='relu', input_shape=(n_seq, 1, n_steps, n_features)))
model.add(Flatten())

下面列出了单步单变量时间序列预测的 ConvLSTM 的完整示例。

# univariate convlstm example
from numpy import array
from keras.models import Sequential
from keras.layers import LSTM
from keras.layers import Dense
from keras.layers import Flatten
from keras.layers import ConvLSTM2D

# split a univariate sequence into samples
def split_sequence(sequence, n_steps):
	X, y = list(), list()
	for i in range(len(sequence)):
		# find the end of this pattern
		end_ix = i + n_steps
		# check if we are beyond the sequence
		if end_ix > len(sequence)-1:
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequence[i:end_ix], sequence[end_ix]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
raw_seq = [10, 20, 30, 40, 50, 60, 70, 80, 90]
# choose a number of time steps
n_steps = 4
# split into samples
X, y = split_sequence(raw_seq, n_steps)
# reshape from [samples, timesteps] into [samples, timesteps, rows, columns, features]
n_features = 1
n_seq = 2
n_steps = 2
X = X.reshape((X.shape[0], n_seq, 1, n_steps, n_features))
# define model
model = Sequential()
model.add(ConvLSTM2D(filters=64, kernel_size=(1,2), activation='relu', input_shape=(n_seq, 1, n_steps, n_features)))
model.add(Flatten())
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')
# fit model
model.fit(X, y, epochs=500, verbose=0)
# demonstrate prediction
x_input = array([60, 70, 80, 90])
x_input = x_input.reshape((1, n_seq, 1, n_steps, n_features))
yhat = model.predict(x_input, verbose=0)
print(yhat)

注意:由于算法或评估程序的随机性,或数值精度的差异,您的结果可能会有所不同。考虑运行示例几次并比较平均结果。

运行该示例将预测序列中的下一个值,我们预计该值是 100。

[[103.68166]]

现在我们已经了解了单变量数据的 LSTM 模型,让我们将注意力转向多变量数据。

二 . 多元 LSTM 模型

多元时间序列数据是指每个时间步骤有多个观测值的数据。

对于多元时间序列数据,我们可能需要两种主要模型;它们是:

  1. 多输入系列。
  2. 多个并行系列。

让我们依次看一下。

1. 多输入系列

一个问题可能有两个或多个并行的输入时间序列和一个依赖于输入时间序列的输出时间序列。

输入时间序列是平行的,因为每个序列在相同的时间步骤都有一个观察值。

我们可以通过两个并行输入时间序列的简单示例来证明这一点,其中输出序列是输入序列的简单加法。

...
# define input sequence
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])

我们可以将这三个数据数组重新整形为单个数据集,其中每行是一个时间步长,每列是一个单独的时间序列。这是在 CSV 文件中存储并行时间序列的标准方法。

...
# convert to [rows, columns] structure
in_seq1 = in_seq1.reshape((len(in_seq1), 1))
in_seq2 = in_seq2.reshape((len(in_seq2), 1))
out_seq = out_seq.reshape((len(out_seq), 1))
# horizontally stack columns
dataset = hstack((in_seq1, in_seq2, out_seq))

完整的示例如下。

# multivariate data preparation
from numpy import array
from numpy import hstack
# define input sequence
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])
# convert to [rows, columns] structure
in_seq1 = in_seq1.reshape((len(in_seq1), 1))
in_seq2 = in_seq2.reshape((len(in_seq2), 1))
out_seq = out_seq.reshape((len(out_seq), 1))
# horizontally stack columns
dataset = hstack((in_seq1, in_seq2, out_seq))
print(dataset)

运行该示例将打印数据集,每个时间步一行,两个输入和一个输出并行时间序列各一列。

[[ 10  15  25]
 [ 20  25  45]
 [ 30  35  65]
 [ 40  45  85]
 [ 50  55 105]
 [ 60  65 125]
 [ 70  75 145]
 [ 80  85 165]
 [ 90  95 185]]

与单变量时间序列一样,我们必须将这些数据结构化为具有输入和输出元素的样本。

LSTM 模型需要足够的上下文来学习从输入序列到输出值的映射。LSTM 可以支持并行输入时间序列作为单独的变量或特征。因此,我们需要将数据拆分为样本,以保持两个输入序列之间的观察顺序。

如果我们选择三个输入时间步骤,那么第一个样本将如下所示:

输入:

10, 15
20, 25
30, 35

输出:

65

也就是说,每个并行系列的前三个时间步骤作为模型的输入,模型将其与第三个时间步骤的输出系列中的值(在本例中为 65)相关联。

我们可以看到,在将时间序列转换为输入/输出样本以训练模型时,我们必须从输出时间序列中丢弃一些值,因为在先前的时间步骤中,输入时间序列中没有值。反过来,输入时间步骤数量的大小选择将对使用多少训练数据产生重要影响。

我们可以定义一个名为split_sequences() 的函数,该函数将采用我们定义的数据集,其中行表示时间步长,列表示并行序列,并返回输入/输出样本。

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps
		# check if we are beyond the dataset
		if end_ix > len(sequences):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :-1], sequences[end_ix-1, -1]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

我们可以使用每个输入时间序列的三个时间步骤作为输入,在我们的数据集上测试此函数。

完整的示例如下。

# multivariate data preparation
from numpy import array
from numpy import hstack

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps
		# check if we are beyond the dataset
		if end_ix > len(sequences):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :-1], sequences[end_ix-1, -1]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])
# convert to [rows, columns] structure
in_seq1 = in_seq1.reshape((len(in_seq1), 1))
in_seq2 = in_seq2.reshape((len(in_seq2), 1))
out_seq = out_seq.reshape((len(out_seq), 1))
# horizontally stack columns
dataset = hstack((in_seq1, in_seq2, out_seq))
# choose a number of time steps
n_steps = 3
# convert into input/output
X, y = split_sequences(dataset, n_steps)
print(X.shape, y.shape)
# summarize the data
for i in range(len(X)):
	print(X[i], y[i])

运行该示例首先打印 X 和 y 分量的形状。

我们可以看到X组件具有三维结构。

第一维是样本数量,在本例中为 7。第二维是每个样本的时间步数,在本例中为 3,即为函数指定的值。最后,最后一个维度指定并行时间序列的数量或变量数量,在本例中为两个并行序列的 2 个变量数量。

这是 LSTM 期望作为输入的精确三维结构。数据可以直接使用,无需进一步重塑。

然后我们可以看到打印了每个样本的输入和输出,显示了两个输入系列中的每一个的三个时间步骤以及每个样本的相关输出。

(7, 3, 2) (7,)

[[10 15]
 [20 25]
 [30 35]] 65
[[20 25]
 [30 35]
 [40 45]] 85
[[30 35]
 [40 45]
 [50 55]] 105
[[40 45]
 [50 55]
 [60 65]] 125
[[50 55]
 [60 65]
 [70 75]] 145
[[60 65]
 [70 75]
 [80 85]] 165
[[70 75]
 [80 85]
 [90 95]] 185

我们现在准备根据这些数据拟合 LSTM 模型。

可以使用上一节中所述的任何种类的 LSTM,例如 Vanilla、Stacked、Bidirectional、CNN 或 ConvLSTM 模型。

我们将使用 Vanilla LSTM,其中通过input_shape参数为输入层指定时间步数和并行序列(特征)。

...
# define model
model = Sequential()
model.add(LSTM(50, activation='relu', input_shape=(n_steps, n_features)))
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')

在进行预测时,模型需要对两个输入时间序列进行三个时间步骤。

我们可以通过提供以下输入值来预测输出序列中的下一个值:

80,	 85
90,	 95
100, 105

具有三个时间步长和两个变量的一个样本的形状必须是 [1, 3, 2]。

我们预计序列中的下一个值是 100 + 105,即 205。

...
# demonstrate prediction
x_input = array([[80, 85], [90, 95], [100, 105]])
x_input = x_input.reshape((1, n_steps, n_features))
yhat = model.predict(x_input, verbose=0)

完整的示例如下。

# multivariate lstm example
from numpy import array
from numpy import hstack
from keras.models import Sequential
from keras.layers import LSTM
from keras.layers import Dense

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps
		# check if we are beyond the dataset
		if end_ix > len(sequences):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :-1], sequences[end_ix-1, -1]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])
# convert to [rows, columns] structure
in_seq1 = in_seq1.reshape((len(in_seq1), 1))
in_seq2 = in_seq2.reshape((len(in_seq2), 1))
out_seq = out_seq.reshape((len(out_seq), 1))
# horizontally stack columns
dataset = hstack((in_seq1, in_seq2, out_seq))
# choose a number of time steps
n_steps = 3
# convert into input/output
X, y = split_sequences(dataset, n_steps)
# the dataset knows the number of features, e.g. 2
n_features = X.shape[2]
# define model
model = Sequential()
model.add(LSTM(50, activation='relu', input_shape=(n_steps, n_features)))
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')
# fit model
model.fit(X, y, epochs=200, verbose=0)
# demonstrate prediction
x_input = array([[80, 85], [90, 95], [100, 105]])
x_input = x_input.reshape((1, n_steps, n_features))
yhat = model.predict(x_input, verbose=0)
print(yhat)

注意:由于算法或评估程序的随机性,或数值精度的差异,您的结果可能会有所不同。考虑运行示例几次并比较平均结果。

运行示例来准备数据、拟合模型并做出预测。

[[208.13531]]

三. 多个并联系列

另一种时间序列问题是存在多个并行时间序列并且必须为每个时间序列预测一个值的情况。

例如,给定上一节的数据:

[[ 10  15  25]
 [ 20  25  45]
 [ 30  35  65]
 [ 40  45  85]
 [ 50  55 105]
 [ 60  65 125]
 [ 70  75 145]
 [ 80  85 165]
 [ 90  95 185]]

我们可能想要预测下一个时间步骤的三个时间序列中的每个值。

这可能被称为多元预测。

再次,为了训练模型,必须将数据分成输入/输出样本。

该数据集的第一个样本是:

输入:

10, 15, 25
20, 25, 45
30, 35, 65

输出:

40, 45, 85

下面的split_sequences ()函数将把多个并行时间序列(以行作为时间步长、以每列一个序列)拆分成所需的输入/输出形状。

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps
		# check if we are beyond the dataset
		if end_ix > len(sequences)-1:
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :], sequences[end_ix, :]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

我们可以在人为设计的问题上证明这一点;完整的例子如下所示。

# multivariate output data prep
from numpy import array
from numpy import hstack

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps
		# check if we are beyond the dataset
		if end_ix > len(sequences)-1:
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :], sequences[end_ix, :]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])
# convert to [rows, columns] structure
in_seq1 = in_seq1.reshape((len(in_seq1), 1))
in_seq2 = in_seq2.reshape((len(in_seq2), 1))
out_seq = out_seq.reshape((len(out_seq), 1))
# horizontally stack columns
dataset = hstack((in_seq1, in_seq2, out_seq))
# choose a number of time steps
n_steps = 3
# convert into input/output
X, y = split_sequences(dataset, n_steps)
print(X.shape, y.shape)
# summarize the data
for i in range(len(X)):
	print(X[i], y[i])

运行该示例首先打印准备好的 X 和 y 分量的形状。

X 的形状是三维的,包括样本数量(6)、每个样本选择的时间步长数量(3)以及并行时间序列或特征的数量(3)。

y 的形状是二维的,正如我们预期的样本数量(6)和每个样本要预测的时间变量数量(3)。

这些数据已准备好用于 LSTM 模型,该模型需要每个样本的 X 和 y 分量的三维输入和二维输出形状。

然后,打印每个样本,显示每个样本的输入和输出组件。

(6, 3, 3) (6, 3)

[[10 15 25]
 [20 25 45]
 [30 35 65]] [40 45 85]
[[20 25 45]
 [30 35 65]
 [40 45 85]] [ 50  55 105]
[[ 30  35  65]
 [ 40  45  85]
 [ 50  55 105]] [ 60  65 125]
[[ 40  45  85]
 [ 50  55 105]
 [ 60  65 125]] [ 70  75 145]
[[ 50  55 105]
 [ 60  65 125]
 [ 70  75 145]] [ 80  85 165]
[[ 60  65 125]
 [ 70  75 145]
 [ 80  85 165]] [ 90  95 185]

我们现在准备根据这些数据拟合 LSTM 模型。

可以使用上一节中所述的任何种类的 LSTM,例如 Vanilla、Stacked、Bidirectional、CNN 或 ConvLSTM 模型。

我们将使用 Stacked LSTM,其中通过input_shape参数为输入层指定时间步长和并行序列(特征)的数量。并行序列的数量也用于指定模型在输出层中要预测的值的数量;同样,这里是三个。

...
# define model
model = Sequential()
model.add(LSTM(100, activation='relu', return_sequences=True, input_shape=(n_steps, n_features)))
model.add(LSTM(100, activation='relu'))
model.add(Dense(n_features))
model.compile(optimizer='adam', loss='mse')

我们可以通过为每个系列提供三个时间步长的输入来预测三个并行系列中的下一个值。

70, 75, 145
80, 85, 165
90, 95, 185

进行单次预测的输入形状必须是 1 个样本、3 个时间步长和 3 个特征,或者 [1, 3, 3]

...
# demonstrate prediction
x_input = array([[70,75,145], [80,85,165], [90,95,185]])
x_input = x_input.reshape((1, n_steps, n_features))
yhat = model.predict(x_input, verbose=0)

我们期望矢量输出为:

[100, 105, 205]

我们可以将所有这些结合在一起,并在下面演示用于多元输出时间序列预测的 Stacked LSTM。

# multivariate output stacked lstm example
from numpy import array
from numpy import hstack
from keras.models import Sequential
from keras.layers import LSTM
from keras.layers import Dense

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps
		# check if we are beyond the dataset
		if end_ix > len(sequences)-1:
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :], sequences[end_ix, :]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])
# convert to [rows, columns] structure
in_seq1 = in_seq1.reshape((len(in_seq1), 1))
in_seq2 = in_seq2.reshape((len(in_seq2), 1))
out_seq = out_seq.reshape((len(out_seq), 1))
# horizontally stack columns
dataset = hstack((in_seq1, in_seq2, out_seq))
# choose a number of time steps
n_steps = 3
# convert into input/output
X, y = split_sequences(dataset, n_steps)
# the dataset knows the number of features, e.g. 2
n_features = X.shape[2]
# define model
model = Sequential()
model.add(LSTM(100, activation='relu', return_sequences=True, input_shape=(n_steps, n_features)))
model.add(LSTM(100, activation='relu'))
model.add(Dense(n_features))
model.compile(optimizer='adam', loss='mse')
# fit model
model.fit(X, y, epochs=400, verbose=0)
# demonstrate prediction
x_input = array([[70,75,145], [80,85,165], [90,95,185]])
x_input = x_input.reshape((1, n_steps, n_features))
yhat = model.predict(x_input, verbose=0)
print(yhat)

注意:由于算法或评估程序的随机性,或数值精度的差异,您的结果可能会有所不同。考虑运行示例几次并比较平均结果。

运行示例来准备数据、拟合模型并做出预测。

[[101.76599 108.730484 206.63577 ]]

三. 多步骤 LSTM 模型

需要预测未来多个时间步骤的时间序列预测问题可以称为多步时间序列预测。

具体来说,这些是预测范围或间隔超过一个时间步长的问题。

有两种主要类型的 LSTM 模型可用于多步预测;它们是:

  1. 矢量输出模型
  2. 编码器-解码器模型

在我们研究这些模型之前,让我们首先看一下多步预测的数据准备。

1. 数据准备

与单步预测一样,用于多步时间序列预测的时间序列必须分为具有输入和输出成分的样本。

输入和输出组件都将由多个时间步骤组成,并且可能具有相同或不同的步骤数。

例如,给定单变量时间序列:

[10, 20, 30, 40, 50, 60, 70, 80, 90]

我们可以使用最后三个时间步骤作为输入并预测接下来的两个时间步骤。

第一个示例如下所示:

输入:

[10, 20, 30]

输出:

[40, 50]

下面的split_sequence()函数实现了这一行为,并将给定的单变量时间序列分成具有指定数量的输入和输出时间步骤的样本。

# split a univariate sequence into samples
def split_sequence(sequence, n_steps_in, n_steps_out):
	X, y = list(), list()
	for i in range(len(sequence)):
		# find the end of this pattern
		end_ix = i + n_steps_in
		out_end_ix = end_ix + n_steps_out
		# check if we are beyond the sequence
		if out_end_ix > len(sequence):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequence[i:end_ix], sequence[end_ix:out_end_ix]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

我们可以在小型人造数据集上演示此功能。

完整的示例如下。

# multi-step data preparation
from numpy import array

# split a univariate sequence into samples
def split_sequence(sequence, n_steps_in, n_steps_out):
	X, y = list(), list()
	for i in range(len(sequence)):
		# find the end of this pattern
		end_ix = i + n_steps_in
		out_end_ix = end_ix + n_steps_out
		# check if we are beyond the sequence
		if out_end_ix > len(sequence):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequence[i:end_ix], sequence[end_ix:out_end_ix]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
raw_seq = [10, 20, 30, 40, 50, 60, 70, 80, 90]
# choose a number of time steps
n_steps_in, n_steps_out = 3, 2
# split into samples
X, y = split_sequence(raw_seq, n_steps_in, n_steps_out)
# summarize the data
for i in range(len(X)):
	print(X[i], y[i])

运行示例将单变量序列分为输入和输出时间步骤,并打印每个步骤的输入和输出组件。

[10 20 30] [40 50]
[20 30 40] [50 60]
[30 40 50] [60 70]
[40 50 60] [70 80]
[50 60 70] [80 90]

现在我们知道如何为多步预测准备数据,让我们看一些可以学习这种映射的 LSTM 模型。

2. 矢量输出模型

与其他类型的神经网络模型一样,LSTM 可以直接输出一个可以解释为多步预测的向量。

上一节中我们看到过这种方法,即将每个输出时间序列的一个时间步长预测为一个向量。

与上一节中针对单变量数据的 LSTM 一样,准备好的样本必须先进行重塑。LSTM 要求数据具有 [样本、时间步长、特征]的三维结构,在本例中,我们只有一个特征,因此重塑非常简单。

...
# define model
model = Sequential()
model.add(LSTM(100, activation='relu', return_sequences=True, input_shape=(n_steps_in, n_features)))
model.add(LSTM(100, activation='relu'))
model.add(Dense(n_steps_out))
model.compile(optimizer='adam', loss='mse')

该模型可以对单个样本进行预测。我们可以通过提供输入来预测数据集末尾的下两个步骤:

[70, 80, 90]

我们期望预测输出为:

[100, 110]

正如模型所预期的那样,进行预测时输入数据的单个样本的形状必须为 [1, 3, 1],针对输入的 1 个样本、3 个时间步长和单个特征。

...
# demonstrate prediction
x_input = array([70, 80, 90])
x_input = x_input.reshape((1, n_steps_in, n_features))
yhat = model.predict(x_input, verbose=0)

将所有这些结合在一起,下面列出了用于使用单变量时间序列进行多步预测的 Stacked LSTM。

# univariate multi-step vector-output stacked lstm example
from numpy import array
from keras.models import Sequential
from keras.layers import LSTM
from keras.layers import Dense

# split a univariate sequence into samples
def split_sequence(sequence, n_steps_in, n_steps_out):
	X, y = list(), list()
	for i in range(len(sequence)):
		# find the end of this pattern
		end_ix = i + n_steps_in
		out_end_ix = end_ix + n_steps_out
		# check if we are beyond the sequence
		if out_end_ix > len(sequence):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequence[i:end_ix], sequence[end_ix:out_end_ix]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
raw_seq = [10, 20, 30, 40, 50, 60, 70, 80, 90]
# choose a number of time steps
n_steps_in, n_steps_out = 3, 2
# split into samples
X, y = split_sequence(raw_seq, n_steps_in, n_steps_out)
# reshape from [samples, timesteps] into [samples, timesteps, features]
n_features = 1
X = X.reshape((X.shape[0], X.shape[1], n_features))
# define model
model = Sequential()
model.add(LSTM(100, activation='relu', return_sequences=True, input_shape=(n_steps_in, n_features)))
model.add(LSTM(100, activation='relu'))
model.add(Dense(n_steps_out))
model.compile(optimizer='adam', loss='mse')
# fit model
model.fit(X, y, epochs=50, verbose=0)
# demonstrate prediction
x_input = array([70, 80, 90])
x_input = x_input.reshape((1, n_steps_in, n_features))
yhat = model.predict(x_input, verbose=0)
print(yhat)

注意:由于算法或评估程序的随机性,或数值精度的差异,您的结果可能会有所不同。考虑运行示例几次并比较平均结果。

运行示例预测并打印序列中的下两个时间步骤。

[[100.98096 113.28924]]

3. 编码器-解码器模型

专门为预测可变长度输出序列而开发的模型称为编码器-解码器 LSTM。

该模型专门用于解决同时存在输入和输出序列的预测问题,即所谓的序列到序列或 seq2seq 问题,例如将文本从一种语言翻译成另一种语言。

该模型可用于多步时间序列预测。

顾名思义,该模型由两个子模型组成:编码器和解码器。

编码器是负责读取和解释输入序列的模型。编码器的输出是一个固定长度的向量,表示模型对序列的解释。编码器传统上是 Vanilla LSTM 模型,但也可以使用其他编码器模型,例如 Stacked、Bidirectional 和 CNN 模型。

...
model.add(LSTM(100, activation='relu', input_shape=(n_steps_in, n_features)))

解码器使用编码器的输出作为输入。

首先,重复编码器的固定长度输出,输出序列中每个所需时间步骤重复一次。

...
model.add(RepeatVector(n_steps_out))

然后将该序列提供给 LSTM 解码器模型。该模型必须为输出时间步长中的每个值输出一个值,该值可以由单个输出模型解释。

...
model.add(LSTM(100, activation='relu', return_sequences=True))

我们可以使用相同的输出层或多个输出层来对输出序列中的每个单步预测进行预测。这可以通过将模型的输出部分包装在TimeDistributed 包装器中来实现。

....
model.add(TimeDistributed(Dense(1)))

多步时间序列预测的编码器-解码器模型的完整定义如下所示。

...
# define model
model = Sequential()
model.add(LSTM(100, activation='relu', input_shape=(n_steps_in, n_features)))
model.add(RepeatVector(n_steps_out))
model.add(LSTM(100, activation='relu', return_sequences=True))
model.add(TimeDistributed(Dense(1)))
model.compile(optimizer='adam', loss='mse')

与其他 LSTM 模型一样,输入数据必须重塑为预期的三维形状 [样本、时间步长、特征]。

...
X = X.reshape((X.shape[0], X.shape[1], n_features))

对于编码器-解码器模型,训练数据集的输出或 y 部分也必须具有这种形状。这是因为模型将针对每个输入样本预测给定数量的时间步长和给定数量的特征。

...
y = y.reshape((y.shape[0], y.shape[1], n_features))

下面列出了用于多步时间序列预测的编码器-解码器 LSTM 的完整示例。

# univariate multi-step encoder-decoder lstm example
from numpy import array
from keras.models import Sequential
from keras.layers import LSTM
from keras.layers import Dense
from keras.layers import RepeatVector
from keras.layers import TimeDistributed

# split a univariate sequence into samples
def split_sequence(sequence, n_steps_in, n_steps_out):
	X, y = list(), list()
	for i in range(len(sequence)):
		# find the end of this pattern
		end_ix = i + n_steps_in
		out_end_ix = end_ix + n_steps_out
		# check if we are beyond the sequence
		if out_end_ix > len(sequence):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequence[i:end_ix], sequence[end_ix:out_end_ix]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
raw_seq = [10, 20, 30, 40, 50, 60, 70, 80, 90]
# choose a number of time steps
n_steps_in, n_steps_out = 3, 2
# split into samples
X, y = split_sequence(raw_seq, n_steps_in, n_steps_out)
# reshape from [samples, timesteps] into [samples, timesteps, features]
n_features = 1
X = X.reshape((X.shape[0], X.shape[1], n_features))
y = y.reshape((y.shape[0], y.shape[1], n_features))
# define model
model = Sequential()
model.add(LSTM(100, activation='relu', input_shape=(n_steps_in, n_features)))
model.add(RepeatVector(n_steps_out))
model.add(LSTM(100, activation='relu', return_sequences=True))
model.add(TimeDistributed(Dense(1)))
model.compile(optimizer='adam', loss='mse')
# fit model
model.fit(X, y, epochs=100, verbose=0)
# demonstrate prediction
x_input = array([70, 80, 90])
x_input = x_input.reshape((1, n_steps_in, n_features))
yhat = model.predict(x_input, verbose=0)
print(yhat)

注意:由于算法或评估程序的随机性,或数值精度的差异,您的结果可能会有所不同。考虑运行示例几次并比较平均结果。

运行示例预测并打印序列中的下两个时间步骤。

四. 多元多步骤 LSTM 模型

在前面的章节中,我们研究了单变量、多变量和多步时间序列预测。

可以将迄今为止介绍的不同类型的 LSTM 模型混合搭配用于不同的问题。这也适用于涉及多变量和多步骤预测的时间序列预测问题,但可能更具挑战性。

在本节中,我们将提供多元多步时间序列预测的数据准备和建模的简短示例作为模板来缓解这一挑战,具体来说:

  1. 多输入多步骤输出。
  2. 多个并行输入和多步输出。

也许最大的绊脚石在于数据的准备,所以我们将把注意力集中在这里。

1. 多输入多步骤输出

存在那些多元时间序列预测问题,其中输出序列是独立的但依赖于输入时间序列,并且输出序列需要多个时间步骤。

例如,考虑上一节中的多元时间序列:

[[ 10  15  25]
 [ 20  25  45]
 [ 30  35  65]
 [ 40  45  85]
 [ 50  55 105]
 [ 60  65 125]
 [ 70  75 145]
 [ 80  85 165]
 [ 90  95 185]]

我们可以使用两个输入时间序列中的每个时间序列的三个先前时间步骤来预测输出时间序列的两个时间步骤。

输入:

10, 15
20, 25
30, 35

输出:

65
85

下面的split_sequences()函数实现了这一行为。

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps_in, n_steps_out):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps_in
		out_end_ix = end_ix + n_steps_out-1
		# check if we are beyond the dataset
		if out_end_ix > len(sequences):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :-1], sequences[end_ix-1:out_end_ix, -1]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

我们可以在我们设计的数据集上证明这一点。

完整的示例如下。

# multivariate multi-step data preparation
from numpy import array
from numpy import hstack

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps_in, n_steps_out):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps_in
		out_end_ix = end_ix + n_steps_out-1
		# check if we are beyond the dataset
		if out_end_ix > len(sequences):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :-1], sequences[end_ix-1:out_end_ix, -1]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])
# convert to [rows, columns] structure
in_seq1 = in_seq1.reshape((len(in_seq1), 1))
in_seq2 = in_seq2.reshape((len(in_seq2), 1))
out_seq = out_seq.reshape((len(out_seq), 1))
# horizontally stack columns
dataset = hstack((in_seq1, in_seq2, out_seq))
# choose a number of time steps
n_steps_in, n_steps_out = 3, 2
# covert into input/output
X, y = split_sequences(dataset, n_steps_in, n_steps_out)
print(X.shape, y.shape)
# summarize the data
for i in range(len(X)):
	print(X[i], y[i])

运行示例首先打印准备好的训练数据的形状。

我们可以看到样本输入部分的形状是三维的,由六个样本组成,有三个时间步骤,以及两个输入时间序列的两个变量。

样本的输出部分是二维的,包括六个样本和每个要预测的样本的两个时间步长。

然后打印准备好的样本以确认数据按照我们指定的方式准备。

(6, 3, 2) (6, 2)

[[10 15]
 [20 25]
 [30 35]] [65 85]
[[20 25]
 [30 35]
 [40 45]] [ 85 105]
[[30 35]
 [40 45]
 [50 55]] [105 125]
[[40 45]
 [50 55]
 [60 65]] [125 145]
[[50 55]
 [60 65]
 [70 75]] [145 165]
[[60 65]
 [70 75]
 [80 85]] [165 185]

我们现在可以开发一个用于多步预测的 LSTM 模型。

可以使用向量输出或编码器-解码器模型。在本例中,我们将演示使用 Stacked LSTM 的向量输出。

完整的示例如下。

# multivariate multi-step stacked lstm example
from numpy import array
from numpy import hstack
from keras.models import Sequential
from keras.layers import LSTM
from keras.layers import Dense

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps_in, n_steps_out):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps_in
		out_end_ix = end_ix + n_steps_out-1
		# check if we are beyond the dataset
		if out_end_ix > len(sequences):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :-1], sequences[end_ix-1:out_end_ix, -1]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])
# convert to [rows, columns] structure
in_seq1 = in_seq1.reshape((len(in_seq1), 1))
in_seq2 = in_seq2.reshape((len(in_seq2), 1))
out_seq = out_seq.reshape((len(out_seq), 1))
# horizontally stack columns
dataset = hstack((in_seq1, in_seq2, out_seq))
# choose a number of time steps
n_steps_in, n_steps_out = 3, 2
# covert into input/output
X, y = split_sequences(dataset, n_steps_in, n_steps_out)
# the dataset knows the number of features, e.g. 2
n_features = X.shape[2]
# define model
model = Sequential()
model.add(LSTM(100, activation='relu', return_sequences=True, input_shape=(n_steps_in, n_features)))
model.add(LSTM(100, activation='relu'))
model.add(Dense(n_steps_out))
model.compile(optimizer='adam', loss='mse')
# fit model
model.fit(X, y, epochs=200, verbose=0)
# demonstrate prediction
x_input = array([[70, 75], [80, 85], [90, 95]])
x_input = x_input.reshape((1, n_steps_in, n_features))
yhat = model.predict(x_input, verbose=0)
print(yhat)

运行该示例适合模型并预测数据集之外的输出序列的接下来两个时间步骤。

我们预计接下来的两个步骤是:[185, 205]

注意:由于算法或评估程序的随机性,或数值精度的差异,您的结果可能会有所不同。考虑运行示例几次并比较平均结果。

这是一个使用极少数据来构建问题的具有挑战性的问题,并且任意配置的模型版本都很接近。

[[188.70619 210.16513]]

2. 多个并行输入和多步输出

并行时间序列问题可能需要预测每个时间序列的多个时间步骤。

例如,考虑上一节中的多元时间序列:

[[ 10  15  25]
 [ 20  25  45]
 [ 30  35  65]
 [ 40  45  85]
 [ 50  55 105]
 [ 60  65 125]
 [ 70  75 145]
 [ 80  85 165]
 [ 90  95 185]]

我们可以使用三个时间序列中的最后三个时间步骤作为模型的输入,并预测三个时间序列中的下一个时间步骤作为输出。

训练数据集中的第一个样本如下。

输入:

10, 15, 25
20, 25, 45
30, 35, 65

输出:

40, 45, 85
50, 55, 105

下面的split_sequences()函数实现了这一行为。

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps_in, n_steps_out):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps_in
		out_end_ix = end_ix + n_steps_out
		# check if we are beyond the dataset
		if out_end_ix > len(sequences):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :], sequences[end_ix:out_end_ix, :]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

我们可以在小型人造数据集上演示此功能。

完整的示例如下。

# multivariate multi-step data preparation
from numpy import array
from numpy import hstack
from keras.models import Sequential
from keras.layers import LSTM
from keras.layers import Dense
from keras.layers import RepeatVector
from keras.layers import TimeDistributed

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps_in, n_steps_out):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps_in
		out_end_ix = end_ix + n_steps_out
		# check if we are beyond the dataset
		if out_end_ix > len(sequences):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :], sequences[end_ix:out_end_ix, :]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])
# convert to [rows, columns] structure
in_seq1 = in_seq1.reshape((len(in_seq1), 1))
in_seq2 = in_seq2.reshape((len(in_seq2), 1))
out_seq = out_seq.reshape((len(out_seq), 1))
# horizontally stack columns
dataset = hstack((in_seq1, in_seq2, out_seq))
# choose a number of time steps
n_steps_in, n_steps_out = 3, 2
# covert into input/output
X, y = split_sequences(dataset, n_steps_in, n_steps_out)
print(X.shape, y.shape)
# summarize the data
for i in range(len(X)):
	print(X[i], y[i])

首先运行示例会打印准备好的训练数据集的形状。

我们可以看到,数据集的输入(X)和输出(Y)元素分别是样本数量、时间步长和变量或并行时间序列的三维。

然后将每个系列的输入和输出元素并排打印出来,以便我们可以确认数据是否按预期准备。

(5, 3, 3) (5, 2, 3)

[[10 15 25]
 [20 25 45]
 [30 35 65]] [[ 40  45  85]
 [ 50  55 105]]
[[20 25 45]
 [30 35 65]
 [40 45 85]] [[ 50  55 105]
 [ 60  65 125]]
[[ 30  35  65]
 [ 40  45  85]
 [ 50  55 105]] [[ 60  65 125]
 [ 70  75 145]]
[[ 40  45  85]
 [ 50  55 105]
 [ 60  65 125]] [[ 70  75 145]
 [ 80  85 165]]
[[ 50  55 105]
 [ 60  65 125]
 [ 70  75 145]] [[ 80  85 165]
 [ 90  95 185]]

我们可以使用向量输出或编码器-解码器 LSTM 来建模这个问题。在本例中,我们将使用编码器-解码器模型。

完整的示例如下。

# multivariate multi-step encoder-decoder lstm example
from numpy import array
from numpy import hstack
from keras.models import Sequential
from keras.layers import LSTM
from keras.layers import Dense
from keras.layers import RepeatVector
from keras.layers import TimeDistributed

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps_in, n_steps_out):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps_in
		out_end_ix = end_ix + n_steps_out
		# check if we are beyond the dataset
		if out_end_ix > len(sequences):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :], sequences[end_ix:out_end_ix, :]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])
# convert to [rows, columns] structure
in_seq1 = in_seq1.reshape((len(in_seq1), 1))
in_seq2 = in_seq2.reshape((len(in_seq2), 1))
out_seq = out_seq.reshape((len(out_seq), 1))
# horizontally stack columns
dataset = hstack((in_seq1, in_seq2, out_seq))
# choose a number of time steps
n_steps_in, n_steps_out = 3, 2
# covert into input/output
X, y = split_sequences(dataset, n_steps_in, n_steps_out)
# the dataset knows the number of features, e.g. 2
n_features = X.shape[2]
# define model
model = Sequential()
model.add(LSTM(200, activation='relu', input_shape=(n_steps_in, n_features)))
model.add(RepeatVector(n_steps_out))
model.add(LSTM(200, activation='relu', return_sequences=True))
model.add(TimeDistributed(Dense(n_features)))
model.compile(optimizer='adam', loss='mse')
# fit model
model.fit(X, y, epochs=300, verbose=0)
# demonstrate prediction
x_input = array([[60, 65, 125], [70, 75, 145], [80, 85, 165]])
x_input = x_input.reshape((1, n_steps_in, n_features))
yhat = model.predict(x_input, verbose=0)
print(yhat)

运行该示例适合模型,并预测数据集结束后接下来两个时间步骤的三个时间步骤中的每一个值。

我们期望这些系列和时间步长的值如下:

90, 95, 185
100, 105, 205

注意:由于算法或评估程序的随机性,或数值精度的差异,您的结果可能会有所不同。考虑运行示例几次并比较平均结果。

我们可以看到模型预测值相当接近预期值。

[[[ 91.86044   97.77231  189.66768 ]
  [103.299355 109.18123  212.6863  ]]]


让我们成为知识的传播者!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

魍魉1988

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值