informer2020
Informer: Beyond Efficient Transformer for Long Sequence Time-Series Forecasting(原文)
Informer 是一个基于Transformer的模型,是为了应对长依赖关系而开发的。本文的主要主题是序列预测。序列预测可以在任何具有不断变化的数据的地方找到,例如股票市场等。尽管人工智能在大多数现实世界应用中都至关重要,但这并不容易;事实上,它需要一个具有高预测能力的稳健模型,可以捕捉长期依赖关系
首先,让我对 Transformers 做一个总结,以防你不了解它。(对于那些熟悉 Transformers 的人,你可以跳过本节)
Transformer 是一种新兴的深度学习模型,其出现的频率正在不断上升。它们采用了自注意力机制,在 NLP 和计算机视觉等具有挑战性的任务中表现出了模型性能的显著提升。Transformer 架构可以分为两个部分,即编码器和解码器,如下图 :<ce
Transformer 的重点在于其不受局部性限制;也就是说,与其他流行模型(如 CNN)相比,Transformer 不受局部性限制。此外,我们没有在 Transformer 中提出任何 CNN 架构;相反,我们在 Transformer 中使用基于注意力机制的结构,这使我们能够获得更好的结果。
注意力机制架构可以概括为图 3:
Q(查询)、K(键)和V(向量)是我们注意力的输入。
有关 transformer 的完整基本实现,可以查阅相关论文。下面开始介绍informer
Informer 架构
ProbSparse自注意力机制:
在这个通知器中,我们不使用公式 1,而是使用公式 2,让每个键只关注u 个主要查询:
编码器:在内存使用限制下处理较长的序列输入
编码器的设计方式是提取长序列输入的鲁棒长程依赖关系。图 4 显示了编码器的示意架构:
从图4可以看出,该结构由多个Attention块、Conv1d和MaxPooling层组成,用于对输入数据进行编码。通过将输入分成两半来构建主堆栈的副本,可以提高蒸馏操作的可靠性。此外,自注意力蒸馏层的数量不断减少。在编码器的末端,研究人员连接了Feature Map,将编码器的输出引导至解码器
class ConvLayer(nn.Module):
def __init__(self, c_in):
super(ConvLayer, self).__init__()
padding = 1 if torch.__version__>='1.5.0' else 2
self.downConv = nn.Conv1d(in_channels=c_in,out_channels=c_in,kernel_size=3,padding=padding,padding_mode='circular')
self.norm = nn.BatchNorm1d(c_in)
self.activation = nn.ELU()
self.maxPool = nn.MaxPool1d(kernel_size=3, stride=2, padding=1)
def forward(self, x):
x = self.downConv(x.permute(0, 2, 1))
x = self.norm(x)
x = self.activation(x)
x = self.maxPool(x)
x = x.transpose(1,2)
return x
class EncoderLayer(nn.Module):
def __init__(self, attention, d_model, d_ff=None, dropout=0.1, activation="relu"):
super(EncoderLayer, self).__init__()d_ff = d_ff or 4*d_model
self.attention = attention
self.conv1 = nn.Conv1d(in_channels=d_model, out_channels=d_ff, kernel_size=1)
self.conv2 = nn.Conv1d(in_channels=d_ff, out_channels=d_model, kernel_size=1)
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
self.dropout = nn.Dropout(dropout)
self.activation = F.relu if activation == "relu" else F.gelu
def forward(self, x, attn_mask=None):
new_x, attn = self.attention(x, x, x,attn_mask = attn_mask)
x = x + self.dropout(new_x)
y = x = self.norm1(x)
y = self.dropout(self.activation(self.conv1(y.transpose(-1,1))))
y = self.dropout(self.conv2(y).transpose(-1,1))
return self.norm2(x+y), attn
class Encoder(nn.Module):
def __init__(self, attn_layers, conv_layers=None, norm_layer=None):
super(Encoder, self).__init__()
self.attn_layers = nn.ModuleList(attn_layers)
self.conv_layers = nn.ModuleList(conv_layers) if conv_layers is not None else None
self.norm = norm_layer
def forward(self, x, attn_mask=None):
# x [B, L, D]
attns = []
if self.conv_layers is not None:
for attn_layer, conv_layer in zip(self.attn_layers, self.conv_layers):
x, attn = attn_layer(x, attn_mask=attn_mask)
x = conv_layer(x)
attns.append(attn)
x, attn = self.attn_layers[-1](x, attn_mask=attn_mask)
attns.append(attn)
else:
for attn_layer in self.attn_layers:
x, attn = attn_layer(x, attn_mask=attn_mask)
attns.append(attn)
if self.norm is not None:
x = self.norm(x)
return x, attns
class EncoderStack(nn.Module):
def __init__(self, encoders, inp_lens):
super(EncoderStack, self).__init__()
self.encoders = nn.ModuleList(encoders)
self.inp_lens = inp_lens
def forward(self, x, attn_mask=None):
# x [B, L, D]
x_stack = []; attns = []
for i_len, encoder in zip(self.inp_lens, self.encoders):
inp_len = x.shape[1]//(2**i_len)
x_s, attn = encoder(x[:, -inp_len:, :])
x_stack.append(x_s); attns.append(attn)
x_stack = torch.cat(x_stack, -2)
return x_stack,attns
Informer 架构
解码器:通过一个前向过程生成长序列输出
解码器结构并不复杂;它是标准解码器结构。它包括两个相同的多头注意力层的堆栈。但是,生成推理的提出是为了缓解长预测中的速度下降,如图 5 所示:
class DecoderLayer(nn.Module):
def __init__(self, self_attention, cross_attention, d_model, d_ff=None,dropout=0.1, activation="relu"):
super(DecoderLayer, self).__init__()
d_ff = d_ff or 4*d_model
self.self_attention = self_attention
self.cross_attention = cross_attention
self.conv1 = nn.Conv1d(in_channels=d_model, out_channels=d_ff, kernel_size=1)
self.conv2 = nn.Conv1d(in_channels=d_ff, out_channels=d_model, kernel_size=1)
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
self.norm3 = nn.LayerNorm(d_model)
self.dropout = nn.Dropout(dropout)
self.activation = F.relu if activation == "relu" else F.gelu
def forward(self, x, cross, x_mask=None, cross_mask=None):
x = x + self.dropout(self.self_attention(x, x, x,attn_mask=x_mask)[0])
x = self.norm1(x)
x = x + self.dropout(self.cross_attention(x, cross, cross,attn_mask=cross_mask)[0])
y = x = self.norm2(x)
y = self.dropout(self.activation(self.conv1(y.transpose(-1,1))))
y = self.dropout(self.conv2(y).transpose(-1,1))
return self.norm3(x+y)
class Decoder(nn.Module):
def __init__(self, layers, norm_layer=None):
super(Decoder, self).__init__()
self.layers = nn.ModuleList(layers)
self.norm = norm_layer
def forward(self, x, cross, x_mask=None, cross_mask=None):
for layer in self.layers:
x = layer(x, cross, x_mask=x_mask, cross_mask=cross_mask)
if self.norm is not None:
x = self.norm(x)
return x
接下来是代码调试过程
主要就是安装环境,数据集,如果数据集不是用项目提供的数据集,可能还要经过一些处理,最好是处理成跟提供的数据集类似的,这样是最快的,当然也可以改代码来适应自己的数据集
还有就是当自己的数据集出现效果不好,指标差的情况,可以调整参数,多次训练。可以改变三个重要的长度参数:延长输入长度(48、96、168、240、336、480、624、720)、编码器输入长度(78、96、168、240、480、624、720)和编码器输入长度(96、168、240、336、480、720)
还有就是学习率和batch_size的调整。
运行方法
首先使用git命名将项目下载到本地
命令为git clone:
git clone https://github.com/zhouhaoyi/Informer2020.git
后面这个连接地址取得的方法:搜索Informer2020,点code,复制出现的链接,右边有复制按钮
然后cd Informer2020, cd是进入该目录,使用git clone命令会将项目下载到 Informer2020目录。
接下来需要创建虚拟环境,我使用的是conda来创建虚拟环境,命令为:
conda create -n informer python=3.8,然后输入y,虚拟环境即可创建完成。
infomer可以进行更改,是该虚拟环境的名称
然后激活该环境:conda activate informer
接下来要安装依赖,可以使用命令pip install -r requirements.txt,等待安装完成即可
在搭建好环境后,需要下载数据集,也可以使用自己的数据集,不过可能需要更改一些代码。
原文使用的数据集为ETT数据集,下载了数据集后,新建一个datasets目录,复制到该目录。
然后可以用vscode打开Informer2020目录,使用以下命令运行该项目:
# ETTh1
python -u main_informer.py --model informer --data ETTh1 --attn prob --freq h
# ETTh2
python -u main_informer.py --model informer --data ETTh2 --attn prob --freq h
# ETTm1
python -u main_informer.py --model informer --data ETTm1 --attn prob --freq t