图神经网络【1】学习笔记

学习GCN不错的英文参考资料,原链接
图神经网络【1】学习笔记_第1张图片

Overview

许多现实世界的重要数据集都以图形或网络的形式出现:社交网络、知识图、蛋白质交互网络、万维网等等。然而,直到最近,很少有人关注神经网络模型泛化到这样的结构化数据集。
在过去的几年里,一些论文重新探讨了将神经网络推广到任意结构图上的问题(Bruna et al., ICLR 2014; Henaff et al., 2015; Duvenaud et al., NIPS 2015; Li et al., ICLR 2016; Defferrard et al., NIPS 2016; Kipf & Welling, ICLR 2017),他们中的一些人现在在一些领域取得了非常有希望的结果,这些领域以前是由基于内核的方法、基于图形的正则化技术等主导的。
在这篇文章中,我将简要概述这一领域的最新发展,并指出各种方法的优缺点。这里的讨论主要集中在最近的两篇论文:

  • Kipf & Welling (ICLR 2017), Semi-Supervised Classification with Graph Convolutional Networks (disclaimer: I’m the first author)
  • Defferrard et al. (NIPS 2016), Convolutional Neural Networks on Graphs with Fast Localized Spectral Filtering

Outline

  • 简要介绍图上的神经网络模型
  • 谱图卷积和图卷积网络(GCNs)
  • Demo:图形嵌入内部嵌入简单的一阶GCN模型
  • GCNs作为weisfeler - lehman算法的可微泛化
    如果你已经熟悉GCNs和相关的方法,你可能想要直接跳到嵌入空手道俱乐部网络9 Embedding the karate club network).

GCN有什么作用

最近的文献

推广一个类似RNNs或CNNs这样的成熟的神经网络模型来处理任意结构的图是一个具有挑战性的问题。最近的一些论文介绍了具体问题的专用体系结构(e.g. Duvenaud et al., NIPS 2015; Li et al., ICLR 2016; Jain et al., CVPR 2016), 其他人利用谱图理论中已知的图卷积(Bruna et al., ICLR 2014; Henaff et al., 2015) 定义多层神经网络模型中使用的参数化过滤器,类似于我们熟悉经典的CNNs.
最近的工作重点是弥补快速启发法和缓慢但更有原则的谱方法之间的差距。Defferrard等人(NIPS 2016)使用Chebyshev多项式在光谱域近似平滑滤波器,使用在类神经网络模型中学习的自由参数。他们在常规领域(如MNIST)取得了令人信服的结果,接近于简单的2D-CNN模型。

GCN Part Ⅰ: 定义

目前,大多数图神经网络模型都有一个通用的体系结构。我将把这些模型称为Graph Convolutional Networks (GCNs);卷积,因为过滤器参数通常在图中的所有位置共享(或其子集,如Duvenaud et al.,NIPS 2015)。

对于这些模型,目标是图 G = ( V , E ) G=(V,E) G=(V,E)上学习信号/特征在的函数,该函数作为输入:

  • x i x_i xi是对于每一个节点 i i i的特征的描述, N × D N×D N×D的特征矩阵 X X X( N N N:点的数量; D D D:输入特征的数量)
  • 矩阵形式的图结构的典型描述;通常以邻接矩阵 A A A(或其某个函数)的形式出现

并产生节点水平的输出 Z Z Z ( N × F N×F N×F特征矩阵,其中 F F F为每个节点的输出特征数)。图水平的输出可以通过引入某种形式的池化pooling操作来建模(参见Duvenaud et al,NIPS 2015).

每个神经网络的layer可以写成一个非线性函数              
             
H ( l + 1 ) = f ( H ( l ) , A ) H^{(l+1)} =f(H^{(l)},A) H(l+1)=f(H(l),A)
H ( 0 ) = X H^{(0)}=X H(0)=X H ( L ) = Z H^{(L)}=Z H(L)=Z ( z z z图水平的输出)、 L L L表示layers的数量。具体的模型根据不同的 f ( ⋅ , ⋅ ) f(·,·) f(,)选择和参数化。

GCNs Part Ⅱ:简单的例子

举一个例子,考虑非常简单的分层传播规则:

f ( H l , A ) = σ ( A H ( l ) W ( l ) ) f(H^{l}, A)= σ(AH^{(l)}W^{(l)}) f(Hl,A)=σ(AH(l)W(l))

W ( l ) W^{(l)} W(l)表示第 l l l个layer的权重矩阵, σ ( ⋅ ) σ(·) σ()是非线性激活函数比如ReLu。尽管这个模型很简单,但它已经非常强大了(我们稍后将讨论这个问题)。
但首先,尝试解决这个简单模型的两个局限性:与A相乘意味着,对于每个节点,我们对所有相邻节点的所有特征向量求和,但不包括节点本身(除非图中存在自循环)。我们可以通过在图中执行自循环来“修复”这个问题:我们只需将单位矩阵添加到 A A A
第二个主要的局限性是 A A A通常是没有经过标准化处理,因此与 A A A的乘法会完全改变特征向量的比例(我们可以通过观察 A A A的特征值来理解)。标准化处理 A A A,所有的r行(rows)和为1,例如 D − 1 A D^{-1}A D1A D D D是一个对角节点矩阵(diagonal node degree matrix),这样就解决的这个问题。与 D − 1 A D^{-1}A D1A相乘相当于对相邻节点的特征进行平均。实际上,当我们使用对称标准化时,动态会变得更有趣,例如. D − 1 2 A D − 1 2 D^{- \frac{1}{2}}AD^{- \frac{1}{2}} D21AD21,因为这不再是相邻节点的平均值。结合这两个技巧,我们基本上得出了在Kipf & Welling中引入的传播规则(ICLR 2017):
                          
f ( H ( l ) , A ) = σ ( D ^ − 1 2 A ^ D ^ − 1 2 H ( l ) W ( l ) ) f(H^{(l)},A) = σ(\hat{D}^{-\frac{1}{2}}\hat{A}\hat{D}^{-\frac{1}{2}}H^{(l)}W{(l)}) f(H(l),A)=σ(D^21A^D^21H(l)W(l)),
A ^ = A + I \hat{A}=A+I A^=A+I的时候, I I I表示单位矩阵, D ^ \hat{D} D^ A ^ \hat{A} A^的对角节点度矩阵

在下一节中,我们将进一步了解这种模型是如何在一个非常简单的示例图上运行的:Zachary’s karate club network (点击查看 Wikipedia的文章)

GCN Part Ⅲ:Embedding the karate club network(嵌入的空手道俱乐部)

图神经网络【1】学习笔记_第2张图片
空手道俱乐部图,颜色表示通过基于模块聚类得到的社区 (Brandes et al., 2008).

让我们来看看我们的简单GCN模型(参见前一节或Kipf & Welling, ICLR 2017)是如何在一个著名的图形数据集Zachary的空手道俱乐部网络上工作的(参见上图)。

我们构造一个3层的GCN,并随机初始化权值。现在,在训练权值之前,我们只需将图的邻接矩阵 X = I X=I X=I(即 I I I为单位矩阵,因为我们没有任何节点特征)插入到模型中。3层的GCN现在在前向传播中执行三个传播步骤,并有效地对每个节点的3阶邻域进行卷积(所有节点到3“跳”远)。值得注意的是,该模型生成了这些节点的嵌入,它们非常类似于图中的社区结构(参见下图)。请记住,我们已经完全随机初始化了权值,并且还没有执行任何训练更新(到目前为止)!

图神经网络【1】学习笔记_第3张图片
GCN embedding (with random weights) for nodes in the karate club network.

这似乎有些令人惊讶。最近一篇关于DeepWalk模型的论文(Perozzi et al., KDD 2014)表明,他们可以在复杂的无监督训练过程中学习类似的嵌入。如何能够可能得到这样的嵌入或多或少“免费”使用我们简单的未经训练的GCN模型?

通过将GCN模型解释为众所周知的图上的Weisfeler - Lehman算法的广义可微版本,我们可以对此有所了解。1维Weisfeiler-Lehman算法流程如下:
对于所有的节点 v i ∈ G v_i∈G viG

  • 获得邻接节点的 v j {v_j} vj的特征集合 h v j {}h_{v_j} hvj
  • 更新节点的特征 h v i ← h a s h ( Σ j h v j ) h_{v_i}←hash(Σ_jh_{v_j}) hvihash(Σjhvj) h a s h ( ⋅ ) hash(·) hash()为u单映射哈希函数
    重复 k k k步直到收敛

原算法表示如下

图神经网络【1】学习笔记_第4张图片

实际上,Weisfeler - Lehman算法为大多数图分配了一组独特的特征。这意味着每个节点都被分配了一个唯一描述其在图中的角色的特性。**例外情况是高度规则的图形,如网格、链等。**对于大多数不规则图,这种特征分配可以用来检查图的同构性(即两个图是否相同,直到节点的排列)。

回到我们的Graph Convolutional layer-wise传递规则的向量形式:

h v i ( l + 1 ) = σ ( ∑ j 1 c i j h v j ( l ) W ( l ) ) h^{(l+1)}_{v_i}=σ(∑_j \frac{1}{c_{ij}}h^{(l)}_{v_j}W^{(l)}) hvi(l+1)=σ(jcij1hvj(l)W(l)),
j j j是节点 v i v_i vi的相邻节点的索引, c i j c_{ij} cij是边 ( v i , v j ) (v_i,v_j) (vi,vj)的normalization constant,通过使用我们GCN模型中的对称标准化邻接矩阵 D − 1 2 A D − 1 2 D^{- \frac{1}{2}}AD^{- \frac{1}{2}} D21AD21。这个传播规则可以解释为在Weisfeler - Lehman算法中使用的哈希函数的可微分和参数化变体( W ( l ) W^{(l)} W(l))。如果选择适当的非线性并初始化随机权重矩阵,使其正交(e.g,初始化使用来自Glorot & Bengio, AISTATS 2010),这个更新规则在实际中就会变得稳定(由于 c i j c_{ij} cij的标准化)。经过观察,得到了meaningful smooth embeddings,我们可以将距离解释为局部图结构的(去)相似性!

GCNs Part Ⅳ:半监督学习

由于我们的模型中的所有东西都是可微的和参数化的,我们可以添加一些标签,训练模型并观察嵌入的反应。我们可以使用Kipf & Welling (ICLR 2017)中提到的GCNs半监督学习算法。我们简单地为每个类/社区标记一个节点(在下面的视频中突出显示的节点),并开始进行多个iterations训练

GCNs的半监督分类:潜在的动态空间进行300个训练迭代,每个类一个标签。标记的节点已经突出显示。
注意到,模型直接产生了一个潜在的二维空间,通过可视化。可以发现3层的GCN模型能够线性地分离社团,每个类只给出一个标记的示例。考虑到模型没有收到节点的特性描述的原因,这是以一个显著的结果。同时,可以提供初始节点特征,正是 (Kipf & Welling, ICLR 2017)中描述的实验中所做的,以实现对多个图数据集的最新分类结果。

Code

import torch
import torch.nn as nn
import torch.nn.functional as F

import networkx as nx

def normalize(A, symmetric=True):
    # 添加单位矩阵
    A = A + torch.eye(A.size(0))
    # 统计所有节点的度
    d = A.sum(1)
    if symmetric:
        #  D^-1/2
        D = torch.diag(torch.pow(d, -0.5))
        #  D^(-1/2)AD^(-1/2)
        # 执行矩阵相乘
        return D.mm(A).mm(D)
    else:
        # D = D^-1
        D = torch.diag(torch.pow(d, -1))
        return D.mm(A)


class GCN(nn.Module):
    def __init__(self, A, dim_in, dim_out):
        super(GCN, self).__init__()
        self.A = A
        self.fc1 = nn.Linear(dim_in, dim_in, bias=False)
        self.fc2 = nn.Linear(dim_in, dim_in//2, bias=False)
        self.fc3 = nn.Linear(dim_in//2, dim_out, bias=False)

    def forward(self,X):
        X = F.relu(self.fc1(self.A.mm(X)))
        X = F.relu(self.fc2(self.A.mm(X)))
        return self.fc3(self.A.mm(X))

# 获得空手道数据
G = nx.karate_club_graph()
A = nx.adjacency_matrix(G).todense()
# A进行Normalize
A_normed = normalize(torch.FloatTensor(A), True)

N = len(A)
X_dim = N
# 没有节点的特征, 简单用一个单位矩阵表示所有节点
X = torch.eye(N, X_dim)
Y = torch.zeros(N, 1).long()
Y_mask = torch.zeros(N, 1, dtype=torch.uint8)
# 一个分类一个样本
Y[0][0] = 0
Y[N-1][0] = 1
# 有样本的地方设置为1
Y_mask[0][0] = 1
Y_mask[N-1][0] = 1

# 真实的空手道俱乐部的分类数据
Real = torch.zeros(34, dtype=torch.long)
for i in [1,2,3,4,5,6,7,8,11,12,13,14,17,18,20,22]:
    Real[i-1] = 0
for i in [9,10,15,16,19,21,23,24,25,26,27,28,29,30,31,32,33,34]:
    Real[i-1] = 1

gcn = GCN(A_normed, X_dim, 2)
# 选择Adam分类器
gd = torch.optim.Adam(gcn.parameters())

for i in range(300):
    y_pred = F.softmax(gcn(X), dim=1)
    loss = (-y_pred.log().gather(1, Y.view(-1, 1)))
    loss = loss.masked_select(Y_mask).mean()
    gd.zero_grad()
    loss.backward()
    gd.step()

    if i % 20 == 0:
        mi = y_pred.max(1)
        print(mi)
        print((mi == Real).float().mean())

你可能感兴趣的:(#,深度学习)