所谓通用框架,是对多种变体GNN网络结构的一般化总结,也是GNN编程的通用范式,这里介绍3类通用框架:
消息传播神经网络(Message Passing Neural Network, MPNN)
:从聚合和更新角度归纳GNN模型的几种变体;非局部神经网络(Non-Local Neural Network, NLNN)
:基于注意力机制的GNN模型的一般化总结;图网络(Graph Network, GN)
:对GNN模型进行更全面的总结
1. MPNN
MPNN的基本思路为:节点表示向量都是通过消息函数
M
M
M(Message)和更新函数
U
U
U(Update)进行K轮消息传播机制的迭代后得到的,消息传播的过程如下:
m
i
(
k
+
1
)
=
∑
v
j
∈
N
(
v
i
)
M
(
k
)
(
h
i
(
k
)
,
h
j
(
k
)
,
e
i
j
)
m_i^{(k+1)}=\sum_{v_j\in N(v_i)} M^{(k)}(h_i^{(k)}, h_j^{(k)}, e_{ij})
mi(k+1)=vj∈N(vi)∑M(k)(hi(k),hj(k),eij)
h
i
(
k
+
1
)
=
U
(
k
)
(
h
i
(
k
)
,
m
i
(
k
+
1
)
)
h_i^{(k+1)}=U^{(k)}(h_i^{(k)},m_i^{(k+1)})
hi(k+1)=U(k)(hi(k),mi(k+1))
其中
e
i
j
e_{ij}
eij表示边
<
v
i
,
v
j
>
<v_i,v_j>
<vi,vj>上的特征向量,
k
k
k表示第
k
k
k次消息传播,在实际编程中,一般和模型中层的概念等价。
消息函数的输入由边本身以及两侧节点构成,借用RDF(Resource Description Framework)三元组来表示这样的输入:
S
o
u
r
c
e
→
P
r
e
d
i
c
t
O
b
j
e
c
t
Source\overset{Predict}\rightarrow Object
Source→PredictObject
Source表示源节点,Object表示目标节点,Predict表示源节点到目标节点的关系。
想象人类学习知识的过程,在自身具有一定知识的基础上,我们会想要从周围的伙伴那里学习到更多的知识,然后将伙伴给予的信息与自身已有的知识组合起来,更新并获得更高阶的知识,这个过程就是一个消息传递过程。
以下图为例,假设我们要计算 [公式] 时刻红色节点的表示,从消息传递的角度来看,会经历以下步骤:1)首先从邻居获取信息:计算红色节点周围的四个邻居节点的消息总和。2)对获得的信息加以利用:将获得的消息与 [公式] 时刻红色节点本身的表示组合起来,计算得到 [公式]时刻的红色节点表示。
在理解了消息传递过程之后,正式介绍形式化的消息传递过程。MPNN共包含两个阶段:消息传递阶段
和读出阶段(readout phase)
。
- 消息传递阶段 :共运行T个时间步,并包含以下两个子函数:
- Aggregation Function:也称消息函数,作用是聚合邻居节点的特征,形成一个消息向量,准备传递给中心节点。
- Combination Function:也称节点更新函数,作用是更新当前时刻的节点表示,组合当前时刻节点的表示以及从Aggregation Function中获得的消息。
- 读出阶段:针对于图级别的任务(如图分类)仅仅获得节点级别的表示是不够的,需要通过读出函数(Readout Function)聚合节点级别的表示,计算获得图级别的表示。
在消息函数的作用下,图里面所有的RDF都会向外广播消息,之后这些消息都会沿着边的方向传播到RDF的两侧节点处进行聚合,聚合后的消息会在之后的更新函数的作用下对节点特征进行更新。
MPNN的核心在于消息函数和更新函数,原则上可以把它们设计成任意一种DNN模型。
因此,不同种类的GNN就是通过设计不同的Aggregation Function,Combination Function以及Readout Function实现的。也就是说,从邻居聚合的信息不同,邻居信息与自身信息的组合方式不同,或者节点的读出方式不同,最终都会导致我们最终获得的表示向量不同。不同的任务为了获得更准确的预测效果,会着重于抓取不同的信息。
在消息传播的视角下,GCN、R-GCN、GraogSAGE、Interaction Network等GCN模型中的消息函数和更新函数,如下表:
模型 | 消息函数 | 更新函数 |
---|---|---|
GCN | M ( h i ( k ) , h j ( k ) ) = L ~ s y m [ i , j ] W ( k ) h ~ j ( k ) M(h_i^{(k)}, h_j^{(k)})=\tilde L_{sym}[i,j]W^{(k)}\tilde h_j^{(k)} M(hi(k),hj(k))=L~sym[i,j]W(k)h~j(k) | U ( m i ( k + 1 ) ) = σ ( m i ( k + 1 ) ) U(m_i^{(k+1)})= \sigma(m_i^{(k+1)}) U(mi(k+1))=σ(mi(k+1)) |
R-GCN | M ( h j ( k ) , r ) = 1 c i , r W r ( k ) h j ( k ) M(h_j^{(k)},r)=\frac{1}{c_{i,r}}W_{r}^{(k)}h_j^{(k)} M(hj(k),r)=ci,r1Wr(k)hj(k) | U ( h i ( k ) , m i ( k + 1 ) ) = σ ( m i ( k + 1 ) + W 0 h i ( k ) ) U(h_i^{(k)},m_i^{(k+1)})=\sigma(m_i^{(k+1)}+W_0h_i^{(k)}) U(hi(k),mi(k+1))=σ(mi(k+1)+W0hi(k)) |
GraphSAGE | ∑ M ( h j ( k ) ) = A g g [ h j ( k ) , v j ∈ N ( v i ) ] \sum M(h_j^{(k)})=Agg[h_j^{(k)},v_j\in N(v_i)] ∑M(hj(k))=Agg[hj(k),vj∈N(vi)] | U ( h i ( k ) , m i ( k + 1 ) ) = σ ( W ( k ) [ m i ( k + 1 ) ; h i ( k ) ] ) U(h_{i}^{(k)}, m_{i}^{(k+1)}) = \sigma(W^{(k)}[m_{i}^{(k+1)}; h_i^{(k)} ]) U(hi(k),mi(k+1))=σ(W(k)[mi(k+1);hi(k)]) |
Interaction Network | NN模型 | 另一个NN模型 |
由于MPNN的消息函数是作用在RDF三元组上的,下面给出常见的同构图、异构图、属性图等类型的图数据使用MPNN框架进行处理的方法:
(1)同构图
:特殊的是有向加权图,对于这类图数据,可以将边的正反方向看成两种关系,借用R-GCN的思路进行处理,同时对边上的权重可以考虑进邻接矩阵中当作归一化项一并处理。
(2)异构图
:可以考虑R-GCN方式,如果关系不多,可以将关系编码成one-hot向量当作边上的特征进行处理。
(3)属性图
:需要考虑的因素有节点的异构以及边属性。前者可以在调用MPNN之前,对不同类型的节点分别送进变换函数,将异构的节点变换到同一维度的同一特征空间,之后当作节点同构图处理。对于后者,参考关系图的处理,如果边上具有一些属性信息,则可以按照消息函数的机制,对其进行特征编码(如类别型属性特征进行one-hot编码或者embedding编码)。
2. NLNN
非局部神经网络(NLNN)
是对注意力机制的一般化总结。《Non-local Neural Networks》发表于2017年,作者王小龙,导师是何凯明。作者提出的非局部神经网络(non-local neural network,NLNN)来捕获深度神经网络的长依赖关系,这种非局部运算是对所有位置特征的加权计算,因此也可以被看作是不同“self-attention”的方法的统一。
卷积运算
和递归运算
是典型的局部运算
(只考虑局部信息),长距离依赖关系
通常是通过不断迭代来形成更大的感受野。但这种方式有很多缺点,包括计算效率低,优化困难等。作者受 CV 领域中经典的非局部均值滤波
的启发提出了 NLNN
,将一个位置响应为所有位置特征的加权计算,这种位置位置可以是空间、时间或者时空,也就是说这种方法的应用领域适用于图像、序列和视频等问题。
这种非局部运算有几大优点:
- 与卷积运算和递归运算相比,非局部运算通过计算任意两个位置之间的交互直接捕获长依赖;
- 即使神经网络的层数不多也能达到最佳效果;
- 非局部运算支持可变的输入大小,并且可以轻松与其他运算(如卷积运算)相结合。
NLNN通过non-local操作将任意位置的输出响应计算为所有位置特征的加权和。位置可以是图像中的空间坐标,也可以是序列数据中时间坐标,在图数据中,位置可以直接以节点代替。
非局部均值滤波(Non-Local Means,NLM)
是 Buades 等人 2005 年的论文《A non-local algorithm for image denoising》提出的一种对传统领域滤波方法进行改进的一种滤波方法,其考虑图像的自相似性,充分利用图像中的非局部信息来辅助滤波降噪。算法的主要原理是计算图像中所有像素与当前像素的相似性,其公式定义为:
N
L
[
v
]
(
i
)
=
∑
j
∈
I
w
(
i
,
j
)
v
(
j
)
NL[v](i)=\sum_j\in I w(i,j) v(j)
NL[v](i)=j∑∈Iw(i,j)v(j)
w
(
i
,
j
)
=
1
Z
(
i
)
e
−
∣
∣
v
(
N
i
)
−
v
(
N
j
)
∣
∣
2
,
a
2
h
2
w(i,j) = \frac{1}{Z(i)}e^{-\frac{||v(N_i)-v(N_j)||_{2,a}^2}{h2}}
w(i,j)=Z(i)1e−h2∣∣v(Ni)−v(Nj)∣∣2,a2
Z
(
i
)
=
∑
j
e
−
∣
∣
v
(
N
i
)
−
v
(
N
j
)
∣
∣
2
,
a
2
h
2
Z(i)=\sum_j e^{-\frac{||v(N_i)-v(N_j)||_{2,a}^2}{h2}}
Z(i)=j∑e−h2∣∣v(Ni)−v(Nj)∣∣2,a2
其中,
w
(
i
,
j
)
w(i,j)
w(i,j)为两个像素的相似性;
N
i
N_i
Ni表示以像素i为中心固定大小的领域;
v
(
N
i
)
v(N_i)
v(Ni)为灰度强度的映射向量;
Z
(
i
)
Z(i)
Z(i)为归一化常数;
h
h
h为平滑参数。
通用的non-local操作的定义如下:
h
i
′
=
1
C
(
h
)
∑
∀
j
f
(
h
i
,
h
j
)
g
(
h
j
)
h'_i=\frac{1}{C(h)}\sum_{\forall j} f(h_i, h_j)g(h_j)
hi′=C(h)1∀j∑f(hi,hj)g(hj)
其中,
i
i
i是输出位置的索引,
j
j
j是枚举所有可能位置的索引。
h
i
h_i
hi表示
i
i
i位置的特征向量,
f
(
h
i
,
h
j
)
f(h_i,h_j)
f(hi,hj)是
i
i
i和
j
j
j位置上元素之间的相关度函数;
g
(
h
j
)
g(h_j)
g(hj)表示对输入
h
j
h_j
hj进行变换的变换函数,因子
1
C
(
h
)
\frac{1}{C(h)}
C(h)1用于归一化结果。
**非局部运算考虑的是所有位置,而局部运算(如卷积操作)只考虑节点的周围信息,递归运算只考虑上一步的信息。**非局部运算与全连接运算的区别主要有3点:
- 非局部运算的多输出值会受到输入值之间关系的影响(因为要计算 f ( ⋅ , ⋅ ) f(\cdot,\cdot) f(⋅,⋅)),而全连接运算通过学习到权重来计算输入到输出的映射,所以不同位置的关系是不会影响到输出,一定程度上损失了位置的相关性;
- 非局部运算支持可变大小的输入,而全连接运算的输入大小是固定的;
- 非局部操作可以很容易地与卷积/递归层一起使用,而不像全连接层只能放到神经网络的最后使用。这便使得我们能够将非局部信息和局部信息结合起来,来构建一个更丰富的层次结构,
同MPNN一样,NLNN的核心也在两个函数上: f f f和 g g g。为了简化,可以使用线性变换作为函数 g : g ( h j ) = W g h j g:g(h_j)=W_gh_j g:g(hj)=Wghj。这里 W g W_g Wg是需要学习的权重矩阵。下面列出函数 f f f的一些选择:
-
内积
Dot product:考虑点乘相似度。函数 f f f的最简单的一种形式就是内积 f ( h i , h j ) = θ ( h i ) T ϕ ( h j ) f(h_i,h_j)=\theta(h_i)^T\phi(h_j) f(hi,hj)=θ(hi)Tϕ(hj),这里 θ ( h j ) = W θ h i , ϕ ( h j ) = W ϕ h j \theta(h_j)=W_\theta h_i,\phi(h_j)=W_{\phi}h_j θ(hj)=Wθhi,ϕ(hj)=Wϕhj,分别表示对输入的一种线性变换, C ( h ) = ∣ h j ∣ C(h)=|h_j| C(h)=∣hj∣。 -
全连接
使用输出为一维标量的全连接层定义 f f f:
f ( h i , h j ) = R e L U ( w f T [ θ ( h i ) , ϕ ( h j ) ] ) f(h_i,h_j)= ReLU(w_f^T[\theta(h_i), \phi(h_j)]) f(hi,hj)=ReLU(wfT[θ(hi),ϕ(hj)])
其中, [ ⋅ , ⋅ ] [\cdot,\cdot] [⋅,⋅]为连接运算, w f w_f wf为将 concat 向量转换成标量的权重向量, C ( h ) = ∣ h j ∣ C(h)=|h_j| C(h)=∣hj∣表示输入向量的数量。 -
高斯函数
Embedded Gaussian:考虑到 Embedding 的向量空间,我们有高斯滤波的变种 f ( h i , h j ) = e θ ( h i ) T ⋅ ϕ ( h j ) f(h_i,h_j)=e^{\theta(h_i)^T\cdot \phi(h_j)} f(hi,hj)=eθ(hi)T⋅ϕ(hj),其中 θ ( h i ) = W θ h i , ϕ ( h j ) = W ϕ h j , C ( h ) = ∑ ∀ j f ( h i , h j ) \theta(h_i)=W_\theta h_i, \phi(h_j)=W_{\phi}h_j,C(h)=\sum_{\forall j}f(h_i,h_j) θ(hi)=Wθhi,ϕ(hj)=Wϕhj,C(h)=∑∀jf(hi,hj),对于给定 i i i, 1 C ( h ) \frac{1}{C(h)} C(h)1表示沿维度 j j j进行归一化之后的值,此时 h i ′ = s o f t m a x j ( θ ( h i ) T ϕ ( h j ) ) g ( h j ) h'_i=softmax_j(\theta(h_i)^T \phi(h_j))g(h_j) hi′=softmaxj(θ(hi)Tϕ(hj))g(hj)。如果将自然对数 e e e的幂指数项改成全连接的形式,就成了GAT中的做法。
另外,作者将非局部运算
变成一个非局部模块
,以便插入到现有的神经网络结构中。作者将其设计成残差模块
,让其学习x的残差:
z
i
=
W
z
y
i
+
h
i
z_i=W_zy_i+h_i
zi=Wzyi+hi
这种残差结构可以使得在任意的预训练模型中直接插入一个新的非局部模块而不用改变原有的网络结构。考虑计算量问题,在输入比较大的情况下可以只在高阶语义层中引入 non-local layer,此外也可以通过 pooling 层来减少计算量。
下图是一个简单的实现:
⊕ 为元素点和,⊗ 为矩阵相乘。通过利用
1
×
1
×
1
1\times 1\times 1
1×1×1的卷积将
W
g
,
W
θ
,
W
ϕ
W_g, W_\theta, W_\phi
Wg,Wθ,Wϕ的channel数量控制为
X
X
X的一半,形成一个bottleneck的结构,可以减少计算量。在输出时利用
W
z
W_z
Wz可以将channel还原,保证了输入输出的维度一致。
此外,还有一个下采样(subsampling)的trick可以使用,例如,将通用公式修改为
h
i
′
=
1
C
(
h
^
)
∑
∀
j
f
(
h
i
,
h
^
j
)
g
(
h
^
j
)
h'_i=\frac{1}{C(\hat{h})}\sum_{\forall j} f(h_i, \hat h_j)g(\hat h_j)
hi′=C(h^)1∀j∑f(hi,h^j)g(h^j),其中
h
^
j
\hat h_j
h^j是
h
j
h_j
hj的下采样,比如可以利用pooling,这样就可以减少
1
/
4
1/4
1/4的pair-wise的计算量。这个trick不会改变non-local的行为,但是可以使得计算更加稀疏,通常会在上图中
ϕ
,
g
\phi, g
ϕ,g的后面增加一层max pooling层来实现。
3. GN
Graph Network相较于MPNN和NLNN,对GNN做了更一般的总结。其基本计算单元包括3个要素:
- 节点的状态 h i h_i hi
- 边的状态 e i j e_{ij} eij
- 图的状态 u u u
围绕着三个元素,GN设计了3个更新函数
ϕ
\phi
ϕ、3个聚合函数
ρ
\rho
ρ,具体如下:
e
i
j
′
=
ϕ
e
(
e
i
j
,
h
i
,
h
j
,
u
)
e'_{ij}=\phi^e(e_{ij,h_i,h_j,u})
eij′=ϕe(eij,hi,hj,u)
e
i
′
ˉ
=
ρ
e
→
h
(
[
e
i
j
′
,
∀
v
j
∈
N
(
v
i
)
]
,
h
i
′
=
ϕ
h
(
e
i
′
ˉ
,
h
i
,
u
)
)
\bar {e'_i}=\rho^{e\rightarrow h}([e'_{ij},\forall v_j\in N(v_i)], h'_i=\phi^h(\bar{e'_i},h_i,u))
ei′ˉ=ρe→h([eij′,∀vj∈N(vi)],hi′=ϕh(ei′ˉ,hi,u))
e
′
ˉ
=
ρ
e
→
u
(
[
e
i
j
′
,
∀
e
i
j
∈
E
]
)
,
h
′
ˉ
=
ρ
h
→
u
(
[
h
i
′
,
∀
v
i
∈
V
]
)
,
u
′
=
ϕ
u
(
e
′
ˉ
,
h
′
ˉ
,
u
)
\bar{e'}=\rho^{e\rightarrow u}([e'_{ij},\forall e^{ij}\in E]),\bar{h'}=\rho^{h\to u}([h'_i,\forall v_i\in V]), u'=\phi^u(\bar{e'},\bar{h'},u)
e′ˉ=ρe→u([eij′,∀eij∈E]),h′ˉ=ρh→u([hi′,∀vi∈V]),u′=ϕu(e′ˉ,h′ˉ,u)
GN的计算过程如下:
蓝色表示正在被更新的元素,黑色表示正在参与更新计算的元素。GN的更新思路是由点更新边,边聚合更新点,点聚合与边聚合更新图,每个元素在更新的时候需要考虑自身上一轮的状态。注意;更新步骤不是一成不变的,可以从全局出发到每个节点,再到每条边。另外全图状态
u
u
u的初始值,可以看成是图的某种固有属性或者先验知识的编码向量。
如果除去这个全图状态值的维护,GN就退化成了一个维护边状态的MPNN。GN对图里面的节点、边、全图都维护了相应的状态,分别对应节点层面的任务、边层面的任务、全图层面的任务。