图上的机器学习是一项艰巨的任务,由于高度复杂但信息量丰富,本文是关于如何利用图卷积网络(GCN)进行深度学习的系列文章中的第二篇。我将简要回顾一下上一篇文章:
- 图形卷积网络的高级介绍
- 具有谱图卷积的半监督学习(本文)
简要回顾一下
在上一篇关于GCNs的文章中,我们看到了在GCNs中表达传播的一个简单的数学框架。总之,给定一个N×F⁰特征矩阵X和一个图的矩阵表示结构,例如,N × N邻接矩阵A的G,在GCN每个隐藏层可以表示为Hⁱ = f(Hⁱ⁻¹, A) ,其中ħ ⁰= X和˚F是传播规则。在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)=变换(聚合(A,Hⁱ),Wⁱ),我们可以使聚合和变换步骤更加明确,其中变换(M,Wⁱ)= σ(MWⁱ)和规则1和聚合(A,Hⁱ)= AHⁱ集合体(A,Hⁱ)= D⁻¹Â Hⁱ 对于规则2。
正如我们在上一篇文章中所讨论的,规则1中的聚合将节点表示为其邻居特征表示的和,其具有两个显着缺点:
- 节点的聚合表示不包含其自身的特征
- 具有较大度的节点将在其特征表示中具有较大值,而具有较小度的节点将具有较小值,这使得可能导致梯度爆炸问题的出现并且使用对特征缩放敏感的随机梯度下降算法进行训练变得更加困难。
为了解决这两个问题,规则2首先通过向A 添加单位矩阵来强制执行自循环,并使用变换后的邻接矩阵Â= A + I进行聚合。接下来,通过乘以逆度矩阵D⁻¹来对特征表示进行归一化,将聚合转换为聚合特征表示的比例对节点度不变的均值。
下面我将把规则1称为求和规则,将规则2称为均值规则。
谱图卷积
Kipf和Welling最近的一篇论文提出了使用光谱传播规则进行快速近似谱图卷积:
与前一篇文章中讨论的总和均值规则相比,谱规则仅在聚合函数的选择上有所不同。虽然它有点类似于均值规则,因为它使用负幂的度矩阵D对聚合进行归一化,但归一化是不对称的。
作为加权和的聚合
到目前为止,我们可以理解所呈现的聚合函数作为加权和,其中每个聚合规则仅在它们的权重选择上不同。我们先来看看如何用加权和来表示相对简单的总和和均值规则表示为加权和。
求和规则
为了了解如何使用求和规则计算第i个节点的聚合特征表示,我们看到如何计算聚合中的第i行。
如上面在等式1a中所示,我们可以将第i个节点的聚合特征表示计算为向量矩阵乘积。我们可以把这个向量矩阵乘积写成一个简单的加权和,如公式1b所示,我们对X中的每N行求和。
等式1b中j节点对集合的贡献由A第i行第j列的值决定。由于A是邻接矩阵,如果第j个节点是第i个节点的邻居,则该值为1 ,否则为0。因此,等式1b对应于对第i个节点的邻居的特征表示求和。
总之,每个邻域的贡献仅取决于邻接矩阵A所定义的邻域。
均值规则
为了了解均值规则如何聚合节点表示,我们再次看到如何计算聚合中的第i行,现在使用均值规则。为简单起见,我们只考虑“raw”邻接矩阵的均值规则,而不考虑A和单位矩阵I之间的加法,这相当于向图中添加自循环。
如上面的等式所示,推导现在略长。在等式2a中,我们首先将邻接矩阵A与逆度矩阵D相乘进行变换。在等式2b中更明确地进行该计算。逆度矩阵是对角线矩阵,其中沿对角线的值是节点的逆度,而位置(i, i)的值是第i个节点的逆度,位置(i,i)处的值是第i个节点的逆度。因此,我们可以移除其中一个求和符号,得到等式2c。在等式2d和2e中可以进一步减少等式2c。
如等式2e所示,我们现在再次对邻接矩阵A中的每N行求和。如在求和规则的讨论中所提到的,这对应于对每个第i个节点的邻居求和。但是,等式2e中加权和的权值现在保证与第i个节点的次数之和为1。因此,等式2e对应于第i个节点的邻居的特征表示的均值。
总和规则仅取决于邻接矩阵A定义的邻域,而均值规则也取决于节点度。
谱规则
我们现在有一个有用的框架来分析谱规则。
与均值规则一样,我们使用度矩阵D对邻接矩阵A进行变换。但是,如等式3a所示,我们将度矩阵提高到-0.5的幂并将其乘以A的每一边。该操作可以如等式3b所示进行细分。度矩阵(及其幂)是对角线的。因此,我们可以进一步简化等式3b,直到达到等式3e中的表达式。
等式3e显示了在计算第i个节点的聚合特征表示时,我们不仅要考虑第i个节点的度,还要考虑第j个节点的度。
与平均规则类似,谱规则对聚合进行归一化,聚合特征表示保持与输入特征大致相同的度。然而,谱规则在加权和中邻居的度越高权值越低,邻居的权值度越低权值越高。当低度邻居比高度邻居提供更多信息时可能会更有用。
GCN的半监督分类
除谱规则外,Kipf和Welling还证明了GCNs如何用于半监督分类。到目前为止,我们已经隐式地假设整个图表是可用的,即我们处于转换环境中。换句话说,我们知道所有节点,但不知道所有节点标签。
在我们看到的所有规则中,我们聚合在节点邻域上,因此共享邻居的节点往往具有相似的特征表示。如果图表具有同质性,则此属性非常有用,连接的节点往往是相似的(例如,具有相同的标签)。同质性发生在许多真实网络中,尤其是社交网络表现出很强的同质性。
正如我们在上一篇文章中看到的那样,即使是随机初始化的GCN也可以通过使用图结构,在同质图中实现节点的特征表示之间的良好分离。通过在标记节点上训练GCN,我们可以更进一步,有效地将节点标签信息传播到未标记的节点。这可以通过以下方式完成:
- 通过GCN执行前向传播。
- 在GCN的最后一层逐行应用sigmoid函数。
- 计算已知节点标签上的交叉熵损失。
- 反向传播损失并更新每层中的权重矩阵W.
Zachary空手道俱乐部的社区预测
让我们看看谱规则如何使用半监督学习将节点标签信息传播到未标记的节点。正如在以前的文章中,我们将使用Zachary空手道俱乐部作为一个例子。
Zachary空手道俱乐部
简而言之,Zachary空手道俱乐部是一个小型的社交网络,管理员和空手道俱乐部的教练之间会发生冲突。任务是预测空手道俱乐部的每个成员选择的冲突的哪一方。网络的图形表示可以在下面看到。每个节点代表空手道俱乐部的成员,并且成员之间的连接指示他们在俱乐部外进行交互。管理员和教练分别标有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分别为输入和输出维数。通过与单位矩阵I相加,将邻接矩阵A加入自循环,计算度矩阵D,和邻接矩阵变换A到A_hat由谱规则的规定。这种变换不是严格必需的,但是计算效率更高,因为在层的每次向前传递过程中都会执行转换。
最后,在__init__的with子句中,我们存储了两个模型参数——A_hat存储为常量,权矩阵W存储为可训练参数。
hybrid_forward是个奇迹发生的地方。在向前传递中,我们使用以下输入执行此方法:X,上一层的输出,以及我们在构造函数__init__中定义的参数A_hat和W。
构建图卷积网络
现在我们已经实现了谱规则,我们可以将这些层叠加在一起。我们使用类似于前一篇文章中的两层架构,其中第一个隐藏层有4个单元,第二个隐藏层有2个单元。这种架构可以轻松地显示最终的二维嵌入。它与前一篇文章中的架构有不同之处:
- 我们使用谱规则而不是平均规则。
- 我们使用不同的激活函数:tanh激活函数用于第一层,否则死亡神经元的概率会很高,而第二层使用identity 函数,因为我们使用最后一层来对节点进行分类。
最后,我们在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成分使以后更容易可视化这些层的激活。作为分类器的Logistic回归器是一个分类层,它对最后一个图卷积层提供的每个节点的特征进行求和,并对这个求和应用sigmoid函数进行Logistic回归。
为了完整起见,构造features成分的代码是
def build_features(A, X): hidden_layer_specs = [(4, 'tanh'), (2, 'tanh')] in_units = in_units=X.shape[1] features = HybridSequential() with features.name_scope(): for layer_size, activation_func in hidden_layer_specs: layer = SpectralRule( A, in_units=in_units, out_units=layer_size, activation=activation_func) features.add(layer) in_units = layer_size return features
Logistic回归的代码是
class LogisticRegressor(HybridBlock): def __init__(self, in_units, **kwargs): super().__init__(**kwargs) with self.name_scope(): self.w = self.params.get( 'w', shape=(1, in_units) ) self.b = self.params.get( 'b', shape=(1, 1) ) def hybrid_forward(self, F, X, w, b): # Change shape of b to comply with MXnet addition API b = F.broadcast_axis(b, axis=(0,1), size=(34, 1)) y = F.dot(X, w, transpose_b=True) + b return F.sigmoid(y)
训练GCN
训练GCN模型的代码如下所示。初始化二进制交叉熵损失函数cross_entropy和SGD优化器,训练器来学习网络参数。然后对模型进行指定次数的训练,其中计算每个训练示例的损失,并使用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(): preds = model(X)[x] loss = cross_entropy(preds, y) loss.backward() trainer.step(1) feature_representations.append(features(X).asnumpy()) return feature_representations
关键的是,只标记了教练和管理员的标签,并且网络中剩余的节点是已知的,但没有标记。GCN可以在图卷积过程中找到标记节点和未标记节点的表示形式,并可以在训练过程中利用这两个信息源进行半监督学习。
可视化功能
如上所述,存储每个时期的特征表示,这允许我们在训练期间看到特征表示如何改变。在下面我考虑两个输入特征表示。
表示1
在第一个表示中,我们简单地使用稀疏34×34 的单位矩阵,I,作为特征矩阵X表示。该表示具有可以在任何图中使用的优点,但是导致网络中的每个节点的输入参数,其需要大量的存储器和计算能力用于大型网络上的训练并且可能导致过度拟合。不过,空手道俱乐部网络非常小。使用该表示对网络进行5000个周期的训练。
通过对网络中的所有节点进行集合分类,我们可以获得上面显示的网络中的错误分布。黑色表示错误分类。尽管近一半(41%)的节点被错误分类,但与管理员或教练(但不是两者)紧密相连的节点往往被正确分类。
我已经说明了在训练期间特征表示如何变化。节点最初是紧密集群的,但随着训练的进行,教练和管理员被拉开,用它们拖动一些节点。
虽然管理员和教练的表示方式完全不同,但他们拖动的节点不一定属于他们的社区。这是因为图卷积在特征空间中嵌入了共享邻居的节点,但是共享邻居的两个节点可能无法同等地连接到管理员和教练。具体地,使用单位矩阵作为特征矩阵导致每个节点的高度局部表示,即,属于图的相同区域的节点可能紧密地嵌入在一起。这使得网络难以以归纳方式在远程区域之间共享公共知识。
表示2
我们将通过添加两个不特定于网络的任何节点或区域的特征来改进表示1。为此,我们计算从网络中的每个节点到管理员和教练的最短路径距离,并将这两个特征连接到先前的表示。
可能会认为这有点欺骗,因为我们注入了关于图中每个节点位置的全局信息; 理想情况下由features成分中的图卷积层捕获的信息。但是,图卷积层始终具有局部视角,并且捕获此类信息的能力有限。尽管如此,它仍然是理解GCNs的有用工具。
和前面一样,我们对网络中的所有节点进行分类,并绘制出错误在网络中的分布。这次,只有四个节点被错误分类;相对于表示1的显著改进。仔细检查特征矩阵后,这些节点要么与教练和管理员等距(在最短路径意义上),要么离管理员更近,但属于教练社区。使用表示1对GCN进行250个周期的训练。
如图所示,这些节点在开始时再次非常紧密地聚集在一起,但在训练开始之前,它们在某种程度上分成了不同社区。随着训练的进行,社区之间的距离也会增加。
结论
在本文中,我对如何在GCNs中执行聚合进行了深入的解释,并展示了如何使用平均值、总和和谱规则来将其表示为加权和。希望您会发现,在您自己的图卷积网络中,这个框架可以帮助您考虑在聚合过程中可能需要的权重。
我还展示了如何在MXNet中实现和训练一个GCN,使用Zachary的空手道俱乐部的谱图卷积对图形执行半监督分类,这是一个简单的示例网络。我们看到仅仅使用两个标记的节点,GCN仍然可以在表示空间中实现两个网络社区之间的高度分离。