关于机器学习:5个时间序列预测的深度学习模型对比总结从模拟统计模型到可以预训练的无监督模型

35次阅读

共计 13150 个字符,预计需要花费 33 分钟才能阅读完成。

工夫序列预测在最近两年内产生了微小的变动,尤其是在 kaiming 的 MAE 呈现当前,当初工夫序列的模型也能够用相似 MAE 的办法进行无监督的预训练

Makridakis M-Competitions 系列(别离称为 M4 和 M5)别离在 2018 年和 2020 年举办(M6 也在往年举办了)。对于那些不理解的人来说,m 系列得较量能够被认为是工夫序列生态系统的一种现有状态的总结,为以后得预测的实践和实际提供了教训和主观的证据。

2018 年 M4 的结果表明,纯正的“ML”办法在很大水平上胜过传统的统计办法,这在过后是出其不意的。在两年后的 M5[1]中,最的高分是仅具备“ML”办法。并且所有前 50 名基本上都是基于 ML 的(大部分是树型模型)。这场较量看到了 LightGBM(用于工夫序列预测)以及 Amazon’s Deepar [2]和 N -Beats [3]的首次亮相。N-Beats 模型于 2020 年公布,并且优于 M4 较量的获胜者 3%!

最近的 Ventilator Pressure Prediction较量展现了应用深度学习办法来应答实时工夫序列挑战的重要性。较量的目标是预测机械肺内压力的工夫程序。每个训练实例都是本人的工夫序列,因而工作是一个多个工夫序列的问题。获胜团队提交了多层深度架构,其中包含 LSTM 网络和 Transformer 块。

在过来的几年中,许多驰名的架构曾经公布,如 MQRNN 和 DSSM。所有这些模型都利用深度学习为工夫序列预测畛域奉献了许多新货色。除了博得 Kaggle 较量,还给咱们带来了更多的提高比方:

  • 多功能性:将模型用于不同工作的能力。
  • MLOP:在生产中应用模型的能力。
  • 解释性和解释性:黑盒模型并不那么受欢迎。

本文探讨了 5 种专门钻研工夫序列预测的深度学习体系结构,论文是:

  1. N-BEATS (ElementAI)
  2. DeepAR (Amazon)
  3. Spacetimeformer [4]
  4. Temporal Fusion Transformer or TFT (Google) [5]
  5. TSFormer(工夫序列中的 MAE)[7]

N-BEATS

这种模式间接来自于 (可怜的) 长寿的 ElementAI 公司,该公司是由 Yoshua Bengio 联结创建的。顶层架构及其次要组件如图 1 所示:

N-BEATS 是一个纯正的深度学习架构,它基于集成前馈网络的深度堆栈,这些网络也通过正向和反向的相互连接进行重叠。

每一个块只对由前一个的 backcast 产生的残差进行建模,而后基于该误差更新预测。该过程模仿了拟合 ARIMA 模型时的 Box-Jenkins 办法。

以下是该模型的次要劣势:

表白性强且易于应用: 该模型易于了解,具备模块化构造,它被设计为须要最小的工夫序列特色工程并且不须要对输出进行缩放。

该模型具备对多个工夫序列进行概括的能力。换句话说,散布略有不同的不同工夫序列能够用作输出。在 N -BEATS 中是通过元学习实现的。元学习过程包含两个过程: 外部学习过程和内部学习过程。外部学习过程产生在块外部,并帮忙模型捕捉部分工夫特色。内部学习过程产生在重叠层,帮忙模型学习所有工夫序列的全局特色。

双重残差叠加: 残差连贯和叠加的想法是十分奇妙的,它简直被用于每一种类型的深度神经网络。在 N -BEATS 的实现中利用了雷同的原理,但有一些额定的批改: 每个块有两个残差分支,一个运行在回看窗口(称为 backcast),另一个运行在预测窗口(称为 forecast)。

每一个间断的块只对由前一个块重建的 backcast 产生的残差进行建模,而后基于该误差更新预测。这有助于模型更好地迫近有用的后推信号,同时最终的堆栈预测预测被建模为所有局部预测的分层和。就是这个过程模仿了 ARIMA 模型的 Box-Jenkins 办法。

可解释性: 模型有两种变体,通用的和可解释性的。在通用变体中,网络任意学习每个块的全连贯层的最终权值。在可解释的变体中,每个块的最初一层被删除。而后将后推 backcast 和预测 forecast 分支乘以模仿趋势 (枯燥函数) 和季节性 (周期性循环函数) 的特定矩阵。

留神: 原始的 N -BEATS 实现只实用于单变量工夫序列。

DeepAR

联合深度学习和自回归个性的新鲜工夫序列模型。图 2 显示了 DeepAR 的顶层架构:

以下是该模型的次要劣势:

DeepAR 在多个工夫序列上工作得十分好: 通过应用多个散布略有不同的工夫序列来构建全局模型。也实用于许多事实场景。例如电力公司可能心愿为每个客户推出电力预测服务,每个客户都有不同的生产模式(这意味着不同的散布)。

除了历史数据,DeepAR 还容许应用已知的将来工夫序列 (自回归模型的一个特点) 和额定的动态属性。在后面提到的电力需求预测场景中,一个额定的工夫变量能够是月份(作为一个整数,值在 1 -12 之间)。假如每个客户都与一个测量功耗的传感器相关联,那么额定的动态变量将是 sensor_id 或 customer_id 之类的货色。

如果莫相熟应用 MLPs 和 rnn 等神经网络架构进行工夫序列预测,那么一个要害的预处理步骤是应用标准化或标准化技术对工夫序列进行缩放。这在 DeepAR 中是不须要手动操作的,因为底层的模型对每个工夫序列 i 的自回归输出 z 进行缩放,缩放因子为 v_i,即该工夫序列的平均值。具体而言,论文基准中应用的比例因子方程如下:

然而在实践中,如果指标工夫序列的大小差别很大,那么在预处理过程中利用本人的缩放还是很有必要的。例如,在能源需求预测场景中,数据集能够蕴含中压电力客户 (例如小工厂,按兆瓦单位耗费电力) 和低压客户(例如家庭,按千瓦单位耗费电力)。

DeepAR 进行概率预测,而不是间接输入将来值。这是以蒙特卡洛样本的模式实现的。这些预测被用来计算分位数预测,通过应用分位数损失函数。对于那些不相熟这种损失类型的人,分位数损失不仅用来计算一个预计,而且用来计算围绕该值的预测区间。

Spacetimeformer

在单变量工夫序列中工夫依赖性是最重要的。然而在多个工夫序列场景中,事件就没那么简略了。例如假如咱们有一个天气预报工作,想要预测五个城市的温度。让咱们假如这些城市属于一个国家。鉴于目前所看到的,咱们能够应用 DeepAR 并将每个城市作为内部动态协变量进行建模。

换句话说,该模型将同时思考工夫和空间关系。这便是 Spacetimeformer 的核心理念:应用一个模型来利用这些城市 / 地点之间的空间关系,从而学习额定的有用依赖,因为模型将同时思考工夫和空间关系。

深入研究时空序列

顾名思义,这种模型在外部应用了基于 transformers 的构造。在应用基于 transformers 的模型进行工夫序列预测时,一种风行的产生工夫感知嵌入的技术是通过 Time2Vec[6]嵌入层传递输出(对于 NLP 工作是应用地位编码向量来代替 Time2Vec)。尽管这种技术对于单变量工夫序列十分无效,但对于多变量工夫输出却没有任何意义。可能是在语言建模中,句子中的每个单词都用嵌入示意,单词实质上是一个是词汇表的一部分,而工夫序列则没那么简略。

在多元工夫序列中,在给定的工夫步长 t,输出的模式为 x_1,t, x2,t, x_m,t 其中 x_i,t 是特色 i 的数值,m 是特色 / 序列的总数。如果咱们将输出通过一个 Time2Vec 层,将产生一个工夫嵌入向量。这种嵌入真正代表什么? 答案是它将把整个输出汇合示意为单个实体(令牌)。因而模型将只学习工夫步之间的工夫动静,但将错过特色 / 变量之间的空间关系。

Spacetimeformer 解决了这个问题,它将输出扁平化为一个大向量,称为时空序列。如果输出蕴含 N 个变量,组织成 T 个工夫步,则生成的时空序列将具备 (NxT) 标记。下图 3 更好地显示了这一点:

论文指出:“(1)蕴含工夫信息的多元输出格局。解码器输出短少 (“?”) 值,在进行预测时设置为零。(2)工夫序列通过一个 Time2Vec 层,生成一个代表周期性输出模式的频率嵌入。(3)二进制嵌入示意该值是作为上下文给出的还是须要预测的。(4)将每个工夫序列的整数索引映射到一个具备查找表嵌入的“空间”示意。(5)利用前馈层投影每个工夫序列的 Time2Vec 嵌入和变量值。(6)将值和工夫,变量和给定的嵌入求和会导致使 MSA 在工夫和可变空间之间以更长的序列作为输出。

换句话说,最初的序列编码了一个蕴含了工夫、空间和上下文信息对立的嵌入。然而这种办法的一个毛病是,序列可能会变得很长导致资源的二次增长。这是因为依据留神机制,每个令牌都要对另一个进行查看。作者应用了一种更无效的体系结构,称为 Performer 留神机制,实用于更大的序列。

Temporal Fusion Transformer

Temporal Fusion Transformer(TFT)是由 Google 公布的基于 Transformer 的工夫序列预测模型。TFT 比以前的模型更加通用。

TFT 的顶层架构如图 4 所示。以下是该模型的次要劣势:

与后面提到的模型一样,TFT 反对在多个异构工夫序列上构建模型。

TFT 反对三种类型的特色:i)具备已知的将来输出的时变数据 ii)仅到目前为止已知的时变数据 iii)分类 / 动态变量,也被称为时不变特色。因而 TFT 比以前的型号更通用。在后面提到的电力需求预测场景中,咱们心愿应用湿度程度作为一个时变特色,这是到目前为止才晓得的。这在 TFT 中是可行的,但在 DeepAR 中不行。

图 5 显示了如何应用所有这些个性的示例:

TFT 十分强调可解释性。具体地说,通过利用 Variable Selection 组件(如上图 4 所示),模型能够胜利地度量每个个性的影响。因而能够说模型学习了个性的重要性。

另一方面,TFT 提出了一种新的可解释的多头留神机制: 该层的留神权重能够揭示在回顾期间哪些工夫步是最重要的。这些权重的可视化能够揭示整个数据集中最显著的节令模式。

预测区间: 与 DeepAR 相似,TFT 通过应用分位数回归输入预测区间和预测值。

综上所述,深度学习无疑彻底改变了工夫序列预测的格局。上述所有模型除了无可比拟的性能之外,还有一个共同点: 它们充分利用多重、多元的工夫数据,同时它们应用外生信息,将预测性能进步到前所未有的程度。然而在自然语言解决 (NLP) 工作中少数都利用了预训练的模型。NLP 工作的 feed 大多是人类发明的数据,充斥了丰盛而优良的信息,简直能够看作是一个数据单元。在工夫序列预测中,咱们能够感觉到不足这种事后训练的模型。为什么咱们不能像在 NLP 中那样在工夫序列中利用这个劣势呢?

这就引出了咱们要介绍的最初一个模型 TSFormer,该模型思考了两个视角,咱们讲从输出到输入将其为四个局部,并且提供 Python 的实现代码(官网也提供了),这个模型是刚刚公布不久的,所以咱们才在这里着重介绍它。

TSFormer

它是一种基于 Transformer(TSFormer)的无监督的工夫序列预训练模型,应用了 MAE 中的训练策略并且可能捕捉数据中十分长的依赖关系。

NLP 和工夫序列:

在某种程度上,NLP 信息和 Time Series 数据是雷同的。它们都是程序数据和部分敏感的,这意味着与它的下一个 / 以前的数据点无关。然而还是有一些区别,在提出咱们的预训练模型时,咱们应该思考两个差别,就像咱们在 NLP 工作中所做的那样:

  • 工夫序列数据的密度比自然语言数据低得多
  • 咱们须要比 NLP 数据更长的工夫序列数据

TSFormer 简介

TSFormer 与 MAE 的次要体系结构根本相似,数据通过一个编码器,而后通过一个解码器,最终的指标是为了重建缺失(人工掩蔽)的数据。

咱们将他总结为以下 4 点

1、掩蔽

作为数据进入编码器的前一步。输出序列(Sᶦ)已散布到 P 片中,其长度为 L。因而,用于预测下一个工夫步长的滑动窗口的 langth 是 P XL。

遮蔽比率为 75%(看着很高,预计是用了 MAE 一样的参数);咱们要实现的是一项自监督工作,所以数据越少编码器的计算速度就越快。

这样做(掩蔽输出序列段)的次要起因是:

  • 段(patch)比独自点好。
  • 它使应用上游模型变得简略(STGNN 将单元段作为输出)
  • 能够合成编码器的输出大小。
class Patch(nn.Module):
    def __init__(self, patch_size, input_channel, output_channel, spectral=True):
        super().__init__()
        self.output_channel = output_channel
        self.P = patch_size
        self.input_channel = input_channel
        self.output_channel = output_channel
        self.spectral = spectral
        if spectral:
            self.emb_layer = nn.Linear(int(patch_size/2+1)*2, output_channel)
        else:
            self.input_embedding = nn.Conv2d(input_channel, output_channel, kernel_size=(self.P, 1), stride=(self.P, 1))

    def forward(self, input):
        B, N, C, L = input.shape
        if self.spectral:
            spec_feat_ = torch.fft.rfft(input.unfold(-1, self.P, self.P), dim=-1)
            real = spec_feat_.real
            imag = spec_feat_.imag
            spec_feat = torch.cat([real, imag], dim=-1).squeeze(2)
            output = self.emb_layer(spec_feat).transpose(-1, -2)
        else:
            input = input.unsqueeze(-1)             # B, N, C, L, 1
            input = input.reshape(B*N, C, L, 1)                                     # B*N,  C, L, 1
            output = self.input_embedding(input)                                    # B*N,  d, L/P, 1
            output = output.squeeze(-1).view(B, N, self.output_channel, -1)
            assert output.shape[-1] == L / self.P
        return output

以下是生成遮蔽的函数

class MaskGenerator(nn.Module):
    def __init__(self, mask_size, mask_ratio, distribution='uniform', lm=-1):
        super().__init__()
        self.mask_size = mask_size
        self.mask_ratio = mask_ratio
        self.sort = True
        self.average_patch = lm
        self.distribution = distribution
        if self.distribution == "geom":
            assert lm != -1
        assert distribution in ['geom', 'uniform']

    def uniform_rand(self):
        mask = list(range(int(self.mask_size)))
        random.shuffle(mask)
        mask_len = int(self.mask_size * self.mask_ratio)
        self.masked_tokens = mask[:mask_len]
        self.unmasked_tokens = mask[mask_len:]
        if self.sort:
            self.masked_tokens = sorted(self.masked_tokens)
            self.unmasked_tokens = sorted(self.unmasked_tokens)
        return self.unmasked_tokens, self.masked_tokens

    def geometric_rand(self):
        mask = geom_noise_mask_single(self.mask_size, lm=self.average_patch, masking_ratio=self.mask_ratio)     # 1: masked, 0:unmasked
        self.masked_tokens = np.where(mask)[0].tolist()
        self.unmasked_tokens = np.where(~mask)[0].tolist()
        # assert len(self.masked_tokens) > len(self.unmasked_tokens)
        return self.unmasked_tokens, self.masked_tokens

    def forward(self):
        if self.distribution == 'geom':
            self.unmasked_tokens, self.masked_tokens = self.geometric_rand()
        elif self.distribution == 'uniform':
            self.unmasked_tokens, self.masked_tokens = self.uniform_rand()
        else:
            raise Exception("ERROR")
        return self.unmasked_tokens, self.masked_tokens

2、编码

包含了输出嵌入,地位编码和 Transformer 块的。编码器只能在未遮蔽的 patchs 上执行(这个也是 MAE 的办法)。

输出嵌入

应用线性的投影来取得输出的嵌入,可将未遮蔽的空间转换为潜在空间。它的公式能够在上面看到:

W 和 B 是可学习的参数,U 是维度中的模型输出向量。

地位编码

简略的地位编码层用于附加新的程序信息。增加了“可学习”一词,这有助于体现出比正弦更好的性能。因而可学习的地位嵌入显示了工夫序列的良好后果。

class LearnableTemporalPositionalEncoding(nn.Module):
    def __init__(self, d_model, dropout=0.1, max_len: int = 1000):
        super().__init__()
        self.dropout = nn.Dropout(p=dropout)
        self.pe = nn.Parameter(torch.empty(max_len, d_model), requires_grad=True)
        nn.init.uniform_(self.pe, -0.02, 0.02)
    
    def forward(self, X, index):
        if index is None:
            pe = self.pe[:X.size(1), :].unsqueeze(0)
        else:
            pe = self.pe[index].unsqueeze(0)
        X  = X + pe
        X  = self.dropout(X)
        return X

class PositionalEncoding(nn.Module):
    def __init__(self, hidden_dim, dropout=0.1):
        super().__init__()
        self.tem_pe = LearnableTemporalPositionalEncoding(hidden_dim, dropout)

    def forward(self, input, index=None, abs_idx=None):
        B, N, L_P, d = input.shape
        # temporal embedding
        input = self.tem_pe(input.view(B*N, L_P, d), index=index)
        input = input.view(B, N, L_P, d)
        # absolute positional embedding
        return input

Transformer 块

论文应用了 4 层 Transformer,比计算机视觉和自然语言解决工作中常见的数量低。这里所应用的 Transformer 是最根本的也是在原始论文中提到的构造,如下图 4 所示:

class TransformerLayers(nn.Module):
    def __init__(self, hidden_dim, nlayers, num_heads=4, dropout=0.1):
        super().__init__()
        self.d_model = hidden_dim
        encoder_layers = TransformerEncoderLayer(hidden_dim, num_heads, hidden_dim*4, dropout)
        self.transformer_encoder = TransformerEncoder(encoder_layers, nlayers)

    def forward(self, src):
        B, N, L, D = src.shape
        src = src * math.sqrt(self.d_model)
        src = src.view(B*N, L, D)
        src = src.transpose(0, 1)
        output = self.transformer_encoder(src, mask=None)
        output = output.transpose(0, 1).view(B, N, L, D)
        return output

3、解码

该解码器包含一系列 Transformer 块。它实用于所有的 patch(相比之下 MAE 是没有地位嵌入,因为他的 patch 曾经有地位信息),并且层数只有一层,而后应用了简略的 MLP,这使得输入长度等于每个 patch 的长度。

4、重建指标

对每一个数据点 (i) 进行遮蔽 patch 的计算,并抉择 mae (Mean-Absolute-Error)作为主序列和重建序列的损失函数。

这就是整体的架构了

上面是代码实现:

def trunc_normal_(tensor, mean=0., std=1.):
    __call_trunc_normal_(tensor, mean=mean, std=std, a=-std, b=std)

def unshuffle(shuffled_tokens):
    dic = {}
    for k, v, in enumerate(shuffled_tokens):
        dic[v] = k
    unshuffle_index = []
    for i in range(len(shuffled_tokens)):
        unshuffle_index.append(dic[i])
    return unshuffle_index

class TSFormer(nn.Module):
    def __init__(self, patch_size, in_channel, out_channel, dropout, mask_size, mask_ratio, L=6, distribution='uniform', lm=-1, selected_feature=0, mode='Pretrain', spectral=True):
        super().__init__()
        self.patch_size = patch_size
        self.seleted_feature = selected_feature
        self.mode = mode
        self.spectral = spectral
        self.patch = Patch(patch_size, in_channel, out_channel, spectral=spectral)
        self.pe = PositionalEncoding(out_channel, dropout=dropout)
        self.mask  = MaskGenerator(mask_size, mask_ratio, distribution=distribution, lm=lm)
        self.encoder = TransformerLayers(out_channel, L)
        self.decoder = TransformerLayers(out_channel, 1)
        self.encoder_2_decoder = nn.Linear(out_channel, out_channel)
        self.mask_token = nn.Parameter(torch.zeros(1, 1, 1, out_channel))
        trunc_normal_(self.mask_token, std=.02)
        if self.spectral:
            self.output_layer = nn.Linear(out_channel, int(patch_size/2+1)*2)
        else:
            self.output_layer = nn.Linear(out_channel, patch_size)

    def _forward_pretrain(self, input):
        B, N, C, L = input.shape
        # get patches and exec input embedding
        patches = self.patch(input)             
        patches = patches.transpose(-1, -2)     
        # positional embedding
        patches = self.pe(patches)
        
        # mask tokens
        unmasked_token_index, masked_token_index = self.mask()

        encoder_input = patches[:, :, unmasked_token_index, :]        

        # encoder
        H = self.encoder(encoder_input)        
        # encoder to decoder
        H = self.encoder_2_decoder(H)
        # decoder
        # H_unmasked = self.pe(H, index=unmasked_token_index)
        H_unmasked = H
        H_masked   = self.pe(self.mask_token.expand(B, N, len(masked_token_index), H.shape[-1]), index=masked_token_index)
        H_full = torch.cat([H_unmasked, H_masked], dim=-2)   # # B, N, L/P, d
        H      = self.decoder(H_full)

        # output layer
        if self.spectral:
            # output = H
            spec_feat_H_ = self.output_layer(H)
            real = spec_feat_H_[..., :int(self.patch_size/2+1)]
            imag = spec_feat_H_[..., int(self.patch_size/2+1):]
            spec_feat_H = torch.complex(real, imag)
            out_full = torch.fft.irfft(spec_feat_H)
        else:
            out_full = self.output_layer(H)

        # prepare loss
        B, N, _, _ = out_full.shape 
        out_masked_tokens = out_full[:, :, len(unmasked_token_index):, :]
        out_masked_tokens = out_masked_tokens.view(B, N, -1).transpose(1, 2)

        label_full  = input.permute(0, 3, 1, 2).unfold(1, self.patch_size, self.patch_size)[:, :, :, self.seleted_feature, :].transpose(1, 2)  # B, N, L/P, P
        label_masked_tokens  = label_full[:, :, masked_token_index, :].contiguous()
        label_masked_tokens  = label_masked_tokens.view(B, N, -1).transpose(1, 2)

        # prepare plot
        ## note that the output_full and label_full are not aligned. The out_full in shuffled
        ### therefore, unshuffle for plot
        unshuffled_index = unshuffle(unmasked_token_index + masked_token_index)
        out_full_unshuffled = out_full[:, :, unshuffled_index, :]
        plot_args = {}
        plot_args['out_full_unshuffled']    = out_full_unshuffled
        plot_args['label_full']             = label_full
        plot_args['unmasked_token_index']   = unmasked_token_index
        plot_args['masked_token_index']     = masked_token_index

        return out_masked_tokens, label_masked_tokens, plot_args

    def _forward_backend(self, input):
        B, N, C, L = input.shape
        # get patches and exec input embedding
        patches = self.patch(input)            
        patches = patches.transpose(-1, -2)     
        # positional embedding
        patches = self.pe(patches)

        encoder_input = patches          # no mask when running the backend.

        # encoder
        H = self.encoder(encoder_input)        
        return H

    def forward(self, input_data):

        if self.mode == 'Pretrain':
            return self._forward_pretrain(input_data)
        else:
            return self._forward_backend(input_data)

看完这个论文,我发现这基本上能够说是复制了 MAE,或者说是工夫序列的 MAE,在预测阶段也是与 MAE 相似,应用编码器的输入作为特色,为上游工作提供特色数据作为输出,有趣味的能够读读原始论文并且看看论文给的代码。

References

[1] Makridakis et al., The M5 Accuracy competition: Results, findings and conclusions, (2020)

[2] D. Salinas et al., DeepAR: Probabilistic forecasting with autoregressive recurrent networks, International Journal of Forecasting (2019).

[3] Boris N. et al., N-BEATS: Neural Basis Expansion Analysis For Interpretable Time Series Forecasting, ICLR (2020)

[4] Jake Grigsby et al., Long-Range Transformers for Dynamic Spatiotemporal Forecasting,

[5] Bryan Lim et al., Temporal Fusion Transformers for Interpretable Multi-horizon Time Series Forecasting, International Journal of Forecasting September 2020

[6] Seyed Mehran Kazemi et al.,Time2Vec: Learning a Vector Representation of Time, July 2019

[7] Pre-training Enhanced Spatial-temporal Graph Neural Network for Multivariate Time Series Forecasting

[8] Masked Autoencoders Are Scalable Vision Learners

https://avoid.overfit.cn/post/1213c27fc4174754bdbc2cec515c1732

正文完
 0