目录
模型一:NN4G(Neural Network for Graph)
模型二:DCNN(Diffusion-Convolution Neural Network)
模型三:DGC(Diffusion Graph Convolution)
模型四:MoNET(Mixture Model Networks)
模型五:GAT(Graph Attention Networks)
摘要
GNN可以将graph的结构和图中每个节点和边的特征转化为一般的神经网络的输入,而GNN中的卷积有两种方法,一种是Spatial-based(基于空间的),另一种是Spectral-based(基于谱域的)Spatial-based GNN方法采用类似CNN的卷积得到每个隐藏层的值,再将所有层的值集合成为整个graph的输出,Spectral-based GNN方法借助了信号与系统中的傅里叶变换,定义了一套Spectral Graph Theory,使用特征值等价频率,再构造傅里叶变换与逆傅里叶变换得到图神经网络的卷积输出。
Abstract
What is GNN?
简单来说GNN就是Graph + Nerual Networks,而这里的Graph的粗略定义就是有节点有边的图形,并不是图片而是数据结构中的图,而是一种结构化的数据。关键问题就是将graph的结构和图中每个节点和边的特征转化为一般的神经网络的输入(张量)。
GNN考虑所有的特征并将其转化为神经网络可以输入的向量,就需要用到类似于CNN的Convolution(卷积),而GNN中的卷积有两种方法,一种是Spatial-based(基于空间的),另一种是Spectral-based(基于谱域的)。
Why need GNN?
当我们在做classfication的时候数据以图的形式出现,而这张图非常大,拥有很多的节点,并且节点之间互相影响,那数据集就会非常大,我们需要考虑节点之间的各种联系,因此需要用到GNN。
例如在一个故事中存在很多个角色,每个角色都有对应的特征,例如姓名职业等,要从这些角色中找到一个杀人凶手,寻找的这个过程可以看成是一个classfication的问题,训练一个分类器,将一个角色(即该角色的各个特征)放入训练出来的classifier,判断该角色是否是杀人凶手。
但是问题是角色之间存在一张庞大的关系网,两两角色之间都存在一定的关系,而这些关系在做classfication的时候可以得到额外的信息,帮助我们做更好的model。因此需要考虑全部的关系,这就必须要用到graph neural network。
Spatial-based GNN
Spatial-based GNN需要做空间域的卷积,也就是在空间上的卷积,需要做两步,第一步是aggregate,aggregate类似于CNN中的convolution也就是卷积,CNN 中的卷积核在计算某一个像素点的 feature 的时候,可以看成把这个像素点周围的像素点的特征按照一定的权重加权求和,Spatial-GNN 想要把这种卷积操作直接推广到 Graph 上,即用邻节点的特征更新下一隐藏层的状态,第二步是readout,把所有节点的特征集合起来代表整个graph。
GNN的卷积和readout有很多种方法
模型一:NN4G(Neural Network for Graph)
NN4G是在上一层的数据基础上更新下一层隐藏层的特征,我们可以认为它的计算方法 COMBINE 的就是上层自己的特征经过一个线性变换。
每个隐藏层都是一个图,有若干个节点,每个节点都有一个对应的权值,权值与上一层相联系,某一节点的权值=(上一层该节点的所有相邻节点的权值之和)✖一个待学习的参数+该点本身的值与一个参数矩阵的乘积(是这个节点本来值)。
Readout的计算方法:对每一个隐藏层计算均值,然后分别乘以可训练矩阵后加起来。
通过卷积的过程可以将各种特征作为输入放入全连接网络中,但是全连接网络中的神经元是固定的,因此对于每个输入的图来说,通过卷积之后的输入网络的向量维度都应该是一样。对于每个图NN4G都是使用固定数量的隐藏层对应固定的神经元,然后将每层的均值输入全连接神经网络。
AGGREGATE:求和之后做一个 Transform
COMBINE:求和之后再加上自身做一个 Transform 的结果。
READOUT:先通过平均操作求除每一层的图的表示方式,然后将所有层的图表示加权求和得到最终的图的表示。
模型二:DCNN(Diffusion-Convolution Neural Network)
DCNN的每一层都是以原始的数据进行更新,第K个隐藏层的某一点的值=原图中与这个点距离为K的点的值取平均值✖待学习的参数。(mean(d(3,.)=1)代表距离节点 为1,mean(d(3,.)=2)代表距离节点
为2的所有节点特征的均值)
通过上面的方法求得每一层网络中所有节点,将这些向量组成一个矩阵以后,
代表第 i层的矩阵,假设有 n 个节点,特征维度为 d ,那么这个矩阵就是一个
的矩阵,然后i层,就有一个
的一个张量。
把每个中对应的节点
都串起来,也就是将
张量中的取出长度为
的一行,也就是某一节点的特征,然后
张量就成了一个
的矩阵,然后做一个 Transform (乘以一个参数矩阵
),最后得到这个节点的特征向量
。
最后像NN4G一样将结果放入一个全连接网络,最后得到一个数值。
模型三:DGC(Diffusion Graph Convolution)
与DCNN不同的是将所有层的结果进行相加,即个
的矩阵进行相加,与DCNN的区别是一个结果是得到一张图一个结果是得到一个数值。
模型四:MoNET(Mixture Model Networks)
之前只是简单的相加,并没有考虑到一个节点的邻居跟邻居之间的区别(有的邻居可能更重要一点,每个邻居对该点的影响程度不一样)。
模型五:GAT(Graph Attention Networks)
不止是简单的weighted sum, 不是像之前那样定的weight ,而且要让他自己去学习这个weight。对邻居做Attention,就是不同的邻居给出不同的weight。
Spectral-based GNN
CNN在卷积的时候会确定一个卷积核,这就是一个特定大小的过滤器,每次只需要考虑kernel范围的信息。但是GNN不行,因为每个节点的邻居都不一样,无法像CNN一样用一个九宫格定义图中某一节点的邻居节点。所以要把图在空间的结构进行思想转换,将输入的图进行傅里叶转换,采用谱域实现类似的过滤器来做卷积,在给定的卷积核再做傅立叶变化,对两次傅立叶变化的结果相乘,再将结果做反的傅立叶变化得到下一层的图。
那如何进行傅里叶变换呢?需要用到信号系统相关知识。
图信号的傅里叶变换
任何一个周期函数都能等价为一系列的正(余)弦函数的和,这就是傅里叶级数。将一个周期函数经过傅里叶变换,也就是将一个时域上的函数表达,转换为其频域和相位的表示。通过对图片的傅里叶变换,可以根据变化后数据的频率特征,提取图片中的特征,如低频率的轮廓特征,高频率的细节特征。最后结果的输出只要将这些数据再做一次傅里叶反变换即可。
拉普拉斯变化是傅里叶变换的一种改进,与傅里叶相比,它能处理信号函数趋向于无穷大及振幅越来越大的情况,它有两种理解方式:一是这种变换引进了衰减因子,使原信号函数趋于平缓稳定;二是其通过增大傅里叶变换的正弦波,从而改变最后输出函数的振幅。
定义
D为度矩阵,它对角线上的值是从 i 节点出发的所有边的权重之和(对角矩阵)。
拉普拉斯矩阵(L)是度矩阵(D)减去邻接矩阵(A),即L = D - A。拉普拉斯矩阵是对称半正定矩阵,因此该矩阵的特征值一定非负,一定有n个线性无关的特征向量,它们是n维空间中的一组标准正交基,组成正交矩阵。
将拉普拉斯矩阵(L)进行特征分解即谱分解,是将矩阵分解为其特征值和特征向量表示的矩阵之积的方法。
,其中
为特征值矩阵,
为特征向量。
在如下的图中,我们把看做是一个信号的属性大小,,它可以是一个向量也可以是一个标量(此文中假定f(0)、f(1)等4个信号都是标量)
上图中每个特征值对应的特征向量
都对应其相应点在该特征值频率下的强度大小。将信号乘以一个拉普拉斯矩阵
,即对其做拉普拉斯变化。下面计算表明,对信号做拉普拉斯变换得到的结果,可以表示某节点与其所有邻域节点的能量差之和。
将图做傅里叶变化后,将矩阵L进行特征分解得到的特征向量看做是一组信号值,可根据上图计算的特征值和特征向量
将原图在频率上表达。
对应的每个特征值就是频率的大小,而每个特征值对应的特征向量的数值就是对应特征值频率下的强度大小(这里暂时不考虑正交的概念)。
GNN模型例子分析
import torch
import torch.nn as nn
from torch_geometric.nn import GCNConv
class GraphConvNet(nn.Module):
def init(self, input_dim, hidden_dim, output_dim):
super(GraphConvNet, self).init()
self.conv1 = GCNConv(input_dim, hidden_dim)
self.conv2 = GCNConv(hidden_dim, output_dim)
def forward(self, x, edge_index):
x = self.conv1(x, edge_index)
x = torch.relu(x)
x = self.conv2(x, edge_index)
return x
input_dim = 5 # Dimensionality of input features
hidden_dim = 64 # Dimensionality of hidden features
output_dim = 32 # Dimensionality of output features
num_nodes = 100 # Number of nodes in the graph
num_edges = 200 # Number of edges in the graph
x = torch.randn(num_nodes, input_dim)
edge_index = torch.randint(num_nodes, (2, num_edges))
model = GraphConvNet(input_dim, hidden_dim, output_dim)
output = model(x, edge_index)
print(“Output shape:”, output.shape)