在 Keras 中实现 Causal CNN 以进行多变量时间序列预测

Posted

技术标签:

【中文标题】在 Keras 中实现 Causal CNN 以进行多变量时间序列预测【英文标题】:Implement Causal CNN in Keras for multivariate time-series prediction 【发布时间】:2019-11-05 18:35:27 【问题描述】:

这个问题是我之前的问题的后续问题:Multi-feature causal CNN - Keras implementation,但是,我认为有很多事情不清楚,我认为需要提出一个新问题。这里有问题的模型是根据上面提到的帖子中接受的答案构建的。

我正在尝试对具有 5 个特征的 10 个序列的多元时间序列数据应用因果 CNN 模型。

lookback, features = 10, 5

过滤器和内核应该设置成什么?

过滤器和内核对网络有什么影响? 这些只是一个任意数字 - 即 ANN 层中的神经元数量吗? 或者它们会对网络如何解释时间步长产生影响?

扩张应该设置成什么?

这只是一个任意数字还是代表模型的lookback
filters = 32
kernel = 5
dilations = 5
dilation_rates = [2 ** i for i in range(dilations)]

model = Sequential()
model.add(InputLayer(input_shape=(lookback, features)))
model.add(Reshape(target_shape=(features, lookback, 1), input_shape=(lookback, features)))

根据前面提到的答案,输入需要按照以下逻辑进行reshape:

Reshape 之后,现在将 5 个输入要素视为 TimeDistributed 层的时间层

当Conv1D应用于每个输入特征时,它认为层的形状是(10, 1)

使用默认的“channels_last”,因此...

10 个时间步是时间维度 1 是“通道”,即特征图的新位置
# Add causal layers
for dilation_rate in dilation_rates:
    model.add(TimeDistributed(Conv1D(filters=filters,
                              kernel_size=kernel,
                              padding='causal',
                              dilation_rate=dilation_rate,
                              activation='elu')))

根据上面提到的答案,模型需要重塑,按照以下逻辑:

将特征图堆叠在一起,这样每个时间步都可以查看之前生成的所有特征 -(10 个时间步,5 个特征 * 32 个过滤器)

接下来,因果层现在独立地应用于 5 个输入特征。

为什么最初是独立应用的? 为什么它们现在独立应用?
model.add(Reshape(target_shape=(lookback, features * filters)))

next_dilations = 3
dilation_rates = [2 ** i for i in range(next_dilations)]
for dilation_rate in dilation_rates:
    model.add(Conv1D(filters=filters,
                     kernel_size=kernel,
                     padding='causal',
                     dilation_rate=dilation_rate,
                     activation='elu'))
    model.add(MaxPool1D())

model.add(Flatten())
model.add(Dense(units=1, activation='linear'))

model.summary()

总结

过滤器和内核应该设置成什么? 它们会影响网络如何解释时间步长吗?

应该将扩张设置为什么来表示 10 的回溯?

为什么最初独立应用因果层?

reshape 后为什么要依赖应用? 为什么不从一开始就独立应用它们?

================================================ ==============================

完整代码

lookback, features = 10, 5

filters = 32
kernel = 5
dilations = 5
dilation_rates = [2 ** i for i in range(dilations)]

model = Sequential()
model.add(InputLayer(input_shape=(lookback, features)))
model.add(Reshape(target_shape=(features, lookback, 1), input_shape=(lookback, features)))

# Add causal layers
for dilation_rate in dilation_rates:
    model.add(TimeDistributed(Conv1D(filters=filters,
                              kernel_size=kernel,
                              padding='causal',
                              dilation_rate=dilation_rate,
                              activation='elu')))


model.add(Reshape(target_shape=(lookback, features * filters)))

next_dilations = 3
dilation_rates = [2 ** i for i in range(next_dilations)]
for dilation_rate in dilation_rates:
    model.add(Conv1D(filters=filters,
                     kernel_size=kernel,
                     padding='causal',
                     dilation_rate=dilation_rate,
                     activation='elu'))
    model.add(MaxPool1D())

model.add(Flatten())
model.add(Dense(units=1, activation='linear'))

model.summary()

================================================ ==============================

编辑:

丹尼尔,谢谢你的回答。

问题:

如果您可以“准确”解释您是如何构建数据的、原始数据是什么以及如何将其转换为输入形状、如果您有独立的序列、如果您正在创建滑动windows等。可以更好地理解这个过程。

答案:

希望我能正确理解您的问题。

每个特征都是时间序列数据的序列数组。它们是独立的,因为它们不是图像,但是它们在某种程度上相互关联。

这就是我尝试使用 Wavenet 的原因,它非常擅长预测单个时间序列数组,但是,我的问题需要我使用多个多个特征。

【问题讨论】:

【参考方案1】:

对给定答案的评论

问题:

为什么因果层最初是独立应用的? 为什么它们在 reshape 后依赖应用? 为什么不从一开始就独立应用它们?

这个答案有点奇怪。我不是专家,但我认为没有必要使用TimeDistributed 层来保留独立功能。但我也不能说它是否给出了更好的结果。起初我会说这只是不必要的。但它可能会带来额外的智能,因为它可能会看到涉及两个特征之间的遥远步骤的关系,而不是仅仅查看“相同的步骤”。 (这个应该测试一下)

不过,这种方法有一个错误

旨在交换回溯和特征大小的重塑没有达到预期的效果。答案的作者显然想swap axes(保持对什么是特征,什么是lookback的解释),这与reshape不同(混合一切,数据失去意义) )

正确的方法需要实际的轴交换,例如 model.add(Permute((2,1))) 而不是重塑。

所以,我不知道这些答案,但似乎没有什么能产生这种需求。 一件可以肯定的事情是:你肯定会想要依赖的部分。如果一个模型不考虑特征之间的关系,它就不会接近原始模型的智能。 (除非你有幸拥有完全独立的数据)

现在,解释一下 LSTM 和 Conv1D 之间的关系

LSTM 可以直接与Conv1D 进行比较,并且使用的形状完全相同,它们的含义几乎相同,只要您使用的是channels_last

也就是说,(samples, input_length, features_or_channels) 的形状对于LSTMConv1D 都是正确的。事实上,在这种情况下,特征和渠道是完全一样的。变化的是每一层在输入长度和计算方面的工作方式。

过滤器和内核的概念

内核是卷积层内的整个张量,它将与输入相乘以获得结果。内核包括其空间大小 (kernel_size) 和 filters 的数量(输出特征)。还有自动输入过滤器。

没有多少内核,但是有一个kernel_size。内核大小是每个输出步骤将连接到长度中的步骤数。 (这个tutorial 非常适合理解 2D 卷积关于它的作用和内核大小是什么——想象一下 1D 图像——尽管本教程没有显示“过滤器”的数量,它就像 1 过滤器动画)

filters的数量与features的数量直接相关,它们是一回事。

过滤器和内核应该设置成什么?

所以,如果你的LSTM 层使用units=256,这意味着它将输出 256 个特征,你应该使用filters=256,这意味着你的卷积将输出 256 个通道/特征。

这不是一个规则,虽然,您可能会发现使用更多或更少的过滤器可以带来更好的结果,因为这些层毕竟做不同的事情。没有必要让所有层都具有相同数量的过滤器!!在这里,您应该进行参数调整。 测试看看哪些数字最适合您的目标和数据。

现在,内核大小是无法与 LSTM 相比的。这是模型中添加的新事物。

数字 3 是一种非常常见的选择。这意味着卷积需要三个时间步才能产生一个时间步。然后滑动一个步骤以采取另一组三个步骤来生成下一步,依此类推。

扩张

Dilations 表示卷积滤波器在步骤之间有多少空间。

卷积dilation_rate=1 需要kernel_size 连续的步骤来产生一个步骤。 与dilation_rate = 2 的卷积例如需要步骤 0、2 和 4 来产生一个步骤。然后执行步骤 1,3,5 以产生下一步,依此类推。

应该将扩张设置为什么来表示回溯 10?

range = 1 + (kernel_size - 1) * dilation_rate

所以,内核大小 = 3:

Dilation = 0 (dilation_rate=1):内核大小的范围为 3 步 Dilation = 1 (dilation_rate=2):内核大小的范围为 5 步 Dilation = 2 (dilation_rate=4):内核大小范围为 9 步 Dilation = 3 (dilation_rate=8):内核大小范围为 17 步

我的问题

如果您能“准确”解释您是如何构建数据的、原始数据是什么以及如何将其转换为输入形状、是否有独立序列、是否正在创建滑动窗口等. 可以更好地理解这个过程。

【讨论】:

以上是关于在 Keras 中实现 Causal CNN 以进行多变量时间序列预测的主要内容,如果未能解决你的问题,请参考以下文章

损失函数中带有 SVM 的最后一层 CNN

MLP初始化Keras中的LSTM细胞状态

Keras 的 BatchNormalization 和 PyTorch 的 BatchNorm2d 的区别?

在 Keras 中实现模型。如何解释填充/步幅值?

在具有条件的 keras 中实现自定义损失函数

如何在 Keras 中实现 Salt&Pepper 层?