之前写过一篇GNN的综述https://blog.csdn.net/weixin_45884316/article/details/115751272,但是感觉对于原理部分理解得不透彻,所以把原理单独拿出来,详细地过了一遍。
状态更新与输出
给定一张图
G
G
G,每个结点都有其自己的特征(feature),用
x
v
x_v
xv表示结点v自身的特征,连接两个结点的边也有自己的特征,用
x
v
,
u
x_{v,u}
xv,u表示。GNN的学习目标是获得每个结点的隐藏状态
h
v
∈
R
s
h_v∈R^s
hv∈Rs(state embedding),这就意味着:对于每个节点,它的隐藏状态包含了来自邻居节点的信息。那么,如何让每个结点都感知到图上其他的结点呢?GNN通过迭代更新所有结点的隐藏状态来实现,在
t
+
1
t+1
t+1时刻,结点
v
v
v的隐藏状态按照如下方式更新,一共更新4部分内容:
h
v
=
f
(
x
v
,
x
c
o
[
v
]
,
h
n
e
[
v
]
,
x
n
e
[
v
]
)
(1)
h_v=f(x_v,x_{co[v]},h_{ne[v]},x_{ne[v]})\tag{1}
hv=f(xv,xco[v],hne[v],xne[v])(1)
- 假设 f ( ⋅ ) f(\cdot) f(⋅)是带有参数的函数,叫做局部转移函数(local transition function)。 f f f就是隐藏状态的状态更新函数。注意: f f f在所有节点中共享(全局共享)
- x c o [ v ] x_{co[v]} xco[v]:表示与节点v关联的边的特征向量
- x n e [ v ] x_{ne[v]} xne[v]:表示节点v的邻居节点特征向量
- h n e [ v ] h_{ne[v]} hne[v]:表示节点v的邻居节点在 t t t时刻的隐藏状态
直接看公式有点不直观,举个小例子,假设对于节点5,其隐藏状态的更新函数如下:
利用更新函数,不断地利用当前时刻邻居结点的隐藏状态作为部分输入来生成下一时刻中心结点的隐藏状态,直到每个结点的隐藏状态变化幅度很小。至此,每个结点都“知晓”了其邻居的信息。
除了不断的更新,最终我们需要一个输出函数,适应不同的下游任务,论文中称之为局部输出函数(local output function):
o
v
=
g
(
h
v
,
x
v
)
(2)
\large o_v=g(h_v,x_v)\tag{2}
ov=g(hv,xv)(2)
对于不同的图,收敛的时刻可能不同(因为收敛是通过两个时刻 p − p- p−范数的差值是否小于某个阈值来判定的)
那么我们与深度学习结合呢?用神经网络来拟合复杂函数 f f f和 g g g
为什么会一定收敛输出
假设将所有的状态向量,所有的输出向量,所有的特征向量和所有的节点特征而得到的向量叠加起来,分别用
H
,
O
,
X
,
X
N
H,O,X,X_N
H,O,X,XN表示,那么可以得到更加紧凑的表示:
H
=
F
(
H
,
X
)
(3)
H=F(H,X)\tag{3}
H=F(H,X)(3)
O = G ( H , X N ) (4) O=G(H,X_N)\tag{4} O=G(H,XN)(4)
根据Banach的不动点定理,GNN使用如下的传统迭代方法来计算状态参量:
H
t
+
1
=
F
(
H
t
,
X
)
(5)
H^{t+1}=F(H^t,X)\tag{5}
Ht+1=F(Ht,X)(5)
其中,
H
t
H^t
Ht 表示
H
H
H 的第
t
t
t 个迭代周期的张量。对于任意的初始值
H
0
H_0
H0,公式(5)能通过快速收敛来得到公式(3)最终的固定点的解。通过更新迭代,为什么一定会收敛呢?
就是因为不动点定理,就是说不管输入 H 0 H^0 H0是什么形式,如下图只要 F F F是个压缩的映射,不断迭代压缩,最终都会收敛到某一个固定的点,称之为不动点。
那么,又带来了一个新的问题:怎么保证 F F F是一个压缩函数?
f f f是用前馈神经网络实现,一种实现方法可以是把每个邻居结点的特征、隐藏状态、每条相连边的特征以及结点本身的特征简单拼接在一起,在经过前馈神经网络后做一次简单的加和。
h v t + 1 = f ( x v , x c o [ v ] , h n t e [ v ] , x n e [ v ] ) = ∑ u ∈ n e [ v ] F N N ( [ x v ; x ( u , v ) ; h u t ; x u ] ) \begin{array}{l} \mathbf{h}_{v}^{t+1}=f\left(\mathbf{x}_{v}, \mathbf{x}_{c} o[v], \mathbf{h}_{n}^{t} e[v], \mathbf{x}_{n} e[v]\right) \\ =\sum_{u \in n e[v]} F N N\left(\left[\mathbf{x}_{v} ; \mathbf{x}_{(u, v)} ; \mathbf{h}_{u}^{t} ; \mathbf{x}_{u}\right]\right) \end{array} hvt+1=f(xv,xco[v],hnte[v],xne[v])=∑u∈ne[v]FNN([xv;x(u,v);hut;xu])
f f f为压缩映射的等价条件是 f f f的梯度(导数)要小于1,我们可以通过对雅可比矩阵(Jacobian Matrix)的惩罚项(Penalty)来实现,限制 f f f对 H H H的偏导数矩阵的大小。
f f f和 g g g的参数学习
不一定所有结点都是有监督的,模型的损失只通过有监督信号的结点得到,可以将损失函数定义为如下形式:
l
o
s
s
=
∑
i
=
1
p
(
t
i
−
o
i
)
(6)
loss=\sum_{i=1}^p{(t_i-o_i)}\tag{6}
loss=i=1∑p(ti−oi)(6)
p
p
p 表示监督节点的数目,
t
i
t_i
ti 和
o
i
o_i
oi 分别表示节点的真实值和预测值。损失函数的学习基于梯度下降策略,由以下步骤组成:
- 状态 h v t h_v^t hvt按照方公式 f f f迭代更新 T T T轮次,直到到达接近公式(3)的定点解的时刻 T T T, 这时得到的 H H H会接近不动点的解 H T ≈ H H^T\approx H HT≈H。
- 对于有监督信号的结点,将其隐藏状态通过 g g g得到输出,进而算出模型的损失
- 反向传播时,权重 W W W 的梯度从loss计算得到,所以可以直接求出 f f f和 g g g对最终隐藏状态 h v T n h_v^{{T_n}} hvTn的梯度,然后 W W W根据上一步中计算的梯度不断更新,经过 T T T次,得到对 h v 0 h_v^0 hv0的梯度,然后该梯度用于更新模型的参数。