PyG(PyTorchGeometric)是一个基于PyTorch的图神经网络框架,建议先了解PyTorch的使用再学习PyG,要不然看不懂,关于PyTorch的使用可以看我的
PyG包含图神经网络训练中的数据集处理、多GPU训练、多个经典的图神经网络模型、多个常用的图神经网络训练数据集而且支持自建数据集,主要包含以下几个模块
通过一些例子介绍PyG的使用,先有一些认识。
PyG用torch_geometric.data.Data保存图结构的数据,导入的data(这个data指的是你导入的具体数据,不是前面那个torch_geometric.data)在PyG中会包含以下属性
除了以上属性,我们还可以通过data.face自定义属性。
下面看如何利用PyG表示下面这个图
importtorchfromtorch_geometric.dataimportData#边的连接信息#注意,无向图的边要定义两次edge_index=torch.tensor([#这里表示节点0和1有连接,因为是无向图#那么1和0也有连接#上下对应着看[0,1,1,2],[1,0,2,1],],#指定数据类型dtype=torch.long)#节点的属性信息x=torch.tensor([#三个节点#每个节点的属性向量维度为1[-1],[0],[1],])#实例化为一个图结构的数据data=Data(x=x,edge_index=edge_index)#查看图数据print(data)#图数据中包含什么信息print(data.keys)#查看节点的属性信息print(data['x'])#节点数print(data.num_nodes)#边数print(data.num_edges)#节点属性向量的维度print(data.num_node_features)#图中是否有孤立节点print(data.has_isolated_nodes())#图中是否有环print(data.has_self_loops())#是否是有向图print(data.is_directed())1.2常用的图神经网络数据集PyG包含了一些常用的图深度学习公共数据集,如
接下来拿ENZYMES数据集(包含600个图,每个图分为6个类别,图级别的分类)举例如何使用PyG的公共数据集
fromtorch_geometric.datasetsimportTUDataset#导入数据集dataset=TUDataset(#指定数据集的存储位置#如果指定位置没有相应的数据集#PyG会自动下载root='../data/ENZYMES',#要使用的数据集name='ENZYMES',)#数据集的长度print(len(dataset))#数据集的类别数print(dataset.num_classes)#数据集中节点属性向量的维度print(dataset.num_node_features)#600个图,我们可以根据索引选择要使用哪个图data=dataset[0]print(data)#随机打乱数据集dataset=dataset.shuffle()1.3如何加载数据集真正的图神经网络训练中我们一般是加载数据集中的一部分到内存中训练图神经网络,叫做一个batch,那么PyG如何加载一个batch呢,PyG会根据我们的数据集将其分割为我们指定的batch大小
举个例子
fromtorch_geometric.loaderimportDataLoaderfromtorch_geometric.datasetsimportTUDataset#数据集dataset=TUDataset(root='../data/ENZYMES',name='ENZYMES',use_node_attr=True,)#建立数据集加载器#每次加载32个数据到内存中loader=DataLoader(#要加载的数据集dataset=dataset,#ENZYMES包含600个图#每次加载32个batch_size=32,#每次加入进来之后是否随机打乱数据(可以增加模型的泛化性)shuffle=True)forbatchinloader:print(batch)print(batch.num_graphs)2、空域图卷积神经网络的建立空域图卷积(注意,图神经网络里的‘卷积’一词,取得是‘特征提取’这个广义意义,跟卷积神经网络里的那个卷积核计算区别开)可以看作是相邻节点之间进行信息传递、融合的过程,计算公式可以一般化为
其中$x_i^k$是当前卷积层的输出,$x_i^{k-1}$是上一个卷积层的输出,作为当前卷积层的输入,$x_j^{k-1}$是$i$节点相邻节点的信息,$e_{j,i}$是其连接边的信息
(建议背下来这个公式,你会发现无论空域图卷积的论文怎么折腾,还是没跑出这个框架,只不过是$\gamma,\phi$两个函数换了)。
其中$\theta$是可学习的参数矩阵,然后用节点的度进行正则化,最后所有的信息相加,作为当前节点新的特征表示。
那么化归到我们上面说的一般化公式,$\gamma$就是一个求和函数,$\phi$是一个线性变换+正则化,那么利用PyG的MessagePassing实现代码为
importtorchfromtorch.nnimportSequentialasSeqfromtorch.nnimportLinear,ReLUfromtorch_geometric.nnimportMessagePassing#定义EdgeConv图卷积神经网络classEdgeConv(MessagePassing):#初始化图卷积神经网络def__init__(self,in_channels,out_channels):#定义伽马函数为求最大值函数super().__init__(aggr='max')#定义一个前馈神经网络self.mlp=Seq(#线性层,后面信息汇聚函数之后的输入是2*in_channelsLinear(2*in_channels,out_channels),#激活函数ReLU(),#输出层Linear(out_channels,out_channels))#定义信息汇聚函数defmessage(self,x_i,x_j):tmp=torch.cat([x_i,x_j-x_i],dim=1)#cat之后tmp的维度为[边数,2*in_channels]returnself.mlp(tmp)#前向传递,进行图卷积defforward(self,x,edge_index):#x是节点属性向量矩阵#edge_index是边的连接信息#进行信息的传递、融合returnself.propagate(edge_index,x=x)3、自建图神经网络数据集PyG将自建数据集分为两个文件夹---raw_dir、processed_dir。row_dir是原始的数据集,processed_dir是PyG处理之后的数据集
对于数据集PyG有三种过滤方法---transform、pre_transform、pre_filter。
PyG将数据集分为两种类型
做4件事:
一个通用模板是这样的
通用模板为
PyG会自动帮我们将图数据集按照我们定义的batch_size分割,然后将每个batch中的数据合并。
如果我们想要控制PyG如何组合一个batch中的数据,我们需要自己重写torch_geometric.data.Data.__inc__()
举两个具体的例子
假设我们的数据集中每个数据(注意是每个数据)包含两个图,每个数据像这样
对于这种数据集,如何控制PyG将多个数据合并成一个batch呢,以batch_size为2举例,batch_size=2意味着将数据集中每两个数据组成一组,形成一个图,每个batch中的数据是这样的
还是batch_size=2,我们想控制PyG让数据变成
importtorchfromtorch_geometric.dataimportDatafromtorch_geometric.loaderimportDataLoader#定义二分图结构classBipartiteData(Data):def__init__(self,edge_index=None,x_s=None,x_t=None):super().__init__()#包含一组边#两组节点self.edge_index=edge_indexself.x_s=x_sself.x_t=x_t#定义每个batch的合并方式def__inc__(self,key,value,*args,**kwargs):#如果要合并两个图的边连接信息ifkey=='edge_index':#左边(边连接信息的第一行)按照第一组节点数合并#右边(边连接信息的第二行)按照第二组节点数合并returntorch.tensor([[self.x_s.size(0)],[self.x_t.size(0)]])else:returnsuper().__inc__(key,value,*args,**kwargs)edge_index=torch.tensor([[0,0,1,1],[0,1,1,2],])x_s=torch.randn(2,16)x_t=torch.randn(3,16)data=BipartiteData(edge_index,x_s,x_t)data_list=[data,data]loader=DataLoader(data_list,batch_size=2)batch=next(iter(loader))print(batch)print(batch.edge_index)5、异质图的建立前面讨论的图可以归为简单图---只包含一种类型的节点以及一种类型的边。
然而在现实中需要对多种类型的节点以及这些节点之间多种类型的边进行处理,这就需要异质图的概念,在异质图中,不同类型的边描述不同类型节点之间不同的关系,异质图神经网络的任务就是在这种图结构上学习出节点或者整个异质图的特征表示。异质图准确定义如下:
异质图(HeterogeneousGraphs):一个异质图$G$由一组节点$V=v_1,v_2,...,v_n$和一组边$E=e_1,e_2,...,e_m$组成,其中每个节点和每条边都对应着一种类型,用$T_v$表示节点类型的集合,$T_e$表示边类型的集合,一个异质图有两个映射函数,分别将每个节点映射到其对应的类型$\phi_v:V\rightarrowT_v$,每条边映射到其对应的类型$\phi_e:E\rightarrowT_e$。
接下来以一个电影评分数据集MovieLens举例如何构建异质图。
MovieLens包含了600个用户对于电影的评分,我们利用这个数据集构建一个二分图,包含电影、用户两种类型的节点,一种类型的边(含有多种类型节点,所以可以看作一个异质图)
MovieLens中的movies.csv文件描述了电影的信息,包括电影在数据集中唯一的ID,电影名,电影所属的类型
ratings.csv包含了用户对于电影的评分
接下来就根据这两个csv建立二分图数据集
在OGB数据集中包含4种类型的节点
4种类型的边
OGB数据集上的任务是预测论文在整个关系网中所属的位置
下面看如何表示这个异质图
fromtorch_geometric.dataimportHeteroData#HeteroData是PyG自带的一个异质图数据结构data=HeteroData()#添加节点的信息data['paper'].x=...data['author'].x=...data['institution'].x=...data['field_of_study'].x=...#添加边的连接信息data['paper','cites','paper'].edge_index=...data['author','writes','paper'].edge_index=...data['author','affiliated_with','institution'].edge_index=...data['author','has_topic','institution'].edge_index=...#添加边的属性信息data['paper','cites','paper'].edge_attr=...data['author','writes','paper'].edge_attr=...data['author','affiliated_with','institution'].edge_attr=...data['paper','has_topic','field_of_study'].edge_attr=...这样上面的异质图就建立完成了,我们可以将它输入到一个异质图神经网络中
#异质图神经网络model=HeteroGNN(...)#获取异质图神经网络网络的输出#注意异质图神经网络的输入是..._dictoutput=model(data.x_dict,data.edge_index_dict,data.edge_attr_dict)如果PyG中包含你想用的异质图,可以直接这样导入
fromtorch_geometric.datasetsimportOGB_MAG#导入数据集dataset=OGB_MAG(root='../data',#预处理方式#转换为向量preprocess='metapath2vec',)print(dataset[0])下面介绍一下HeteroData中常用的函数
#获取异质图中的某种节点或边paper_node_data=data['paper']cites_edge_data=data['paper','cites','paper']#如果边的连接节点集合或者边的命名是唯一的还可以这样写#使用连接端点获取边cites_edge_data=data['paper','paper']#使用边的名字获取cites_edge_data=data['cites']#给节点添加新属性data['paper'].year=...#删除节点的某些属性defdata['field_of_study']#通过metadata获取异质图中所有类型的信息node_types,edge_types=data.metadata()#所有类型的节点print(node_types)#所有类型的边print(edge_types)#判断异质图自身的一些属性print(data.has_isolated_nodes())#如果不同类型信息之间维度匹配还可以将异质图融合为一个简单图homogeneous_data=data.to_homogeneous()importtorch_geometric.transformsasT#对异质图进行变换#变为无向图data=T.ToUndirected()(data)#添加到自身的环data=T.AddSelfLoops()(data)下面介绍如何建立异质图神经网络
PyG可以通过torch_geometric.nn.to_hetero(),或者torch_geometric.nn.to_hetero_with_bases()将一个简单图神经网络转换成异质图的形式
importtorchimporttorch_geometric.transformsasTfromtorch_geometric.datasetsimportOGB_MAGfromtorch_geometric.nnimportSAGEConv,to_hetero#导入数据集data=OGB_MAG(root='./data',preprocess='metapath2vec',transform=T.ToUndirected())[0]#定义一个普通的图神经网络classGNN(torch.nn.Module):def__init__(self,hidden_channels,out_channels):super().__init__()self.conv1=SAGEConv((-1,-1),hidden_channels)self.conv2=SAGEConv((-1,-1),out_channels)defforward(self,x,edge_index):x=self.conv1(x,edge_index).relu()x=self.conv2(x,edge_index)returnx#实例化我们定义的图神经网络model=GNN(hidden_channels=64,out_channels=dataset.num_classes)#将其转换为异质图形式model=to_hetero(model,data.metadata(),aggr='sum')PyG的to_hetero具体工作方式是这样的
它根据我们的异质图数据结构,自动将我们定义的简单图神经网络结构中的层结构进行了复制,并添加了信息传递路径。
torch_geometric.nn.conv.HeteroConv卷积层同样起到类似的功能
fromtorch_geometric.nnimportHeteroConv,GCNConv,SAGEConv,GATConv,LinearclassHeteroGNN(torch.nn.Module):def__init__(self,hidden_channels,out_channels,num_layers):super().__init__()self.convs=torch.nn.ModuleList()#定义图卷积层for_inrange(num_layers):#最外面用HeteroConv将里面的卷积层转换为异质图版本conv=HeteroConv(#要转换的卷积层{('paper','cites','paper'):GCNConv(-1,hidden_channels),('author','writes','paper'):GATConv((-1,-1),hidden_channels),('author','affiliated_with','institution'):SAGEConv((-1,-1),hidden_channels),},aggr='sum')self.convs.append(conv)self.lin=Linear(hidden_channels,out_channels)defforward(self,x_dict,edge_index_dict):forconvinself.convs:x_dict=conv(x_dict,edge_index_dict)x_dict={key:x.relu()forkey,xinx_dict.items()}returnself.lin(x_dict['author'])model=HeteroGNN(hidden_channels=64,out_channels=dataset.num_classes,num_layers=2)7、GraphGym的使用GraphGym是在PyG基础上的进一步封装,可以利用参数化的方式进行图神经网络的实验,具体可见
(感觉还是自己动手搭吧,不用封装)
PyG中包含多个经典图神经网络论文中的卷积层
我会给出一部分卷积层论文和源代码的解读,等我更新。
PyG踩坑
:github连接较慢导致的,点击Planetoid的源码文件,找到第一个url属性,设置为