前言
卷积神经网络模型,简称CNNs,可以应用于时间序列预测。
有许多类型的CNN模型可以用于每一种特定类型的时间序列预测问题。
在本教程中,您将了解如何为一系列标准时间序列预测问题开发一套CNN模型。
本教程的目标是提供关于每种类型的时间序列问题的每个模型的独立示例,作为模板,您可以复制并适应特定的时间序列预测问题。
完成本教程后,您将了解:
- 如何开发用于单变量时间序列预测的CNN模型。
- 如何建立多元时间序列预测的CNN模型。
- 如何建立CNN模型进行多步时间序列预测。
这是一篇大而重要的文章;你可能想把它作为书签,以备将来参考。
提示:以下是本篇文章正文内容,下面案例可供参考
概述
在本文中,我们将探讨如何为时间序列预测开发一套不同类型的CNN模型。
这些模型是在小的人为时间序列问题上演示的,目的是给出所处理的时间序列问题类型的特征。选择的模型配置是任意的,不是针对每个问题进行优化的;这不是目标。
本文分为四个部分,分别是
- 单变量CNN模型
- 多元CNN模型
- 多步CNN模型
- 多变量多步CNN模型
单变量CNN模型
尽管传统的CNNs是针对二维图像数据开发的,但CNNs可以用来模拟单变量时间序列预测问题。
单变量时间序列是由具有时间顺序的单个观测序列组成的数据集,需要一个模型从过去的观测序列中学习,以预测序列中的下一个值。
本节分为两部分,分别是:
- 数据准备
- CNN模型
数据准备
在一个单变量序列可以被建模之前,它必须被准备好。
CNN模型将学习一个函数,该函数将一系列过去的观测值映射为一个输出观测值的输入。因此,观测序列必须转化为多个实例,模型可以从中学习。
考虑一个给定的单变量序列:
[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
既然我们知道了如何准备一个用于建模的单变量序列,那么让我们来看看开发一个CNN模型,它可以学习输入到输出的映射。
CNN模型
一维CNN是一种CNN模型,它有一个卷积隐藏层,在一维序列上运行。在某些情况下,这之后可能是第二卷积层,例如很长的输入序列,然后是池层,其工作是将卷积层的输出提取到最显著的元素。
卷积层和池层之后是一个密集的完全连接层,该层解释了模型卷积部分提取的特征。在卷积层和稠密层之间使用平坦层将特征映射简化为单个一维向量。
我们可以为单变量时间序列预测定义一个一维CNN模型,如下所示。
# define model
model = Sequential()
model.add(Conv1D(filters=64, kernel_size=2, activation='relu', input_shape=(n_steps, n_features)))
model.add(MaxPooling1D(pool_size=2))
model.add(Flatten())
model.add(Dense(50, activation='relu'))
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')
定义中的关键是输入的形状;这就是模型所期望的每个样本输入的时间步数和特征数。
我们使用的是一个单变量序列,所以特征的数量是一个变量。
作为输入的时间步数是我们在准备数据集作为split_sequence()函数的参数时选择的数。
每个样本的输入形状在第一个隐藏层的定义上的输入形状参数中指定。
我们几乎总是有多个样本,因此,模型希望输入部分的训练数据的具有以下维度或形状:
[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))
CNN实际上并没有将数据视为具有时间步长,而是将其视为可以执行卷积读取操作的序列,如一维图像。
在本例中,我们定义了一个具有64个过滤器映射和2个内核大小的卷积层。然后是一个最大池层和一个密集层来解释输入特征。指定了预测单个数值的输出层。
该模型采用有效的Adam随机梯度下降法进行拟合,并采用均方误差(mse)损失函数进行优化。
一旦定义了模型,我们就可以将其拟合到训练数据集上。
# fit model
model.fit(X, y, epochs=1000, 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)
我们可以将所有这些联系在一起,演示如何开发用于单变量时间序列预测的一维CNN模型,并进行单个预测。
# univariate cnn example
from numpy import array
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Flatten
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 s