图神经网络(GNN)算法
此笔记以我自己容易理解的方式重新整理了原笔记,主要用于自己的学习。
初始化参数 w;
重复:
利用梯度下降法更新参数w;
利用更新后的参数w 重新对所有节点的状态进行更新;
直到 (停止标准);
返回 w.
初始化
重复:
(为状态转换函数,这里的包含三种类型信息:节点的特征向量,与节点相连边的特征向量以及与节点相连节点的特征向量。)
直到 :
(收敛条件)
返回:(返回最终时刻所有节点的状态向量)
补充:
循环GNN通过不断交换邻域信息来更新节点状态,直到达到稳定均衡。节点的状态向量由以下函数来进行周期性更新:。其中:
:节点n的特征向量
:与n相连的边的特征向量
:节点n相邻节点t时刻的状态向量
:节点n相邻节点的特征向量.
这里的只是形式化的定义,不同的GNN有不同的定义,如随机稳态嵌入(SSE)中是义如下:
。
由更新公式可知,当所有节点的状态都趋于稳定状态时,此时所有节点的状态向量都包含了其邻居节点和相连边的信息。这与图嵌入有些类似:如果是节点嵌入,我们最终得到的是一个节点的向量表示,而这些向量是根据随机游走序列得到的,随机游走序列中又包括了节点的邻居信息, 因此节点的向量表示中包含了连接信息。
(forward我们得到了节点状态向量,我们需要用这些节点向量做一些应用,比如预测。因此我们需要一个输出函数来对节点状态进行变换,得到我们想要的输出,比如某一类别,某一具体的数值等)
有了输出后,就能算出损失,然后利用损失反向传播算出梯度,最后再利用梯度下降法对神经网络的参数(此处参数为w)进行更新。所有节点的损失可以定义为:。我们定义:
因此我们可以继续计算:
初始化
重复:
直到: (达到收敛)
返回:
以上GNN算法存在着不少的问题,比如对不动点隐藏状态的更新比较低效。图卷积神经网络ConvGNN分为两大类:频域方法(spectral-based method )和空间域方法(spatial-based method)。2009年,Micheli在继承了来自RecGNN(循环GNN)的消息传递思想的同时,在架构上复合非递归层,首次解决了图的相互依赖问题。在过去的几年里还开发了许多替代GNN,包括GAE和STGNN。这些学习框架可以建立在RecGNN、ConvGNN或其他用于图形建模的神经架构上。
A Gentle Introduction to Graph Neural Networks
上学期看了这篇博客论文,当初学习到很多(入门级),当时还看了b站一个博主的讲解,我一般会把要读的论文打印出来再做笔记,现在又来重温一遍,打算整理一下各方面的理解形成一个较为系统的学习。看到了一个CSDN很优秀的博主,他的博客讲的很棒都是自己的理解,所以我也会借鉴,我写笔记主要目的就是用于自己的学习,记笔记是一个很好的习惯,眼过千遍不如手过一遍嘛,整理的时候可以发现很多只读的时候不一样的细节。
原文来自网站:Distill(里面有很多高质量博客,当初研究生论文指导老师推荐过这个,他当时还推荐了arxiv这个网站,我一直在用,超级方便)。
原文链接:A Gentle Introduction to Graph Neural Networks (distill.pub)
在CNN实战中,我们利用PIL包的Image来处理图像数据:
def Myloader(path):
return Image.open(path).convert('RGB')
读入彩色图的时候,读出的是一个二维矩阵,矩阵中每个元素(像素)有RGB三个值。因此,我们通常将图像视为具有图像通道的矩形网格,每个像素代表一个节点,并与周围的像素点相连(8个):
图三要素:点、边、图(子图,整个图)。可用邻接矩阵、邻接列表来表示一个图。这部分内容大家可以去原文体验交互式图,会有更深的理解(B站李沐有讲过,很容易理解)
Graph Neural Networks
图的描述是排列不变的矩阵格式,我们将描述使用图神经网络 (GNN) 来解决图预测任务。GNN 是对图的所有属性(节点、边、全局上下文)的可优化转换,它保留了图的对称性(排列不变性)。GNN 采用 "图入图出 "架构,即这些模型类型接受一个图作为输入,并将信息加载到其节点、边和全局上下文中,然后逐步转换这些嵌入,而不改变输入图的连接性。既:我们初始给定了节点或者边或者全局的属性,GNN将对这些属性进行变换,但是这种变换不会影响节点之间的连接性,变换优化后的图依旧保持着原图的连接结构。
最简单的 GNN
对于顶点状态向量、边状态向量还有全局的状态向量,我们分别构造一个输入大小等于输出大小的多层感知机(MLP,类似于forward里面的状态转换函数。MLP后,我们就得到了更新后的状态向量。三个MLP组成了GNN的一层,经过GNN的一层后,原图的节点、边以及全局的状态向量都被更新过,但整个图的结构并没有发生变化。
Pooling
在Backward中,我们需要一个转换函数(上图)对状态向量进行输出,比如做预测。上图,我们做顶点预测。如果一个是2分类问题,我们将经过最后一层后输出的节点状态向量加一个输出为2的全连接层(上图),再加一个softmax就可以得到我们的输出。如果为n分类,我们加一个输出为n的全连接层。这里所有节点都是共用一个全连接层,也就是所有节点共享同一个全连接层的参数。
但,当我们不知道节点向量表示,但知道该节点有关的边、全局向量,我们就需要pooling.
如果我们没有右上角黑色那个节点的向量表示,此时我们就可以把与该节点相连的四条边的状态向量以及全局状态向量相加,得到这个节点的状态向量,然后再经过全连接层进行预测。其他情况类似。
具体来讲,上面描述的GNN可以通过下图概括:
我们将原始graph通过一个个GNN层(每一层都有三个MLP,分别对三种状态进行转换),然后,无论是顶点、边还是全局,都通过同一个全连接层进行输出预测。
局限性
我们在GNN层对节点或者边进行更新时,每层内所有节点共用一个MLP,所有边共用一个MLP,此时我们并没有考虑连接信息,也就是说我们在对节点更新时没有考虑与该节点相连的其余节点或者边,更新边时没有考虑与该边相连的节点。简单来说,我们在更新时没有将图的结构信息考虑进去。
消息传递
消息传递:相邻节点或边缘会交换信息,并影响彼此的更新嵌入。
如上图,同右上角实心点,我们在更新每一个节点的向量时,并不只是简单地将该节点的向量通过一个MLP后得到更新后的向量,而是还要考虑与该节点相连节点的向量,也就是在下述更新公式中:,我们要考虑。
这让人想起标准的卷积:从本质上讲,信息传递和卷积都是汇总和处理元素邻域信息以更新元素值的操作。在图中,元素是一个节点,而在图像中,元素是一个像素。然而,图中相邻节点的数量可以是可变的,这与图像中每个像素都有固定数量的相邻元素不同。
以上GCN 架构示意图,该架构通过汇集相邻节点的一个度数距离(1近邻,距离为1,可理解为相连)来更新图形的节点表示。
我们的数据集并不总是包含所有类型的信息(节点、边和全图)。当我们想对节点进行预测,但我们的数据集只有边信息时,上面我们使用pooling将信息从边汇聚到节点,但仅限于模型的最后预测步骤。我们可以使用消息传递在 GNN 层内的节点和边之间共享信息:
图中:将每个边连接的两个顶点的信息加到边自己的向量里面,假设维度不一致,则做进一步的投影,投影到同样的维度然后加进去。同样,对于某一个节点的更新,我们也可以将与该节点相连的边的向量加入到该节点中,然后再对该节点进行更新。
我们可以先把边的信息传递给顶点,顶点更新后,再将更新后的顶点信息传递给边,边再更新(上图左),或者相反(上图右)。
还有一种办法是交替传递:我们可以同时进行两种操作:将边的信息给节点,然后节点的信息也给边。此时的节点和边都包含了各自的信息,然后再进行一次传递,将二者的信息互相传递,随后再用两个MLP对节点和边进行更新。
迄今为止,我们所描述的网络存在一个缺陷:图中相距较远的节点可能永远无法有效地相互传递信息,即使我们多次应用消息传递也是如此。对于一个节点,如果我们有 k 层,信息最多只能传播 k 步。如果预测任务依赖于相距甚远的节点或节点组,这就会成为一个问题。一种解决方案是让所有节点都能相互传递信息。遗憾的是,对于大型图来说,这种方法的计算成本很快就会变得很高(尽管这种被称为 "虚拟边 "的方法已被用于分子等小型图)。
全局表示
一种解决办法是加入虚拟点(主节点、上下文向量)。我们假设它与图中所有边和顶点均相连。因此在进行顶点或者边的更新时,如果我们加上全局表示U,就能保证所有顶点(边)间都能传递信息。