机器学习周记(第二十三周:文献阅读-DSTGN)2023.12.25~2023.12.31

目录

摘要

ABSTRACT

1 论文标题

2 论文摘要

3 论文背景

4 问题描述

4.1 图的定义

4.2 图信号矩阵的定义

4.3 问题的定义

5 论文模型

6 相关代码


摘要

  本周阅读了一篇关于GNN用于多元时间序列的时空预测的文章,文章的模型是一个具有自适应传播机制的动态时空图网络(dynamic spatio-temporal graph network with adaptive propagation mechanism,DSTGN),该模型包含了一种新的动态图矩阵估计方法来建模隐藏在数据中的变量之间的动态依赖关系,无需任何先验知识。它根据变化的图信号和学习到的节点嵌入来推断图结构,允许模型仍然调整测试集中节点之间的权重。同时为了充分利用多层网络的能力,还包含了一个自适应引导传播模块,可以自动改变每层的传播和聚合过程。在端到端神经网络中集成了动态图估计和自适应传播,设计了多个损失来联合优化网络。

ABSTRACT

This week, We read a paper on the spatio-temporal prediction of multivariate time series using Graph Neural Networks (GNN). The model proposed in the paper is a Dynamic Spatio-Temporal Graph Network with Adaptive Propagation Mechanism (DSTGN). This model incorporates a novel dynamic graph matrix estimation method to model dynamic dependencies among variables hidden in the data, without the need for any prior knowledge. It infers the graph structure based on the changing graph signals and learned node embeddings, allowing the model to adjust the weights between nodes in the test set. Additionally, to fully exploit the capabilities of a multi-layered network, an Adaptive Guided Propagation Module is included, capable of automatically adjusting the propagation and aggregation processes at each layer. The integration of dynamic graph estimation and adaptive propagation in an end-to-end neural network is designed, with multiple losses for joint optimization of the network.

1 论文标题

Dynamic spatio-temporal graph network with adaptive propagation mechanism for multivariate time series forecasting

2 论文摘要

  多元时间序列(MTS)时空预测在现实世界中有着广泛的应用,但变量间未知的动态时空依赖关系使得该任务具有挑战性。图神经网络(GNN)由于其强大的建模依赖关系的能力,被应用于时间序列,目前的方法要么依赖预定义的或学习好的固定图来建模节点间的链接。它忽略了时空数据中变量之间的动态性,在网络的不同层中保持相同的信息传播路径(图结构),导致网络性能次优。本文提出了一种新的动态时空图神经网络(dynamic spatio-temporal graph neural network,DSTGN),其关键部分是动态图估计(dynamic graph estimation,DGE)自适应引导传播(adaptive guided propagation,AGP)。在DGE中,基于不断变化的节点级输入和固定的拓扑信息来推断节点之间的动态关联,通过可训练的节点嵌入来学习,并引入图损失来控制图的学习方向。为了充分利用堆叠网络的能力,本文提出了AGP,根据每层提取的特征自动改变传播和聚合过程。为了自适应地学习该过程,设计了可学习的引导矩阵,并将其纳入端到端模式训练的图卷积框架中。实验结果表明,该方法在4个数据集上优于最先进的基准方法,包括预定义图和基于图学习的GNN方法。

3 论文背景

  近年来,时空预测因其在日常生活中有着广泛的应用而被广泛研究。准确的交通流量预测可以帮助人们更好地规划出行路线,准确的气候预测可以更好地保护环境和预防极端灾害,准确的电力消耗预测可以帮助发电公司更好地规划其发电等。因此,准确的时空预测对提高此类应用的服务质量具有重要作用。但这是一项具有挑战性的任务,主要是因为其数据中变量之间未知的动态时空依赖关系,如windtraffic数据。以交通数据为例,存在一个如下图所示的交通网络,其中图(a)为四个传感器的实际位置,图(b)为四个传感器在不同时期记录的交通流量。如图(b)的黑框所示,可以观察到传感器1和2具有相似的模式,传感器3和4也观察到这种现象,这表明变量之间存在依赖关系。它并不完全等同于真实的路网,因为传感器2和3、4有连接,但变化模式不一致。此外,变量之间的关联是动态的。如图(b)所示,第一个黑框表示传感器3和4之间的相关性较强(绿色和蓝色序列一致上升),第二个红框表示它们之间的相关性较弱(绿色序列下降,蓝色序列上升)。图神经网络(GNN)由于其置换不变性、局部连通性和组合性,在关系依赖建模方面取得了巨大成功。随着GNN的发展,时空图建模受到越来越多的关注。时空图网络目前分为两种主要架构,分别以DCRNNGraphWaveNet为代表。前者将GNN循环神经网络(RNN)相结合,分别获得空间和时间表示,后者使用堆叠卷积神经网络(CNN)代替循环结构,以提高训练稳定性和效率。无论网络结构如何,GNN都需要图结构来进行信息传播和聚合操作,这使得图结构成为GNN性能的瓶颈。寻找最优图结构的研究已经成为一个热点。

  首先,基于先验知识预定义图结构,如交通数据中传感器在道路上的距离和引文网络数据中的引文关系。然而,预定义的图结构独立于下游任务,导致GNN的性能次优。寻找最优图的方法可分为两类。(1).辅助图结构学习。例如AM-GCN通过节点特征构建属性图,提高了拓扑结构的特征提取能力。STFGNN利用动态时间规整(DTW)构建基于道路交通流数据的语义矩阵,作为预定义道路图结构的补充。(2).从头学习图结构。越来越多的研究直接放弃预定义的图,将图结构学习与GNN参数集成在端到端网络框架中进行优化。例如AGCNR使用节点嵌入自动推断数据中的隐藏结构,并将图结构学习集成到RNN框架中。GTS通过优化图分布的平均性能来学习概率图。MTGNN通过可训练的节点嵌入推断图结构,对多元时间序列中变量之间的隐藏依赖关系进行建模。尽管图学习方法已经取得了很大的进步,但上述方法都忽略了数据中的动态性,即它们都旨在寻求一个固定的图结构,该结构在训练完成后与数据无关。时空数据的显著特征是变量之间关联关系的动态变化,而上述固定的图结构无法真实地模拟时空数据。此外,目前的时空图网络利用堆栈神经网络从数据中提取时空表示,每一层的信息传播都遵循相同的图结构,忽略了每一层的个性化信息传播需求。为了克服这些挑战,本文提出了一种具有自适应传播机制的动态时空图网络(dynamic spatio-temporal graph network with adaptive propagation mechanism,DSTGN)。如下图所示,该框架由三个核心组件组成:动态图估计模块(DGE)自适应引导传播模块(AGP)时间卷积模块(TCN)。首先,为了对时空数据中变量的动态依赖关系进行建模,设计了一个基于动态节点级输入构建邻接矩阵的动态图估计模块(DGE)。值得注意的是,该方法不仅可以在训练过程中自适应地构建基于变化输入的邻接矩阵,而且可以在测试集中自适应地构建邻接矩阵,从变化的输入中挖掘变量之间的依赖关系。此外,在图估计过程中,引入了可学习的节点嵌入,以帮助模型更好地收敛,因为纯粹基于动态输入的图学习模型会导致模型难以收敛。引入拉普拉斯平滑来控制学习图的质量,即稀疏性、局部连通性。其次,提出自适应引导传播机制(AGP),以满足堆叠网络各层的个性化传播需求。其核心是根据从网络不同层提取的特征自适应地改变信息传播过程。该机制由引导矩阵估计器和自适应传播组成,其中引导矩阵来自于每层时间卷积模块提取的特征和学习到的动态图结构。将可学习的引导矩阵融入到图卷积框架中,根据引导矩阵自动改变邻居之间的传播权重,即自适应传播。本文将动态图估计和自适应引导传播过程集成在一个联合优化的端到端网络中。注意,本文的方法是第二种图学习方法,即从头学习图结构,因为它更通用,更适合实际情况。在很多情况下,数据挖掘的目标是多个变量之间的关系,而不是作为先验知识提供的。通过在两类真实数据集上的实验,即提供了先验图结构的数据集和没有提供先验图结构的数据集,验证了所提方法的有效性。其贡献总结如下:

  DSTGN方法以通过动态图结构对增强时间序列预测建模变量之间的动态依赖关系的有效性为目的。值得注意的是,本文的目的是提高预测质量,而不是发现基本真值图结构和推断因果关系。多变量的因果发现是一个成熟的领域,但确定多元时间序列之间的因果关系需要更深地研究。

4 问题描述

  论文主要研究多元时间序列预测问题,利用图结构来构建多个变量之间的关联。将数据中的变量视为图中的节点,将每个节点上的观测值视为每个节点的特征,即图信号。使用图邻接矩阵来描述节点之间的关联。注意,这里需要强调两点。图的邻接矩阵事先是未知的;节点上的图信号是随时间改变的。

4.1 图的定义

  用G(V,E,A)来表示变量之间的联系,其中V \in \mathbb{R}^{N}表示一组N个节点(变量),E是一组边,A \in \mathbb{R}^{N \times N}表示节点之间联系的一个邻接矩阵。

4.2 图信号矩阵的定义

  用x^{i}_{t} \in \mathbb{R}^{F}表示节点i在t时刻的观测值,其中F是特征的数量。X_{t} =(x^{1}_{t},x^{2}_{t},...,x^{N}_{t})\in \mathbb{R}^{N\times F}表示所有节点在t时刻的观测值,X =(X_{1},X_{2},...,X_{T})\in \mathbb{R}^{N \times F \times T}表示所有节点在所有时间的观测值。

4.3 问题的定义

  在过去的T_{h}时间片给定一个过去近期历史时空图信号矩阵序列X =(X_{t-T_{h+1}},X_{t-T_{h+2}},...,X_{t})\in \mathbb{R}^{N \times F \times T_{h}},我们的目标是学习映射函数f在未来的T_{p}时间片预测信号矩阵的序列Y =(X_{t+1},X_{t+2},...,X_{t+T_{p}})\in \mathbb{R}^{N \times F \times T_{p}},或者说在下一个时间步T_{p}Y=(X_{t+T_{p}})\in \mathbb{R}^{N \times F}

[X_{t-T_{h+1}},X_{t-T_{h+2}},...,X_{t}] \overset{f_{1}}{\rightarrow}[X_{t+1},X_{t+2},...,X_{t+T_{p}}]

[X_{t-T_{h+1}},X_{t-T_{h+2}},...,X_{t}] \overset{f_{2}}{\rightarrow}[X_{t+T_{p}}]

5 论文模型

  为了对时空数据中的动态性进行建模,本文提出了一种新的动态图矩阵估计方法,可以根据动态节点级输入自动推断节点之间的关联。同时,为了更好地利用深度网络的能力,设计了自适应引导传播来自适应地引导图的信息传播,即自适应地改变不同层邻居之间的权重。本文将动态图矩阵估计、自适应引导传播和时间卷积整合在一个端到端的框架中,称为具有自适应传播机制的动态时空图网络(DSTGN),如下图所示。动态图矩阵估计,通过同时考虑随时间变化的节点级输入(语义信息)和拓扑结构(节点嵌入)推断节点之间的依赖关系,如下图(a)所示。节点之间的动态依赖关系是隐藏变量,也就是说无法观察到它们。但它表现在节点的观测值中,因此图学习必须从节点级输入(动态语义)中推断出关联关系。然而,现实中的观察图信号包含噪声和巧合,使得模型仅基于节点级输入推断图难以收敛。为了克服这一困难,提出了一种自适应学习的拓扑结构。节点之间的依赖模式往往具有长期固定的结构,如交通数据中的路网和气候中的长期模式。在之前研究的基础上,采用可学习的节点嵌入来推断节点之间的固定关联,有助于图学习的收敛。

  图卷积操作可以被视为图信号在图矩阵上的信息扩散。目前的时空图神经网络一般采用堆叠图卷积来捕获数据中的空间信息。现有方法在进行图卷积操作时,每层都坚持相同的图结构,忽略了每层的个性化传播需求。为了解决这个问题,论文提出了自适应传播操作,如下图(b)所示。具体来说,论文基于从每层时间卷积中提取的特征和学习到的图共同获得一个引导矩阵,进一步用于自适应地改变邻域间的传播权重。整个框架由动态图估计、时间卷积模块和自适应引导传播组成。为了在端到端网络中集成模块,论文设计了三种损失,图损失,MLP损失和预测损失,如下图(c)所示。图损失用于控制学习到的图的质量(稀疏性、连通性),MLP损失用于指导每层引导矩阵的生成。预测损失是模型最终预测结果的损失,本文使用MAE作为损失函数。在学习过程中,多个损失共同优化网络。

6 相关代码

Dyanmic Adaptive Spatio-temporal GCN:

class model_dynamic(nn.Module):
    def __init__(self, device, num_nodes, dropout=0.1,gated=False, supports=None, gcn_bool=True, addaptadj=True, residual=True, pool=False, in_dim=2,residual_channels=32,dilation_channels=32,end_channels=32,kernel_size=2,blocks=1,layers=1):
        super(gwnet_dynamic, self).__init__()

        self.dropout = dropout
        self.blocks = blocks
        self.layers = layers
        self.gcn_bool = gcn_bool
        self.pool = pool
        self.res = residual
        self.addaptadj = addaptadj
        self.filter_convs = nn.ModuleList()
        self.gate_convs = nn.ModuleList()
        self.residual_convs = nn.ModuleList()
        self.skip_convs = nn.ModuleList()
        self.bn = nn.ModuleList()
        self.gconv = nn.ModuleList()
        self.gconv = nn.ModuleList()
        self.pool_layers = nn.ModuleList()

        self.r = residual_channels
        self.start_conv = nn.Conv2d(in_channels=in_dim,
                                    out_channels=residual_channels,
                                    kernel_size=(1,1))
        self.supports_len = 0
        self.gated = gated
        self.adjs_s = nn.ParameterList()
        self.adjs_t = nn.ParameterList()
        receptive_field = 1
        self.supports = None

        for _ in range(layers*blocks):
            nodevec_s = nn.Parameter(torch.randn(num_nodes, 10).to(device), requires_grad=True).to(device)
            nodevec_t = nn.Parameter(torch.randn(10, num_nodes).to(device), requires_grad=True).to(device)
            self.adjs_s.append(nodevec_s)
            self.adjs_t.append(nodevec_t)


        self.supports_len +=1


        #print(self.supports.shape)

        for b in range(blocks):
            additional_scope = kernel_size - 1
            new_dilation = 1
            for i in range(layers):
                # dilated convolutions

                self.filter_convs.append(nn.Conv2d(in_channels=residual_channels,
                                                   out_channels=dilation_channels,
                                                   kernel_size=(1,kernel_size),dilation=new_dilation))

                self.gate_convs.append(nn.Conv2d(in_channels=residual_channels,
                                                 out_channels=dilation_channels,
                                                 kernel_size=(1, kernel_size),dilation=new_dilation))

                # 1x1 convolution for residual connection
                self.residual_convs.append(nn.Conv1d(in_channels=dilation_channels,
                                                     out_channels=residual_channels,
                                                     kernel_size=(1, 1)))


                self.bn.append(nn.BatchNorm2d(residual_channels))

                #self.pool_layers.append(nn.MaxPool2d(1,2))

                new_dilation *=2
                receptive_field += additional_scope
                additional_scope *= 2
                if self.gcn_bool:
                    self.gconv.append(gcn(dilation_channels,residual_channels, dropout=0.1, support_len=self.supports_len))


        #print('recieptive_field = {}'.format(receptive_field))
        self.end_conv_1 = nn.Conv2d(in_channels=residual_channels,
                                  out_channels=end_channels,
                                  kernel_size=(1,1),
                                  bias=True)
        self.avg_pool = nn.AdaptiveAvgPool2d(1)
        self.last_linear = nn.Linear(num_nodes*end_channels,2)



    def forward(self, input):
        # input shape B,D,N,T

        in_len = input.size(3)
        x = input
        x = self.start_conv(x)
        skip = 0

        for i in range(self.blocks * self.layers):


            residual = x  # B,C,N,T

            # Two dilated causal convolution for temporal feature extraction
            filter = self.filter_convs[i](residual)
            filter = torch.tanh(filter)
            gate = self.gate_convs[i](residual)
            gate = torch.sigmoid(gate)
            x = filter * gate
            adp = F.softmax(F.relu(torch.mm(self.adjs_s[i], self.adjs_t[i])), dim=1)
            new_supports = [adp]
            x = self.gconv[i](x, new_supports)
            x = x + residual[:, :, :, -x.size(3):]
            x = self.bn[i](x)


        x = F.relu(self.end_conv_1(x))# B,1,N,T
        x = torch.mean(x,dim=-1)  # B,1,N,1
        x = torch.flatten(x,start_dim=1)
        x =F.dropout(x, self.dropout, training=self.training)
        x = self.last_linear(x)
        return x
  • 43
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值