https://machinelearningmastery.com/how-to-develop-lstm-models-for-time-series-forecasting/
挺不错的一篇博客,翻译记录一下
长短期记忆网络,或简称LSTM,可以应用于时间序列预测。
有许多类型的LSTM模型可用于每个特定类型的时间序列预测问题。
在本教程中,您将发现如何为一系列标准时间序列预测问题开发一组LSTM模型。
本教程的目的是提供关于每种类型的时间序列问题的每个模型的独立示例,作为模板,您可以复制并适应特定的时间序列预测问题。
完成本教程后,您将知道:
如何建立单变量时间序列预测的LSTM模型。
如何建立多变量时间序列预测的LSTM模型。
如何建立多步时间序列预测的LSTM模型。
这是一个大而重要的帖子,你可能想把它收藏起来以备将来参考。
让我们开始吧。
教程概述
在本教程中,我们将探讨如何开发用于时间序列预测的一组不同类型的LSTM模型。
这些模型在小型人工时间序列问题上被演示,旨在给出所处理的时间序列问题的类型。所选择的模型的配置是任意的,并且没有针对每个问题进行优化;这不是目标。
本教程分为四个部分:
单变量LSTM模型
多元LSTM模型
多步LSTM模型
多元多步LSTM模型
单变量LSTM模型
LSTM可用于单变量时间序列预测问题的建模。
这些问题由单个观测序列组成,并且需要模型从过去的观测序列中学习以预测序列中的下一个值。
我们将演示用于单变量时间序列预测的LSTM模型的若干变体。
本部分分为六个部分:
Data Preparation
Vanilla LSTM
Stacked LSTM
Bidirectional LSTM
CNN LSTM
ConvLSTM
这些模型中的每一个都被演示用于单步单变量时间序列预测,但是对于其他类型的时间序列预测问题,这些模型可以很容易地被调整并用作模型的输入部分。
在单变量序列建模之前,必须对其进行准备。
LSTM模型将学习将过去的观察序列映射为输出观察的函数。因此,观察序列必须转换为多个示例,LSTM可以从这些示例中学习。
考虑给定的单变量序列:
[10, 20, 30, 40, 50, 60, 70, 80, 90]
我们可以将序列划分为称为样本的多个输入/输出模式,其中三个时间步用作输入,一个时间步用作输出,用于正在学习的一步预测。
X, y
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开始。
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()函数用形状[样本,时间步]输出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单元和一个预测单个数值的输出层。
该模型适用于使用随机梯度下降(SGD)的有效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]]
在所谓的堆叠LSTM模型中,多个隐藏LSTM层可以逐层堆叠。
LSTM层需要三维输入,默认情况下,LSTM将从序列末尾生成二维输出作为解释。
通过在层上设置return_sequences=True参数,我们可以通过让LSTM为输入数据中的每个时间步骤输出一个值来解决这个问题。这使得我们可以从隐藏的LSTM层得到3D输出作为下一个输入。
因此,我们可以定义一个堆叠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]]
对于某些序列预测问题,允许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]]
卷积神经网络,简称CNN,是一种开发用于处理二维图像数据的神经网络。
CNN能够有效地从一维序列数据(如单变量时间序列数据)中自动提取和学习特征。
CNN模型可用于具有LSTM后端的混合模型,其中CNN用于解释输入的子序列,这些子序列一起作为序列提供给LSTM模型以解释。这种混合模型被称为CNN-LSTM。
第一步是将输入序列分成可由CNN模型处理的子序列。例如,我们可以首先将单变量时间序列数据分割成输入/输出样本,其中四个步骤作为输入,一个步骤作为输出。然后将每个样本分成两个子样本,每个子样本具有两个时间步长。CNN可以解释两个时间步的每个子序列,并且向LSTM模型提供子序列的一系列解释以作为输入进行处理。
我们可以将其参数化,并将子序列的数量定义为n_seq,而每个子序列的时间步数定义为n_step。然后可以重新输入输入数据以具有所需的结构:
[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模型。
这可以通过在TimeDistributed包装器中包装整个CNN模型来实现,每个输入通过TimeDistributed包装器将应用整个模型一次,在本例中,每个输入子序列应用一次。
CNN模型首先具有用于跨子序列读取的卷积层,这需要指定多个过滤器和内核大小。过滤器的数量是输入序列的读取或解释的数量。内核大小是输入序列的每个“读取”操作所包括的时间步数。
卷积层之后是最大池化层,该层将滤波器映射提取到包括最显著特征的其大小的1/4。然后,这些结构被平坦化成单个一维向量,用作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部分,该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]]
与CNN-LSTM相关的LSTM的一种类型是ConvLSTM,其中输入的卷积读取被直接构建到每个LSTM单元中。
ConvLSTM是用于读取二维时空数据的,但是可以适用于单变量时间序列预测。
该层期望输入作为二维图像的序列,因此输入数据的形状必须是:
[samples, timesteps, rows, columns, features]
出于我们的目的,我们可以将每个样本分割成子序列,其中时间步长将变成子序列的数目,或n_seq,列将变成每个子序列或n_step的时间步长。当我们使用一维数据时,行数固定在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。
然后,模型的输出必须被压平( flattened ),然后才能对其进行解释和预测。
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模型
多变量时间序列数据指的是每一个时间步长有多于一个观测值的数据。
有两个主要模型,我们可能需要多变量时间序列数据,它们是:
多输入序列。
多重并行序列
让我们依次看看每一个。
一个问题可能具有两个或多个并行的输入时间序列和一个依赖于输入时间序列的输出时间序列。
输入时间序列是并行的,因为每个序列在相同的时间步长上有一个观测值。
我们可以用两个并行输入时间序列的简单示例来演示这一点,其中输出序列是输入序列的简单相加。
# 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个。第二个维度是每个样本的时间步数,在本例中是函数指定的值。最后,最后一个维度指定并行时间序列的数量或变量的数量,在本例中是两个并行序列的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、堆叠、双向、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)。
数据已经准备好用于预期每个样本为三维输入的X和二维输出y的LSTM模型。
然后,打印每个样本,显示每个样本的输入和输出组件。
(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,例如Vailla、堆叠、双向、CNN或ConvLSTM模型。
我们将使用一个堆叠LSTM,其中通过input_shape参数为输入层指定了时间步数和并行序列(特性)。并行序列的数目还用于指定输出层中模型要预测的值的数量;同样,这是3。
# 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]
我们可以把所有这些联系在一起,并在下面演示用于多变量输出时间序列预测的堆叠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模型可用于多步预测,它们是:
向量输出模型
编码器解码器模型
在研究这些模型之前,我们首先看一下多步预测的数据准备。
与一步预测一样,用于多步时间序列预测的时间序列必须分成具有输入和输出分量的样本。
输入和输出组件都将由多个时间步骤组成,并且可以具有相同的步骤数量,也可以不具有相同的步骤数量。
例如,给定单变量时间序列:
[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模型,它们可以学习这种映射。
与其他类型的神经网络模型一样,LSTM可以直接输出一个向量,该向量可以被解释为多步预测。
这个方法在上一节可以看到,每个输出时间序列的一个时间步长被预测为一个向量。
与前面部分中针对单变量数据的LSTM一样,必须首先重塑制备的样品。LSTM期望数据具有[样本、时间步骤、特征]的三维结构,在这种情况下,我们只有一个特征,因此重塑是简单的。
# reshape from [samples, timesteps] into [samples, timesteps, features]
n_features = 1
X = X.reshape((X.shape[0], X.shape[1], n_features))
利用n_step_in和n_step_out变量中指定的输入和输出步骤的数量,我们可以定义一个多步时间序列预测模型。
可以使用任何提出的LSTM模型类型,如Vailla、堆叠、双向、CNN-LSTM或ConvLSTM。下面定义了用于多步预测的堆叠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,3,1]。
# 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)
结合所有这些,下面列出了用于单变量时间序列的多步预测的叠加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]]
一种专门用于预测可变长度输出序列的模型被称为编码器解码器LSTM。
该模型被设计用于预测问题,其中存在输入和输出序列,即所谓的序列到序列,或者SEQ2SEQ问题,例如从一种语言到另一种语言的文本翻译。
该模型可用于多步时间序列预测。
顾名思义,该模型由两个子模型组成:编码器和解码器。
编码器是负责读取和解释输入序列的模型。编码器的输出是一个固定长度的向量,它表示模型的序列解释。编码器是传统的Vaillna LSTM模型,尽管其他编码器模型也可以使用,如堆叠、双向和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)
运行示例预测并打印序列中接下来的两个时间步骤。
[[[101.9736
[116.213615]]]
多元多步LSTM模型
在前面的章节中,我们讨论了单变量、多变量和多步时间序列预测。
对于不同的问题,可以混合和匹配到目前为止提出的不同类型的LSTM模型。这也适用于涉及多变量和多步预测的时间序列预测问题,但它可能更具挑战性。
在本节中,我们将提供用于多变量多步时间序列预测的数据准备和建模的简短示例,作为减轻这一挑战的模板,具体而言:
多输入多步输出。
多并行输入和多步输出。
也许最大的障碍在于数据的准备,所以我们将集中注意力。
存在输出序列独立但依赖于输入时间序列的多元时间序列预测问题,并且输出序列需要多个时间步骤。
例如,从先前的部分考虑我们的多变量时间序列:
[[ 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模型。
可以使用矢量输出或编码器解码器模型。在这种情况下,我们将演示一个堆叠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]]
并行时间序列的问题可能需要对每个时间序列的多个时间步长进行预测。
例如,从先前的部分考虑我们的多变量时间序列:
[[ 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 ]]]
在本教程中,您发现了如何为一系列标准时间序列预测问题开发一组LSTM模型。
具体来说,你学到:
如何建立单变量时间序列预测的LSTM模型。
如何建立多变量时间序列预测的LSTM模型。
如何建立多步时间序列预测的LSTM模型。
你有什么问题吗?
在下面的评论中问你的问题,我会尽我最大的努力去回答。