**摘要:**深度学习还没学完,怎么图深度学习又来了?别怕,这里有份系统教程,可以将0基础的你直接送到图深度学习。还会定期更新哦。
本系列内容争取每月更新2到4篇。
主要是基于图深度学习的入门内容。讲述最基本的基础知识,其中包括深度学习、数学、图神经网络等相关内容。在实践方面不会涉及太多基础内容。
文章涉及使用到的框架以PyTorch和TensorFlow为主。默认读者已经掌握Python和TensorFlow基础。如有涉及到PyTorch的部分,会顺带介绍相关的入门使用。
本教程主要针对的人群:
已经掌握TensorFlow基础应用,并想系统学习的学者。
PyTorch学习者。
正在从TensorFlow转型到PyTroch的学习者。
已经掌握Python,并开始学习人工智能的学者。
本篇文章将介绍一下与图深度学习有关的框架工具。其中包括TensorFLow、PyTorch以及其上面对应第三方的GNN库。
1 训练模型是怎么一回事
训练模型是指,通过程序的反复迭代来修正神经网络中各个节点的值,从而实现具有一定拟合效果的算法。
1.1 模型内部数据流向
模型的数据流向分为正向和反向:
1.正向
正向,是将输入和各个节点定义的运算串在一起,一直运算到输出,是模型最基本的数据流向。它直观的表现了网络模型的结构,在模型的训练、测试、使用的场景下都会用到。
正向负责预测生成结果,即沿着网络节点的运算方向一层一层地计算下去。
2.反向
反向只有在训练场景下才会用到,这里使用了一个叫做反向链式求导的方法,即,先从正向的最后一个节点开始,计算与真实值的误差,然后对误差所相关的学习参数方程进行每个参数的求导,得到其梯度修正值,同时反推出上一层的误差,这样就将该层节点的误差按照正向的相反方向传到上一层,并接着去计算上一层的修正值,如此反复下去进行一步一步的转播,直到传到正向的第一个节点。
反向负责优化调整模型参数,即用链式求导将误差和梯度从输出节点开始一层一层地传递归去,对每层的参数进行调整。
1.2 训练模型的步骤
训练模型的完整的步骤如下:
(1)通过正向生成一个值,然后计算该值与真实标签之间的误差。
(2)利用反向求导的方式,将误差从网络的最后一层传到前一层。
(3)对前一层中的参数求偏导,并按照偏导结果的方向和大小来调整参数。
(4)通过循环的方式,不停地执行(1)(2)(3)这3步操作。从整个过程中可以看到,步骤(1)的误差越来越小。这表示模型中的参数所需要调整的幅度越来越小,模型的拟合效果越来越好。
在反向的优化过程中,除简单的链式求导外,还可以加入一些其他的算法,使得训练过程更容易收敛。
在深度学习框架中,反向传播的算法已经被封装到具体的函数中,读者只需要明白各种算法的特点即可。使用时,可以根据适用的场景直接调用对应的API,不再需要手动实现。
1.3 模型里的内容及意义
一个标准的模型结构分为输入、中间节点、输出三大部分,而如何让这三个部分联通起来学习规则并可以进行计算,则是框架所做的事情。
无论是TensorFlow还是PyTorch内部都会根据计算节点自己组成一张“计算图”.
构建一个完整的计算图一般需要定义三种变量:
输入节点:即是网络的入口。
用于训练的模型参数(也叫学习参数):是连接各个节点的路径;l模型中的节点:它可以用来代表模型中的中间节点,也可以代表最终的输出节点。它是网络中的真正结构。
图中将这三种变量放在计算图中就组成了神经网络的模型。
2 框架的计算方式
我们知道,在TensorFLow中有两种计算图的运行方式(动态图和静态图),而在PyTorch中,计算图的方式会更为简单,它与Numpy的使用方式几乎一致。
2.1 TensorFLow的静态图方式
“静态图”是TensorFlow 1.x版本中张量流的主要运行方式。其运行机制是将“定义”与“运行”相分离。相当于:先用程序搭建起一个结构(即在内存中构建一个图),让数据(张量流)按照图中的结构顺序进行计算,最终运行出结果。
2.2 TensorFLow的动态图方式
“动态图”(eager)是在TensorFlow 1.3版本之后出现的。到了1.11版本时,它已经变得较完善。在TensorFlow 2.x版本中,它已经变成了默认的工作方式。
动态图主要是在原始的静态图上做了编程模式的优化。它使得使用TensorFlow变得更简单、更直观。
例如,调用函数tf.matmul后,在动态图与静态图中的区别如下:
在动态图中,程序会直接得到两个矩阵相乘的值。
在静态图中,程序只会生成一个OP(操作符)。该OP必须在绘画中使用run方法才能进行真正的计算,并输出结果。
2.3 该使用动态图还是静态图,我需要如何选择?
在TensorFlow1.13之后,框架是支持静态图和动态图两种方式的。动态图更像是为用户提供的上层高级接口,而静态图仍是TensorFLow的底层实现。
1 动态图的方便与不足
在创建动态图的过程中,默认也建立了一个会话(session)。所有的代码都在该会话(session)中进行,而且该会话(session)具有进程相同的生命周期。这表示:当前程序中只能有一个会话(session),并且该会话一直处于打开状态,无法被关闭。
动态图的不足之处是:在动态图中,无法实现多会话(session)操作。
对于习惯了多会话(session)开发模式的用户,需要将静态图中的多会话逻辑转化单会话逻辑后才可以移植到动态图中。
2 静态图开发费力,但兼容性更好
如果要使代码在TensorFlow多版本中有更大的兼容性,优先是选择静态图的。另外,在一些需要对底层操作的功能中(比如构建特殊的op),动态图会显得力不从心。但是,因为静态图更加底层,而使其使用起来更为费劲。这也是很多人吐槽TensorFlow难用的问题。
3 静态图与动态图嵌套开发
为了想使用二者的优点,一般会在程序中使二者互相嵌套使用。例如,在静态图中使用动态图、在动态图中使用静态图。这种模式随不被官方推崇。但它却是TensorFlow使用者的最优选择。
TensorFLow推出动态图的动机是为了使开发变得简单。但是动态图所支持的功能还不够完善,版本间兼容性也没有解决。这使得更加增大了使用的门槛(用户需要学习更多与框架相关的知识)。
2.4 PyTorch框架的计算方式
PyTorch框架使用的计算方式,更为自然。就好像在Python上使用Numpy库一样的简单。这使得其没有太多的学习成本。直接拿来就用即可。
3 框架的张量封装
在神经网络框架中,主要是通过张量的封装来完成计算图上的操作的。下面来看看主流框架中是如何对张量进行封装的。
3.1 TensorFLow的张量封装
张量可以说是TensorFlow的标志,因为整个框架的名称TensorFlow就是张量流的意思。下面来一起全面的认识一下张量。
1.张量介绍
TensorFlow程序使用tensor数据结构来代表所有的数据。计算图中,操作间传递的数据都是tensor。
可以把TensorFlow tensor看作是一个n维的数组或列表。每个tensor包含了类型(type)、阶(rank)和形状(shape)。
2 底层张量流的运行机制
TensorFlow的命名来源于本身的运行原理。Tensor(张量)意味着N维数组,Flow(流)意味着基于数据流图的计算。TensorFlow是张量从图像的一端流动到另一端的计算过程,这也是TensorFlow的编程模型。
TensorFlow的底层运行机制属于“定义”与“运行”相分离。从操作层面可以抽象成两种:模型构建和模型运行。
在模型构建过程中,需要先了解几个概念,见下表。
表中定义的内容都是在一个叫做“图”的容器中完成的。关于“图”,有以下几点需要理解。
一个“图”代表一个计算任务。
在模型运行的环节中,“图”会在绘话(session)里被启动。
session将图的 OP 分发到诸如CPU或GPU之类的设备上, 同时提供执行OP的方法。这些方法执行后,将产生的tensor返回。在Python语言中,返回的tensor是numpy ndarray对象;在C和C++语言中,返回的tensor是TensorFlow::Tensor实例。
session与图的工作关系如图所示。
在实际环境中,这种运行情况会有三种应用场景,训练场景、测试场景与使用场景。在训练场景下图的运行方式与其他两种不同,具体介绍如下:
(1)训练场景:主要是实现模型从无到有的过程,通过对样本的学习训练,调整学习参数,形成最终的模型。其过程是将给定的样本和标签作为输入节点,通过大量的循环迭代,将图中的正向运算得到输出值,再进行反向运算更新模型中的学习参数。最终使模型产生的正向结果最大化的接近样本标签。这样就得到了一个可以拟合样本规律的模型。
(2)测试场景和使用场景:测试场景是利用图的正向运算得到结果比较与真实值的产别;使用场景也是利用图的正向运算得到结果,并直接使用。所以二者的运算过程是一样的。对于该场景下的模型与正常编程用到的函数特别相似。大家知道,在函数中,可以分为:实参、形参、函数体与返回值。同样在模型中,实参就是输入的样本,形参就是占位符,运算过程就相当于函数体,得到的结果相当于返回值。
另外session与图的交互过程中,还定义了两种数据的流向机制:
注入机制(feed):通过占位符向模式中传入数据;
取回机制(fetch):从模式中得到结果。
3.2 PyTorch中的张量封装
在PyTorch中主要起到承载数据,进行计算的作用。张量的处理是被封装到张量类中,通过最底层的Aten运算库进行计算的。Aten库是一个用C++开发的底层运算库,具有非常好的计算性能。
4 PyTorch中的张量操作
本文重点介绍下PyTorch中的张量封装内容。
4.1 定义张量的方法
在PyTorch中定义张量的函数可以分为两种:
函数torch.tensor:相对简单,直接将传入的数值原样转成张量。
函数torch.Tensor:功能更为强大,可以指定数值和形状来定义张量。
1. 函数torch.tensor介绍
函数torch.tensor只支持一个参数,其功能就是将传入的对象转成张量。该函数不仅支持Python中的原生类型,还支持Numpy类型。下面举例:
import torch#引入PyTorch库
import numpy as np#引入Numpy库
a = torch.tensor(5)#定义一个张量5
print(a)#打印该张量,输出:tensor(5)
anp = np.asarray([4])#定义一个Numpy数组
a = torch.tensor(anp)#将Numpy数组转成张量
print(a)#打印该张量,输出:tensor([4], dtype=torch.int32)
2. 函数torch.Tensor介绍
通过使用torch.Tensor函数可以直接定义一个张量。在定义张量时可以指定张量的形状,也可以指定张量的内容。下面举例:
import torch#引入PyTorch库
a = torch.Tensor(2)#定义一个指定形状的张量
print(a) #输出:tensor([1.1210e-43, 4.7265e-01])
b = torch.Tensor(1,2)#定义一个指定形状的张量
print(b) #输出:tensor([[-1.4754e+04, 4.5909e-41]])
c = torch.Tensor([2])#定义一个指定内容的张量
print(c) #输出:tensor([2.])
d = torch.Tensor([1,2])#定义一个指定内容的张量
print(d) #输出:tensor([1., 2.])
上面的例子代码解读如下:
定义张量a时,向torch.Tensor函数中传入了2,指定了张量的形状,系统便生成一个含有2个数的一维数组。
定义张量b时,向torch.Tensor函数中传入了2和3,指定张量形状,系统便生成一个二维数组。
定义张量c、d时,向torch.Tensor函数中传入一个列表。系统直接生成与该列表内容相同的张量。
通过这个例子可以看出:向torch.Tensor中传入数值,可以生成指定形状的张量;向torch.Tensor中传入列表,可以生成指定内容的张量。
3. 张量的判断
在PyTorch中还封装了函数is_tensor,用于判断一个对象是否是张量。具体用法如下
import torch#引入PyTorch库
a = torch.Tensor(2)#定义一个指定形状的张量
print(torch.is_tensor(a))#判断a是否是张量,输出:True
4. 获得张量中元素的个数
可以通过torch.numel函数来获得张量中元素的个数。具体用法如下:
import torch#引入PyTorch库
a = torch.Tensor(2)#定义一个指定形状的张量
print(torch.numel (a))#获得a中元素的个数,输出:2
4.2 张量的类型
PyTorch中的张量也包含了多种类型。每个类型的张量都有单独的定义函数。
1. 张量的默认类型
如果没有特殊要求,直接用函数torch.Tensor所定义的张量是32位浮点型。与调用torch.FloatTensor函数定义张量的效果是一样的。这是由PyTorch中的默认类型来控制的。当然也可以通过修改默认类型来设置torch.Tensor生成的张量类型。代码示例如下:
import torch#引入PyTorch库
print(torch.get_default_dtype())#输出默认类型:torch.float32
print(torch.Tensor([1, 3]).dtype ) #输出torch.Tensor函数返回的类型:torch.float32
torch.set_default_dtype(torch.float64) #将默认的类型修改成torch.float64
print(torch.get_default_dtype())#输出默认类型:torch.float64
print(torch.Tensor([1, 3]).dtype ) #输出torch.Tensor函数返回的类型:torch.float64
2. 默认类型在其它函数中的应用
在PyTorch中还提供了一些固定值的张量函数,方便开发。例如:
使用torch.ones生成指定形状,值为1的张量数组。
使用torch.zeros生成指定形状,值为0的张量数组。
使用torch.ones_like生成指定形状,值为1的张量数组。
使用torch.zeros_like生成指定形状,值为0的张量数组。
使用torch.randn生成指定形状的随机数张量数组。
使用torch.eye生成对角矩阵的张量。
使用torch.full生成全为1的矩阵的张量
这些函数都会根据系统的默认类型来生成生成张量。
4.3 张量的type方法
PyTorch将张量以类的形式封装起来,每一个具体类型的张量都有其自身的若干属性。其中type方法是张量的属性之一,该属性可以实现张量的类型转换。例如:
import torch#引入PyTorch库
a = torch.FloatTensor([4])#定义一个浮点型张量
#使用type方法将其转成int类型
print(a.type(torch.IntTensor)) #输出:tensor([4], dtype=torch.int32)
#使用type方法定义一个Double类型
print(a.type(torch.DoubleTensor)) #输出:tensor([4.], dtype=torch.float64)
数值类的张量还可以直接通过该类特有的属性方法实现更简洁的类型变换。例如上面代码还可以写成如下:
print(a.int())#转为int类型
print(a.double())#转为double类型
PyTorch为每个张量封装了强大的属性方法,不仅仅适用与类型转换,还包括一些常规的计算函数。例如使用mean进行均值计算,使用sqrt进行开平方等。具体代码如下:
print(a.mean())输出:tensor(4.)
print(a.sqrt())输出:tensor([2.])
另外还可以在编译器中,使用系统自带的提示功能找到更多的可用函数。如图4-2所示。
5 张量与Numpy
Numpy是数据科学中用处最广的Python库之一, PyTorch框架对Numpy的支持也非常到位。在PyTorch中,可以实现张量与Numpy类型数据的任意转换。
5.1. 张量与Numpy间的相互转换
下面通过代码来演示张量与Numpy间的相互转换,具体如下:
import torch#引入PyTorch库
import numpy as np#引入Numpy库
a = torch.FloatTensor([4])#定义一个张量
print(a.numpy())#将张量转成Numpy类型的对象。输出:[4.]
anp = np.asarray([4]) #定义一个Numpy类型的对象
#将Numpy类型的对象转成张量
print(torch.from_numpy(anp))#输出:tensor([4], dtype=torch.int32)
print(torch.tensor (anp))#另一种方法实现将Numpy转成张量
张量与Numpy类型数据的转换是基于零复制技术实现的。在转换过程中,PyTorch张量与 Numpy 数组对象共享同一内存区域,PyTorch张量会保留一个指向内部 Numpy 数组的指针,而不是直接复制Numpy的值。
5.2. 张量与Numpy各自的形状获取
张量与Numpy的形状获取方式也非常相似,具体代码如下:
x = torch.rand(2,1) #定义一个张量
print(x.shape)#打印张量形状,输出:torch.Size([2, 1])
print(x.size())#打印张量大小,输出:torch.Size([2, 1])
anp = np.asarray([4,2]) #定义一个Numpy类型的对象
print(anp.shape, anp.size)#打印Numpy变量的形状和大小,输出:(2,) 2
二者也都可以通过reshape属性函数进行变形,接上面代码,具体如下:
print(x.reshape([1,2]).shape)#输出:torch.Size([1, 2])
print(anp.reshape([1,2]).shape)#输出:(1, 2)
5.3 张量与Numpy各自的切片操作
切片处理是Python的基础语法,该方法可以使数组取值变得简单。更详细的原理和用法可以参考《python带我起飞——入门、进阶、商业实战》一书4.4.4和4.4.5小节。
张量与Numpy的切片操作也几乎完全一样,具体代码如下:
x = torch.rand(2,1) #定义一个张量
print(x[:])#输出:tensor([[0.1273],[0.3797]])
anp = np.asarray([4,2]) #定义一个Numpy类型的对象
print(anp[:])#输出:[4 2]
从上面代码中可以看出,通过切片取值时,二者的用法完全一样。
提示:张量和Numpy还支持条件类型的切片,例如:
print(x[x > 0.5]) #输出:tensor([0.5795, 0.9994])
print(anp[anp > 3]) #输出:[4]
5.4 张量与Numpy相互转换间的陷阱
将Numpy转化成张量时,只是简单的指针赋值,并不会发生复制现象。然而,这种快捷的方式却会带来安全隐患:由于两个变量共享一块内存,一旦修改了其中某一个变量,势必会影响到另一个变量的值。
其实PyTorch考虑到了这一点,当Numpy转成张量后,如果对张量进行修改,则其内部会触发复制机制,额外开辟一块内存,并将值复制过去。不会影响到原来Numpy的值。
但是在Numpy转成张量后,如果对Numpy进行修改,那结果就不一样了,因为Numpy并没有PyTorch这种共享内存的设置。这会导致对Numpy修改时,偷偷的使张量的值发生了变化。例如下面代码:
import torch#引入PyTorch库
import numpy as np#引入Numpy库
nparray = np.array([1,1])#定义一个Numpy数组
x = torch.from_numpy(nparray)#将数组转成张量
print(x)#显示张量的值,输出tensor([1, 1], dtype=torch.int32)
nparray+=1#对Numpy进行加1
print(x) #再次显示张量的值,输出tensor([2, 2], dtype=torch.int32)
在上面代码中,没有对张量x进行任何操作,但是从两次的输出来看,张量的值确发生了变化。这种风险会使代码埋藏一个很深的bug,在开发时一定要当心。
在对Numpy进行变化时,如果不使用替换内存的运算操作,则不会遇到这个问题。例如下面代码:
nparray = np.array([1,1])#定义一个Numpy数组
x = torch.from_numpy(nparray)#将数组转成张量
print(x)#显示张量的值,输出tensor([1, 1], dtype=torch.int32)
nparray = nparray+1#对Numpy进行加1
print(x) #再次显示张量的值,输出tensor([1, 1], dtype=torch.int32)
上面代码的写法(nparray = nparray+1),系统会额外复制一份内存将nparray+1的结果赋值给nparray变量。并没有在nparray的原有内存上进行改变。所以张量x的值没有受到影响,并不会发生变化。
提示:虽然Python语言将内存细节放到了幕后。但是对底层深入的了解,才有助于写出更稳定的代码。有关Python内存相关的更多知识,可以参考《python带我起飞——入门、进阶、商业实战》一书第4章的内容。
6 计算图的硬件调度问题——指派GPU
由于人工智能的相关计算大多需要在GPU上处理。硬件的调度的相关操作也是深度学习框架中最常见的操作之一。分配GPU的运算资源是很常见的事情。
6.1 在TensorFlow中指派GPU
在TensorFlow中,分配GPU的运算资源是很常见的事情。大体可以分为3种情况:
为整个程序指定GPU卡。
为整个程序指定所占的GPU显存。
在程序内部调配不同的OP(操作符)到指定GPU卡。
在实现时,可以通过环境变量的CUDA_VISIBLE_DEVICES、构建tf.ConfigProto类、with tf.device语句、分布策略等方式。官方比较推荐的是分布策略,因为它更加智能。
6.2 在PyTorch中指派GPU
PyTorch会默认将张量定义在CPU所控制的内存之上。如果想要使用GPU进行加速运算,有两种方法可以实现,具体如下。
1. 将CPU内存中上的张量转化到GPU内存中
先在CPU上创建张量,再调用该张量的cuda方法进行转化,该方法会将张量重新在GPU所管理的内存中创建。具体代码如下:
import torch#引入PyTorch库
a = torch.FloatTensor([4])#定义一个张量
b = a.cuda()
print(b)#输出:tensor([4.], device='cuda:0')
同样如果要将GPU上的张量创建到CPU上还可以使用cpu方法,例如:
print(b.cpu())#输出:tensor([4.])
2. 直接在GPU内存中定义张量
通过调用函数torch.tensor并指定device参数为cuda,可以直接在GPU控制的内存中定义张量。具体代码如下:
import torch#引入PyTorch库
a = torch.tensor([4],device="cuda")#定义一个张量
print(a)#输出:tensor([4], device='cuda:0')
3. 使用to方法来指定设备
在PyTorch中,将前面张量的cpu和cuda两种方法合并到一起。通过张量的to方法来实现对设备的任意指定。这种方法也是PyTorch中推荐的主要用法。具体代码如下:
import torch#引入PyTorch库
a = torch.FloatTensor([4])#定义一个张量
print(a)#输出:tensor([4.])
print(a.to("cuda:0"))#输出:tensor([4.], device='cuda:0')
在计算机中,多块GPU卡的编号是从0开始的。代码中的"cuda:0"是指使用计算机的第1块GPU卡。
4. 使用环境变量CUDA_VISIBLE_DEVICES来指定设备
使用环境变量CUDA_VISIBLE_DEVICES来为代码指定所运行的设备,是PyTorch中最常见的方式。它可以不用对代码中的各个变量依次设置。只需要在运行python程序时统一设置一次即可。例如,在命令行中,输入如下启动命令:
CUDA_VISIBLE_DEVICES=0 python 自己的代码.py
该命令可以指定“自己的代码.py”在第1块GPU卡上运行。
使用CUDA_VISIBLE_DEVICES时,还支持基于代码的设置。例如,在代码的最前端加入如下语句:
import osos.environ["CUDA_VISIBLE_DEVICES"] = "0"
该语句表示当前代码将在第1块GPU卡上运行。
7 图深度学习的相关工具介绍
图深度学习的实现是在深度学习基础上完成的。本质是依赖于深度学习框架。在深度学习框架之上,实现了针对图神经网络计算的库,将其搭载到原有的深度学习框架上,可以实现更好的性能。
7.1 第三方图计算库介绍
常用的第三方图计算库有DGL、PyG、Spektral和StellarGraph。其中DGL与PyG支持PyTorch框架,Spektral和StellarGraph支持keras语法,可以在TensorFlow框架上使用。下面简单介绍几款:
1、NGra
NGra是由北京大学和微软亚洲研究院开发和维护一款GNN平台。
开始时间:2018
地址: https://arxiv.org/pdf/1810.08403.pdf
2、Graph_nets
Graph_nets是由DeepMind, Google Corp开发和维护的.
开始时间:2018
地址:https://github.com/deepmind/graph_nets
3、PyTorch Geometric
PyTorch Geometric由德国杜特蒙德大学开发和维护的GNN平台。
开始时间:2019
地址:https://github.com/rusty1s/pytorch_geometric
相关论文:https://arxiv.org/abs/1903.02428?context=cs.LG
4、PyTorch-BigGraph(PBG)
PBG是由Facebook人工智能研究开发和维护的GNN平台。
开始时间:2019
地址: https://github.com/facebookresearch/PyTorch-BigGraph
相关论文:https://arxiv.org/abs/1903.12287
5、DGL
DGL库是由纽约大学、亚马逊联手推出的一款图神经网络框架。它不仅全面上线了对异构图的支持,复现并开源了相关异构图神经网络的代码,在GCMC、RCGN等业内知名的模型实现上,也取得了更好的效果。
地址: https://www.dgl.ai/
github地址:https://github.com/jermainewang/dgl
本教程主要介绍DGL库的具体使用。
7.2 DGLGraph图与NetWorkx图的相互转化
DGLGraph类与NetWorkx模块深度绑定,并在其基础之上做了扩展,使其更方便的应用在图计算领域。
1. 将DGLGraph图转成NetWorkx图并显示
将DGLGraph图转成NetWorkx图便可以借助NetWorkx图的显示功能来可视化其内部结构。在为DGLGraph图添加完节点和边之后,可以使用如下代码进行可视化,具体如下:
nx.draw(g_dgl.to_networkx(), with_labels=True)
该代码先调用to_networkx方法,将DGLGraph图转成NetWorkx图,再调用NetWorkx的draw方法进行显示。代码运行后输出的可视化结果如图所示。
2.从NetWorkx图创建DGLGraph图
DGLGraph图还可以从NetWorkx图转化而来。具体代码如下:
g_nx = nx.petersen_graph()#创建一个NetWorkx类型的petersen 无向图
g_dgl = dgl.DGLGraph(g_nx) #将NetWorkx类型的图转化为DGLGraph
plt.figure(figsize=(20, 6))
plt.subplot(121)
plt.title('NetWorkx无向图',fontsize=20)
nx.draw(g_nx, with_labels=True)
plt.subplot(122)plt.title('DGL有向图', fontsize=20)
nx.draw(g_dgl.to_networkx(), with_labels=True) #将DGLGraph转化为NetWorkx类型的图
上面代码中,通过调用dgl.DGLGraph可以将NetWorkx图转化为DGLGraph图,接着又调用了DGLGraph图对象的to_networkx方法,将其转换为NetWorkx图并进行显示。
该代码运行后,输出如下结果如图所示。
图中图节点和边的结构是代码中调用nx.petersen_graph所生成的。该函数在没有参数的情况下,会生成10个节点,并且每个节点与周围3个节点相连,共30条边。
使用DGLGraph对象的local_var方法,可以看到其图中的结构。具体如下:
DGLGraph(num_nodes=10, num_edges=30,
ndata_schemes={}
edata_schemes={})
7.3 了解NetWorkx库
NetWorkx是一个用Python语言开发的图论与复杂网络建模工具,内置了常用的图与复杂网络分析算法,可以方便的进行复杂网络数据分析、仿真建模等工作。
利用NetWorkx可以以标准化和非标准化的数据格式存储网络、生成多种随机网络和经典网络、分析网络结构、建立网络模型、设计新的网络算法、进行网络绘制等。
1. NetWorkx库的安装和使用
因为NetWorkx库默认会集成到anaconda软件中,所以如果已经安装了Anaconda则可以直接使用NetWorkx库。
在使用之前可以使用如下代码查看当前NetWorkx库的版本:
import networkxprint(networkx.__version__)
在NetWorkx库支持四种图结构,具体如下:
Graph:无多重边无向图
DiGraph:无多重边有向图
MultiGraph:有多重边无向图
MultiDiGraph:有多重边有向图
针对每种图结构都有一套对应的操作接口,这些接口可以对图、边、定点进行创建、增加、删除、修改、检索等操作。这些基本操作都可以在NetWorkx库的官方帮助文档中找到:
https://networkx.github.io/documentation/latest/index.html
2 NetWorkx库中的图数据对象
NetWorkx库中的图数据对象可以通过nx.generate_graphml接口转化成graphml文件格式的字符串。该字符串是以生成器形式存储的,每一个子图为生成器中的一个元素。例如
G=nx.path_graph(4)print( list(nx.generate_graphml(G)))
该代码执行后,会将graphml文件格式的图数据对象输出,具体如下:
['' ,
' ' ,
' ',
' ',
' ',
' ',
' ',
' ',
' ',
' ',
'']
通过graphml文件格式的描述,可以将图数据以文本的形式体现出来。用户直接修改graphml文件格式的内容,也完成对图数据的维护。它比使用接口函数的方式更直接,也更灵活。
NetWorkx库还可以通过读写graphml文件的方式完成图数据的持久化。使用nx.write_graphml接口将内存中的图对象输出。待编辑好之后,在使用nx.read_graphml接口将文件加载到内存中。
扩展名为graphml的文件使用的是xml格式,它还可以用"yED Graph Editor"工具打开,该工具的下载地址如下:https://www.yworks.com/products/yed/download#download
7.4 TensorFlow相关的GNN例子
在2019年,微软开源了一个用TF实现的GNN例程库。里面实现了多种GNN模型。对于擅长使用TF的用户,可以直接从这份源码入手。
代码地址: https://github.com/microsoft/tf-gnn-samples
开源这份代码的是微软剑桥 Deep Program Understanding 组开源的,实现了一系列特征线性调和的图神经网络(GNN-FiLM: Graph Neural Networks with Feature-wise Linear Modulation,2019),包含GNN的网络架构:
Gated Graph Neural Networks (GGNN) (Li et al., 2015).
Relational Graph Convolutional Networks (RGCN) (Schlichtkrull et al., 2016).
Relational Graph Attention Networks (RGAT) (Veličković et al., 2018).
Relational Graph Isomorphism Networks (RGIN) (Xu et al., 2019) .
Graph Neural Network with Edge MLPs (GNN-Edge-MLP)
Relational Graph Dynamic Convolution Networks (RGDCN)
Graph Neural Networks with Feature-wise Linear Modulation (GNN-FiLM)
欢迎大家的观看,如果发现有什么问题,还望积极指正,非常感谢。