本文尝试使用傅里叶卷积改进itransformer,相结合进行时间序列的预测,作为一个创新性模型。
1.介绍
基于Transformer模型的时序预测架构通常将同一时间步的不同变量编码成一个统一的多维temporal token,并通过注意力机制来建模不同时间步之间的时序相关性。然而,近年来线性时序预测模型重新崛起,展现出比Transformer模型更优的效果,促使人们反思当前基于Transformer的时序预测架构的一些局限性:
-
对于同一时间步的数据点,变量之间可能具有不同的物理意义,采集时间可能不一致,且尺度差异显著。将这些变量强行编码为统一的temporal token,不再区分不同的通道(channels),可能会导致多变量间的相关性被削弱,从而无法有效学习基于变量的高效表征,不适用于多变量时序预测任务。在某些数据集中,保持变量通道的独立性并考虑变量之间的互相关性是非常必要的。此外,由于变量之间存在时滞性,一个时间点的temporal token所包含的信息量有限,从这些token出发,可能不利于建模全局的时序相关性。
-
在建模时间方向上的长期依赖性时,随着历史窗口长度的增加,Transformer面临性能下降和计算量爆炸的问题。
基于这些思考,提出了一种全新的基于Transformer的时序预测架构,该架构并未改变Transformer的网络结构,而是重新定义了注意力机制和前馈网络的作用。iTransformer将不同的变量独立编码为各自的token,通过注意力机制来建模变量之间的相关性,同时通过前馈网络建模变量的时序相关性,从而获取更优的时序表征。
2.工作原理
图上部分是传统Transformer的运行机制,传统的Transformer是将同一时间戳下的各个变量赋予相同的Token值,会影响各个变量之间相关性的提取;同时当遇到时间不对齐事件时,这种方式也会引入噪声。传统的Transformer的self-attention和Embedding也会将时序信息打乱,这样也会对预测产生影响。基于此,本文提出iTransformer“倒置Transformer”,简而言之就是对时间序列采取一种“倒置视角”,将每个变量的整个时间序列独立地Embedding为一个token,并用注意力机制进行多元关联,同时利用FNN进行序列表示。
具体介绍可以看源码论文:https://arxiv.org/pdf/2310.06625
目前清华大学排列的不同任务预测效果,itransformer也排在前列,是一个相对优秀的模型。
3.傅里叶混合卷积模块
傅里叶混合卷积模块是2024年最近发布的一个基于傅里叶的卷积层,简单的过程如下:
空间域特征提取:FFCM首先使用点卷积(Pointwise Convolution)和深度可分离卷积(Depthwise Separable Convolution)对输入特征进行多尺度局部特征提取,生成空间域特征图。
频域特征提取:接着,FFCM将空间域特征图通过离散傅里叶变换(Discrete Fourier Transform, DFT)转换到频域,生成频域特征图。频域特征图包含了图像的全局信息,特别是雨线噪声的频率特性。
频域特征处理:在频域中,FFCM对频域特征图应用卷积操作,进一步提取和增强全局特征。然后,通过逆傅里叶变换(Inverse DFT)将处理后的频域特征转换回空间域。
特征融合:最后,FFCM将处理后的频域特征和原始空间域特征通过残差连接(Residual Connection)进行融合,生成包含丰富全局和局部信息的输出特征图。
下面有详细的视频介绍,这里就不多赘述,需要的同学可以看论文和视频
原始论文:https://link.springer.com/chapter/10.1007/978-3-031-72940-9_14
3.实验
数据集
数据集都可以,只要是时间序列格式,不限领域,类似功率预测,风电光伏预测,负荷预测,流量预测,浓度预测,机械领域预测等等各种时间序列直接预测。可以做验证模型,对比模型。格式类似顶刊ETTH的时间序列格式即可。
比如这里是时间列+7列影响特征+1列预测特征
部分代码
import torch
import torch.nn as nn
import torch.nn.functional as F
from layers.Transformer_EncDec import Encoder, EncoderLayer
from layers.SelfAttention_Family import FullAttention, AttentionLayer
from layers.Embed import DataEmbedding_inverted
import numpy as np
class Model(nn.Module):
"""
Paper link: https://arxiv.org/abs/2310.06625
"""
def __init__(self, configs):
super(Model, self).__init__()
self.task_name = configs.task_name
self.seq_len = configs.seq_len
self.pred_len = configs.pred_len
self.output_attention = configs.output_attention
# Embedding
self.enc_embedding = DataEmbedding_inverted(configs.seq_len, configs.d_model, configs.embed, configs.freq,
configs.dropout)
# Encoder
self.encoder = Encoder(
[
EncoderLayer(
AttentionLayer(
FullAttention(False, configs.factor, attention_dropout=configs.dropout,
output_attention=configs.output_attention), configs.d_model, configs.n_heads),
configs.d_model,
configs.d_ff,
dropout=configs.dropout,
activation=configs.activation
) for l in range(configs.e_layers)
],
norm_layer=torch.nn.LayerNorm(configs.d_model)
)
# Decoder
if self.task_name == 'long_term_forecast' or self.task_name == 'short_term_forecast':
self.projection = nn.Linear(configs.d_model, configs.pred_len, bias=True)
if self.task_name == 'imputation':
self.projection = nn.Linear(configs.d_model, configs.seq_len, bias=True)
if self.task_name == 'anomaly_detection':
self.projection = nn.Linear(configs.d_model, configs.seq_len, bias=True)
if self.task_name == 'classification':
self.act = F.gelu
self.dropout = nn.Dropout(configs.dropout)
self.projection = nn.Linear(configs.d_model * configs.enc_in, configs.num_class)
def forecast(self, x_enc, x_mark_enc, x_dec, x_mark_dec):
# Normalization from Non-stationary Transformer
means = x_enc.mean(1, keepdim=True).detach()
x_enc = x_enc - means
stdev = torch.sqrt(torch.var(x_enc, dim=1, keepdim=True, unbiased=False) + 1e-5)
x_enc /= stdev
_, _, N = x_enc.shape
# Embedding
enc_out = self.enc_embedding(x_enc, x_mark_enc)
enc_out, attns = self.encoder(enc_out, attn_mask=None)
dec_out = self.projection(enc_out).permute(0, 2, 1)[:, :, :N]
# De-Normalization from Non-stationary Transformer
dec_out = dec_out * (stdev[:, 0, :].unsqueeze(1).repeat(1, self.pred_len, 1))
dec_out = dec_out + (means[:, 0, :].unsqueeze(1).repeat(1, self.pred_len, 1))
return dec_out
def imputation(self, x_enc, x_mark_enc, x_dec, x_mark_dec, mask):
# Normalization from Non-stationary Transformer
means = x_enc.mean(1, keepdim=True).detach()
x_enc = x_enc - means
stdev = torch.sqrt(torch.var(x_enc, dim=1, keepdim=True, unbiased=False) + 1e-5)
x_enc /= stdev
_, L, N = x_enc.shape
# Embedding
enc_out = self.enc_embedding(x_enc, x_mark_enc)
enc_out, attns = self.encoder(enc_out, attn_mask=None)
dec_out = self.projection(enc_out).permute(0, 2, 1)[:, :, :N]
# De-Normalization from Non-stationary Transformer
dec_out = dec_out * (stdev[:, 0, :].unsqueeze(1).repeat(1, L, 1))
dec_out = dec_out + (means[:, 0, :].unsqueeze(1).repeat(1, L, 1))
return dec_out
def anomaly_detection(self, x_enc):
# Normalization from Non-stationary Transformer
means = x_enc.mean(1, keepdim=True).detach()
x_enc = x_enc - means
stdev = torch.sqrt(torch.var(x_enc, dim=1, keepdim=True, unbiased=False) + 1e-5)
x_enc /= stdev
_, L, N = x_enc.shape
# Embedding
enc_out = self.enc_embedding(x_enc, None)
enc_out, attns = self.encoder(enc_out, attn_mask=None)
dec_out = self.projection(enc_out).permute(0, 2, 1)[:, :, :N]
# De-Normalization from Non-stationary Transformer
dec_out = dec_out * (stdev[:, 0, :].unsqueeze(1).repeat(1, L, 1))
dec_out = dec_out + (means[:, 0, :].unsqueeze(1).repeat(1, L, 1))
return dec_out
def classification(self, x_enc, x_mark_enc):
# Embedding
enc_out = self.enc_embedding(x_enc, None)
enc_out, attns = self.encoder(enc_out, attn_mask=None)
# Output
output = self.act(enc_out) # the output transformer encoder/decoder embeddings don't include non-linearity
output = self.dropout(output)
output = output.reshape(output.shape[0], -1) # (batch_size, c_in * d_model)
output = self.projection(output) # (batch_size, num_classes)
return output
def forward(self, x_enc, x_mark_enc, x_dec, x_mark_dec, mask=None):
if self.task_name == 'long_term_forecast' or self.task_name == 'short_term_forecast':
dec_out = self.forecast(x_enc, x_mark_enc, x_dec, x_mark_dec)
return dec_out[:, -self.pred_len:, :] # [B, L, D]
if self.task_name == 'imputation':
dec_out = self.imputation(x_enc, x_mark_enc, x_dec, x_mark_dec, mask)
return dec_out # [B, L, D]
if self.task_name == 'anomaly_detection':
dec_out = self.anomaly_detection(x_enc)
return dec_out # [B, L, D]
if self.task_name == 'classification':
dec_out = self.classification(x_enc, x_mark_enc)
return dec_out # [B, N]
return None
4.源码地址及其讲解
源码地址:https://www.bilibili.com/video/BV1kaSMYoEUe/?spm_id_from=333.999.0.0