文章目录
Abstract
大规模GCN的训练:目前基于SGD的gcn算法,1)面临随着gcn层数增长,计算复杂度呈指数增长;2)需要保存整个Graph和每个node的embedding,存储量巨大。
本文提出了一种基于Graph聚类结构结构,且基于SGD训练的GCN算法:Cluster-GCN。在每一个步骤中,Cluster-GCN通过graph聚类算法来筛选联系紧密的sub-graph,从而在sub-graph中的一组node进行采样,并且限制该sub-graph中的邻居搜索,可以显著提高搜索效率。
在Amazon2M数据集:200万个node,6100万个边,比Reddit大五倍,实验了三层四层gcn。
Introduction
**对于Graph,GCN网络通过Graph Convolutional Neural逐层地获取节点的embedding:在每一层的每一个节点的embedding,需要采集下一层相邻节点的embedding进行激活。最后一层的embedding将用于一些最终任务。**例如在Graph Classification中,最后一层embedding被输入到分类器(如softmax)来预测各个节点的标签。
需要注意地是,由于GCN运算需要利用graph中节点之间地交互来学习更新embedding,这使得GCN的训练非常有挑战性。与cnn等网络不同,训练loss可以完美地分解为每个样本单独影响(decomposed into individual terms),GCN的损失函数必须依赖大量的其他节点。特别是GCN变深时,由于这种节点依赖,GCN的训练变得非常慢–反向传播时需要计算Graph上的所有embedding。
Previous GCN Training Algorithm
1、 GCN开山之作:Semi-Supervised Classification with Graph Convolutional Networks,应用全批次梯度下降法(Full-batch Gradient Descent),需要计算整个梯度,存储所有节点的embeddings,导致 O ( N F L ) O(NFL) O(NFL)内存需求。由于每个epoch只更新一次参数,梯度下降的收敛速度非常慢。
2、Graph-SAGE:Inductive Representation Learning on Large Graphs,提出了mini-batch SGD。由于每次更新参数仅仅基于一个batch的梯度,大大降低了内存需求,并且可以在每个epoch执行多次更新,从而加快了收敛速度。Graph-SAGE存在节点邻居扩展问题,需要计算节点的邻居节点在L-1层的embeddings,而这些邻居节点又需要求在L-2层的embeddings,周而复始,这导致大量的计算复杂度。Graph-SAGE同过在各层的反向传播过程中固定大小的邻居节点,降低了复杂度。
3、VR-GCN采用减小Variance方法来减小邻域采样节点的大小。尽管成功地减小了采样大小,但仍然需要将所有节点的中间embedding,导致 O ( N F L ) O(NFL) O(NFL)的内存需求。如果节点数量增加到数百万个,那么对于内存要求过高。
本文利用Graph的聚类结构,目的是划分节点的分区,使同一分区中的节点之间sub-Graph的链接,比不同分区更多。一个mini-batch算法的效率,由embedding utilization(嵌入利用率)来描述:与一个batch或者within-batch的节点之间的链接数量成正比。
- Cluster-GCN在大型Graph上实现了最好的内存使用,特别是在deep GCN上;
- 对于浅层网络(2层),Cluster-GCN可以达到与VR-GCN类似的训练速度。但当网络加深时(4层),可以比VR-GCN更快。这是因为Cluster-GCN的复杂度与网络层数L成线性关系,而VR-GCN时指数关系。
- Cluster-GCN能够训练一个非常深且极大规模的网络。
Bachground
Graph Convolutional Neural
GCN中的每一层通过考虑相邻节点的embedding,来更新Graph中的每个节点的特征向量表示。具体来说,GCN的逐层正向传播可以总结为:
X
(
l
+
1
)
=
f
(
X
l
,
A
)
=
δ
(
D
^
−
1
2
A
^
D
^
−
1
2
X
(
l
)
W
(
l
)
)
X^{(l+1)}=f(X^l,A)=\delta(\hat D^{-\frac{1}{2}}\hat A\hat D^{-\frac{1}{2}}X^{(l)}W^{(l)})
X(l+1)=f(Xl,A)=δ(D^−21A^D^−21X(l)W(l))
- X X X是所有节点的特征向量构成的特征矩阵(每一行表示一个节点的特征);
- X ( l ) X^{(l)} X(l)和 X ( l + 1 ) X^{(l+1)} X(l+1)分别是 l l l层的输入和输出矩阵, X ( l ) X^{(l)} X(l)代表第 l l l层对应节点的embedding;
- A A A是Graph的邻接矩阵;
- A ^ = A + I N \hat A=A+I_N A^=A+IN是带有自环的无向图的邻接矩阵;
- D ^ i i = ∑ j A ^ i j \hat D_{ii}=\sum_{j}\hat A_{ij} D^ii=∑jA^ij是度量矩阵;
- W ( l ) W^{(l)} W(l)是一个可训练权重矩阵或参数矩阵;
- δ ( ⋅ ) \delta(\cdot) δ(⋅)是激活函数,如ReLU等。
Z ( l + 1 ) = A ′ X ( l ) W ( l ) , X ( l + 1 ) = δ ( Z ( l + 1 ) ) . Z^{(l+1)}=A^{'}X^{(l)}W^{(l)},\\ X^{(l+1)}=\delta(Z^{(l+1)}). Z(l+1)=A′X(l)W(l),X(l+1)=δ(Z(l+1)).
损失函数
L
=
1
∣
Y
L
∣
∑
i
∈
Y
L
Loss
(
y
i
,
z
i
L
)
,
\mathcal{L}=\frac{1}{|\mathcal{Y}_\mathcal L|\sum_{i\in \mathcal{Y}_\mathcal L}}\text{Loss}(y_i,z^L_i),
L=∣YL∣∑i∈YL1Loss(yi,ziL),
- Y L \mathcal{Y}_\mathcal L YL是部分有标签的节点的标签;
- z i L z^L_i ziL是ground-truth标签为 y i y_i yi的 Z L Z^L ZL的第 i i i行,表示节点 i i i的最终预测;
- Cross-Entropy Loss。
Cluster-GCN
batch_size=
∣
B
∣
|\mathcal{B}|
∣B∣表示一个batch的节点,使用基于mini-batch的SGD:
1
∣
B
∣
∑
i
∈
B
∇
Loss
(
y
i
,
z
i
L
)
,
\frac{1}{|\mathcal{B}|}\sum_{i\in \mathcal B}\nabla\text{Loss}(y_i,z^L_i),
∣B∣1i∈B∑∇Loss(yi,ziL),
需要注意的是,尽管基于mini-batch的SGD在每个epoch收敛得更快,但由于引入了另一个计算开销,所以比Full Gradient Descent要慢。
Why does vanilla mini-batch SGD have slow per-epoch time?
考虑计算一个节点 i i i相关的梯度: ∇ Loss ( y i , z i L ) \nabla\text{Loss}(y_i,z^L_i) ∇Loss(yi,ziL)。显然,需要存储节点 i i i的embedding,而节点 i i i的embedding的计算依赖于, L − 1 L-1 L−1层的邻居节点的embeddings。**假设一个 L L L层的GCN网络,每个节点的平均度数为 d d d,为了获得节点 i i i的相关梯度,需要对Graph中的一个节点聚合 O ( d L ) O(d^L) O(dL)个节点的特征。**换句话说,需要获取Graph中节点的 h o p − k ( k = 1 , … , L ) hop-k(k=1,\dots,L) hop−k(k=1,…,L)邻居节点的信息来执行一次更新。
Embedding utilization can reflect computational efficiency
嵌入利用率用来反应计算效率。
如果节点 i i i在第 l l l层得embedding: z i ( l ) z^{(l)}_i zi(l),在计算第 l + 1 l+1 l+1层得embeddings时被重复使用了 u u u次,那么 z i ( l ) z^{(l)}_i zi(l)的嵌入利用率就是 u u u。
为了使mini-batch SGD工作,以前的方法试图限制邻域扩展的数量,但是这并没有提高嵌入使用率。
- GraphSAGE使用一个固定大小的均匀采样来确定邻居节点集,而不是使用一个完整的邻域集合;
- FastGCN提出了一种改进梯度估计的重要采样策略;
- VR-GCN提出了一种策略来存储所有 N N N个节点和 L L L层在先前计算的embedding,并对未采样的邻居节点重复利用。
Vanilla Cluster-GCN
在mini-batch SGD更新中,尝试设计一个batch和相应的计算sub-graph来最大限度地提高embedding utilization。Cluste-GCN通过将embedding utilization连接到一个聚类目标上来实现。
对于一个Graph:
G
\mathcal G
G,把节点分成
c
c
c个组:
V
=
[
V
1
,
…
,
V
c
]
V=[V_1,\dots,V_c]
V=[V1,…,Vc],这样就会得到
c
c
c个sub-graph:
G
‾
=
[
G
1
,
…
,
G
c
]
=
[
{
V
1
,
E
1
}
,
…
,
{
V
c
,
E
c
}
]
\overline G=[G_1,\dots,G_c ]=[\{V_1,E_1\},\dots,\{V_c,E_c\}]
G=[G1,…,Gc]=[{V1,E1},…,{Vc,Ec}]
-
每个 E t E_t Et只包含在 V t V_t Vt中的节点之间的边;
对节点进行重组后,邻接矩阵被划分为 c 2 c^2 c2个子矩阵:
KaTeX parse error: Undefined control sequence: \label at position 142: …x} \right] \̲l̲a̲b̲e̲l̲{eq:a}
其中
A ˉ = [ A 11 … 0 ⋮ ⋱ ⋮ 0 … A c c ] , Δ = [ 0 … A 1 c ⋮ ⋱ ⋮ A c 1 … 0 ] \bar A=\left[ \begin{matrix} A_{11}&\dots&0\\ \vdots&\ddots&\vdots\\ 0&\dots&A_{cc} \end{matrix} \right], \Delta=\left[ \begin{matrix} 0&\dots&A_{1c}\\ \vdots&\ddots&\vdots\\ A_{c1}&\dots&0 \end{matrix} \right] Aˉ=⎣⎢⎡A11⋮0…⋱…0⋮Acc⎦⎥⎤,Δ=⎣⎢⎡0⋮Ac1…⋱…A1c⋮0⎦⎥⎤ -
对角块 A t t A_{tt} Att是一个包含的边在sub-graph G t G_t Gt内 ∣ V t ∣ × ∣ V t ∣ |V_t|\times|V_t| ∣Vt∣×∣Vt∣维的邻接矩阵;
-
A ˉ \bar A Aˉ是 G ˉ \bar G Gˉ的邻接矩阵;
-
A s t A_{st} Ast包含了两个部分 V s V_s Vs和 V t V_t Vt之间的边;
-
Δ \Delta Δ是由 A A A的所有非对角块组成的矩阵。
类似的,将特征矩阵 X X X和训练矩阵 Y Y Y根据区分 [ V 1 , … , V c ] [V_1,\dots,V_c] [V1,…,Vc]分组为 [ X 1 , … , X c ] [X_1,\dots,X_c] [X1,…,Xc]和 [ Y 1 , … , Y c ] [Y_1,\dots,Y_c] [Y1,…,Yc]。
块对角近似的好处是GCN的目标函数可以分解成不同的batches(clusters)。设
A
′
ˉ
\bar{A^{'}}
A′ˉ为
A
ˉ
\bar A
Aˉ的归一化版本,则最终embedding矩阵由
A
ˉ
\bar A
Aˉ的块对角形式变成(
A
t
t
′
ˉ
\bar{A^{'}_{tt}}
Att′ˉ是
A
′
ˉ
\bar{A^{'}}
A′ˉ的块对角):
KaTeX parse error: No such environment: equation at position 8: \begin{̲e̲q̲u̲a̲t̲i̲o̲n̲}̲ \begin{aligned…
损失函数也被分解成:
KaTeX parse error: Undefined control sequence: \label at position 163: …ss}(y_i,x^L_i) \̲l̲a̲b̲e̲l̲{eq:step2}
然后Cluster-GCN基于公式KaTeX parse error: Undefined control sequence: \eqref at position 1: \̲e̲q̲r̲e̲f̲{eq:step1}和KaTeX parse error: Undefined control sequence: \eqref at position 1: \̲e̲q̲r̲e̲f̲{eq:step2}中的分解形式。**在每一步中,对一个聚类
V
t
V_t
Vt进行采样,然后根据
L
A
′
ˉ
\mathcal L_{\bar{A^{'}}}
LA′ˉ的梯度进行SGD更新,**这只需要当前batch上的sub-graph
A
t
t
,
X
t
,
Y
t
A_{tt},X_t,Y_t
Att,Xt,Yt和模型
{
W
(
l
)
}
l
L
\{W^{(l)}\}^L_l
{W(l)}lL。
Cluster-GCN使用了Graph聚类算法来划分Graph。Graph聚类的方法,如metis和graclus等,旨在在Graph中的顶点上构建分区,使得簇内连接远大于簇间连接,从而更好的捕捉聚类和区分结构。
- embedding utilization相当于每个batch的簇内的连接。每个节点及其相邻节点通常位于同一个簇中,因此经过几次跳跃后,高概率的邻域节点仍然位于同一个簇中;
- 由于使用
A
A
A的对角近似
A
ˉ
\bar A
Aˉ取代
A
A
A,并且误差和簇间的连接成正比,所以要找到一种分区方法最小化簇间连接的数量。
在上图中,全图 G G G和聚类分区图 G ˉ \bar G Gˉ来进行邻域展开,如右边所示,Cluster-GCN可以避免大量的邻域搜索,而集中在每个簇中的邻居上。
Time and Space complexity
由于在分区 V t V_t Vt中的节点,只连接 V t V_t Vt中的节点,所以每个节点不需要再 A t t A_{tt} Att外部执行邻居搜索。对每个batch的计算将很纯粹的只是 A t t ′ ˉ X ( l ) W ( l ) \bar{A_{tt}^{'}}X^{(l)}W^{(l)} Att′ˉX(l)W(l)的矩阵乘积和一些element-wise的操作,时间复杂度低。并且每个batch只需要计算 O ( b L ) O(bL) O(bL)的embeddings,是线性的,存储要求也低。
Stochastic Multiple Partitions
Cluster-GCN实现了良好的计算和内存复杂性,却存在两个问题:
- Graph被分割后,一些链接(等式KaTeX parse error: Undefined control sequence: \eqref at position 1: \̲e̲q̲r̲e̲f̲{eq:a}中的 Δ \Delta Δ部分)被删除。因此,性能可能会受影响。
- 图聚类算法往往将相似的节点聚集在一起,因此聚类的分布可能不同于原始数据集,从而导致在执行SGD更新时对full gradient的估计有偏差。
随即多聚类方法,在簇与簇之间进行合并,减少batch间的差异(variance)。首先用一个较大的p值把图分割为
V
1
,
…
,
V
p
V_1,\dots,V_p
V1,…,Vp,然后对于SGD更新重新构建一个batch
B
B
B,而不是只考虑一个簇。随机的选择q个簇:
t
1
,
…
,
t
q
t_1,\dots,t_q
t1,…,tq,并把它们的节点
V
t
1
∪
⋯
∪
V
t
q
V_{t_1}\cup\dots\cup V_{t_q}
Vt1∪⋯∪Vtq包含在这个batch
B
B
B中。此外,在选择的簇之间的连接:
A
i
j
∣
i
,
j
∈
t
1
,
…
,
t
q
A_{ij}|i,j\in t_1,\dots,t_q
Aij∣i,j∈t1,…,tq
被添加回去。这样,簇间的连接会被重新合并,使得batch之间的差异更小。
Issues of training deeper GCN
类似与Resnet的跳接:
Z
(
l
+
1
)
=
A
′
X
(
l
)
W
(
l
)
,
X
(
l
+
1
)
=
δ
(
Z
(
l
+
1
)
)
+
X
(
l
)
Z^{(l+1)}=A'X^{(l)}W^{(l)},\\ X^{(l+1)}=\delta(Z^(l+1))+X^{(l)}
Z(l+1)=A′X(l)W(l),X(l+1)=δ(Z(l+1))+X(l)
本文通过放大每个GCN层中使用的邻接矩阵
A
A
A的对角部分,实现在每个GCN层的聚合中对上一层的表示施加更多的权重,如将单位矩阵添加到
A
ˉ
\bar A
Aˉ中:
Z
(
l
+
1
)
=
(
A
′
+
I
)
X
(
l
)
W
(
l
)
,
X
(
l
+
1
)
=
δ
(
Z
(
l
+
1
)
)
,
X
(
l
+
1
)
=
δ
(
(
A
′
+
I
)
X
(
l
)
W
(
l
)
)
Z^{(l+1)}=(A'+I)X^{(l)}W^{(l)},\\ X^{(l+1)}=\delta(Z^{(l+1)}),\\ X^{(l+1)}=\delta((A'+I)X^{(l)}W^{(l)})
Z(l+1)=(A′+I)X(l)W(l),X(l+1)=δ(Z(l+1)),X(l+1)=δ((A′+I)X(l)W(l))
进而提出对角增强(diagonal enhancement):
-
首先向原始 A A A添加一个单位矩阵,然后标准化:
A ~ = ( D + I ) − 1 ( A + I ) \tilde A=(D+I)^{-1}(A+I) A~=(D+I)−1(A+I) -
X ( l + 1 ) = δ ( ( A ~ + λ diag ( A ~ ) ) X ( l ) W ( l ) ) X^{(l+1)}=\delta((\tilde A+\lambda\text{diag}(\tilde A))X^{(l)}W^{(l)}) X(l+1)=δ((A~+λdiag(A~))X(l)W(l))
Experiment
Training Deeper GCN
多层GCNs,在下表中对比了Cluster-GCN和VRGCN:
对比发现,VRGCN由于其邻域查找的代价呈指数增长,而Cluster-GCN线性增长。
通过对Cluster-GCN的归一化方法,可以进行更深的训练,对于PPI数据集,Cluster-GCN可以通过训练一个包含2048个隐藏单元的5层GCN来达到最先进的效果。对于Reddit数据集,使用了一个包含128个隐藏单元的4层GCN。
Conclusion
此文提出了一种新的训练算法Cluster-GCN。实验结果表明,该方法可以在大型图上训练非常深的GCN,例如在一个超过200万个节点的图上,使用2G左右的内存训练时间不到1小时,精度达到90.41 (F1 score)。使用该方法,能够成功地训练更深入的GCNs,它可以在PPI数据集和Reddit数据集上获得最先进的测试F1 score。