这里我们只显示影响输出最后一个值的输入的影响。同样,只显示最后一个输出值所必需的补零项。显然,最后的输出值依赖于整个输入覆盖率。实际上,给定超参数,input_length最多可以使用15,同时保持完全的接收野覆盖。一般来说,每增加一层,当前接受野宽度就增加一个d*(k-1)值,其中d计算为d=b**i, i表示新层下面的层数。因此,给出了基b指数膨胀时TCN的感受场宽度w、核大小k和层数n为
然而,根据b和k的值,这个接受野可能会有“洞”。考虑以下网络,其dilation_base为3,内核大小为2:
接受野的范围确实大于输入的大小(即15)。然而,接受野是有洞的;也就是说,在输入序列中有输出值不依赖的条目(如上面红色所示)。为了解决这个问题,我们需要将内核大小增加到3,或者将膨胀基数减小到2。一般来说,对于没有孔的感受野,核的大小k至少要与膨胀基b一样大。
考虑到这些观察结果,我们可以计算出我们的网络需要多少层才能覆盖整个历史。给定核大小k,膨胀基b,其中k≥b,输入长度l,为了实现全历史覆盖,必须满足以下不等式:
我们可以求解n,得到所需的最小层数
我们可以看到,在输入长度方面,层数现在是对数的,而不是线性的。这是一个显著的改进,可以在不牺牲接受野覆盖率的情况下实现。
现在,唯一需要指定的是每一层所需的零填充项的数量。假设膨胀基为b,核大小为k,当前层以下有i个层,则当前层所需的补零项数p计算如下:
基本TCN概述
给定input_length, kernel_size, dilation_base和覆盖整个历史所需的最小层数,基本的TCN网络看起来像这样:
预测
到目前为止,我们只讨论了‘输入序列’和‘输出序列’,而没有深入了解它们之间是如何相互关联的。在预测方面,我们希望预测未来时间序列的下一个条目。为了训练我们的TCN网络进行预测,训练集将由给定时间序列的等大小子序列对(输入序列、目标序列)组成。目标序列将是相对于其各自的输入序列向前移动一定数量output_length的序列。这意味着长度input_length的目标序列包含其各自输入序列的最后(input_length - output_length)元素作为第一个元素,位于输入序列最后一个条目之后的output_length元素作为它的最后一个元素。在预测方面,这意味着该模型所能预测的最大预测视界等于output_length。使用滑动窗口的方法,许多重叠的输入和目标序列可以创建出一个时间序列。
模型的改进
S. Bai等人(*)建议对基本的TCN体系结构进行一些添加,以提高本节将讨论的性能,即残差连接、正则化和激活函数。
残差块
我们对之前介绍的基本模型做的最大的修改是将模型的基本构建块从简单的一维因果卷积层改为由相同膨胀因子和残差连接的2层组成的残差块。
让我们从基本模型中考虑一个膨胀系数d为2、内核大小k为3的层,看看这是如何转化为改进模型的剩余块的。
变为
这两个卷积层的输出将被添加到残差块的输入中,从而产生下一个块的输入。对于网络的所有内部块,即除了第一个和最后一个之外的所有内部块,输入和输出通道宽度是相同的,即num_filters。由于第一个残块的第一卷积层和最后一个残块的第二卷积层可能有不同的输入和输出通道宽度,所以可能需要调整残差张量的宽度,这是通过1x1卷积来完成的
此更改会影响对完整覆盖所需的最小层数的计算。现在我们必须考虑需要多少残差块才能实现接收域的完全覆盖。在TCN中添加一个残差块所增加的接受野宽度是添加一个基本因果层时的两倍,因为它包含两个这样的层。因此,扩张基为b的TCN的感受场总大小r、k≥b的核大小k和剩余块数n可计算为
这保证了最小的残差块数n为input_length l的完整历史覆盖
激活,规范化、正规化
为了使我们的TCN不仅仅是一个过于复杂的线性回归模型,需要在卷积层的顶部添加激活函数来引入非线性。ReLU激活被添加到两个卷积层之后的残差块中。
为了规范化隐含层的输入(抵消了梯度爆发的问题),权值规范化应用于每一个卷积层。
为了防止过拟合,在每个剩余块的每个卷积层之后通过dropout引入正则化。下图显示了最终的剩余块。
第二个ReLU单元中的星号表示该层存在于除最后一层之外的所有层中,因为我们希望最终输出也能够具有负值(这与本文中概述的体系结构不同)。
模型
下图显示了我们最终的TCN模型,其中l等于input_length,k等于kernel_size,b等于dilation_base,k≥b,并且对于完整的历史覆盖n为最小数量的残差块,其中n可以从其他值计算得出 如上所述。
示例
让我们看一个示例,该示例说明如何使用Darts库使用TCN架构预测时间序列。
首先,我们需要一个时间序列来训练和评估我们的模型。为此,我们使用了Kaggle数据集,其中包含来自西班牙的每小时能源生产数据。更具体地说,我们选择预测“河流上游水电”的产量。此外,为了使问题的计算量减少,我们将每天的平均能源生产量取平均以获得每日的时间序列。
fromdartsimportTimeSeriesfromdarts.dataprocessing.transformersimportMissingValuesFillerimportpandasaspddf=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)
现在,我们将数据分为训练和验证组件并执行标准化。
fromdarts.dataprocessing.transformersimportScalertrain, 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,因为我们不想预测我们之前添加的助手时间序列。我们尝试了几种不同的超参数组合,但是大多数值是任意选择的。
fromdarts.modelsimportTCNModelmodel=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得分。
fromdarts.metricsimportr2_scoreimportmatplotlib.pyplotaspltseries_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上查看 https://github.com/unit8co/darts/blob/develop/examples/TCN-examples.ipynb
结论
在大多数情况下,序列建模中的深度学习仍与递归神经网络架构广泛相关。但是研究表明,在预测性能和效率方面,TCN可以在许多任务中胜过这些类型的模型。在本文中,我们探讨了如何通过简单的构建块(例如一维卷积层,膨胀和残差连接)理解这种有前途的模型,以及它们如何融合在一起。此外,我们成功地应用了TCN体系结构的当前Darts实现来预测实际时间序列。














