声明:本文翻译自Tobias Skovgaard Jepsen写在Medium上的博客文章,已获得其本人的许可;版权归原作者所有,未经同意请勿转载;文中带有“注”标记的内容为本人添加,以便阅读。
原文链接:https://towardsdatascience.com/how-to-do-deep-learning-on-graphs-with-graph-convolutional-networks-62acf5b143d0
在图上进行机器学习是一项困难的任务,因为它的复杂性很高,同时也是由于信息丰富的图结构。这篇文章是关于如何用图卷积网络(GCNs)在图上进行深度学习的系列文章中的第一篇。GCNs是一种强大的神经网络,旨在直接处理图并利用它们的结构信息。我将简单回顾一下前一篇博文,你也可以在这找到该系列的其他文章:
在上一篇文章中,我对GCNs做了一个深入介绍,并演示了如何基于邻居表示更新节点表示。在这篇文章中,我们首先对在前一篇文章中讨论的相当简单的图卷积中的聚合操作进行了深入的理解。然后,我们看看最近发表的一种图卷积传播规则,我会展示如何用它进行半监督学习,任务是在一个小型的社会网络(即扎卡里空手道俱乐部)上进行社区预测。如下面所示,尽管每个社区只使用一个训练示例,但它能够学习每个节点的潜在特征表示,将两个社区分隔成两个合理的内部聚合和外部隔离的群体。
在上篇文章中,我们了解了GCNs中用于演示传播过程的一个简单数学框架。给定一个 N × F 0 N×F^0 N×F0 的特征矩阵 X \pmb X XXX,一个表示图结构的 N × N N×N N×N 的邻居矩阵 A \pmb {A} AAA,GCN中的每一个隐藏层表示为 H i = f ( H i − 1 , A ) \pmb {H^i}=f(\pmb {H^{i-1}}, \pmb A) HiHiHi=f(Hi−1Hi−1Hi−1,AAA),其中 H 0 = X \pmb {H^0}=\pmb X H0H0H0=XXX, f \ f f是传播规则。每层 H i \pmb {H^i} HiHiHi对应于一个 N × F i N×F^i N×Fi的特征矩阵,其中每行就是一个节点的特征表示。
我们看下两种形式的传播规则:
这些规则将节点的特征表示作为其邻居的特征表示的集合,然后应用权重 W i \pmb {W^i} WiWiWi和激活函数 σ \sigma σ对其进行变换。为使聚合和转换步骤更加明确,我们可以将上述传播规则1和2表示为 f ( H i , A ) = t r a n s f o r m ( a g g r e g a t e ( A , H i ) , W i ) \ f({\pmb {H^i}, \pmb A})=transform(aggregate(\pmb A, \pmb {H^i}), \pmb {W^i}) f(HiHiHi,AAA)=transform(aggregate(AAA,HiHiHi),WiWiWi),其中 t r a n s f o r m ( M , W i ) = σ ( M W i ) \ transform(\pmb M, \pmb {W^i})=\sigma(\pmb {MW^i}) transform(MMM,WiWiWi)=σ(MWiMWiMWi),规则1里面 a g g r e g a t e ( A , H i ) = A H i \ aggregate(\pmb A, \pmb {H^i})=\pmb {AH^i} aggregate(AAA,HiHiHi)=AHiAHiAHi,规则2里面 a g g r e g a t e ( A , H i ) = D − 1 A ^ H i \ aggregate(\pmb A, \pmb {H^i})=\pmb {D^{-1}\hat AH^i} aggregate(AAA,HiHiHi)=D−1A^HiD−1A^HiD−1A^Hi.
在上篇文章中我们讨论过,规则1中的聚合函数将节点表示成其邻居特征表示的和,这样操作有两个明显不足:
为了解决这两个问题,规则2首先通过添加一个单位阵到邻接矩阵 A \pmb A AAA里面强行添加自环,然后在变换后的邻接矩阵 A ^ = A + I \pmb {\hat A=A+I} A^=A+IA^=A+IA^=A+I上进行聚合操作。其次,通过将 A ^ \pmb {\hat A} A^A^A^ 与节点度矩阵的逆 D − 1 \pmb {D^{-1}} D−1D−1D−1相乘,进行特征表示归一化,这样就将聚合后的特征表示变成了均值,不受节点度数的影响。
下面我会把规则1称为求和法,规则2称为均值法。
Kipf和Welling在最近的一篇文章中提出了一种使用谱传播规则的快速近似谱图卷积方法:
f ( X , A ) = σ ( D − 0.5 A ^ D − 0.5 X W i ) \ f(X,A)=\sigma(\pmb {D^{-0.5}\hat AD^{-0.5}XW^i}) f(X,A)=σ(D−0.5A^D−0.5XWiD−0.5A^D−0.5XWiD−0.5A^D−0.5XWi)同求和法以及均值法相比,谱图卷积的唯一区别就在于聚合函数的选择。尽管它有点类似于均值法,因为它使用负幂律的度矩阵 D \pmb D DDD来标准化聚合,但标准化化是不对称的。让我们试试看这有什么用。
我们可以将我到目前为止提出的聚合函数理解为加权和,其中每个聚合规则只在其权重的选择上有所不同。我们将首先看到如何将相对简单的求合法以及均值法表示为加权和,然后再讨论谱方法。
为了了解如何使用求和法计算第i个节点的聚合特征表示,我们先看看如何计算聚合中的第i行。
a g g r e g a t e ( A , X ) i = A i X ( 1 a ) = ∑ j = 1 N A i , j X j ( 1 b ) \begin{aligned} \ aggregate({\pmb A, \pmb X})_i &= {\pmb A_i \pmb X} \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ (1a) \\ \ &= \sum_{j=1}^N{A_{i,j}\pmb X_j} \ \ \ \ \ \ \ \ \ \ (1b) \end{aligned} aggregate(AAA,XXX)i =AAAiXXX (1a)=j=1∑NAi,jXXXj (1b)
如上式1a所示,我们可以将第i个节点的聚合特征表示作为向量矩阵乘积来计算。我们可以将这个向量矩阵乘积表示为一个简单的加权和,如方程1b所示,其中我们对 X \pmb X XXX中的N行中的每一行求和。
式1b中,第j个节点在集合中的贡献由 A \pmb A AAA的第i行的第j列的值确定。由于 A \pmb A AAA是邻接矩阵,如果第j个节点是第i个节点的邻居,则该值为1,否则为0。因此,等式1b对应于第i节点的邻居的特征表示的加和。这与前一篇文章的观察一致。
总之,每个邻居的贡献仅取决于由邻接矩阵 A \pmb A AAA定义的邻居。
要看均值法如何聚合节点表示,我们再次看看如何计算聚合中的第i行,现在使用均值法。为了简单起见,我们只考虑在“原始”邻接矩阵上进行均值聚合操作,而不考虑 A \pmb A AAA加上单位矩阵 I \pmb I III之后的变换矩阵,后者对应于向图中添加自环。
从上面的方程中可以看出,推导过程现在稍微长一些。在方程2a中,我们首先通过将邻接矩阵 A \pmb A AAA与度矩阵 D \pmb D DDD的逆相乘来变换邻接矩阵 A \pmb A AAA。该计算在方程2b中更加明确。逆度矩阵是对角线矩阵,其中沿对角线的值是节点度的逆,也就是说位置 ( i , i ) \ (i,i) (i,i)的元素是第 i \ i i个节点的倒数。因此,我们可以去掉方程2c的一个求和符号。方程2c进一步简化后得到方程2d和2e。
如等式2e所示,我们现在再次对邻接矩阵 A \pmb A AAA中的 N \ N N行中的每一行求和。如在讨论求和规则时所述,这对应于对第 i \ i i个节点的每个邻居求和。然而,等式2e中的加权和中的权重之和现在保证为1。因此,方程2e对应于第 i \ i i个节点的邻居的特征表示的平均值。
求和法仅依赖于邻接矩阵 A \pmb A AAA定义的邻域,而均值法还依赖于节点的度。
我们现在有了一个有用的框架来分析光谱方法。让我们看看它带我们去哪里!
与均值法一样,我们使用度矩阵 D \pmb D DDD来转换邻接矩阵 A \pmb A AAA。但是,如等式3a所示,我们将度矩阵的幂变为-0.5,并在 A \pmb A AAA的每一侧将其相乘。此操作可以如等式3b所示进行分解。再次提醒,度矩阵(及其幂)是对角的。因此,我们可以进一步简化方程3b,直到我们得到方程3d中的表达式。
方程式3d显示了一些非常有趣的东西。在计算第 i \ i i个节点的聚合特征表示时,不仅考虑了第 i \ i i个节点的度,而且还考虑了第 j \ j j个节点的度。
与均值法相似,谱方法规范化了聚合操作,也就是说聚合特征表示与输入特征保持大致相同的尺度。然而,谱方法在求加权和时,会给予低度邻居以较高的权重,大度邻居以较低的权重。当低度邻居比大度邻居提供更多有用信息时,这可能会有用。
除了谱方法外,kipf和welling还演示了GCNs如何用于半监督分类[1]。在半监督学习中,我们希望同时使用有标签和无标签的样本。到目前为止,我们已经隐式地假设了整个图是可用的,也就是说,我们面对的是一个直推式学习任务。换句话说,我们知道所有的节点,但不知道所有的节点的标签。
在我们看到的所有方法中,我们都是在节点的邻域上聚合信息,因此共享邻域的节点往往具有相似的特征表示。如果图表现出同质性,即相互连接的节点趋于相似(例如具有相同的标签),则此属性非常有用。亲和力存在于许多现实的网络中,尤其是社交网络表现出强烈的亲和力。
(注:网络中的亲和力指相似的个体之间容易互相吸引,进而产生链接。)
正如我们在上一篇文章中所看到的,即使是随机初始化的GCN,也可以通过使用图的结构信息来很好地分离同构图中节点的特征表示。我们可以进一步在有标记的节点上训练GCN,通过更新所有节点共享的权重矩阵,有效地将节点标记信息传播到未标记节点。这可以按如下[1]进行:
让我们看看谱方法如何使用半监督学习将节点标签信息传播到未标记的节点。和上一篇文章一样,我们将以Zachary空手道俱乐部为例。
如果您想继续,您可以在这里找到数据集和包含训练和评估GCN代码的Jupyter笔记本。
简言之,Zachary空手道俱乐部是一个小型的社交网络,在那里,空手道俱乐部的管理者和教练之间产生了冲突。任务是预测空手道俱乐部每个成员选择冲突的哪一方。网络如下所示。每个节点代表空手道俱乐部的一个成员,成员之间的链接表示他们在俱乐部外部进行交互。管理员和教练分别用A和I标记。
我在MXNet中实现了谱卷积,这是一个易于使用和高效的深度学习框架。具体实施如下:
class SpectralRule(HybridBlock):
def __init__(self,
A, in_units, out_units,
activation, **kwargs):
super().__init__(**kwargs)
I = nd.eye(*A.shape)
A_hat = A.copy() + I
D = nd.sum(A_hat, axis=0)
D_inv = D**-0.5
D_inv = nd.diag(D_inv)
A_hat = D_inv * A_hat * D_inv
self.in_units, self.out_units = in_units, out_units
with self.name_scope():
self.A_hat = self.params.get_constant('A_hat', A_hat)
self.W = self.params.get(
'W', shape=(self.in_units, self.out_units)
)
if activation == 'ident':
self.activation = lambda X: X
else:
self.activation = Activation(activation)
def hybrid_forward(self, F, X, A_hat, W):
aggregate = F.dot(A_hat, X)
propagate = self.activation(
F.dot(aggregate, W))
return propagate
__init__
将邻接矩阵以及卷积层里每个节点的特征表示的输入维度和输出维度作为输入,分别为 A \pmb A AAA,in_units
和out_units
。通过与单位矩阵 I \pmb I III相加,将自环添加到邻接矩阵 A \pmb A AAA中,计算度矩阵 D \pmb D DDD,并根据谱方法将邻接矩阵 A \pmb A AAA变换为A_hat
。这种变换不是严格必要的,但在计算效率上更高,否则变换将在这层的每个前向运算上都执行一次。
最后,在__init__
中的with
中,我们存储了两个模型参数——A_hat
被存储为常数,并且将权重矩阵W
作为可训练参数存储。
hybrid_forward
是魔术发生的地方。在前向传递中,我们用下面的输入执行这个方法:x
,前一层的输出,以及我们在构造函数__init__
中定义的参数A_hat
和W
。
我们现在已经实现了谱方法,可以将这些层堆叠在一起。我们使用与前一篇文章相似的两层架构,其中第一个隐藏层有4个单元,第二个隐藏层有2个单元。这种体系结构使可视化的二维嵌入变得容易。它与前一篇文章中的体系结构有三种不同之处:
最后,我们在GCN的最上面添加一个逻辑回归层以进行节点分类。
上述架构的Python实现如下。
def build_model(A, X):
model = HybridSequential()
with model.name_scope():
features = build_features(A, X)
model.add(features)
classifier = LogisticRegressor()
model.add(classifier)
model.initialize(Uniform(1))
return model, features
我将包含卷积层的网络的特征学习部分分离为features
分量,并将分类部分分成classifier
组件。单独的features
组件使得以后更容易可视化这些层的激活。LogisticRegressor
是一个分类层,它通过对最后一个图卷积层提供的每个节点的特征求和,并在这个和上应用sigmoid函数来执行逻辑回归。
您可以在附带的Jupyter笔记本中找到构建features
组件的代码和LogisticRegressor
代码。
训练GCN模型的代码如下所示。简而言之,我初始化了一个二进制交叉熵损失函数,cross_entropy
,和一个SGD优化器,trainer
来学习网络参数。然后根据指定的迭代次数来训练模型,其中计算每个训练实例的损失loss
,并且用loss.backward()
反向传播误差。然后,调用trainer.step
来更新模型参数。在每次遍历之后,由GCN层构建的特征表示被存储在feature_representations
列表中,我们将立即检查。
def train(model, features, X, X_train, y_train, epochs):
cross_entropy = SigmoidBinaryCrossEntropyLoss(from_sigmoid=True)
trainer = Trainer(
model.collect_params(), 'sgd',
{'learning_rate': 0.001, 'momentum': 1})
feature_representations = [features(X).asnumpy()]
for e in range(1, epochs + 1):
for i, x in enumerate(X_train):
y = array(y_train)[i]
with autograd.record():
pred = model(X)[x] # Get prediction for sample x
loss = cross_entropy(pred, y)
loss.backward()
trainer.step(1)
feature_representations.append(features(X).asnumpy())
return feature_representations
关键的是,只有讲师和管理员的标签被标记,网络中剩余的节点是已知的,但没有标记!在图卷积过程中,GCN可以找到标记节点和未标记节点的表示,并且可以在训练过程中利用这两个信息源来执行半监督学习。
具体地说,半监督学习在GCN中进行,因为它通过聚合节点的有标记和无标记邻居来产生节点的潜在特征表示。在训练期间,我们反向传播有监督的二进制交叉熵损失来更新所有节点之间共享的权重。然而,这种损失依赖于标记节点的潜在特征表示,而这些特征表示又依赖于标记节点和未标记节点。这样学习就变成了半监督的。
上面提到,每次迭代中特征表示都被存储下来了,这使得我们可以看到在训练过程中这些特征表示是如何变化的。下面我考虑了两种输入特征表示。
在第一个表示中,我们仅使用稀疏的34×34的单位矩阵 I \ I I作为特征矩阵 X \ X X,即图中每个节点的独热编码。这种表示形式的优点是可以在任何图中使用,但是会导致网络中每个节点都有一个输入参数,这需要大量的内存和计算能力才能在大型网络上进行训练,并且可能导致过拟合。值得庆幸的是,空手道俱乐部网络很小。使用此表示,我将网络训练5000遍。
通过对网络中的所有节点进行统一分类,我们可以得出上面所示网络中的错误分布。在此,黑色表示分类错误。尽管将近一半(41%)的节点被错误分类,但是与管理员或教练(但不是同时存在!)紧密联系的节点往往被正确分类。
在上面,我已经说明了特征表示在训练过程中如何变化。这些节点最初紧密地聚集在一起,但是随着训练的进行,教练和管理员被拉开了,并拖着一些节点移动。
尽管为管理员和教练提供了完全不同的表示形式,但是他们拖曳的节点不一定属于他们的社区。这是因为图卷积将在特征空间中紧密共享邻居的节点嵌入在一起,但是共享邻居的两个节点可能不是同时连接到管理员和教练。特别地,使用单位矩阵作为特征矩阵导致每个节点的高度局部表示,即属于图的相同区域的节点可能紧密地嵌入在一起。这使得网络很难以归纳的方式在距离较远的地区之间共享共同的知识。
我们将通过添加两个独立于网络的任何节点或区域的功能来改善表示1,这些功能可以衡量其他人员与管理员和教练的连接程度。 为此,我们计算了从网络中每个节点到管理员和教练的最短路径距离,并将这两个功能加入到先前的表示形式中。
也许有人会认为这有点作弊,因为我们加入了有关图中每个节点位置的全局信息。features
组件中的图卷积层应该(理想地)捕获这些信息。然而,图卷积层始终是从局部的角度(获取信息),获此类全局信息的能力有限。 尽管如此,它还是了解GCN的有用工具。
和以前一样,我们对网络中的所有节点进行了分类,并绘制了上面显示的网络中错误的分布图。 这次,只有四个节点被错误分类; 这是对表示1的重大改进! 在仔细检查特征矩阵后,这些节点要么与教练和管理员等距(在最短路径上),要么更接近管理员,但属于教练社区。 采用表示2,GCN被训练了250遍。
如上图所示,这些节点最初非常紧密地聚集在一起,但是在训练开始之前就被分成了一些社区! 随着训练的进行,社区之间的距离越来越大。
在这篇文章中,我对GCN中的聚合方式进行了深入的说明,并展示了如何使用均值法,总和法和谱方法将其表示为加权总和。我真诚希望,当你在构建自己的图卷积网络中考虑使用什么样的加权方法时,发现我给出的框架很有用。
我还展示了如何在MXNet中实施和训练GCN,以Zachary的空手道俱乐部作为简单示例网络,对其进行了半监督分类。我们看到了仅使用两个标记节点,GCN仍然有可能在表示空间中实现两个社区之间的高度隔离。
尽管有更多关于图卷积网络的知识,我希望将来有时间与您分享,但这是(目前)该系列的最后一篇文章。如果您有兴趣进一步阅读,我想以以下论文作为结尾,这些论文我觉得很有趣:
喜欢您阅读的内容吗? 考虑在Twitter上关注我,在那里我会分享一些除了我自己的帖子之外的一些我感兴趣的与数据科学和机器学习的实践、理论和道德相关的论文、视频和文章。
对于专业查询,请通过LinkedIn或通过Twitter上的直接消息与我联系。
[1] Paper called Semi-Supervised Classification with Graph Convolutional Networks by Thomas Kipf and Max Welling.
该系列其他文章
图卷积网络详细介绍(一)
图卷积网络详细介绍(二)