点击上方“AI公园”,关注公众号,选择加“星标“或“置顶”
导读作者:Tobias Skovgaard Jepsen
编译:ronghuaiyang
这是第二篇,用谱图卷积来做半监督学习。
How to do Deep Learning on Graphs with Graph Convolutional Networks
Part 2: Semi-Supervised Learning with Spectral Graph Convolutions
Tobias Skovgaard Jepsen
https://towardsdatascience.com/how-to-do-deep-learning-on-graphs-with-graph-convolutional-networks-62acf5b143d0
图的机器学习是一项非常困难的任务,因为图的结构非常复杂,但同时也提供了丰富的信息。本文是关于如何利用图卷积网络(GCNs)对图进行深度学习系列文章的第二篇。GCNs是一种功能强大的神经网络,旨在直接处理图并利用图的结构信息。
在前一篇文章中,我对GCNs进行了高层次的介绍,并展示了如何根据节点的邻居表示更新节点表示。在这篇文章中,我们首先深入了解了在前一篇文章中讨论的相当简单的图卷积期间执行的聚合。然后我们继续最近发布的图卷积传播规则,我展示了如何实现和用它来(semi-supervised学习)对Zachary空手道俱乐部社区进行预测。如下所示,GCN能够学习每个节点的潜在特征表示,这些节点将两个社区分隔为两个具有合理内聚性和分离的集群,尽管每个社区只使用了一个训练样本。
简单回顾
在上一篇文章中,我们用了一个简单的数学框架来表示GCNs的传播。简单来说,给定一个N × F⁰的特征矩阵X,还有一个表示图结构的矩阵,N × N的邻接矩阵A,GCN中的每个隐层可以表示为Hⁱ = f(Hⁱ⁻¹, A)),其中 H⁰ =X ,f 是传播法则。每一层的Hⁱ对应一个N × Fⁱ 的特征矩阵,其中每一行表示一个节点。
传播规则的形式为:
f(Hⁱ,A) = σ(AHⁱWⁱ)
f(Hⁱ,A) = σ(D⁻¹ÂHⁱWⁱ),其中  = A + I, I是单位矩阵, D⁻¹是 Â的度矩阵的逆矩阵。
使用这些规则来计算一个节点的特征表达,先聚合其邻居节点,再通过一个权值矩阵Wⁱ进行变换,然后通过一个激活函数σ。我们可以通过细化规则1和2来把聚合和转换的步骤更加明确,比如f(Hⁱ, A) = transform(aggregate(A,Hⁱ),Wⁱ),其中对于规则1,transform(M, Wⁱ) = σ(MWⁱ),aggregate(A,Hⁱ) = AHⁱ ,对于规则2, aggregate(A,Hⁱ) =D⁻¹Â Hⁱ 。
正如我们在前一篇文章中所讨论的,规则1中的聚合将节点表示为其邻居特征表示的和,这有两个明显的缺点:
节点的聚合表示不包含其本身的特征
度大的节点在特征表示上会有较大的值,度小的节点会有较小的值,这可能会导致梯度爆炸的问题,使得使用对特征尺度敏感的随机梯度下降等算法进行训练更加困难。
为了解决这两个问题,规则2首先通过向A添加单位矩阵来强制自循环,然后使用转换后的邻接矩阵A = A+I进行聚合。接下来,是归一化特征表示,与度矩阵的逆D⁻¹相乘,把聚合转换为均值,使得聚合节点具有特征不变性。
在下文中,我将把规则1称为求和规则,把规则2称为平均规则。
谱图卷积
Kipf和Welling最近的一篇文章提出了使用频谱传播规则快速近似谱图卷积:
与前一篇文章中讨论的求和规则和均值规则相比,谱规则只在聚合函数的选择上有所不同。虽然它在某种程度上类似于均值规则,因为它使用度矩阵D取负幂对聚合进行标准化,但标准化是不对称的。我们来试一下。
把加权和作为聚合
到目前为止,我们可以将我所介绍的聚合函数理解为加权和,其中每个聚合规则只在权重的选择上有所不同。我们将首先看到如何用加权和来表示相对简单的和和规则,然后再看谱规则。
要查看如何使用Sum规则计算第i个节点的聚合特征表示,我们将看到聚合中的第i行是如何计算得到的。
如式1a所示,我们可以将第i个节点的聚合特征表示为向量矩阵乘积。我们可以将这个向量矩阵乘积表示为一个简单的加权和,如公式1b所示,其中我们对X中的N行求和。
式1b中第j个节点在聚合中的贡献由A 的第i行的第j列的值决定。由于A是一个邻接矩阵,如果第j个节点是第i个节点的邻居,则该值为1,否则为0。因此,方程1b对应于对第i个节点的邻居的特征表示进行求和。
综上所述,每个邻居的贡献完全依赖于由邻接矩阵A定义的邻域。
要查看平均规则如何聚合节点表示,我们再次看到如何使用平均规则计算聚合中的第i行。为了简单起见,我们只考虑“原始”邻接矩阵上的均值规则,而不考虑A与单位矩阵I之间的加法,它只对应于向图中添加自循环。
从上面的方程式可以看出,推导过程稍微长了一些。在方程2a中,我们首先对邻接矩阵A进行变换,将其与度矩阵D的倒数相乘。这个计算在式2b中更加明确。逆度矩阵是一个对角矩阵,其中沿对角线的值是节点度的相反数。因此,我们可以去掉产生方程2c的一个求和符号。方程2c可进一步简化为方程2d和2e。
如公式2e所示,我们现在再次对邻接矩阵A中的N行求和。正如在讨论和规则时提到的,这对应于对每个第i个节点的邻居求和。然而,方程2e中加权和的权重保证了第i个节点的和为1。因此,方程2e对应于第i个节点邻居特征表示的均值。
求和规则只依赖于邻接矩阵A定义的邻域,而均值规则也依赖于节点度数。
我们现在有一个有用的框架来分析谱规则。看看会指引我们走向哪里。
与均值规则一样,我们使用度矩阵D对邻接矩阵A进行变换。然而,如式3a所示,我们将度矩阵取-0.5的幂,并在A的两边乘以它。这个操作可以分解如下式3b所示。再次回顾,度矩阵(及其幂)是对角的。因此,我们可以进一步简化方程3b,直到得到方程3d中的表达式。
方程3e显示了一些很有趣的东西。在计算第i个节点的聚合特征表示时,不仅要考虑第i个节点的程度,还要考虑第j个节点的度。
与均值规则相似,谱规则对聚合进行标准化。聚合特征表示与输入特征大致保持相同的尺度。然而,在加权和中,谱规则对相邻的权重在低度时较高,在高度时较低。当低度邻居比高度邻居提供更多有用的信息时,这可能是有用的。
使用GCNs来进行半监督分类
除了谱规则,Kipf和Welling还演示了GCNs如何用于半监督分类。在半监督学习中,我们希望同时使用带标签和未带标签的示例。到目前为止,我们已经隐式地假设整个图是可用的。换句话说,我们知道所有的节点,但不知道所有的节点标签。
在我们所见过的所有规则中,我们对节点邻居进行聚合,因此共享邻居的节点往往具有类似的特征表示。如果图形显示同质性,则此属性非常有用,即,有连接的节点趋于相似(例如具有相同的标签)。同质性存在于许多真实的网络中,特别是社交网络表现出很强的同质性。
我们看到在前面的文章,在同构的节点上使用图结构,即使GCN随机初始化就可以实现很好的分离的特征表示。我们可以更进一步,在标记节点上训练GCN,通过更新所有节点共享的权重矩阵,有效地将节点标记信息传播给未标记节点。这可以做到如下:
通过GCN执行正向传播。
在GCN的最后一层按行应用sigmoid函数。
计算已知节点标签上的交叉熵损失。
反向传播损耗并更新每一层的权值矩阵W。
在Zachary空手道俱乐部上进行社群预测
让我们看看谱规则如何使用半监督学习将节点标记信息传播到未标记节点。和前一篇文章一样,我们将以Zachary的空手道俱乐部为例。
Zachary空手道俱乐部
简单地说,Zachary 's karate_club是一个小型的社交网络,其中管理员和空手道俱乐部的教练之间会发生冲突。任务是预测空手道俱乐部的每个成员选择冲突的哪一方。网络的图形表示如下所示。每个节点表示空手道俱乐部的一个成员,成员之间的链接表示他们在俱乐部之外进行交互。管理员和教练分别用A和I标记。
在MXNet上做谱图卷积
我在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,输入和输出维度 in_units
, out_units
作为输入,输出每个节点的特征表示。A中加上了单位矩阵,构成了自循环,计算度矩阵D,然后将邻接矩阵A转换为A_hat,这是谱规则中指定的。变换的过程并不是必须的。
最后,我们存了两个模型参数— A_hat
是一个常量,权值矩阵 W
是可训练的参数。
hybrid_forward
就是最神奇的地方。在前向中,我们调用这个方法,这个方法的输入为: X
, 这是前一层的输出,参数 A_hat
和 W
,这两个是在 __init__
中定义的。
构建图卷积网络
现在我们已经实现了谱规则,我们可以将这些层叠加在一起。我们使用了一个类似于前一篇文章的两层架构,其中第一个隐藏层有4个单元,第二个隐藏层有2个单元。这种体系结构使生成的二维嵌入很容易可视化。它与前一篇文章的架构有三个不同之处:
我们使用谱规则而不是平均规则。
我们使用不同的激活函数:第一层使用tanh激活函数,否则死神经元的概率会很高,第二层使用恒等函数,因为我们使用最后一层对节点进行分类。
最后,我们在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
我将网络中包含图卷积层的特征学习部分分解为“特征”组件,将分类部分分解为“分类器”组件。单独的“功能”组件使以后更容易可视化这些层的激活。 LogisticRegressor
是使用逻辑回归的一个分类层,把每个节点的特征加起来,然后经过一个sigmoid函数。
训练GCN
GCN模型的训练代码如下所示。简而言之,我初始化了一个二进制交叉熵损失函数和一个SGD优化器,来学习网络参数。然后,对模型进行指定数量的训练,其中每个训练示例计算“损失”,并使用“loss. backwards()”反向传播损失。然后调用step更新模型参数。在每个epoch之后,由GCN层构造的特征表示将存储在“feature_representation”列表中,稍后我们将查看这个列表。
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中,它通过聚合节点的标记邻居和未标记邻居来生成节点的潜在特征表示。在训练过程中,我们将监督的二进制交叉熵损失反向传播,以更新所有节点共享的权值。然而,这种损失依赖于标记节点的潜在特征表示,而这些潜在特征表示又依赖于标记节点和未标记节点。这样,学习就变成了半监督的。
特征可视化
如上所述,存储每个epoch的特征表示,这使我们能够看到在训练期间特征表示是如何变化的。在下面,我考虑两个输入特征表示。
表示 1
在第一个表示方法中,我们简单地使用稀疏的34×34单位矩阵,I作为特征矩阵“X”,即, 对图中每个节点进行一次热编码。这种表示方法的优点是它可以用于任何图,但是会导致网络中每个节点的输入参数,这需要大量内存和计算能力,以便在大型网络上进行训练,并可能导致过度拟合。幸运的是,空手道俱乐部的网络非常小。使用这种表示,网络被训练了5000个epochs。
通过对网络中所有节点进行分类,得到了误差在网络中的分布情况,如上图所示。这里,黑色表示分类错误。尽管将近一半(41%)的节点是错误分类的,但是与管理员或教练(但不是两者都)紧密连接的节点往往是正确分类的。
在左边,我已经说明了特征表示在训练期间是如何变化的。这些节点最初紧密地聚集在一起,但是随着训练的进行,教练和管理员被分开,拖着一些节点。
虽然管理员和教练得到了完全不同的表示,但是他们拖拽的节点不一定属于他们的社区。这是因为图卷积嵌入了在特征空间中紧密共享邻居的节点,但是共享邻居的两个节点可能不平等地连接到管理员和教练。特别是使用单位矩阵作为特征矩阵,使得每个节点具有高度的局部表示,即,属于图中相同区域的节点很可能紧密地嵌入在一起。这使得网络很难以归纳的方式在遥远的地区之间共享共同的知识。
表示 2
我们将通过添加两个特征来改进表示法1,这两个特征不是特定于网络的任何节点或区域,而是度量与管理员和教练之间的连接程度。为此,我们计算从网络中的每个节点到管理员和教练的最短路径距离,并将这两个特征连接到前面的表示形式。
有些人可能会认为这有点欺骗,因为我们注入了关于图中每个节点位置的全局信息,(理想情况下)这应该由“特征”组件中的图形卷积层捕获的信息。然而,图卷积层总是有一个局部的视角,并且捕捉这些信息的能力有限。尽管如此,它仍然是理解GCNs的一个有用工具。
与之前一样,我们对网络中的所有节点进行了分类,并绘制了错误在网络中的分布情况,如上图所示。这一次,只有四个节点被错误分类,与表示法1相比,这是一个显著的改进!在对特征矩阵进行更仔细的检查之后,这些节点要么与教练和管理员之间的距离相等(在最短路径的意义上),要么更接近管理员,但属于教练社区。GCN使用表示2为250个epochs进行训练。
如左图所示,这些节点最初再次非常紧密地聚集在一起,但是在训练开始之前就已经在一定程度上被划分为多个社区!随着训练的进展,社区之间的距离也在增加。
总结
在这篇文章中,我深入地解释了GCNs中的聚合是如何执行的,并以平均值、求和以及谱规则为例,展示了如何将其表示为加权和。我真诚地希望,你会发现这个框架有助于考虑在你自己的图卷积网络中进行聚合时可能需要哪些权重。
我还展示了如何在MXNet中实现和训练一个GCN,以Zachary的空手道俱乐部为简单示例网络,使用频谱图卷积对图进行半监督分类。我们看到仅仅使用两个标记节点,GCN仍然有可能在表示空间中实现两个网络社区之间的高度分离。
![640?wx_fmt=png](https://i-blog.csdnimg.cn/blog_migrate/f26db2441c55d286cc8f35e567ed66f7.png)
英文原文:https://towardsdatascience.com/how-to-do-deep-learning-on-graphs-with-graph-convolutional-networks-62acf5b143d0
请长按或扫描二维码关注本公众号
喜欢的话,请给我个好看吧!