6 Graph Neural Networks in Practice
这一部分,主要会介绍在实践过程中,如何优化GNN模型。比如,我们该选择什么样的损失函数、如何进行正则化、是否要进行预训练等等。
文章目录
6.1 Applications and Loss Functions
GNN的学习任务一般可以分为3类:
(1)节点分类(node classification)
(2)图分类(graph classification)
(3)关联预测(relation classification)
针对这几类不同的任务,我们可以因地制宜地在 z u , z G ∈ R d z_u,z_G\in R^{d} zu,zG∈Rd上,定义出不同的损失函数,并且可以通过预训练(pre-training)方法来提高模型的表达能力。
6.1.1 GNNs for Node Classification
节点分类是GNN中最流行的基准测试任务之一。这类方法主要基于Cora、Citeseer和Pubmed等引文网络数据集,根据论文在引文网络中的位置,使用基于语言的节点特征(词向量)对论文的类别或主题进行分类,并且每个类别只给出很少数量的带标记(labeled)示例。
针对这种节点分类任务,标准做法是使用完全监督(fully-supervised)的方式来进行训练。
L
=
∑
u
∈
V
t
r
a
i
n
−
log
(
s
o
f
t
m
a
x
(
z
u
,
y
u
)
)
s
o
f
t
m
a
x
(
z
u
,
z
G
)
=
∑
i
=
1
c
y
u
[
i
]
⋅
e
z
u
T
w
i
∑
j
=
1
c
e
z
u
T
w
j
\mathcal L=\sum_{u\in V_{train}}-\log(softmax(z_u,y_u)) \\[2ex] softmax(z_u,z_G)=\sum_{i=1}^{c}y_u[i]\cdot \frac{e^{z_u^Tw_i}}{\sum_{j=1}^{c}e^{z_u^Tw_j}} \\
L=u∈Vtrain∑−log(softmax(zu,yu))softmax(zu,zG)=i=1∑cyu[i]⋅∑j=1cezuTwjezuTwi
其中
y
u
∈
Z
c
y_u\in Z^{c}
yu∈Zc是一个「0,1」二值化的独热(one-hot)向量,可以根据节点的label生成,指示了节点u到底在c个类别中属于哪一类。
w
i
∈
R
d
w_i\in R^{d}
wi∈Rd是可训练的参数,是为了和
z
u
T
z_u^T
zuT一起生成一个标量。这样,softmax函数就很好理解了,代表了节点u属于
y
u
y_u
yu所指示的类别的概率。
Supervised, semi-supervised, transductive, and inductive
节点分类任务到底是监督还是半监督方式,需要根据在训练中不同的节点类型来判别。
节点类型总共分为3类:
(1)训练集节点
V
t
r
a
i
n
V_{train}
Vtrain,有标记,训练时可见。需要进行消息传递+计算损失。
(2)传递测试节点
V
t
r
a
n
s
V_{trans}
Vtrans,无标记,训练时可见。只需要进行消息传递,不需要计算损失。
(3)归纳测试节点
V
i
n
d
V_{ind}
Vind,无标记,训练时不可见。既不消息传递,也不计算损失。
使用
V
t
r
a
n
s
V_{trans}
Vtrans进行测试的方法属于半监督方法。
6.1.2 GNNs for Graph Classification
图分类任务可以借鉴节点分类任务,使用相似的softmax分类损失;也可以将其视为回归任务,使用均方误差损失。
L
=
∑
G
i
∈
T
∣
∣
M
L
P
(
z
G
i
)
−
y
G
i
∣
∣
2
2
\mathcal L=\sum_{G_i\in \Tau}||MLP(z_{G_i})-y_{G_i}||_2^2\\
L=Gi∈T∑∣∣MLP(zGi)−yGi∣∣22
其中
T
\Tau
T是一个图的标签集合,表示了图
G
i
G_i
Gi属于哪一个类别
y
G
i
∈
R
y_{G_i}\in R
yGi∈R。MLP是一个稠密连接的NN,它的输出是一个标量。
6.1.3 GNNs for Relation Prediction
可以直接使用之前几章中提到的,知识图谱的链接预测模型的损失函数。
6.1.4 Pre-training GNNs
预训练是深度学习的一个技巧。
在GNN模型中,事实证明,随机初始化GNN已经可以取得和使用邻域重构损失进行预训练基本相同的表现。因此,我们需要寻找除了邻域重构损失之外的其他预训练方法。
一个比较成功的模型是DGI,它通过最大化
z
u
z_u
zu和
z
G
z_G
zG之间的信息,来进行无监督预训练。
L
=
−
∑
u
∈
V
t
r
a
i
n
E
G
log
(
D
(
z
u
,
z
G
)
)
+
γ
E
G
~
log
(
1
−
D
(
z
~
u
,
z
G
)
)
\mathcal L=-\sum_{u\in V_{train}}\mathbb E_{G}\log(D(z_u,z_G))+\gamma \mathbb E_{\tilde G}\log(1-D(\tilde z_u,z_{G}))\\
L=−u∈Vtrain∑EGlog(D(zu,zG))+γEG~log(1−D(z~u,zG))
其中比较关键的是(人为)损坏后的图
G
~
\tilde G
G~,用它训练出来的节点嵌入表示为
z
~
u
\tilde z_u
z~u。判别器D是用来区分节点嵌入到底是用哪个图训练出来的(
G
G
G或
G
~
\tilde G
G~)。
这一类的无监督预训练方法,核心思想基本都是最大化不同级别的嵌入表示之间的信息、区分真实和损坏的嵌入表示。有时候,它也和NLP当中的“content masking”预训练方法比较相似。
6.2 Efficiency Concerns and Node Sampling
在实践中,如果我们直接按照之前介绍的节点级GNN公式来进行计算,会产生以下的问题:如果节点之间有共同的邻居,会导致冗余计算(???)。
6.2.1 Graph-level Implementations
一种方法是使用GNN的图级实现,使用基于稀疏矩阵乘法来进行消息传递操作。
H
(
k
+
1
)
=
σ
(
A
H
(
k
)
W
n
e
i
g
h
(
k
+
1
)
+
H
(
k
)
W
w
e
i
g
h
(
k
+
1
)
)
H^{(k+1)}=\sigma(AH^{(k)}W_{neigh}^{(k+1)}+H^{(k)}W_{weigh}^{(k+1)})
H(k+1)=σ(AH(k)Wneigh(k+1)+H(k)Wweigh(k+1))
这样做的好处是,每个节点每层的嵌入表示
h
u
(
k
)
h_u^{(k)}
hu(k)只会计算一次。缺点也显而易见:整张图一块进行计算可能会引发内存不足,整批(full-batch)梯度下降可能会影响到模型的表现。
6.2.2 Subsampling and Mini-Batching
为了解决图级GNN实现的内存占用问题,我们可以使用mini-batch的方法,每个batch在节点的子集上运行节点级的GNN实现。
当然,每次的节点子集不能随意乱取,需要遵循一定的方法,否则会对模型的效果产生不利影响。首先,选取一个节点的子集,之后每次都对子集中节点的邻居做固定样本大小的采样,得到下一轮batch所用到的节点子集。
6.3 Parameter Sharing and Regularization
正则化是机器学习模型的一个重要组成部分,有一些专门用于GNN的正则化方法。
Parameter Sharing Across Layers
如果GNN的模型有很多层,那么层与层之间的参数共享就很有必要了。核心思想是,在GNN所有的聚合和更新函数中共享参数。
Edge Dropout
在训练过程中,丢弃邻接矩阵中的某些边,可以让模型不容易过拟合。如果之前在训练中采用mini-batch的子采样方法,那么这种正则化方法就会在不知不觉中运用到训练当中,因为每次采样我们都会移除掉邻接矩阵中的一些边。