TCN+itransformer时间序列预测项目源码

本文尝试将TCN和itransformer相结合进行时间序列的预测,作为一个创新性模型。

1.介绍

基于Transformer模型的时序预测架构通常将同一时间步的不同变量编码成一个统一的多维temporal token,并通过注意力机制来建模不同时间步之间的时序相关性。然而,近年来线性时序预测模型重新崛起,展现出比Transformer模型更优的效果,促使人们反思当前基于Transformer的时序预测架构的一些局限性:

  1. 对于同一时间步的数据点,变量之间可能具有不同的物理意义,采集时间可能不一致,且尺度差异显著。将这些变量强行编码为统一的temporal token,不再区分不同的通道(channels),可能会导致多变量间的相关性被削弱,从而无法有效学习基于变量的高效表征,不适用于多变量时序预测任务。在某些数据集中,保持变量通道的独立性并考虑变量之间的互相关性是非常必要的。此外,由于变量之间存在时滞性,一个时间点的temporal token所包含的信息量有限,从这些token出发,可能不利于建模全局的时序相关性。

  2. 在建模时间方向上的长期依赖性时,随着历史窗口长度的增加,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.TCN模块


TCN(Temporal Convolutional Network,时序卷积网络)是一种用于处理时间序列数据的深度学习模型。它主要基于卷积神经网络(CNN),但在结构上进行了调整,以适应时间序列的特性。TCN 的设计目标是替代循环神经网络(RNN)在时间序列建模中的作用,特别是长依赖关系的建模。

3.1TCN 的关键机制


一维卷积(1D Convolutions)
TCN 使用一维卷积来处理时间序列数据。与传统 CNN 不同,TCN 的卷积核在时间维度上滑动,从而在不改变输入长度的情况下提取特征。
因果卷积(Causal Convolutions)
为了确保模型只利用当前及之前的时间步信息,而不会泄露未来的信息,TCN 使用因果卷积。具体来说,因果卷积保证输出序列中时间步 ttt 仅依赖于输入序列中时间步 ttt 及之前的值,避免了信息“穿越”。
膨胀卷积(Dilated Convolutions):
TCN 采用膨胀卷积(也称为扩张卷积),使得卷积核可以在更长的时间范围内捕捉依赖关系,而不需要增加计算量。膨胀卷积通过在卷积核之间插入间隔,从而扩展感受野。例如,当膨胀因子为 2 时,卷积核在时间步 1、3、5 等位置上采样,而不是连续的 1、2、3。
残差连接(Residual Connections):
为了构建更深的网络并减轻梯度消失的问题,TCN 引入了残差连接。残差连接允许跳过某些层,将输入直接传递到更深的层次,保留信息的同时促进梯度传播。
完全卷积网络(Fully Convolutional Network):
TCN 是一个完全卷积网络,即没有使用池化层。它通过卷积层的堆叠和膨胀因子的变化,逐渐增加感受野,最终覆盖整个输入序列。因此,TCN 的输出序列长度与输入序列长度相同,这非常适合时间序列任务中的需求。

4.结合优势

  • 局部时序模式捕捉(TCN):

    • TCN具有较强的局部时序特征提取能力,能够高效捕捉时间序列数据中的短期依赖关系。通过使用扩展卷积(dilated convolution),TCN可以在较短的计算时间内处理较长的序列,同时保持良好的感受野。因此,TCN能够很好地捕捉局部时序模式,如季节性变化和短期趋势。
  • 多变量依赖建模(iTransformer):

    • iTransformer专注于建模多变量时序数据中的跨变量依赖性。通过将每个变量独立编码为token,并利用注意力机制建模不同变量之间的相关性,iTransformer能够有效捕捉和利用变量之间的交互信息。同时,前馈网络能够进一步建模各个变量的时序相关性,获取更全面的时序表征。
  • 长期依赖关系处理:

    • iTransformer在处理长期依赖关系时具有一定的优势,特别是通过注意力机制,能够在不受固定窗口大小限制的情况下,捕捉全局时序依赖关系。然而,结合TCN后,能够进一步增强模型在处理长期和短期依赖关系方面的能力。TCN的卷积结构为处理长序列提供了高效手段,而iTransformer的全局注意力机制则补充了更复杂的跨时间步的相关性。

5.实验

5.1数据集

数据集都可以,只要是时间序列格式,不限领域,类似功率预测,风电光伏预测,负荷预测,流量预测,浓度预测,机械领域预测等等各种时间序列直接预测。可以做验证模型,对比模型。格式类似顶刊ETTH的时间序列格式即可。

比如这里是时间列+7列影响特征+1列预测特征

5.2

实验结果

将顶刊训练集划分为80%,20%。

拟合效果还是相对比较优秀的。精度很高。模型也可以继续优化,加一些优化方法或者替换TCN为其他的一些创新的方法。

部分代码

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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值