基于DGL库图神经网络教程(1)——基本的建图操作

最近接触了图神经网络,感觉这个玩意可以提供多粒度视角。在对研究对象进行抽象后,既可以对节点进行分类,也可以对边进行分类,还可以对图整体进行分类。 图神经网络这种结构就提供一种多粒度描述特定对象的能力。想想还是蛮不错的啊。

所以就搞搞图神经网络。
目前来看图神经网络的理论学习路线有:

  1. 图论基本知识。这个很熟,不用学。
  2. 线性代数,尤其是傅里叶变换的矩阵形式。这个只要回去看看书就行,我看了图卷积网络的理论基础,其实就是傅里叶变换矩阵形式的应用,这个在矩阵分析课已经很常见了,不算什么新知识点。但是要接触图神经网络的话,这一部分需要仔细掌握!
  3. 神经网络相关知识。

大概过了一遍《深入浅出图神经网络》,感觉差不多,还是实操为主。于是写写实践方面的东西。

图神经网络库

本人用的库是DGL :https://docs.dgl.ai/index.html
github地址:https://github.com/dmlc/dgl
这个库好像是纽约大学写的。它里面实现了目前常见的图神经网络模型,直接搭积木一样构造网络,然后构造好数据就可以了。
基于DGL库图神经网络教程(1)——基本的建图操作_第1张图片

安装DGL库

无cuda安装:

pip3 install dgl -i https://mirrors.aliyun.com/pypi/simple/

cuda10安装:

python3 -m pip install dgl-cu100 -i https://mirrors.aliyun.com/pypi/simple/

DGL默认使用pytorch作为底层神经网络库

教程

加节点、加边、及可视化图

DGL里面的边都是有向边,对于无向边,可以同时建立两边反向边。

__author__ = 'dk'
#构建图,添加节点和边
import networkx as nx
import  dgl
import matplotlib.pyplot as plt
#构建星型图
u=[0,0,0,0,0]
v=[1,2,3,4,5]
#第一种方式,u和v的数组,他们是相同的长度
star1 = dgl.DGLGraph((u,v))
nx.draw(star1.to_networkx(),with_labels=True)#可视化图
plt.show()

star2 = dgl.DGLGraph((0,v))
#对于星型,是可以广播的
nx.draw(star2.to_networkx(),with_labels=True)
plt.show()

star3= dgl.DGLGraph([(0,1),(0,2),(0,3),(0,4),(0,5)])
#直接枚举
nx.draw(star3.to_networkx(),with_labels=True)
plt.show()

也可以后续加边,而不是在构造函数里面加边:

#也可以边构图,边加边,而不是在构造函数里面加边
g = dgl.DGLGraph()#这是一张空白图
g.add_nodes(9)#添加节点,注意一定要先加节点,后加边
for i in range(1,8):
    g.add_edge(0,i)
nx.draw(g.to_networkx(),with_labels=True)
plt.show()

注意! 添加边(u,v)的时候,u和v不能超过网络已经拥有的最大节点ID(节点数减1),对于多余的没有出入度的节点,DGL认为他们是孤立的。
比如上图:8号节点是没有变跟它连接的。
基于DGL库图神经网络教程(1)——基本的建图操作_第2张图片

节点特征的赋值、提取

建立好图后,就可以给节点加特征了。
在DGL里面,节点的特征是被当做字典来处理的。用户可以给节点的特征取一个有表征意义的key_name,同时用户也可以给一个node同时定义多个特征。
比如上面的星型模型:

import numpy as np
features = np.random.normal(0,1,(9,5)) #随机生成一个9x5的正态分布矩阵
print(features)
g.ndata['features'] = features

输出:

[[-0.73241917  0.78738566  1.21160063 -0.83944648 -0.15739715]
 [-0.05520377  0.83418124 -0.68477259 -1.29054549 -1.2375015 ]
 [-0.23807425 -0.40030208  1.74318389 -0.70699831 -0.61449034]
 [-0.48597221  0.65284435 -0.27101916 -0.69242791 -0.83134013]
 [-0.00580359  1.29773141  1.28545031 -0.41703836  0.97254182]
 [-1.19173936  1.18409306 -0.24284504 -1.93560515 -1.1080128 ]
 [-0.4854841   0.06257814 -1.3432515  -0.53297016 -0.01270537]
 [-0.16906863  0.17349874  1.0057332   1.85554737  0.13355367]
 [-1.45619866  0.77784642  1.52454762 -0.86608947  0.28595569]]

ndata属性就是node-data的缩写。它是一个dict。
注意features的行数需要和图目前拥有的节点数一致。
否则报错:

dgl._ffi.base.DGLError: Expect number of features to match number of nodes (len(u)). Got 7 and 9 instead.

访问节点特征:
直接g.ndata[特征名][节点ID] 就可以:
例如:访问节点3的特征

print(g.ndata['features'][3])

输出:就是features矩阵的第三行

tensor([-0.4860,  0.6528, -0.2710, -0.6924, -0.8313], dtype=torch.float64)

当然可以修改某个节点的特征:

g.ndata['features'][3]=th.zeros(1,5)
print(g.ndata['features'][3])

输出:

tensor([0., 0., 0., 0., 0.], dtype=torch.float64)

可以看出,DGL内部用一个张量来存储节点特征。节点特征的修改、访问最后落实到对这个矩阵的修改、访问。

print(g.ndata)

输出:

{'features': tensor([[-0.4771,  1.7900, -1.1160,  0.2916, -0.7986],
        [-1.6190, -0.5006, -0.0437,  1.6412, -1.6979],
        [ 1.8872,  0.5236,  0.5123, -0.7658, -0.5050],
        [ 0.0000,  0.0000,  0.0000,  0.0000,  0.0000],
        [-0.3382, -0.4218,  0.8622,  1.1720,  0.3460],
        [-0.1710,  0.2713, -0.1639, -1.1159, -0.3623],
        [-0.9241,  1.2943,  0.1137,  1.5348,  0.1791],
        [-1.0372,  1.4145, -2.0653, -0.1469, -0.6041],
        [ 0.1035, -1.4215,  0.3288, -0.5195, -1.4120]], dtype=torch.float64)}

边特征的赋值和访问、修改

与节点特征一样,边也可以指定特征以及访问、修改特征
赋值:

g.edata['w']=th.randn(len(g.edges),2)

注意,赋值矩阵的行函数需要等于边的数目
访问某条边的特征,最终需要落实到访问边矩阵里面的某一行。而到底是那一行,则是边的ID。
因此需要确定,我们访问的边的ID是多少。
比如:获取(0,7)这条边的ID:

g.edge_id(0,7)

访问这条边的特征:

g.edata(g.edge_id(0,7))

然后也可以删除特征:这其实就是字典的操作

g.ndata.pop('features')
g.edata.pop('w')

你可能感兴趣的:(图神经网络)