下面图片显示
这是下面的文档链接
时间卷积网络和预测
目录
1.动机.... 2
2.基本模型.... 2
2.1概述.... 2
2.2一维卷积网络.... 2
2.3因果卷积.... 4
2.4扩张.... 5
2.5基本 TCN 概述.... 7
3预测.... 8
4模型的改进.... 9
4.1剩余块.... 9
4.2激活、归一化、正则化.... 10
5最终模型.... 11
6例子.... 11
7结论.... 14
时间卷积网络和预测
尽管通常与图像分类任务相关联,但经过正确修改,卷积神经网络 (CNN) 已被证明是用于序列建模和预测的宝贵工具。在本文中,我们详细探讨了时间卷积网络 (TCN) 的基本构建块,以及它们如何组合在一起以创建强大的预测模型。使用我们的开源 Darts TCN 实现,我们表明只需几行代码就可以在真实世界的数据集上实现准确的预测。
以下对时间卷积网络的描述基于以下论文https://arxiv.org/pdf/1803.01271.pdf(上一个文章已经翻译过这篇文章)对本文的引用将用 (*) 表示。
1.动机
直到最近,深度学习上下文中的序列建模主题主要与循环神经网络架构(如 LSTM 和 GRU)相关。 S. Bai 等人。 (*) 表明这种思维方式已经过时,在对序列数据进行建模时,应将卷积网络作为主要候选之一。他们能够证明卷积网络可以在许多任务中获得比 RNN 更好的性能,同时避免了循环模型的常见缺点,例如梯度爆炸/消失问题或缺乏记忆保留。此外,使用卷积网络而不是循环网络可以提高性能,因为它允许并行计算输出。他们提出的架构称为时间卷积网络 (TCN),将在以下部分进行解释。为了便于理解 TCN 架构及其 Darts 实现,本文将尽可能使用与库中相同的模型参数名称(以粗体表示)。
2.基本模型
2.1概述
TCN 是 Temporal Convolutional Network 的缩写,由具有相同输入和输出长度的扩张的因果 1D 卷积层组成。以下部分详细介绍了这些术语的实际含义。
2.2一维卷积网络
一维卷积网络将一个 3 维张量作为输入,并输出一个 3 维张量。我们 TCN 实现的输入张量的形状为 (batch_size, input_length, input_size),输出张量的形状为 (batch_size, input_length, output_size)。由于 TCN 中的每一层都具有相同的输入和输出长度,因此只有输入和输出张量的第三维不同。在单变量情况下, input_size 和 output_size 都等于 1。在更一般的多元情况下, input_size 和 output_size 可能不同,因为我们可能不想预测输入序列的每个组件。
一个单一的 1D 卷积层接收形状为 (batch_size, input_length, nr_input_channels) 的输入张量并输出形状为 (batch_size, input_length, nr_output_channels) 的张量。要了解单个层如何将其输入转换为输出,让我们看一下批处理中的一个元素(批处理中的每个元素都进行相同的过程)。让我们从最简单的情况开始,其中 nr_input_channels 和 nr_output_channels 都等于 1。在这种情况下,我们正在查看一维输入和输出张量。下图显示了如何计算输出张量的一个元素。
我们可以看到,为了计算输出的一个元素,我们查看输入的一系列长度为 kernel_size 的连续元素。在上面的例子中,我们选择了 3 的 kernel_size。为了获得输出,我们取输入的子序列和相同长度的学习权重的核向量的点积。要获得输出的下一个元素,应用相同的过程,但输入序列的 kernel_size 大小的窗口向右移动一个元素(对于此预测模型,步幅始终设置为 1) .请注意,将使用相同的一组内核权重来计算一个卷积层的每个输出。下图显示了两个连续的输出元素及其各自的输入子序列。
为了使可视化更简单,不再显示与内核向量的点积,而是针对具有相同内核权重的每个输出元素进行。
为了确保输出序列与输入序列具有相同的长度,应用了一些零填充。这意味着将额外的零值条目添加到输入张量的开头或结尾,以确保输出具有所需的长度。这将在后面的部分中解释。
现在让我们看看我们有多个输入通道的情况,即 nr_input_channels 大于 1。在这种情况下,对每个输入通道重复上述过程,但每次使用不同的内核。这导致 nr_input_channels 中间输出向量和 kernel_size * nr_input_channels 的多个内核权重。然后,将所有中间输出向量相加得到最终的输出向量。在某种意义上,这等效于具有形状为 (input_size, nr_input_channels) 的输入张量和形状为 (kernel_size, nr_input_channels) 的内核的 2D 卷积,如下图所示。从窗口仅沿单个轴移动的意义上讲,它仍然是 1D 的,但在我们使用二维核矩阵的意义上,我们确实在每一步都有一个 2D 卷积。
在这个例子中,我们选择 nr_input_channels 等于 2。现在,不是内核向量在 1 维输入序列上滑动,我们有一个 nr_input_channels by kernel_size 内核矩阵沿着 nr_input_channels 宽系列长度 input_length 滑动。
如果 nr_input_channels 和 nr_output_channels 都大于 1,则对具有不同内核矩阵的每个输出通道简单地重复上述过程。然后将输出向量堆叠在一起,从而产生形状为 (input_length, nr_output_channels) 的输出张量。在这种情况下,内核权重的数量等于 kernel_size*nr_input_channels*nr_output_channels。
两个变量 nr_input_channels 和 nr_output_channels 取决于层在网络中的位置。第一层将有 nr_input_channels = input_size,最后一层将有 nr_output_channels = output_size。所有其他层将使用由 num_filters 给出的中间通道数。
2.3因果卷积
对于具有因果关系的卷积层,对于 {0, …, input_length — 1} 中的每个 i,输出序列的第 i 个元素可能仅取决于具有索引 {0, …, i} 的输入序列的元素。换句话说,输出序列中的元素只能依赖于输入序列中它之前的元素。如前所述,为了确保输出张量与输入张量具有相同的长度,我们需要应用零填充。如果我们只在输入张量的左侧应用零填充,则将确保因果卷积。要理解这一点,请考虑最右边的输出元素。鉴于输入序列的右侧没有填充,它所依赖的最后一个元素是输入的最后一个元素。现在考虑输出序列的倒数第二个输出元素。与最后一个输出元素相比,它的内核窗口向左移动了一个,这意味着它在输入序列中最右边的依赖是输入序列的倒数第二个元素。由归纳可知,对于输出序列中的每个元素,其在输入序列中的最新依赖项都具有与其自身相同的索引。下图显示了 input_length 为 4 和 kernel_size 为 3 的示例。
我们可以看到,使用 2 个条目的左零填充,我们可以在遵守因果关系规则的同时实现相同的输出长度。事实上,在没有扩张的情况下,维持输入长度所需的零填充条目的数量总是等于 kernel_size - 1。
2.4扩张
预测模型的一个理想特性是输出中特定条目的值取决于输入中的所有先前条目,即索引小于或等于自身的所有条目。这是在感受野(即影响输出的特定条目的原始输入的条目集)具有 input_length 大小时实现的。我们也称其为“完整的历史报道”。正如我们之前看到的,一个传统的卷积层在输出中创建一个依赖于输入的 kernel_size 条目的条目,这些条目的索引小于或等于自身。例如,如果我们的 kernel_size 为 3,则输出中的第 5 个元素将取决于输入的元素 3、4 和 5。当我们将多层堆叠在一起时,这种范围会扩大。在下图中我们可以看到,通过使用 kernel_size 3 堆叠两层,我们得到了 5 的感受野大小。
更一般地,具有 n 层和 kernel_size k 的一维卷积网络具有大小为 r 的感受野
r=1+n*(k-1)
要知道完全覆盖需要多少层,我们可以将感受野大小设置为 input_length l 并求解层数 n(我们需要在非整数值的情况下四舍五入):
n=⌈(l-1)/(k-1)⌉
这意味着,给定一个固定的 kernel_size,完整历史覆盖所需的层数与输入张量的长度呈线性关系,这将导致网络非常快速地变得非常深,从而导致模型具有非常多的参数需要更长的时间来训练。此外,大量层已被证明会导致与损失函数梯度相关的退化问题。在保持相对较小的层数的同时增加感受野大小的一种方法是向卷积网络引入扩张。
卷积层上下文中的膨胀是指输入序列的元素之间的距离,这些元素用于计算输出序列的一个条目。所以传统的卷积层可以看作是一个 1-dilated 层,因为 1 个输出值的输入元素是相邻的。下图显示了 input_length 为 4 和 kernel_size 为 3 的 2-dilated 层的示例。
与 1-dilated 的情况相比,该层的感受野扩展为 5 而不是 3。更一般地说,核大小为 k 的 d-dilated 层的感受野扩展为 1+d *(k-1)。如果 d 是固定的,这仍然需要一个与输入张量长度成线性关系的数字来实现完整的感受野覆盖(我们只是减少了常数)。
这个问题可以通过在我们向上移动层时以指数方式增加 d 的值来解决。为此,我们选择了一个常量 dilation_base 整数 b,它可以让我们计算特定层的膨胀 d 作为其下方层数 i 的函数,因为 d = b**i。下图显示了一个 input_length 为 10、kernel_size 为 3 和 dilation_base 为 2 的网络,这导致 3 个扩张的卷积层实现全覆盖。
这里我们只显示影响输出最后一个值的输入的影响。同样,只显示最后一个输出值所需的零填充条目。显然,最后一个输出值取决于整个输入覆盖范围。实际上,给定超参数,可以使用高达 15 的 input_length,同时保持完整的感受野覆盖。一般来说,每个附加层都会将 d*(k-1) 的值添加到当前的感受野宽度,其中 d 计算为 d=b**i,其中 i 表示我们新层下面的层数。因此,具有基数 b、内核大小 k 和层数 n 指数膨胀的 TCN 的感受野宽度 w 由下式给出
w=1+i=0n-1 (k-1)⋅bi=1+(k-1)⋅bn-1b-1
然而,根据 b 和 k 的值,这个感受野可能有“洞”。考虑以下网络,其 dilation_base 为 3,内核大小为 2:
感受野确实覆盖了一个大于输入大小(即 15)的范围。然而,感受野中有洞。也就是说,输入序列中存在输出值不依赖的条目(如上图红色所示)。为了解决这个问题,我们需要将核大小增加到 3,或者将扩张基减少到 2。 一般来说,对于没有孔的感受野,核大小 k 必须至少与扩张基b一样大。
考虑到这些观察结果,我们可以计算出我们的网络需要多少层才能实现完整的历史覆盖。给定内核大小 k、膨胀基数 b(其中 k ≥ b)和输入长度 l,以下不等式必须满足完整历史覆盖:1+(k-1)⋅bn-1b-1≥l
我们可以求解 n 并获得所需的最小层数为
n=logb(l-1)⋅(b-1)(k-1)+1
我们可以看到,层数现在是对数而不是输入长度的线性。这是一个显着的改进,可以在不牺牲接收域覆盖范围的情况下实现。
现在唯一需要指定的是每一层所需的零填充条目的数量。给定膨胀基 b、内核大小 k 和当前层以下的 i 层数,则当前层所需的零填充条目数 p 计算如下:
p=bi⋅(k-1)
2.5基本 TCN 概述
给定 input_length、kernel_size、dilation_base 和完整历史覆盖所需的最小层数,基本的 TCN 网络将如下所示:
3预测
到目前为止,我们只讨论了“输入序列”和“输出序列”,而没有讨论它们之间的关系。在预测的背景下,我们希望预测未来时间序列的下一个条目。为了训练我们的 TCN 网络进行预测,训练集将由(输入序列、目标序列)对给定时间序列的大小相等的子序列对组成。目标系列将是相对于其各自的输入系列向前移动一定数量 output_length 的系列。这意味着长度为 input_length 的目标序列包含其各自输入序列的最后 (input_length - output_length) 个元素作为第一个元素,并将输入序列的最后一个条目之后的 output_length 元素作为其最终元素。在预测的上下文中,这意味着可以使用此类模型预测的最大预测范围等于 output_length。使用滑动窗口方法,可以从一个时间序列中创建许多重叠的输入和目标序列对。
4模型的改进
S. Bai 等人。 (*) 建议对基本 TCN 架构添加一些改进以提高性能,这将在本节中讨论,即残差连接、正则化和激活函数。
4.1剩余块
我们对之前介绍的基本模型所做的最大修改是将模型的基本构建块从简单的一维因果卷积层更改为由具有相同膨胀因子和残差连接的 2 层组成的残差块。
让我们考虑一个膨胀因子 d 为 2 且内核大小 k 为 3 的基本模型层,看看它如何转化为改进模型的残差块。
变成
两个卷积层的输出将与残差块的输入相加,以产生下一个块的输入。对于网络的所有内部块,即除了第一个和最后一个块之外的所有块,输入和输出通道宽度相同,即 num_filters。由于第一个残差块的第一个卷积层和最后一个残差块的第二个卷积层可能具有不同的输入和输出通道宽度,因此可能需要调整残差张量的宽度,这是使用 1x1 卷积完成的。
此更改会影响完全覆盖所需的最小层数的计算。现在我们必须考虑需要多少个残差块才能实现完整的感受野覆盖。将残差块添加到 TCN 会增加两倍于添加基本因果层时的感受野宽度,因为它包括 2 个这样的层。因此,具有膨胀基数 b、内核大小 k 且 k ≥ b 和残差块数 n 的 TCN 的感受野 r 的总大小可以计算为
r=1+i=0n-1 2⋅(k-1)⋅bi=1+2⋅(k-1)⋅bn-1b-1
这导致最小数量的残差块 n 对于 input_length l 的完整历史覆盖
n=logb(l-1)⋅(b-1)(k-1)⋅2+1
4.2激活、归一化、正则化
为了使我们的 TCN 不仅仅是一个过于复杂的线性回归模型,需要在卷积层之上添加激活函数以引入非线性。 ReLU 激活被添加到两个卷积层之后的残差块中。
为了归一化隐藏层的输入(这可以抵消梯度爆炸问题等),权重归一化应用于每个卷积层。
为了防止过拟合,在每个残差块中的每个卷积层之后通过 dropout 引入正则化。下图显示了最终的残差块。
第二个 ReLU 单元中的星号表示它存在于除最后一层之外的每一层,因为我们希望我们的最终输出也能够采用负值(这与论文中概述的架构不同)。
5最终模型
下图显示了我们的最终 TCN 模型,其中 l 等于 input_length,k 等于 kernel_size,b 等于 dilation_base,k ≥ b 以及完整历史覆盖的最小残差块数 n,其中 n 可以从其他值计算如上所述。
6例子
让我们看一下如何使用 TCN 架构使用 Darts 库预测时间序列的示例。
首先,我们需要一个时间序列来训练和评估我们的模型。为此,我们使用包含来自西班牙的每小时能源生产数据的 Kaggle 数据集。更具体地说,我们选择预测“径流水力发电”的生产。此外,为了减少问题的计算密集度,我们平均每天的能源产量以获得每日时间序列。
from darts import TimeSeries
from darts.dataprocessing.transformers import MissingValuesFiller
import pandas as pd
df = pd.read_csv('energy_dataset.csv', delimiter=",")
df['time'] = pd.to_datetime(df['time'], utc=True)
df['time']= df.time.dt.tz_localize(None)
df_day_avg = df.groupby(df['time'].astype(str).str.split(" ").str[0]).mean().reset_index()
value_filler = MissingValuesFiller()
series = value_filler.transform(TimeSeries.from_dataframe(df_day_avg,
'time', ['generation hydro run-of-river and poundage']))
series.plot()
我们可以看到,除了每年的季节性之外,能源生产还会定期出现“峰值”,每月间隔一次。由于 TCN 模型支持多个输入通道,我们可以向当前时间序列添加额外的时间序列组件,以编码当月的当前日期。这可以帮助我们的 TCN 模型更快地收敛。
series = series.add_datetime_attribute('day', one_hot=True)
现在我们将数据拆分为训练和验证组件并执行标准化。
from darts.dataprocessing.transformers import Scaler
train, val = series.split_after(pd.Timestamp('20170901'))
scaler = Scaler()
train_transformed = scaler.fit_transform(train)
val_transformed = scaler.transform(val)
series_transformed = scaler.transform(series)
现在是时候创建和训练我们的 TCN 模型了。请注意,上述架构描述中出现的所有粗体变量名称都可以用作 Darts TCN 实现的构造函数的参数。 output_length 参数设置为 7,因为我们要执行每周预测。在训练模型时,我们仅将训练系列的第一个组件指定为 target_series,因为我们不想预测我们之前添加的辅助时间序列。我们尝试了几种不同的超参数组合,但大多数值都是随意选择的。
from darts.models import TCNModelmodel = TCNModel(
input_size=train.width,
n_epochs=20,
input_length=365,
output_length=7,
dropout=0,
dilation_base=2,
weight_norm=True,
kernel_size=7,
num_filters=4,
random_state=0
)model.fit(
training_series=train_transformed,
target_series=train_transformed['0'],
val_training_series=val_transformed,
val_target_series=val_transformed['0'], verbose=True
)
为了评估我们的模型,我们希望使用 7 天预测范围在验证集中的许多不同时间点上测试其性能。为此,我们使用了 Darts 的历史回测功能。请注意,该模型为每个预测提供了新的输入数据,但从未重新训练过。为了节省时间,我们将步幅设置为 5。
pred_series = model.backtest(
series_transformed,
target_series=series_transformed['0'],
start=pd.Timestamp('20170901'),
forecast_horizon=7,
stride=5,
retrain=False,
verbose=True,
use_full_output_length=True
)
让我们将 TCN 模型对真实数据点的历史预测预测可视化并计算 R2 分数。
from darts.metrics import r2_score
import matplotlib.pyplot as plt
series_transformed[900:]['0'].plot(label='actual')
pred_series.plot(label=('historic 7 day forecasts'))
r2_score_value = r2_score(series_transformed['0'], pred_series)
plt.title('R2:' + str(r2_score_value))
plt.legend()
有关更多详细信息和其他示例,请查看我们在 GitHub 上的 TCN 示例笔记本。
7结论
序列建模中的深度学习在很大程度上仍与循环神经网络架构广泛相关。但研究表明,这些类型的模型在许多任务中都可以胜过 TCN,无论是在预测性能还是效率方面。在本文中,我们探讨了如何从简单的构建块(例如一维卷积层、膨胀和残差连接)以及它们如何组合在一起来理解这个有前途的模型。此外,我们成功地应用了 TCN 架构的当前 Darts 实现来预测现实世界的时间序列。
不全,这是下面文章链接