图上的机器学习是一项艰巨的任务,由于高度复杂但信息量丰富,本文是关于如何利用图卷积网络(GCN)进行深度学习的系列文章中的第一篇,这是一种强大的神经网络,直接在图上工作并利用其结构信息。该文章分为上下两部分:
- 图卷积网络的高级介绍(本)
- 具有谱图卷积的半监督学习
在本文中,我将介绍GCN,并说明如何使用编码示例通过GCN的隐藏层传播信息。我们将看到GCN如何聚合来自先前层的信息以及该机制如何在图中生成节点的有用特征表示。
什么是图卷积网络?
GCNs是一种非常强大的用于图形机器学习的神经网络结构。即使是随机启动的2层GCN也可以生成网络中节点的有用特征表示。下图显示了GCN如何产生网络中每个节点的二维表示,这种节点表示在不经过任何训练的情况下保留了它们在网络中的相对距离。
图卷积网络(GCN)是一种对图形进行操作的神经网络。给定图G =(E,V),GCN作为输入
- 输入特征矩阵N×F⁰特征矩阵X,其中N是节点数,F⁰是每个节点的输入特征数,一个N×N矩阵表示的图结构,例如G 的邻接矩阵A。
隐藏层的GCN因此可以写成Hⁱ = f (Hⁱ⁻¹,A) ,其中H ⁰= X和F是一个传播。每一层Hⁱ对应于N × F ⁱ特征矩阵,其中每行是一个节点的特征表示。在每一层,使用传播规则f聚合这些特征以形成下一层的特征。通过这种方式,特征在每个连续层变得越来越抽象。在这个框架中,GCN的变体仅在传播规则f 的选择上有所不同。
一个简单的传播规则
最简单的传播规则之一是:
f(Hⁱ,A)=σ(AHⁱWⁱ)
其中Wⁱ是层i的权重矩阵,σ是非线性激活函数,例如ReLU函数。权重矩阵的尺寸为F ⁱ ×Fⁱ ⁺ ¹ ; 权重矩阵的第二维度的大小决定了下一层的特征的数量。如果您熟悉卷积神经网络,那么这个操作就类似于过滤操作,因为这些权重在图中的节点之间是共享的。
简化
让我们从最简单的层次来研究传播规则。
- i = 1,t检验是输入特征矩阵的函数,
- σ是恒等函数,
- 选择权重使得AH⁰W⁰ =AXW⁰ = AX。
或者是,f(X,A)= AX。这个传播规则可能有点过于简单,但我们稍后会添加缺少的部分。另外,AX现在相当于多层感知器的输入层。
一个简单的图形示例
举个简单的例子,我们将使用下图:
以下是其numpy邻接矩阵表示。
A = np.matrix([ [0, 1, 0, 0], [0, 0, 1, 1], [0, 1, 0, 0], [1, 0, 1, 0]], dtype=float)
接下来,我们需要特征!我们根据每个节点的索引为其生成2个整数特征。这可以让我们更容易地确认矩阵计算。
In [3]: X = np.matrix([ [i, -i] for i in range(A.shape[0]) ], dtype=float) XOut[3]: matrix([ [ 0., 0.], [ 1., -1.], [ 2., -2.], [ 3., -3.] ])
应用传播规则
现在有一个图形,它的邻接矩阵A和一组输入特征X。当我们应用传播规则时会发生什么:
In [6]: A * XOut[6]: matrix([ [ 1., -1.], [ 5., -5.], [ 1., -1.], [ 2., -2.]]
每个节点(每行)的表示现在是其邻居特征的总和。换句话说,图卷积层将每个节点表示为其邻域的集合。如果存在从v到n的边,则节点n是节点v的邻居。
您可能已经发现了问题:
- 节点的聚合表示不包含其自身的特征。表示是邻居节点的特征的集合,因此只有具有自循环的节点才会在聚合中包含它们自己的特征(除非节点具有自循环)。
- 具有较大度数的节点将在其特征表示中具有较大值,而具有较小度数的节点将具有较小值。这可能导致梯度消失或爆炸,但对于随机梯度下降算法也是有问题的,这种算法通常用于训练这类网络并且对每个输入特征的比例(或值的范围)敏感。
添加自循环
为了解决第一个问题,可以简单地为每个节点添加一个自循环。在实践中,这是通过在应用传播规则之前将单位矩阵I添加到邻接矩阵A来完成的。
In [4]: I = np.matrix(np.eye(A.shape[0])) IOut[4]: matrix([ [1., 0., 0., 0.], [0., 1., 0., 0.], [0., 0., 1., 0.], [0., 0., 0., 1.] ])In [8]: A_hat = A + I A_hat * XOut[8]: matrix([ [ 1., -1.], [ 6., -6.], [ 3., -3.], [ 5., -5.]])
由于节点现在是其自身的邻居,因此在总结其邻居的特征时包括节点自己的特征。
规范化特征表示
通过将邻接矩阵A乘以逆矩阵D来变换邻接矩阵,可以通过节点度对特征表示进行归一化。因此,我们的简化传播规则如下所示:
f(X, A) = D⁻¹AX
让我们看看发生了什么。我们首先计算度矩阵(degree matrix)。
In [9]: D = np.array(np.sum(A, axis=0))[0] D = np.matrix(np.diag(D)) DOut[9]: matrix([ [1., 0., 0., 0.], [0., 2., 0., 0.], [0., 0., 2., 0.], [0., 0., 0., 1.] ])
在应用规则之前,让我们先看看在变换后邻接矩阵会发生什么。
变换之前
A = np.matrix([ [0, 1, 0, 0], [0, 0, 1, 1], [0, 1, 0, 0], [1, 0, 1, 0]], dtype=float)
变换之后
In [10]: D**-1 * AOut[10]: matrix([ [0. , 1. , 0. , 0. ], [0. , 0. , 0.5, 0.5], [0. , 0.5, 0. , 0. ], [0.5, 0. , 0.5, 0. ]])
观察到邻接矩阵的每一行中的权重(值)都除以对应于该行对应的节点的度。我们对变换后的邻接矩阵应用传播规则:
In [11]: D**-1 * A * XOut[11]: matrix([ [ 1. , -1. ], [ 2.5, -2.5], [ 0.5, -0.5], [ 2. , -2. ] ])
并得到与相邻节点的特征均值相对应的节点表示。这是因为(变换后的)邻接矩阵中的权重对应于相邻节点的特征的加权和中的权重。
综合起来
我们现在结合自循环和归一化技巧。我们将重新引入我们之前放弃的权重值和激活函数。
增加权重
首先要做的是应用权重。注意,这里D_hat是度矩阵A_hat = A + I,即A具有强制自循环的度矩阵。
In [45]: W = np.matrix([ [1, -1], [-1, 1] ]) D_hat**-1 * A_hat * X * WOut[45]: matrix([ [ 1., -1.], [ 4., -4.], [ 2., -2.], [ 5., -5.] ])
如果我们想要减小输出特征表示的维数,我们可以减小权矩阵W的大小:
In [46]: W = np.matrix([ [1], [-1] ]) D_hat**-1 * A_hat * X * WOut[46]: matrix([[1.], [4.], [2.], [5.]])
增加激活函数
我们选择保留特征表示的维数并应用ReLU激活函数。
In [51]: W = np.matrix([ [1, -1], [-1, 1] ]) relu(D_hat**-1 * A_hat * X * W)Out[51]: matrix([[1., 0.], [4., 0.], [2., 0.], [5., 0.]])
一个完整的隐藏层,具有邻接矩阵,输入特征,权重值和激活函数。
回到现实
最后,我们可以在真实图形上应用图卷积网络。
Zachary的空手道俱乐部(Zachary’s Karate Club)
Zachary的空手道俱乐部是一个着名的大学空手道俱乐部社交网络,由Wayne W. Zachary在“小群体中的冲突和裂变的信息流模型”一文中描述
Zachary’s Karate Club是一个常用的社交网络,其中节点代表俱乐部的成员和边缘的相互关系。当Zachary在研究空手道俱乐部时,管理员和教练之间出现了冲突,导致俱乐部分裂成两部分。下图显示了网络的图形表示,并根据俱乐部的哪个部分标记节点。管理员“A”和教练“I”。
构建GCN
现在让我们构建图卷积网络。实际上我们不会去训练网络,而只是随机初始化它以生成我们在本文开头看到的特征表示。我们将使用networkx,它有一个很容易得到的俱乐部的图形表示,并计算A_hat和D_hat矩阵。
from networkx import to_numpy_matrixzkc = karate_club_graph()order = sorted(list(zkc.nodes()))A = to_numpy_matrix(zkc, nodelist=order)I = np.eye(zkc.number_of_nodes())A_hat = A + ID_hat = np.array(np.sum(A_hat, axis=0))[0]D_hat = np.matrix(np.diag(D_hat))
接下来,我们将随机初始化权重。
W_1 = np.random.normal( loc=0, scale=1, size=(zkc.number_of_nodes(), 4))W_2 = np.random.normal( loc=0, size=(W_1.shape[1], 2))
堆叠GCN层。我们这里只使用单位矩阵作为特征表示,即每个节点表示为一个独热编码的分类变量。
def gcn_layer(A_hat, D_hat, X, W): return relu(D_hat**-1 * A_hat * X * W)H_1 = gcn_layer(A_hat, D_hat, I, W_1)H_2 = gcn_layer(A_hat, D_hat, H_1, W_2)output = H_2
我们提取特征表示。
feature_representations = { node: np.array(output)[node] for node in zkc.nodes()}
特征表示将Zachary空手道俱乐部中的社区分开。我们还没有开始训练。
对于这个例子,由于ReLU函数的作用,随机初始化的权重很可能在x轴或y轴上给出0个值,因此需要一些随机初始化才能生成上图。
结论
在本文中,我已经对图卷积网络进行了高级介绍,并说明了GCN中每个层节点的特征表示是如何基于其邻域的聚合。我们看到了如何使用numpy构建这些网络以及它们有多强大:即使是随机初始化的GCNs也可以将Zachary空手道俱乐部中的社区分开。
在下一篇文章中,我将进一步了解技术细节,并展示如何使用半监督学习来实施和训练GCN。