有什么设计网站,企业电话,西安小程序,网站开发费怎么做账文章目录 摘要Abstract 0 提升有效感受野#xff08;ERF#xff09;1 相关知识1.1 标准卷积1.2 深度分离卷积#xff08;Depthwise Convolution#xff0c;DWConv#xff09;1.3 逐点卷积#xff08;Pointwise Convolution#xff0c;PWConv#xff09;1.4 组卷积(Grou… 文章目录 摘要Abstract 0 提升有效感受野ERF1 相关知识1.1 标准卷积1.2 深度分离卷积Depthwise ConvolutionDWConv1.3 逐点卷积Pointwise ConvolutionPWConv1.4 组卷积(Group Convolution) 2 ModernTCN2.1 Modern convolution block2.2 相关修改2.2.1 保留变量维度2.2.2 DWConv2.2.3 ConvFFN 2.3 最终结构2.4 Modern TCN代码总结 摘要
本周阅读了ModernTCN一种时间序列分析的现代纯卷积结构其在多个主流时间序列分析任务上取得了与最先进的基于Transformer和MLP的模型相媲美的性能同时保持了卷积模型的效率优势。ModernTCN借鉴了Transformer中的架构设计采用了深度卷积和逐点卷积的组合以提高模型的表示能力。同时为了更好地处理时间序列数据ModernTCN引入了变量独立嵌入和跨变量依赖捕获机制。ModernTCN通过使用大核心尺寸而不是堆叠更多小核心ModernTCN显著扩大了ERF这有助于更好地捕捉时间序列数据中的长期依赖性。
Abstract
This week, I read ModernTCN: a modern pure convolutional structure for time series analysis that has achieved performance comparable to state-of-the-art Transformer and MLP based models on multiple mainstream time series analysis tasks, while maintaining the efficiency advantage of convolutional models. ModernTCN draws inspiration from the architecture design of Transformer and adopts a combination of deep convolution and pointwise convolution to enhance the model’s representation capability. Meanwhile, in order to better handle time series data, ModernTCN introduces variable independent embedding and cross variable dependency capture mechanisms. ModernTCN significantly expands ERF by using larger core sizes instead of stacking more small cores, which helps better capture long-term dependencies in time series data.
0 提升有效感受野ERF
基于Transformer的模型和基于MLP的模型具有全局有效感受野ERF因此可以更好地捕获长期时间跨时间依赖性从而显著优于传统的TCN。而SCINet和MICN主要关注的是设计更复杂的结构来处理传统的卷积而忽略了更新卷积本身的重要性下图给出了这两种模型的感受野对比。 其中ModernTCN通常采用大核来有效地提高ERF。
SCINet 和 MICN 是两个基于 TCN 的预测模型它们的感受野都很小。作者发现 ModernTCN 中采用大的卷积核所对应的感受野要大很多。其次是充分利用卷积可以捕获跨变量依赖性也就是多变量时间序列中变量之间的关系
1 相关知识
1.1 标准卷积
标准卷积利用若干个多通道卷积核对输入的多通道图像进行处理输出的feature map既提取了通道特征又提取了空间特征。
如图所示假设输入层为一个大小为5×5像素3通道色彩的输入图片shape为5×5×3经过3×3卷积核的卷积层假设输出通道为4则卷积核shape为3×3×3×4最终输出4个Feature Map。其中卷积层的参数量为3×3×3×4108 1.2 深度分离卷积Depthwise ConvolutionDWConv DWConv就是深度(channel)维度不变改变H/W。 DWConv的一个卷积核负责一个通道一个通道只被一个卷积核卷积。而常规卷积每个卷积核是同时操作输入图片的每个通道这样就不会混合通道只会混合token。
DWConv的核为单通道模式需要对输入的每一个通道进行卷积这样就会得到和输入特征图通道数一致的输出特征图。即有输入特征图通道数卷积核个数输出特征图个数。
如下图所示同样是对于一张5×5像素、三通道彩色输入图片shape为5×5×3Depthwise Convolution首先经过第一次卷积运算不同于上面的常规卷积DW完全是在二维平面内进行。卷积核的数量与上一层的通道数相同通道和卷积核一一对应。所以一个三通道的图像经过运算后生成了3个Feature map(如果有same padding则尺寸与输入层相同为5×5)。 卷积层的参数量为3×3×327 DWConv完成后的Feature map数量与输入层的通道数相同无法扩展Feature map。而且这种运算对输入层的每个通道独立进行卷积运算没有有效的利用不同通道在相同空间位置上的feature信息。因此需要PWConv来将这些Feature map进行组合生成新的Feature map。
1.3 逐点卷积Pointwise ConvolutionPWConv PWConv就是W/H维度不变改变channel 由于DWConv输入特征图通道数卷积核个数输出特征图个数这样会导致输出的特征图个数过少或者说输出特征图的通道数过少可看成是输出特征图个数为1通道数为3从而可能影响信息的有效性。此时就需要进行逐点卷积。
逐点卷积实质上是用1x1的卷积核进行升维
PWConv的运算与常规卷积运算非常相似它的卷积核的尺寸为 1×1×MM为上一层的通道数。所以这里的卷积运算会将上一步的map在深度方向上进行加权组合生成新的Feature map。有几个卷积核就有几个输出Feature map。
如下图所示从DWConv得到的3个单通道特征图经过4个大小为1×1×3的卷积核的卷积计算后输出4个特征图而输出特征图的个数取决于Filter的个数。卷积层的参数量为1×1×3×412
1.4 组卷积(Group Convolution)
将输入特征图的通道分成若干组并在每组内单独进行卷积操作从而减少计算复杂度和参数量同时有效利用模型的结构特性。
如下图输入数据shape H i n × W i n × C i n H_{in}×W_{in}×C_{in} Hin×Win×Cin假设将输入数据的通道分为G组即每组数据的通道数为 C i n G \frac{C_{in}}{G} GCin,对这G组数据进行单独设计卷积核最终的输出结果将这G组的卷积结果进行拼接;其中每个卷积核shape为:k×k 卷积层的参数量为 k × k × C i n G × C o u t G × G k×k×\frac{C_{in}}{G}×\frac{C_{out}}{G}×G k×k×GCin×GCout×G k × k × C i n × C o u t G \frac{k×k×C_{in}×C_{out}}{G} Gk×k×Cin×Cout
而标准卷积的卷积层的参数量为: k × k × C i n × C o u t k×k×C_{in}×C_{out} k×k×Cin×Cout
即分组卷积可将参数量减小为原来的1/G。 分组卷积的用途:
减少参数量分成组则该层的参数量减少为原来的1/Group Convolution可以看成是structured sparse每个卷积核的尺寸由∗∗变为/∗∗可以将其余(− / )∗∗的参数视为0有时甚至可以在减少参数量的同时获得更好的效果相当于正则当分组数量等于输入map数量输出map数量也等于输入map数量即个卷积核每个尺寸为1∗∗时Group Convolution就成了DWConv
2 ModernTCN
ModernTCN采用了现代化的卷积块设计借鉴了Transformer中的架构设计使用了深度卷积和逐点卷积的组合以提高模型的表示能力。
论文贡献
作者深入研究了如何更好地利用卷积在时间序列中的问题并提出了一种新的解决方案。实验结果表明所提方法在时间序列分析中比现有的基于卷积的模型更能发挥卷积的潜力。ModernTCN在多个主流时间序列分析任务上实现了一致的最先进性能展示了出色的任务泛化能力。ModernTCN提供了效率和性能的更好平衡。它保持了基于卷积的模型的效率优势同时在性能方面与最先进的基于Transformer的模型竞争甚至更好。
2.1 Modern convolution block
ModernTCN模块设计,其中M,N,D分别是变量维度、时间维度和特征维度的大小 DWConv负责在每个特征的基础上学习token之间不同时间步之间的时间信息其作用与Transformer中的自关注模块相同。
ConvFFN类似于Transformer中的FFN模块结果等价。它由两个PWConv组成采用倒瓶颈结构先升维后降维其中ConvFFN块的隐藏通道比输入通道宽 r rr 倍。该模块独立学习每个 token 新的 feature representation。
上述设计实现了时间信息和特征信息混合的分离。DWConv和ConvFFN中的每一个都只混合一个时间或特征维度的信息。与传统的卷积不同传统的卷积将两个维度的信息混合在一起。这种解耦设计使对象任务更容易学习降低了计算复杂度
但作者发现简单地以与CV相同的方式对卷积进行现代化改造在时间序列任务中几乎没有带来性能改进。作者注意到时间序列除了特征维和时间维之外时间序列还有一个变量维。**其中ConvFFN可以建模通道间关系但无法建模变量间关系。为了使现代1D卷积更适合于时间序列分析还需要更多与时间序列相关的修改
2.2 相关修改
2.2.1 保留变量维度
时间序列变量之间存在复杂的依赖关系简单的嵌入层,忽略了变量的维度,无法学习这种依赖性甚至可能因为不考虑变量的不同行为而丢失变量的独立特性。 作者提出了一种“patchify variable-independent embedding”分块变量独立嵌入的方法。 提出了一种新的时间序列数据嵌入方法通过分块和全卷积的方式更好地适应时间序列数据的特性同时保留了变量维度为进一步的分析和建模提供了基础。
输入表示 X i n ∈ R M × L X_{in}\in R^{M×L} Xin∈RM×L 表示输入的时间序列,其长度为L,其特征数量为M。
Patchify Variable-Independent Embedding 步骤:
将输入 X i n ∈ R M × L X_{in}\in R^{M×L} Xin∈RM×L 解压缩为 X i n ∈ R M × 1 × L X_{in}\in R^{M×1×L} Xin∈RM×1×Lpadding operation对原始序列 X i n X_{in} Xin采用padding操作确保NL//S(L为序列长度S为步长)具体来说我们重复Xin的最后一个值P-S次然后将它们填充回Xin的末尾。将填充好的 X i n X_{in} Xin通过1D卷积层进行patching和embedding 其中卷积层的核大小为P步长为S将1个输入通道映射到D个输出通道。在这个过程中每个单变量时间序列被独立嵌入从而保留了变量维度。 其中分块处理将输入的时间序列在适当的填充后将时间序列分成N个大小为P的块分块过程中的步长为SS也是两个连续块之间不重叠区域的长度。 嵌入变量将这些块嵌入到D维的嵌入向量中, X e m b E m b e d d i n g ( X i n ) X_{emb}Embedding(X_{in}) XembEmbedding(Xin)得到 X e m b ∈ R M × D × L X_{emb}\in R^{M×D×L} Xemb∈RM×D×L 这里将原始的变量的隐藏维度等于1计入输入变量 X i n ∈ R M × L X_{in}\in R^{M×L} Xin∈RM×L 调整为 X i n ∈ R M × 1 × L X_{in}\in R^{M×1×L} Xin∈RM×1×L然后对每个变量单独进行 embedding 将每个变量映射为指定的隐藏维度D得到嵌入 X e m b ∈ R M × D × L X_{emb}\in R^{M×D×L} Xemb∈RM×D×L
优势: 保留变量维度通过独立嵌入可以保留时间序列的变量维度这对于捕捉时间序列数据的复杂特性至关重要。信息捕获后续的修改将使结构能够从额外的变量维度中捕获信息。 2.2.2 DWConv
DWConv最初是为学习时间信息而设计的。由于单独使用DWConv来共同学习跨时间和跨变量的依赖关系比较困难所以不适合让DWConv同时负责跨变量维度的信息混合。因此作者将原来的DWConv从仅特征独立修改为特征和变量独立使其独立学习每个单变量时间序列的时间依赖性。同时在DWConv中采用大核来增加ERF提高时间建模能力。
2.2.3 ConvFFN
由于DWConv是特征和变量独立的ConvFFN作为补充应该混合跨特征和变量维度的信息。一种简单的方法是通过单个ConvFFN共同学习特征和变量之间的依赖关系。但这种方法的计算复杂度较高性能较差。因此我们进一步将单个ConvFFN解耦为ConvFFN1和ConvFFN2方法是将PWConvs替换为grouped PWConvs 并设置不同的组数。ConvFFN1负责学习每个变量的特征信息ConvFFN2负责捕获每个特征的交叉变量依赖性。
2.3 最终结构
最终修改得到的ModernTCN block如下 其中DWConv、ConvFFN1和ConvFFN2中的每一个只在时间、特征或可变维度中的一个上混合信息这保持了现代卷积中解耦设计的思想。现深度可分离卷积其实也是组数等于深度数的组卷积既简单又有效。
上图中 shape 在每一个模块的前后变化首先用 DWConv 来建模时间上的关系但又不希望它参与到通道间和变量间的建模上。因此作者将M和D这两个表示变量通道的维度 reshape 在一起再进行深度可分离卷积。其次希望独立建模通道和变量。因此作者采用了两个组卷积其中一个组卷积的 Group 数为 M表示每 D 个通道构成一个组因此用来建模通道间关系另一个组卷积的 Group 数为 D表示每 M 个变量构成一个组因此用来建模变量间关系。注意两个组卷积之间存在着 reshape 和 permute 操作这是为了正确的分组最后会再 reshape 和 permute 回去。最后整体再用一个残差连接即可得到最终的 ModernTCN block。堆叠多个 block 即可得到 ModernTCN 模型。综上所述作者将时间上、通道上、变量上的三种关系解耦建模用三种组卷积来巧妙地进行实现深度可分离卷积其实也是组数等于深度数的组卷积既简单又有效。
2.4 Modern TCN代码
ModernTCN_Layer.py
import torch
from torch import nn
import math
# decompositionclass moving_avg(nn.Module):Moving average block to highlight the trend of time seriesdef __init__(self, kernel_size, stride):super(moving_avg, self).__init__()self.kernel_size kernel_sizeself.avg nn.AvgPool1d(kernel_sizekernel_size, stridestride, padding0)def forward(self, x):# padding on the both ends of time seriesfront x[:, 0:1, :].repeat(1, (self.kernel_size - 1) // 2, 1)end x[:, -1:, :].repeat(1, (self.kernel_size - 1) // 2, 1)x torch.cat([front, x, end], dim1)x self.avg(x.permute(0, 2, 1))x x.permute(0, 2, 1)return xclass series_decomp(nn.Module):Series decomposition blockdef __init__(self, kernel_size):super(series_decomp, self).__init__()self.moving_avg moving_avg(kernel_size, stride1)def forward(self, x):moving_mean self.moving_avg(x)res x - moving_meanreturn res, moving_mean# forecast task head
class Flatten_Head(nn.Module):def __init__(self, individual, n_vars, nf, target_window, head_dropout0):super(Flatten_Head, self).__init__()self.individual individualself.n_vars n_varsif self.individual:self.linears nn.ModuleList()self.dropouts nn.ModuleList()self.flattens nn.ModuleList()for i in range(self.n_vars):self.flattens.append(nn.Flatten(start_dim-2))self.linears.append(nn.Linear(nf, target_window))self.dropouts.append(nn.Dropout(head_dropout))else:self.flatten nn.Flatten(start_dim-2)self.linear nn.Linear(nf, target_window)self.dropout nn.Dropout(head_dropout)def forward(self, x): # x: [bs x nvars x d_model x patch_num]if self.individual:x_out []for i in range(self.n_vars):z self.flattens[i](x[:, i, :, :]) # z: [bs x d_model * patch_num]z self.linears[i](z) # z: [bs x target_window]z self.dropouts[i](z)x_out.append(z)x torch.stack(x_out, dim1) # x: [bs x nvars x target_window]else:x self.flatten(x)x self.linear(x)x self.dropout(x)return xmoving_avg模块移动平均块用于突出时间序列的趋势通过在时间序列两端进行填充然后应用一维平均池化操作实现.series_decomp模块序列分解块将时间序列分解为残差和趋势两部分其中趋势部分通过moving_avg模块计算得到.Flatten_Head模块预测任务头部模块用于将特征进行展平和线性变换以输出预测结果.支持个体化和非个体化两种模式个体化模式为每个变量分别进行操作非个体化模式则对所有变量统一操作.
ModernTCN.py
文件定义了基于时间卷积网络TCN的现代时间序列预测模型ModernTCN及其相关组件包括模型的构建、前向传播过程以及模型的结构重参数化等.
import torch
from torch import nn
import torch.nn.functional as F
import math
from layers.RevIN import RevIN
from models.ModernTCN_Layer import series_decomp, Flatten_Headclass LayerNorm(nn.Module):def __init__(self, channels, eps1e-6, data_formatchannels_last):super(LayerNorm, self).__init__()self.norm nn.Layernorm(channels)def forward(self, x):B, M, D, N x.shapex x.permute(0, 1, 3, 2)x x.reshape(B * M, N, D)x self.norm(x)x x.reshape(B, M, N, D)x x.permute(0, 1, 3, 2)return xdef get_conv1d(in_channels, out_channels, kernel_size, stride, padding, dilation, groups, bias):return nn.Conv1d(in_channelsin_channels, out_channelsout_channels, kernel_sizekernel_size, stridestride,paddingpadding, dilationdilation, groupsgroups, biasbias)def get_bn(channels):return nn.BatchNorm1d(channels)def conv_bn(in_channels, out_channels, kernel_size, stride, padding, groups, dilation1,biasFalse):if padding is None:padding kernel_size // 2result nn.Sequential()result.add_module(conv, get_conv1d(in_channelsin_channels, out_channelsout_channels, kernel_sizekernel_size,stridestride, paddingpadding, dilationdilation, groupsgroups, biasbias))result.add_module(bn, get_bn(out_channels))return resultdef fuse_bn(conv, bn):kernel conv.weightrunning_mean bn.running_meanrunning_var bn.running_vargamma bn.weightbeta bn.biaseps bn.epsstd (running_var eps).sqrt()t (gamma / std).reshape(-1, 1, 1)return kernel * t, beta - running_mean * gamma / stdclass ReparamLargeKernelConv(nn.Module):def __init__(self, in_channels, out_channels, kernel_size,stride, groups,small_kernel,small_kernel_mergedFalse, nvars7):super(ReparamLargeKernelConv, self).__init__()self.kernel_size kernel_sizeself.small_kernel small_kernel# We assume the conv does not change the feature map size, so padding k//2. Otherwise, you may configure padding as you wish, and change the padding of small_conv accordingly.padding kernel_size // 2if small_kernel_merged:self.lkb_reparam nn.Conv1d(in_channelsin_channels, out_channelsout_channels, kernel_sizekernel_size,stridestride, paddingpadding, dilation1, groupsgroups, biasTrue)else:self.lkb_origin conv_bn(in_channelsin_channels, out_channelsout_channels, kernel_sizekernel_size,stridestride, paddingpadding, dilation1, groupsgroups,biasFalse)if small_kernel is not None:assert small_kernel kernel_size, The kernel size for re-param cannot be larger than the large kernel!self.small_conv conv_bn(in_channelsin_channels, out_channelsout_channels,kernel_sizesmall_kernel,stridestride, paddingsmall_kernel // 2, groupsgroups, dilation1,biasFalse)def forward(self, inputs):if hasattr(self, lkb_reparam):out self.lkb_reparam(inputs)else:out self.lkb_origin(inputs)if hasattr(self, small_conv):out self.small_conv(inputs)return outdef PaddingTwoEdge1d(self,x,pad_length_left,pad_length_right,pad_values0):D_out,D_in,ksx.shapeif pad_values 0:pad_left torch.zeros(D_out,D_in,pad_length_left)pad_right torch.zeros(D_out,D_in,pad_length_right)else:pad_left torch.ones(D_out, D_in, pad_length_left) * pad_valuespad_right torch.ones(D_out, D_in, pad_length_right) * pad_valuesx torch.cat([pad_left,x],dims-1)x torch.cat([x,pad_right],dims-1)return xdef get_equivalent_kernel_bias(self):eq_k, eq_b fuse_bn(self.lkb_origin.conv, self.lkb_origin.bn)if hasattr(self, small_conv):small_k, small_b fuse_bn(self.small_conv.conv, self.small_conv.bn)eq_b small_beq_k self.PaddingTwoEdge1d(small_k, (self.kernel_size - self.small_kernel) // 2,(self.kernel_size - self.small_kernel) // 2, 0)return eq_k, eq_bdef merge_kernel(self):eq_k, eq_b self.get_equivalent_kernel_bias()self.lkb_reparam nn.Conv1d(in_channelsself.lkb_origin.conv.in_channels,out_channelsself.lkb_origin.conv.out_channels,kernel_sizeself.lkb_origin.conv.kernel_size, strideself.lkb_origin.conv.stride,paddingself.lkb_origin.conv.padding, dilationself.lkb_origin.conv.dilation,groupsself.lkb_origin.conv.groups, biasTrue)self.lkb_reparam.weight.data eq_kself.lkb_reparam.bias.data eq_bself.__delattr__(lkb_origin)if hasattr(self, small_conv):self.__delattr__(small_conv)class Block(nn.Module):def __init__(self, large_size, small_size, dmodel, dff, nvars, small_kernel_mergedFalse, drop0.1):super(Block, self).__init__()self.dw ReparamLargeKernelConv(in_channelsnvars * dmodel, out_channelsnvars * dmodel,kernel_sizelarge_size, stride1, groupsnvars * dmodel,small_kernelsmall_size, small_kernel_mergedsmall_kernel_merged, nvarsnvars)self.norm nn.BatchNorm1d(dmodel)#convffn1self.ffn1pw1 nn.Conv1d(in_channelsnvars * dmodel, out_channelsnvars * dff, kernel_size1, stride1,padding0, dilation1, groupsnvars)self.ffn1act nn.GELU()self.ffn1pw2 nn.Conv1d(in_channelsnvars * dff, out_channelsnvars * dmodel, kernel_size1, stride1,padding0, dilation1, groupsnvars)self.ffn1drop1 nn.Dropout(drop)self.ffn1drop2 nn.Dropout(drop)#convffn2self.ffn2pw1 nn.Conv1d(in_channelsnvars * dmodel, out_channelsnvars * dff, kernel_size1, stride1,padding0, dilation1, groupsdmodel)self.ffn2act nn.GELU()self.ffn2pw2 nn.Conv1d(in_channelsnvars * dff, out_channelsnvars * dmodel, kernel_size1, stride1,padding0, dilation1, groupsdmodel)self.ffn2drop1 nn.Dropout(drop)self.ffn2drop2 nn.Dropout(drop)self.ffn_ratio dff//dmodeldef forward(self,x):input xB, M, D, N x.shapex x.reshape(B,M*D,N)x self.dw(x)x x.reshape(B,M,D,N)x x.reshape(B*M,D,N)x self.norm(x)x x.reshape(B, M, D, N)x x.reshape(B, M * D, N)x self.ffn1drop1(self.ffn1pw1(x))x self.ffn1act(x)x self.ffn1drop2(self.ffn1pw2(x))x x.reshape(B, M, D, N)x x.permute(0, 2, 1, 3)x x.reshape(B, D * M, N)x self.ffn2drop1(self.ffn2pw1(x))x self.ffn2act(x)x self.ffn2drop2(self.ffn2pw2(x))x x.reshape(B, D, M, N)x x.permute(0, 2, 1, 3)x input xreturn xclass Stage(nn.Module):def __init__(self, ffn_ratio, num_blocks, large_size, small_size, dmodel, dw_model, nvars,small_kernel_mergedFalse, drop0.1):super(Stage, self).__init__()d_ffn dmodel * ffn_ratioblks []for i in range(num_blocks):blk Block(large_sizelarge_size, small_sizesmall_size, dmodeldmodel, dffd_ffn, nvarsnvars, small_kernel_mergedsmall_kernel_merged, dropdrop)blks.append(blk)self.blocks nn.ModuleList(blks)def forward(self, x):for blk in self.blocks:x blk(x)return xclass ModernTCN(nn.Module):def __init__(self,patch_size,patch_stride, stem_ratio, downsample_ratio, ffn_ratio, num_blocks, large_size, small_size, dims, dw_dims,nvars, small_kernel_mergedFalse, backbone_dropout0.1, head_dropout0.1, use_multi_scaleTrue, revinTrue, affineTrue,subtract_lastFalse, freqNone, seq_len512, c_in7, individualFalse, target_window96):super(ModernTCN, self).__init__()# RevINself.revin revinif self.revin:self.revin_layer RevIN(c_in, affineaffine, subtract_lastsubtract_last)# stem layer down sampling layers(if needed)self.downsample_layers nn.ModuleList()stem nn.Sequential(nn.Conv1d(1, dims[0], kernel_sizepatch_size, stridepatch_stride),nn.BatchNorm1d(dims[0]))self.downsample_layers.append(stem)for i in range(3):downsample_layer nn.Sequential(nn.BatchNorm1d(dims[i]),nn.Conv1d(dims[i], dims[i 1], kernel_sizedownsample_ratio, stridedownsample_ratio),)self.downsample_layers.append(downsample_layer)self.patch_size patch_sizeself.patch_stride patch_strideself.downsample_ratio downsample_ratioif freq h:time_feature_num 4elif freq t:time_feature_num 5else:raise NotImplementedError(time_feature_num should be 4 or 5)self.te_patch nn.Sequential(nn.Conv1d(time_feature_num, time_feature_num, kernel_sizepatch_size, stridepatch_stride,groupstime_feature_num),nn.Conv1d(time_feature_num, dims[0], kernel_size1, stride1, groups1),nn.BatchNorm1d(dims[0]))# backboneself.num_stage len(num_blocks)self.stages nn.ModuleList()for stage_idx in range(self.num_stage):layer Stage(ffn_ratio, num_blocks[stage_idx], large_size[stage_idx], small_size[stage_idx], dmodeldims[stage_idx],dw_modeldw_dims[stage_idx], nvarsnvars, small_kernel_mergedsmall_kernel_merged, dropbackbone_dropout)self.stages.append(layer)# Multi scale fusing (if needed)self.use_multi_scale use_multi_scaleself.up_sample_ratio downsample_ratioself.lat_layer nn.ModuleList()self.smooth_layer nn.ModuleList()self.up_sample_conv nn.ModuleList()for i in range(self.num_stage):align_dim dims[-1]lat nn.Conv1d(dims[i], align_dim, kernel_size1,stride1)self.lat_layer.append(lat)smooth nn.Conv1d(align_dim, align_dim, kernel_size3, stride1, padding1)self.smooth_layer.append(smooth)up_conv nn.Sequential(nn.ConvTranspose1d(align_dim, align_dim, kernel_sizeself.up_sample_ratio, strideself.up_sample_ratio),nn.BatchNorm1d(align_dim))self.up_sample_conv.append(up_conv)# headpatch_num seq_len // patch_strideself.n_vars c_inself.individual individuald_model dims[-1]if use_multi_scale:self.head_nf d_model * patch_numself.head Flatten_Head(self.individual, self.n_vars, self.head_nf, target_window,head_dropouthead_dropout)else:if patch_num % pow(downsample_ratio,(self.num_stage - 1)) 0:self.head_nf d_model * patch_num // pow(downsample_ratio,(self.num_stage - 1))else:self.head_nf d_model * (patch_num // pow(downsample_ratio, (self.num_stage - 1))1)self.head Flatten_Head(self.individual, self.n_vars, self.head_nf, target_window,head_dropouthead_dropout)def up_sample(self, x, upsample_ratio):_, _, _, N x.shapereturn F.upsample(x, sizeN, scale_factorupsample_ratio, modebilinear)def forward_feature(self, x, teNone):B,M,Lx.shapex x.unsqueeze(-2)for i in range(self.num_stage):B, M, D, N x.shapex x.reshape(B * M, D, N)if i0:if self.patch_size ! self.patch_stride:# stem layer paddingpad_len self.patch_size - self.patch_stridepad x[:,:,-1:].repeat(1,1,pad_len)x torch.cat([x,pad],dim-1)else:if N % self.downsample_ratio ! 0:pad_len self.downsample_ratio - (N % self.downsample_ratio)x torch.cat([x, x[:, :, -pad_len:]],dim-1)x self.downsample_layers[i](x)_, D_, N_ x.shapex x.reshape(B, M, D_, N_)x self.stages[i](x)return xdef forward(self, x, teNone):# instance normif self.revin:x x.permute(0, 2, 1)x self.revin_layer(x, norm)x x.permute(0, 2, 1)x self.forward_feature(x,te)x self.head(x)# de-instance normif self.revin:x x.permute(0, 2, 1)x self.revin_layer(x, denorm)x x.permute(0, 2, 1)return xdef structural_reparam(self):for m in self.modules():if hasattr(m, merge_kernel):m.merge_kernel()class Model(nn.Module):def __init__(self, configs):super(Model, self).__init__()# hyper paramself.stem_ratio configs.stem_ratioself.downsample_ratio configs.downsample_ratioself.ffn_ratio configs.ffn_ratioself.num_blocks configs.num_blocksself.large_size configs.large_sizeself.small_size configs.small_sizeself.dims configs.dimsself.dw_dims configs.dw_dimsself.nvars configs.enc_inself.small_kernel_merged configs.small_kernel_mergedself.drop_backbone configs.dropoutself.drop_head configs.head_dropoutself.use_multi_scale configs.use_multi_scaleself.revin configs.revinself.affine configs.affineself.subtract_last configs.subtract_lastself.freq configs.freqself.seq_len configs.seq_lenself.c_in self.nvars,self.individual configs.individualself.target_window configs.pred_lenself.kernel_size configs.kernel_sizeself.patch_size configs.patch_sizeself.patch_stride configs.patch_stride# decompself.decomposition configs.decompositionif self.decomposition:self.decomp_module series_decomp(self.kernel_size)self.model_res ModernTCN(patch_sizeself.patch_size,patch_strideself.patch_stride,stem_ratioself.stem_ratio, downsample_ratioself.downsample_ratio, ffn_ratioself.ffn_ratio, num_blocksself.num_blocks, large_sizeself.large_size, small_sizeself.small_size, dimsself.dims, dw_dimsself.dw_dims,nvarsself.nvars, small_kernel_mergedself.small_kernel_merged, backbone_dropoutself.drop_backbone, head_dropoutself.drop_head, use_multi_scaleself.use_multi_scale, revinself.revin, affineself.affine,subtract_lastself.subtract_last, freqself.freq, seq_lenself.seq_len, c_inself.c_in, individualself.individual, target_windowself.target_window)self.model_trend ModernTCN(patch_sizeself.patch_size,patch_strideself.patch_stride,stem_ratioself.stem_ratio, downsample_ratioself.downsample_ratio, ffn_ratioself.ffn_ratio, num_blocksself.num_blocks, large_sizeself.large_size, small_sizeself.small_size, dimsself.dims, dw_dimsself.dw_dims,nvarsself.nvars, small_kernel_mergedself.small_kernel_merged, backbone_dropoutself.drop_backbone, head_dropoutself.drop_head, use_multi_scaleself.use_multi_scale, revinself.revin, affineself.affine,subtract_lastself.subtract_last, freqself.freq, seq_lenself.seq_len, c_inself.c_in, individualself.individual, target_windowself.target_window)else:self.model ModernTCN(patch_sizeself.patch_size,patch_strideself.patch_stride,stem_ratioself.stem_ratio, downsample_ratioself.downsample_ratio, ffn_ratioself.ffn_ratio, num_blocksself.num_blocks, large_sizeself.large_size, small_sizeself.small_size, dimsself.dims, dw_dimsself.dw_dims,nvarsself.nvars, small_kernel_mergedself.small_kernel_merged, backbone_dropoutself.drop_backbone, head_dropoutself.drop_head, use_multi_scaleself.use_multi_scale, revinself.revin, affineself.affine,subtract_lastself.subtract_last, freqself.freq, seq_lenself.seq_len, c_inself.c_in, individualself.individual, target_windowself.target_window)def forward(self, x, teNone):if self.decomposition:res_init, trend_init self.decomp_module(x)res_init, trend_init res_init.permute(0, 2, 1), trend_init.permute(0, 2, 1)if te is not None:te te.permute(0, 2, 1)res self.model_res(res_init, te)trend self.model_trend(trend_init, te)x res trendx x.permute(0, 2, 1)else:x x.permute(0, 2, 1)if te is not None:te te.permute(0, 2, 1)x self.model(x, te)x x.permute(0, 2, 1)return xLayerNorm层归一化模块对输入特征进行归一化处理支持不同的数据格式.get_conv1d、get_bn、conv_bn、fuse_bn卷积层、批量归一化层、卷积批量归一化组合模块以及批量归一化融合函数用于构建和优化卷积神经网络.ReparamLargeKernelConv可重参数化的大核卷积模块通过将大核卷积分解为小核卷积和大核卷积的组合实现模型的结构重参数化以提高模型的表达能力和效率.Block模型的基本构建块包含深度可分离卷积、归一化、前馈网络等组件用于提取特征和进行特征变换.Stage由多个Block组成的一个阶段用于构建模型的不同层次结构.ModernTCN主模型类定义了模型的整体架构和前向传播过程包括RevIN可逆归一化层、茎层、下采样层、多尺度融合层以及预测头部等组件.
总结
ModernTCN是一种现代化的纯卷积结构它通过以下几个关键创新点成功地将卷积技术重新带回时间序列分析的舞台
现代化的卷积块设计借鉴了Transformer中的架构设计ModernTCN采用了深度卷积和逐点卷积的组合以提高模型的表示能力。时间序列相关的修改为了更好地处理时间序列数据ModernTCN引入了变量独立嵌入和跨变量依赖捕获机制。扩大有效感受野ERF通过使用大核心尺寸而不是堆叠更多小核心ModernTCN显著扩大了ERF这有助于更好地捕捉时间序列数据中的长期依赖性。