Python实现时间序列预测中的深度学习模型:TCN/Informer/Autoformer的架构分析

好的,我们开始。

时间序列预测中的深度学习模型:TCN/Informer/Autoformer架构分析

今天,我们来深入探讨时间序列预测中三个重要的深度学习模型:TCN (Temporal Convolutional Network)、Informer和Autoformer。我们将从架构、原理、优缺点以及代码实现等方面进行详细分析。

一、时间序列预测简介

时间序列预测是指利用过去一段时间的历史数据来预测未来一段时间的数据。它广泛应用于金融、交通、能源、气象等领域。传统的时间序列预测方法包括ARIMA、指数平滑等,但这些方法在处理非线性、长依赖关系的时间序列数据时表现不佳。近年来,深度学习模型在时间序列预测领域取得了显著进展。

二、TCN (Temporal Convolutional Network)

TCN 是一种专门为处理时间序列数据而设计的卷积神经网络。与传统的RNN (Recurrent Neural Network) 相比,TCN具有并行计算、梯度消失问题较少等优点。

  • TCN架构

TCN的核心思想是利用因果卷积 (Causal Convolution) 和膨胀卷积 (Dilated Convolution) 来捕捉时间序列数据的长期依赖关系。

*   **因果卷积:** 保证t时刻的预测只依赖于t时刻及之前的历史数据,不会泄露未来信息。

*   **膨胀卷积:** 通过跳跃式地采样输入数据,扩大卷积核的感受野,从而捕捉更长范围的时间依赖关系。膨胀率 (Dilation Rate) 控制了卷积核的采样间隔。

*   **残差连接 (Residual Connection):** TCN通常采用残差连接来缓解梯度消失问题,提高模型的训练效果。
  • TCN原理

TCN通过堆叠多个膨胀卷积层,逐渐扩大感受野,最终能够覆盖整个输入序列。每个卷积层都包含一个或多个卷积核,用于提取不同时间尺度的特征。

  • TCN优缺点

    • 优点:

      • 并行计算:TCN可以并行处理整个输入序列,而RNN需要逐个时间步进行计算。
      • 梯度消失问题较少:残差连接和卷积操作有助于缓解梯度消失问题。
      • 感受野可控:通过调整膨胀率,可以灵活地控制感受野的大小。
    • 缺点:

      • 参数量较大:膨胀卷积可能会增加模型的参数量。
      • 对超参数敏感:膨胀率、卷积核大小等超参数的选择对模型性能有较大影响。
  • TCN代码实现 (PyTorch)

import torch
import torch.nn as nn
import torch.nn.functional as F

class TemporalBlock(nn.Module):
    def __init__(self, n_inputs, n_outputs, kernel_size, stride, dilation, padding, dropout=0.2):
        super(TemporalBlock, self).__init__()
        self.conv1 = nn.Conv1d(n_inputs, n_outputs, kernel_size,
                                           stride=stride, padding=padding, dilation=dilation,
                                           bias=False)
        self.chomp1 = Chomp1d(padding)
        self.relu1 = nn.ReLU()
        self.dropout1 = nn.Dropout(dropout)

        self.conv2 = nn.Conv1d(n_outputs, n_outputs, kernel_size,
                                           stride=stride, padding=padding, dilation=dilation,
                                           bias=False)
        self.chomp2 = Chomp1d(padding)
        self.relu2 = nn.ReLU()
        self.dropout2 = nn.Dropout(dropout)

        self.net = nn.Sequential(self.conv1, self.chomp1, self.relu1, self.dropout1,
                                    self.conv2, self.chomp2, self.relu2, self.dropout2)
        self.downsample = nn.Conv1d(n_inputs, n_outputs, 1) if n_inputs != n_outputs else None
        self.relu = nn.ReLU()
        self.init_weights()

    def init_weights(self):
        self.conv1.weight.data.normal_(0, 0.01)
        self.conv2.weight.data.normal_(0, 0.01)
        if self.downsample is not None:
            self.downsample.weight.data.normal_(0, 0.01)

    def forward(self, x):
        out = self.net(x)
        res = x if self.downsample is None else self.downsample(x)
        return self.relu(out + res)

class Chomp1d(nn.Module):
    def __init__(self, chomp_size):
        super(Chomp1d, self).__init__()
        self.chomp_size = chomp_size

    def forward(self, x):
        return x[:, :, :-self.chomp_size].contiguous()

class TemporalConvNet(nn.Module):
    def __init__(self, num_inputs, num_channels, kernel_size=2, dropout=0.2):
        super(TemporalConvNet, self).__init__()
        layers = []
        num_levels = len(num_channels)
        for i in range(num_levels):
            dilation_size = 2 ** i
            in_channels = num_inputs if i == 0 else num_channels[i-1]
            out_channels = num_channels[i]
            layers += [TemporalBlock(in_channels, out_channels, kernel_size, stride=1, dilation=dilation_size,
                                     padding=(kernel_size-1) * dilation_size, dropout=dropout)]

        self.network = nn.Sequential(*layers)

    def forward(self, x):
        return self.network(x)

class TCN(nn.Module):
    def __init__(self, input_size, output_size, num_channels, kernel_size, dropout):
        super(TCN, self).__init__()
        self.tcn = TemporalConvNet(input_size, num_channels, kernel_size=kernel_size, dropout=dropout)
        self.linear = nn.Linear(num_channels[-1], output_size)

    def forward(self, x):
        # x needs to be (batch, input_size, seq_len) for Conv1D
        y = self.tcn(x)
        return self.linear(y[:, :, -1]) # Take the last value for prediction

三、Informer

Informer 是一种基于Transformer的深度学习模型,专门为解决长序列时间序列预测问题而设计。它通过ProbSparse Self-Attention、Self-Attention Distilling和Generative Decoder等机制,有效地降低了计算复杂度,提高了预测精度。

  • Informer架构

    • Encoder: 负责提取输入序列的特征。Encoder主要由ProbSparse Self-Attention和Self-Attention Distilling组成。

      • ProbSparse Self-Attention: 传统的Self-Attention计算复杂度为O(L^2),其中L为序列长度。ProbSparse Self-Attention只关注重要的Query-Key对,从而降低了计算复杂度到O(L log L)。它通过计算每个Query的稀疏度,只选择稀疏度高的Query进行计算。

      • Self-Attention Distilling: 通过pooling操作,减少序列的长度,从而降低后续层的计算复杂度。

    • Decoder: 负责生成预测序列。Decoder采用Generative Decoder结构,通过Masked Self-Attention和Cross-Attention,逐步生成预测序列。

  • Informer原理

    Informer通过ProbSparse Self-Attention和Self-Attention Distilling,降低了Encoder的计算复杂度,使其能够处理更长的序列。Generative Decoder则能够生成高质量的预测序列。

  • Informer优缺点

    • 优点:

      • 计算复杂度低:ProbSparse Self-Attention和Self-Attention Distilling有效地降低了计算复杂度。
      • 预测精度高:能够处理长序列时间序列预测问题,并取得较高的预测精度。
    • 缺点:

      • 模型结构复杂:Informer的结构相对复杂,需要一定的理解成本。
      • 训练时间较长:虽然计算复杂度降低了,但训练长序列数据仍然需要较长的时间。
  • Informer代码实现 (PyTorch)

由于Informer的代码实现较为复杂,这里提供一个简化的示例代码,仅包含ProbSparse Self-Attention的核心部分。完整的Informer实现可以参考官方代码库或其他开源实现。

import torch
import torch.nn as nn
import math

class ProbSparseAttention(nn.Module):
    def __init__(self, mask_flag=True, factor=5, scale=None, attention_dropout=0.1):
        super(ProbSparseAttention, self).__init__()
        self.mask_flag = mask_flag
        self.factor = factor
        self.scale = scale
        self.dropout = nn.Dropout(attention_dropout)

    def _prob_qk(self, Q, K, sample_k, n_top): # Q [B, H, L, D] K [B, H, L, D]
        B, H, L_Q, D = Q.shape
        L_K = K.shape[-2]

        # calculate the sampled Q*K
        K_expand = K.unsqueeze(-3).expand(B, H, L_Q, L_K, D)
        index_sample = torch.randint(0, L_K, (L_Q, sample_k)) # real U = L_K
        K_sample = K_expand[:, :, torch.arange(L_Q).unsqueeze(1), index_sample, :]
        Q_K_sample = torch.matmul(Q.unsqueeze(-2), K_sample.transpose(-2, -1)).squeeze(-2)

        # find the Top_k query with sparisty measurement
        M = Q_K_sample.max(-1)[0] - torch.mean(Q_K_sample, dim=-1)
        M_top = M.topk(n_top, sorted=False)[1]

        # use the reduced Q, K, V
        Q_reduce = Q[torch.arange(B)[:, None, None],
                      torch.arange(H)[None, :, None],
                      M_top, :]
        K_reduce = K
        return Q_reduce, K_reduce, M_top

    def forward(self, Q, K, V, mask=None):
        B, H, L_Q, D = Q.shape
        L_V = V.shape[-2]

        if self.mask_flag:
            mask = mask.unsqueeze(1).to(Q.device)  # for head axis

        # Sample the Queries (ProbSparse Attention)
        U_part = self.factor * math.ceil(math.log(L_Q)) # num of sampled queries
        U_part = L_Q if U_part > L_Q else U_part
        Q_reduce, K_reduce, M_top = self._prob_qk(Q, K, sample_k=self.factor * math.ceil(math.log(L_K)), n_top=U_part)

        # calculate values using the reduced Q, K
        if self.scale is None:
            scores = torch.matmul(Q_reduce, K_reduce.transpose(-2, -1)) / math.sqrt(D)
        else:
            scores = torch.matmul(Q_reduce, K_reduce.transpose(-2, -1)) / self.scale

        if mask is not None:
            scores.masked_fill_(mask[:, :, M_top, :].bool(), -1e9)

        A = torch.softmax(scores, dim=-1)
        A = self.dropout(A)

        # calculate the final values
        V_reduce = V
        output = torch.matmul(A, V_reduce)

        return output

四、Autoformer

Autoformer是另一种基于Transformer的深度学习模型,同样是为了解决长序列时间序列预测问题而设计。它引入了自相关机制 (Auto-Correlation Mechanism),能够更好地捕捉时间序列数据的周期性特征。

  • Autoformer架构

    • Encoder: 负责提取输入序列的特征。Encoder主要由自相关机制和序列分解模块 (Series Decomposition Module) 组成。

      • 自相关机制: 通过计算序列的自相关系数,找到与当前时刻最相关的历史时刻,从而捕捉序列的周期性特征。自相关系数越高,表示两个时刻的相似度越高。

      • 序列分解模块: 将输入序列分解为趋势 (Trend) 分量和季节性 (Seasonal) 分量。趋势分量反映了序列的长期变化趋势,季节性分量反映了序列的周期性变化。

    • Decoder: 负责生成预测序列。Decoder也采用自相关机制和序列分解模块,逐步生成预测序列。

  • Autoformer原理

    Autoformer通过自相关机制和序列分解模块,能够更好地捕捉时间序列数据的周期性特征和长期趋势,从而提高预测精度。

  • Autoformer优缺点

    • 优点:

      • 能够捕捉周期性特征:自相关机制能够有效地捕捉时间序列数据的周期性特征。
      • 预测精度高:在具有明显周期性的时间序列数据上,Autoformer通常能够取得较高的预测精度。
    • 缺点:

      • 模型结构复杂:Autoformer的结构相对复杂,需要一定的理解成本。
      • 对非周期性数据效果不佳:对于非周期性的时间序列数据,Autoformer的效果可能不如其他模型。
  • Autoformer代码实现 (PyTorch)

由于Autoformer的代码实现较为复杂,这里提供一个简化的示例代码,仅包含自相关机制的核心部分。完整的Autoformer实现可以参考官方代码库或其他开源实现。

import torch
import torch.nn as nn
import torch.fft

class AutoCorrelation(nn.Module):
    def __init__(self, mask_flag=True, factor=1, scale=None, attention_dropout=0.1):
        super(AutoCorrelation, self).__init__()
        self.mask_flag = mask_flag
        self.factor = factor
        self.scale = scale
        self.dropout = nn.Dropout(attention_dropout)

    def time_delay_agg(self, values, corr, top_k):
        # values: [B, H, L, D]
        # top_k: int
        batch = values.shape[0]
        head = values.shape[1]
        length = values.shape[2]
        channel = values.shape[3]

        top_k_index = torch.topk(corr, top_k, dim=-1)[1]
        # top_k_index = torch.randint(0, length//2, [B, H, top_k])
        shift = top_k_index.unsqueeze(-1).repeat(1,1,1,channel)
        gather_values = torch.gather(values, dim=2, index=(torch.arange(length).unsqueeze(0).unsqueeze(0).unsqueeze(-1).expand_as(shift) - shift + length)%length)

        return gather_values

    def forward(self, Q, K, V, mask=None):
        B, H, L, D = Q.shape
        if self.mask_flag:
            mask = mask.unsqueeze(1).to(Q.device)

        # Q: [B, H, L, D]
        # K: [B, H, L, D]
        # V: [B, H, L, D]

        # compute correlation
        u = torch.fft.fft(Q, dim=[2]).real
        v = torch.fft.fft(K, dim=[2]).real
        corr = torch.fft.ifft(u * torch.conj(v), dim=[2]).real

        # find top k
        top_k = int(self.factor * math.log(L))
        top_k = min(top_k, L//2)
        weights = self.time_delay_agg(V, corr, top_k)
        return weights

五、模型对比

为了更清晰地对比这三个模型,我们将其优缺点总结如下表:

| 模型 | 优点 | 缺点 | 适用场景
六、总结

TCN,Informer和Autoformer是三个重要的深度学习时间序列预测模型。TCN以其并行处理能力和梯度消失问题的缓解而著称,但可能存在参数量大的问题。Informer通过ProbSparse Self-Attention降低计算复杂度,从而更好地处理长序列,但模型结构相对复杂。Autoformer引入自相关机制以更好地捕捉时间序列的周期性特征,但在处理非周期性数据时可能不尽如人意。选择合适的模型取决于特定任务的需求和数据的性质。

更多IT精英技术系列讲座,到智猿学院

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注