很羡慕嫉妒周围大佬的学习速度,对于图神经网络的学习到应用到发表论文速度之快,表示跟不上。
这段时间忙着弄老师的不平衡数据分类的问题,但是还是在抽时间看图神经网络的论文以及博文。网络上的图卷积文章已经十分多了,推荐可以先看reference 3,比较简单易懂,然后reference 2,总结的内容蛮详近的, 再去知乎(reference 1)看看大佬们的相关讨论,对于在discrete graph上的梯度,散度的解释可以结合reference 9(quora上的一个问题,给了计算的例子)。本文主要是从代码角度来看GCN的卷积要了解的多深。
图神经网络的开端是从2009年reference 7的论文开始的,以不动点理论为核心,通过结点信息的传播使整张图达到收敛(要进行循环迭代),在其基础上再进行预测。而GCN则可以通过学习优化参数(应用梯度下降算法)提取特征。
现在GCN的卷积方式主要有两种:
1. 空域卷积
2. 频域卷积
空域主要处理图像方面的问题,现在GCN主要使用的是频域卷积。
下述程序来自reference 6,8,使用下述的图进行模拟。
import math import torch from torch.nn.parameter import Parameter from torch import nn class GraphConv(nn.Module): def __init__(self, in_features, out_features, bias=True): """ :param in_features: 输入特征 :param out_features: 输出特征 :param bias: 偏置 """ super(GraphConv, self).__init__() self.in_features = in_features self.out_features = out_features # Parameter用于将参数自动加入到参数列表 self.weight = Parameter(torch.FloatTensor(in_features, out_features)) if bias: self.bias = Parameter(torch.FloatTensor(out_features)) else: self.register_parameter('bias', None) # 为模型添加参数 self.reset_parameters() def reset_parameters(self): stdv = 1. / math.sqrt(self.weight.size(1)) self.weight.data.uniform_(-stdv, stdv) if self.bias is not None: self.bias.data.uniform_(-stdv, stdv) def forward(self, input, adj): support = torch.mm(input, self.weight) # 最新spmm函数是在torch.sparse模块下,但是不能用 # 使用稀疏矩阵乘法, output = torch.spmm(adj, support) if self.bias is not None: return output + self.bias else: return output def __repr__(self): return self.__class__.__name__ + ' (' \ + str(self.in_features) + ' -> ' \ + str(self.out_features) + ')' if __name__ == '__main__': inputs = torch.FloatTensor([[1, 0], [2, 1], [3, 2], [4, 3], [5, 4], [6,5]]) adjs = torch.FloatTensor([ [0, 1, 0, 0, 1, 0], [1, 0, 1, 0, 1, 0], [0, 1, 0, 1, 0, 0], [0, 0, 1, 0, 1, 1], [1, 1, 0, 1, 0, 0], [0, 0, 0, 1, 0, 0] ]) g_conv = GraphConv(2, 10) outputs = g_conv.forward(inputs, adjs) print(outputs)

看到程序的forward函数,我们可以知道传递规则是: \(\)\(O = AIW\)\(\)(其中\(\)\(O\)\(\)为output,\(\)\(I\)\(\)为input),也即更新规则: \(\)\(H_{l+1} = \sigma (A H_l W)\)\(\),但在reference 8论文中写的为:\(\)\(H_{l+1} = \sigma (\hat{D}^{-1/2} \hat{A} \hat{D}^{-1/2} H_l W)\)\(\),大佬似乎还是喜欢使用邻接矩阵变换。
下面是个人写的邻接矩阵的处理,内容参考知乎reference 1中纵横回答
def processAdj(adj, self_loop=True, **kwargs): """ :param adj: 邻接矩阵,type:np.array(float) :param self_loop: boolean :return: adj """ adj = adj.astype(float) opt = {} opt['normal'] = kwargs.get('normal', False) opt['sym_normal'] = kwargs.get('sym_normal', False) if not opt['normal'] and not opt['sym_normal']: return adj if self_loop: adj = adj + np.eye(adj.shape[0]) if opt['sym_normal']: # 对称归一化 degree = np.diag(np.sum(adj, axis=1)) ** -0.5 return np.matmul(np.matmul(degree, adj), degree) if opt['normal']: # 归一化 degree = np.diag(np.sum(adj, axis=1)) ** -1 return np.matmul(degree, adj)
看了一堆的论文文章,对于傅里叶变化那块,还是处于记住的阶段,通过上述的代码,不负责任的说:其实知道传递规则就可以编写gcn(看知乎reference 1中纵横的回答就可以)了,但是要对gcn有更深的理解,则需要对傅里叶转化到拉普拉斯算子到gcn进行公式推导。傅里叶可以看看马同学的文章。
referenece
1. https://www.zhihu.com/question/54504471
2. https://www.cnblogs.com/SivilTaram/p/graph_neural_network_1.html
3. https://zhuanlan.zhihu.com/p/37091549
4. https://mayi1996.top/2019/03/14/%E5%9B%BE%E7%BD%91%E7%BB%9C/
5. https://victorzhou.com/blog/intro-to-cnns-part-1/
6. https://github.com/tkipf/pygcn/blob/master/pygcn/layers.py
7. Scarselli F, Gori M, Tsoi A C, et al. The graph neural network model[J]. IEEE Transactions on Neural Networks, 2008, 20(1): 61-80.
8. Kipf T N, Welling M. Semi-supervised classification with graph convolutional networks[J]. arXiv preprint arXiv:1609.02907, 2016.
9. https://www.quora.com/Whats-the-intuition-behind-a-Laplacian-matrix-Im-not-so-much-interested-in-mathematical-details-or-technical-applications-Im-trying-to-grasp-what-a-laplacian-matrix-actually-represents-and-what-aspects-of-a-graph-it-makes-accessible