《Graph Representation Learning》【5】——The Graph Neural Network Model

Part II Graph Neural Networks

5 The Graph Neural Network Model

之前,我们一直在介绍基于浅嵌入方法的编解码器模型,在本章中,我们将重点放在更复杂的编码器模型上——GNN模型的通用框架。它的关键思想是,我们希望生成实际依赖于图结构和特征信息的节点表示(node representation)。
它所面临的挑战也是严峻的——通用的深度学习工具箱(CNN、RNN等)对于图数据来说并不适用,因此,我们需要针对图数据,定义一种新的深度学习框架。

Permutation invariance and equivariance
设计图上神经网络的关键,是它们(神经网络)应该具有排列(置换)不变性(或排列等价性)。也就是说,这些函数的结果不应该依赖于输入矩阵中的节点排列顺序。所有的函数,必须要满足排列不变性或排列等变性这两个条件之一。
(1)排列不变性:
f ( P A P T ) = f ( A ) f(PAP^T)=f(A) f(PAPT)=f(A)
(2)排列等变性:
f ( P A P T ) = P f ( A ) f(PAP^T)=Pf(A) f(PAPT)=Pf(A)
其中,P是一个排列(置换)矩阵。
确保排列不变性或排列等变性是我们进行图学习的一个关键挑战。

5.1 Neural Message Passing

GNN最普遍的一种通用框架一般称为消息传播框架(message passing framework),它使用一种神经信息传递的形式,在节点之间交换向量信息(message),并使用神经网络进行更新(update)。
在本章中,我们会重点关注消息传播框架。它将图的结构和特征信息作为输入,生成节点的嵌入表示(node embedding)。

5.1.1 Overview of the Message Passing Framework

在这里插入图片描述

简单来说,消息传递就是利用聚合后的邻居信息,来更新隐藏嵌入(hidden embedding) h u ( k ) h_u^{(k)} hu(k)的。
既然是神经网络,那么必然会有层(layer)的概念。在此框架中,层就是我们刚刚提到的隐藏嵌入 h u ( k ) ∈ R d h_u^{(k)}\in \mathbb R^d hu(k)Rd,它表示了在第k层(第k次迭代)中,节点u的嵌入表示是怎样的。
消息传递可以表示成这种形式:
m N ( u ) ( k ) = A g g r e g a t e ( k ) ( { h v ( k ) , ∀ v ∈ N ( u ) } ) h u ( k + 1 ) = U p d a t e ( k ) ( h u ( k ) , m N ( u ) ( k ) ) \bold m_{N(u)}^{(k)}=Aggregate^{(k)}\left(\{h_v^{(k)},\forall v\in N(u)\}\right) \\[2ex] h_u^{(k+1)}=Update^{(k)}\left(h_u^{(k)},\bold m_{N(u)}^{(k)}\right) mN(u)(k)=Aggregate(k)({ hv(k),vN(u)})hu(k+1)=Update(k)(hu(k),mN(u)(k))
其中 m N ( u ) \bold m_{N(u)} mN(u)表示邻域聚合后产生的消息。这个式子简单明了的说明了,我们要如何利用消息和上一层的输出来得到本层的嵌入表示。
一般将第0层的嵌入向量初始化为节点的特征或属性向量(feature), h u ( 0 ) = x u h_u^{(0)}=x_u hu(0)=xu,GNN要求节点特征作为输入,这也是它和之前浅嵌入方法的不同之处;最后一层第K层的输出表示为最终学习到的节点嵌入, z u = h u ( K ) z_u=h_u^{(K)} zu=hu(K)。需要注意的一点是,为了保证排列等变性,聚合函数的操作对象是集合。
至此,我们将问题转化为了到底使用什么聚合(Aggregate)函数和更新(Update)函数。

5.1.2 Motivations and Intuitions

使用消息传递框架的直觉是这样的:通过这些迭代过程(层),每个节点的嵌入表示都包含了更多来自远处节点的信息,从而达到邻域聚合的目的。
它主要编码了两方面的信息。
(1)结构信息(structure)。例如,节点的度信息。
(2)特征信息(feature)。每个节点的嵌入对K跳邻居以内的特征信息进行编码。

5.1.3 The Basic GNN

这一部分会介绍基本GNN(basic GNN)框架,它是原始GNN模型的简化版本。
基本GNN的消息传递定义如下,为了简单起见,实例化聚合函数和更新函数时一般省略上标。
m N ( u ) = ∑ v ∈ N ( u ) h v U p d a t e ( h u , m N ( u ) ) = σ ( W s e l f h u + W n e i g h m N ( u ) + b ) \bold m_{N(u)}=\sum_{v\in N(u)}h_v \\[2ex] Update\left(h_u,\bold m_{N(u)}\right)=\sigma \left(W_{self}h_u+W_{neigh}\bold m_{N(u)}+b\right) mN(u)=vN(u)hvUpdate(hu,mN(u))=σ(Wselfhu+WneighmN(u)+b)
将实例化后的函数带入,可以得到更新后节点u的嵌入表示为:
h u ( k + 1 ) = σ ( W s e l f ( k + 1 ) h u ( k ) + W n e i g h ( k + 1 ) ∑ v ∈ N ( u ) h v ( k ) + b ( k + 1 ) ) h_u^{(k+1)}=\sigma \left(W_{self}^{(k+1)}h_u^{(k)}+W_{neigh}^{(k+1)}\sum_{v\in N(u)}h_v^{(k)}+b^{(k+1)}\right) hu(k+1)=σWself(k+1)hu(k)+Wneigh(k+1)vN(u)hv(k)+b(k+1)
其中 W s e l f , W n e i g h ∈ R d ( k + 1 ) × d ( k ) ;   h u ( k ) , h v ( k ) ∈ R d ( k ) ; h u ( k + 1 ) ∈ R d ( k + 1 ) W_{self},W_{neigh}\in \mathbb R^{d^{(k+1)}\times d^{(k)}};\ h_u^{(k)},h_v^{(k)} \in \mathbb R^{d^{(k)}};h_u^{(k+1)} \in \mathbb R^{d^{(k+1)}} Wself,WneighRd(k+1)×d(k); hu(k),hv(k)Rd(k);hu(k+1)Rd(k+1)。另外, b ( k + 1 ) ∈ R d ( k + 1 ) b^{(k+1)}\in \mathbb R^{d^{(k+1)}} b(k+1)Rd(k+1)是一个偏置,会使得模型具有更好的表现,也可以省略。
这个过程,可以简单理解为先对信息进行一个线性组合,再通过一个逐元素的非线性层。除了节点级的定义,还可以进行图级的定义,大同小异,只不过是转换为了矩阵运算。

5.1.4 Message Passing with Self-loops

有一种偷懒的方法可以省略更新函数:自循环GNN(self-loop GNN)。它在聚合函数中显式的聚合了自身,因此可以不再需要更新函数进行组合。
h u ( k + 1 ) = m N ( u ) ∪ { u } ( k ) h_u^{(k+1)}=\bold m_{N(u)\cup \{u\}}^{(k)} hu(k+1)=mN(u){ u}(k)
实现起来也相当的简单,就是将自身也算作邻居节点集合中。
这种方法共享W参数,可以缓解过拟合,但是也限制了GNN的表达能力。

5.2 Generalized Neighborhood Aggregation

基本GNN可以从很多方面进行改进。这一部分主要是讨论聚合操作的推广和改进,也就是如何通过邻居节点地嵌入集合来生成 m N ( u ) \bold m_{N(u)} mN(u)

5.2.1 Neighborhood Normalization

因为是对节点的邻居进行聚合,如果仅仅像基本GNN一样简单的进行求和操作,一个显而易见的问题是, m N ( u ) \bold m_{N(u)} mN(u)是对节点的度高度敏感。节点的度越高,说明邻居越多,那么求和聚合之后的向量值也越大,但是度越高的节点真不一定就越重要。这会导致数值的不稳定和优化困难等问题。
一种解决方法就是根据节点的度进行标准化(Normalization)。可以直接取平均值,也可以使用其他的一些标准化方法,如对称标准化(symmetric Normalize)。
m N ( u ) = ∑ v ∈ N ( u ) h v ∣ N ( u ) ∣ m N ( u ) = ∑ v ∈ N ( u ) h v ∣ N ( u ) ∣ ∣ N ( v ) ∣ \bold m_{N(u)}=\frac{\sum_{v\in N(u)}h_v}{|N(u)|} \\[2ex] \bold m_{N(u)}=\sum_{v\in N(u)}\frac{h_v}{\sqrt{|N(u)||N(v)|}} mN(u)=N(u)vN(u)hvmN(u)=vN(u)N(u)N(v) hv

Graph convolutional networks (GCNs)
GCN模型的聚合操作采用的方案是:对称标准化+自循环更新。
h u ( k + 1 ) = σ ( W ( k + 1 ) ∑ v ∈ N ( u ) ∪ { u } h v ( k ) ∣ N ( u ) ∣ ∣ N ( v ) ∣ ) h_u^{(k+1)}=\sigma\left(W^{(k+1)}\sum_{v\in N(u)\cup \{u\}}\frac{h_v^{(k)}}{\sqrt{|N(u)||N(v)|}} \right) hu(k+1)=σW(k+1)vN(u){ u}N(u)N(v) hv(k)

To normalize or not to normalize?
那么,我们到底什么时候应该进行标准化呢?
适当的标准化可以提高模型的稳定性和表达能力,但同时也会损失掉一些结构信息。当特征信息远比结构信息重要或者节点的度对模型稳定性产生极大影响时,需要进行标准化。

5.2.2 Set Aggregators

为了满足排列不变性,聚合函数必须要对集合进行操作。实际上,之前提到的求和或标准化都在不自觉的默认了这个条件。我们希望这个函数可以将邻居节点的嵌入集合映射为 m N ( u ) \bold m_{N(u)} mN(u)

Set pooling
下面定义通用的集合函数逼近器:
m N ( u ) = M L P θ ( ∑ v ∈ N ( u ) M L P ϕ ( h v ) ) \bold m_{N(u)}=MLP_{\theta}\left(\sum_{v\in N(u)}MLP_{\phi}(h_v)\right) mN(u)=MLPθvN(u)MLPϕ(hv)
里面的求和操作可以换成mean,max,min或者之前提到的标准化操作。将上式的MLP都使用等变函数,那么就退化为了基本GNN的聚合函数。MLP一般只包含一个隐藏层,集合池化的方法可以使表达能力得到小幅的提升。

Janossy pooling
在Janossy pooling方法中,为了能够满足排列不变性,它使用了置换敏感函数(permutation-sensitive),并且取所有可能的排列结果的平均值。它的表达能力比集合池化方法更加强大。
emm,有点没搞明白是怎么操作的,里面还涉及到LSTM这种模型,什么时候看懂了再过来写吧。。。

5.2.3 Neighborhood Attention

改进聚合操作的另一类方法是添加注意力机制。它的主要思想是:为节点的每个邻居都分配一个注意力权重,代表了其重要程度。
类GAT模型的聚合操作可以这样定义:
m N ( u ) = ∑ v ∈ N ( u ) α u , v h v \bold m_{N(u)}=\sum_{v\in N(u)}\alpha_{u,v}h_v mN(u)=vN(u)αu,vhv
其中 α u , v \alpha_{u,v} αu,v是个标量,它表示边(u,v)的注意力权重系数。计算 α u , v \alpha_{u,v} αu,v的方法有很多,它们的共同点是,都含有可训练的参数,例如:
α u , v = exp ⁡ ( a T [ W h u ⊕ W h v ] ) ∑ k ∈ N ( u ) exp ⁡ ( a T [ W h u ⊕ W h k ] ) \alpha_{u,v}=\frac{\exp(a^T[Wh_u\oplus Wh_v])}{\sum_{k\in N(u)}\exp(a^T[Wh_u\oplus Wh_k])} αu,v=kN(u)exp(aT[WhuWhk])exp(aT[WhuWhv])
其中 a T a^T aT是一个可学习的向量,W是可学习的矩阵, ⊕ \oplus 是拼接操作。
此外,还有一种不太常见的多头(multi-head)注意力机制。它主要是使用了K个独立的注意力权重(K个注意力头)来进行聚合操作,具体操作不进行展开。
增加注意力机制是提高GNN模型的表达能力的一个有用的策略,特别是在事先知道一些邻居可能比其他邻居更有用的情况下。

5.3 Generalized Update Methods

基本GNN的更新函数只是节点嵌入和消息的线性组合,这一部分,我们将介绍更多样化的更新操作,以缓解模型过平滑(over-smooth)的问题。

Over-smoothing and neighbourhood influence
过平滑指的是在GNN消息传递的多次迭代之后,图中所有的节点嵌入表示会变得非常相似。由于过平滑问题的存在,使得GNN模型的深度和表达能力都收到了限制。广义的更新方法就可以帮助缓解过平滑的问题。

5.3.1 Concatenation and Skip-Connections

之所以出现过平滑,是因为更新后的节点表示 h u ( k + 1 ) h_u^{(k+1)} hu(k+1)过于依赖当前层的输入信息 m N ( u ) \bold m_{N(u)} mN(u),而牺牲了来自前一层的节点表示 h u ( k ) h_u^{(k)} hu(k)。借鉴了CNN中的方法,缓解此问题的一种方法是使用跳跃连接(skip-connection),直接保留前几轮的消息传递结果。
基于拼接的跳跃连接定义为:
U c o n c a t ( h u , m N ( u ) ) = [ U b a s e ( h u , m N ( u ) ) ⊕ h u ] U_{concat}\left(h_u,\bold m_{N(u)}\right)=[U_{base}\left(h_u,\bold m_{N(u)}\right)\oplus h_u] Uconcat(hu,mN(u))=[Ubase(hu,mN(u))hu]
其中 U b a s e ( h u , m N ( u ) ) U_{base}\left(h_u,\bold m_{N(u)}\right) Ubase(hu,mN(u))为基本GNN的更新函数。可以看出,改进后的函数,将基本GNN产生的结果和当前节点嵌入进行拼接,使得 m N ( u ) \bold m_{N(u)} mN(u) h u h_u hu分离开来,从而保留了前一层的信息。
除此之外,还可以使用线性插值法引入可学习的参数。
U i n t e r p o l a t e ( h u , m N ( u ) ) = α ⃗ 1 ∘ U b a s e ( h u , m N ( u ) ) + ( 1 − α ⃗ 1 ) ∘ h u U_{interpolate}\left(h_u,\bold m_{N(u)}\right)=\vec \alpha_1\circ U_{base}\left(h_u,\bold m_{N(u)}\right)+ (1-\vec\alpha_1)\circ h_u Uinterpolate(hu,mN(u))=α 1Ubase(hu,mN(u))+(1α 1)hu
其中 α ⃗ 1 \vec \alpha_1 α 1是可学习的参数,可以和模型一起进行学习。
一般来说,跳跃连接是比较简单的策略,它可以帮助缓解GNN中的过平滑问题,同时也可以提高优化的数值稳定性。它们对2-5层GNN模型的节点分类任务最有用。

5.3.2 Gated Updates

这种gated method是借鉴了RNN当中的GRU方法。迁移到GNN模型当中,隐藏状态 h ( t ) h^{(t)} h(t)替换为节点的隐藏状态 h u h_u hu,观测向量 x ( t ) x^{(t)} x(t)替换为消息 m N ( u ) \bold m_{N(u)} mN(u),因此,这种方法的更新函数可以定义为:
h u ( k + 1 ) = G R U ( h u ( k ) , m N ( u ) ( k ) ) h_u^{(k+1)}=GRU(h_u^{(k)},\bold m_{N(u)}^{(k)}) hu(k+1)=GRU(hu(k),mN(u)(k))
其中GRU是循环门控单元,并且GRU单元中的参数在节点之间是共享的。

5.3.3 Jumping Knowledge Connections

之前我们一直假设,最后一层的节点嵌入作为节点最终的嵌入表示: z u = h u ( K ) z_u=h_u^{(K)} zu=hu(K)。乍看之下很有道理,前面的很多改进也是基于这种假设的,但其实它也限制了各类方法对GNN过平滑问题的改进。
那么,我们如果利用每一层的节点嵌入来生成最终的节点嵌入表示呢?我们称之为跳跃知识(JK)连接。
z u = f J K ( h u ( 0 ) ⊕ h u ( 1 ) ⊕ . . . ⊕ h u ( K ) ) z_u=f_{JK}(h_u^{(0)}\oplus h_u^{(1)}\oplus ...\oplus h_u^{(K)}) zu=fJK(hu(0)hu(1)...hu(K))
其中 f J K f_{JK} fJK是任意可微的函数,一般来说可以直接取全等函数。因此, z u z_u zu就可以看作是每一层的嵌入表示拼接起来的结果。

5.4 Edge Features and Multi-relational GNNs

之前都是对于简单图的GNN框架介绍,接下来我们将GNN的消息传递框架推广到多关系图上。

5.4.1 Relational Graph Neural Networks

最早的一个模型是RGCN,它为每种类型的关系都指定了一个独立的转换矩阵(transformation matrix)。
m N ( u ) = ∑ τ ∈ R ∑ v ∈ N τ ( u ) W τ h v f n ( N ( u ) , N ( v ) ) \bold m_{N(u)}=\sum_{\tau \in \mathcal R}\sum_{v\in N_{\tau}(u)}\frac{W_{\tau}h_v}{f_n(N(u),N(v))} mN(u)=τRvNτ(u)fn(N(u),N(v))Wτhv
其中 f n f_n fn是标准化函数, W τ W_{\tau} Wτ是每种关系单独的转换矩阵,是可学习的参数。RGCN对不同种类的关系进行单独的聚合。

Parameter sharing
但是,上面的式子中,每种关系都有一个可学习的矩阵 W τ W_{\tau} Wτ,如果关系类型比较多,就会导致参数数量剧增。
在这里 ,我们使用基矩阵(basis matrix)来共享参数,以减少参数的数量。
W τ = ∑ i = 1 b α i , τ B i W_{\tau}=\sum_{i=1}^{b}\alpha_{i,\tau}B_i Wτ=i=1bαi,τBi
其中 α i , τ \alpha_{i,\tau} αi,τ是标量,代表某种关系的b个组合权重系数, B i B_i Bi是基矩阵。关系转换矩阵就是基矩阵的线性组合,每种关系的参数数量减少为了b个。

5.4.2 Attention and Feature Concatenation

对于离散边特征的多关系图来说(每条边的关系类型都由一个明确的标量数字来表示),上面的方法就可以解决。但是,如果给出的边特征是一个特征向量,而非离散值呢?我们需要在注意力机制中,利用这些特征。
m N ( u ) = A g g r e g a t e ( { h v ⊕ e ⃗ ( u , τ , v ) , ∀ v ∈ N ( u ) } ) \bold m_{N(u)}=Aggregate\left(\{h_v\oplus \vec e_{(u,\tau,v)},\forall v\in N(u)\}\right) mN(u)=Aggregate({ hve (u,τ,v),vN(u)})
其中 e ⃗ ( u , τ , v ) \vec e_{(u,\tau,v)} e (u,τ,v)代表边(u,τ,v)的向量值特征。

5.5 Graph Pooling

对于图级的任务,我们需要使用图池化操作,将所有学习到的节点嵌入表示 z u z_u zu的集合,池化为全图的嵌入表示 z G z_{\mathcal G} zG

Set pooling approaches
和之前聚合操作的集合池化操作有点类似,都是将一个集合映射为一个向量,满足排列不变性。
在实践当中,有两种比较常用的方法。
一种是简单的使用sum或mean来一次性池化所有节点的嵌入表示。
z G = ∑ u ∈ V z u f n ( ∣ V ∣ ) \bold z_{\mathcal G}=\frac{\sum_{u \in V}z_u}{f_n(|V|)} zG=fn(V)uVzu
分母上的标准化函数一般取全等函数。这种池化方法在小规模图上是有效的。
另一种方法比较复杂,它结合了LSTM和注意力机制。。。我现在还比较懵,正所谓不能一口吃个胖子,还是要慢慢来,等仔细研究了LSTM之后再回过头来看这个方法。

Graph coarsening approaches
集合池化方法的缺点在于,它没有利用图的结构信息。
图坍缩的方法,合理利用了图的拓扑结构进行池化操作,因此效果往往更好。它的主要思想是不断对图进行坍缩(聚类)。
我们需要通过聚类函数 f c ( G , Z ) f_c(\mathcal G,Z) fc(G,Z)来找到或学习到分配矩阵 S ∈ R ∣ V ∣ × c S\in \mathbb R^{|V|\times c} SRV×c,将图分为c个簇。这个矩阵至关重要, S [ u , i ] S[u,i] S[u,i]代表了节点u和簇的关联强度,换言之,就是节点u属于簇i的可能性大小。聚类函数 f c ( G , Z ) f_c(\mathcal G,Z) fc(G,Z)可以使用谱聚类方法,也可以使用另外的GNN。
有了这个分配矩阵S,我们就可以进行图坍缩,得到坍缩后的邻接矩阵和节点特征。
A n e w = S T A S ∈ R c × c Z n e w = S T Z ∈ R c × d A_{new}=S^TAS\in \mathbb R^{c\times c} \\[2ex] Z_{new}=S^TZ\in \mathbb R^{c\times d} Anew=STASRc×cZnew=STZRc×d
其中 A n e w A_{new} Anew表示坍缩后簇之间的连接强度, Z n e w Z_{new} Znew表示坍缩后每个簇的嵌入表示。重复上述坍缩的过程,逐步减少簇的个数,最终得到一个进行了足够坍缩操作的图。最终图的所有簇的嵌入表示,再进行集合池化操作后,得到图的嵌入表示 z G \bold z_{\mathcal G} zG
在实践中,图坍缩的方法可以带来很强的性能,但它们也可能不稳定且难以训练。

5.6 Generalized Message Passing

GNN的消息传递方法也可以被推广到,在消息传递的每个阶段利用边级和图级的信息。下面的公式也是一些GNN综述性论文中,一上来就放出来“劝退”科研小白们的!!!如果没有前面的基础,真的很容易直接被这几个公式劝退了。
h ( u , v ) ( k ) = U e d g e ( h ( u , v ) ( k − 1 ) , h u ( k − 1 ) , h v ( k − 1 ) , h G ( k − 1 ) ) m N ( u ) = A n o d e ( h ( u , v ) ( k ) , ∀ v ∈ N ( u ) ) h u ( k ) = U n o d e ( h u ( k − 1 ) , m N ( u ) , h G ( k − 1 ) ) h G ( k ) = U g r a p h ( h G ( k − 1 ) , { h u ( k ) , ∀ u ∈ V } , { h ( u , v ) ( k ) , ∀ ( u , v ) ∈ E } ) h_{(u,v)}^{(k)}=U_{edge}\left(h_{(u,v)}^{(k-1)},h_{u}^{(k-1)},h_{v}^{(k-1)},h_{\mathcal G}^{(k-1)}\right) \\[2ex] \bold m_{N(u)}=A_{node}\left(h_{(u,v)}^{(k)},\forall v\in N(u)\right) \\[2ex] h_{u}^{(k)}=U_{node}\left(h_{u}^{(k-1)},\bold m_{N(u)},h_{\mathcal G}^{(k-1)}\right) \\[2ex] h_{\mathcal G}^{(k)}=U_{graph}\left(h_{\mathcal G}^{(k-1)},\{h_{u}^{(k)},\forall u\in V\},\{h_{(u,v)}^{(k)},\forall (u,v)\in E\}\right) \\[2ex] h(u,v)(k)=Uedge(h(u,v)(k1),hu(k1),hv(k1),hG(k1))mN(u)=Anode(h(u,v)(k),vN(u))hu(k)=Unode(hu(k1),mN(u),hG(k1))hG(k)=Ugraph(hG(k1),{ hu(k),uV},{ h(u,v)(k),(u,v)E})
就不解释了,解释也解释不清楚,我太难了。

猜你喜欢

转载自blog.csdn.net/weixin_41650348/article/details/110391668