图论基础
参考教材:“Chapter 2 - Foundations of Graphs, Deep Learning on Graphs”
包含图的基本概念、图的种类等,此处不展开。
图数据表示——基于PyG库
本部分代码实现基于PyTorch 1.8.1 + cuda 11.1
PyG库
PyG,全称PyTorch Geometric,它包含各种对图和其他不规则结构进行深度学习的方法,也称为几何深度学习,集成了来自各种已发表论文的内容。
PyG是面向几何深度学习的PyTorch的扩展库,几何深度学习指的是应用于图和其他不规则、非结构化数据的深度学习。基于PyG库,我们可以轻松地根据数据生成一个图对象,然后很方便的使用它;我们也可以容易地为一个图数据集构造一个数据集类,然后很方便的将它用于神经网络。
其当前安装要求python <= 3.8,PyTorch >= 1.4.0,cuda。
安装教程可以参考官网 PyG安装。
Data类
torch_geometric.data.Data()
是PyG的一个基础数据类,用于表示图数据,其官方文档为torch_geometric.data.Data。
由于PyG库基于PyTorch,因此,Data类的所有参数对象均torch.tensor()
对象,可以参考torch.tensor,常见操作包括torch.tensor.shape
,torch.tensor.item()
等。
一个Data对象(一个图)一般来说至少包含x
, edge_index
,edge_attr
, y
, num_nodes
5个属性,当图包含其他属性时,我们可以通过指定额外的参数使Data
对象包含其他的属性:
graph = Data(x=x, edge_index=edge_index, edge_attr=edge_attr, y=y, num_nodes=num_nodes, other_attr=other_attr)
在Data里,edge_index
的每一列定义一条边,其中第一行为边起始节点的索引,第二行为边结束节点的索引,为稀疏矩阵表示方式。
可以通过一个实例,了解Data的表示方式,对下图
import torch
from torch_geometric.data import Data
edge_index = torch.tensor([[0, 1, 1, 2],
[1, 0, 2, 1]], dtype=torch.long)
#存为无向图
x = torch.tensor([[-1], [0], [1]], dtype=torch.float)
#node单一属性
data = Data(x=x, edge_index=edge_index)
Dataset类
PyG内置了大量常用的基准数据集,可以在torch_geometric.datasets上看到。
以PyG内置的Planetoid
数据集为例torch_geometric.datasets.Planetoid。
from torch_geometric.datasets import Planetoid
dataset = Planetoid(root='/dataset/Cora', name='Cora')
data = dataset[0]
其中只含一个 图对象,可以看到该data对象的结构
{'edge_attr': None,
'edge_index': tensor([[ 0, 0, 0, ..., 2707, 2707, 2707],
[ 633, 1862, 2582, ..., 598, 1473, 2706]]),
'face': None,
'normal': None,
'pos': None,
'test_mask': tensor([False, False, False, ..., True, True, True]),
'train_mask': tensor([ True, True, True, ..., False, False, False]),
'val_mask': tensor([False, False, False, ..., False, False, False]),
'x': tensor([[0., 0., 0., ..., 0., 0., 0.],
[0., 0., 0., ..., 0., 0., 0.],
[0., 0., 0., ..., 0., 0., 0.],
...,
[0., 0., 0., ..., 0., 0., 0.],
[0., 0., 0., ..., 0., 0., 0.],
[0., 0., 0., ..., 0., 0., 0.]]),
'y': tensor([3, 4, 4, ..., 3, 3, 3])}
其中test_mask
,train_mask
,val_mask
描述了指定哪些点为测试集、训练集及验证集。
并非所有dataset的结构都一致,如TUDataset
数据torch_geometric.datasets.TUDataset。
from torch_geometric.datasets import TUDataset
dataset = TUDataset(root='/dataset/IMDB-BINARY', name='IMDB-BINARY')
len(dataset)
>>> 1000
其中包含1000个图对象,取第一个data图对象,其结构如下。
{'__num_nodes__': 20,
'edge_attr': None,
'edge_index': tensor([[ 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3,
3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9,
9, 9, 9, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12,
12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14,
15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17,
18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19],
[ 2, 4, 5, 9, 10, 2, 6, 8, 12, 14, 17, 18, 19, 0, 1, 3, 4, 5,
6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 2, 6, 11, 12,
16, 19, 0, 2, 5, 9, 10, 0, 2, 4, 9, 10, 1, 2, 3, 8, 11, 12,
14, 16, 17, 18, 19, 2, 13, 15, 1, 2, 6, 12, 14, 17, 18, 19, 0, 2,
4, 5, 10, 0, 2, 4, 5, 9, 2, 3, 6, 12, 16, 19, 1, 2, 3, 6,
8, 11, 14, 16, 17, 18, 19, 2, 7, 15, 1, 2, 6, 8, 12, 17, 18, 19,
2, 7, 13, 19, 2, 3, 6, 11, 12, 19, 1, 2, 6, 8, 12, 14, 18, 19,
1, 2, 6, 8, 12, 14, 17, 19, 1, 2, 3, 6, 8, 11, 12, 14, 15, 16,
17, 18]]),
'face': None,
'normal': None,
'pos': None,
'x': None,
'y': tensor([0])}
这是一个node没有属性的图。
作业:机构-作者-论文类的实现
具体任务如下
请通过继承
Data
类实现一个类,专门用于表示“机构-作者-论文”的网络。该网络包含“机构“、”作者“和”论文”三类节点,以及“作者-机构“和“作者-论文“两类边。对要实现的类的要求:
1)用不同的属性存储不同节点的属性;
2)用不同的属性存储不同的边(边没有属性);
3)逐一实现获取不同节点数量的方法。
为方便实现,进行如下假设:
新增属性x_type
记录节点的种类,0代表机构(institution),1代表作者(author),2代表文章(paper)。
实现代码如下
class Paper(Data):
def __init__(self,x=None,x_type=None, edge_index=None, y=None,
pos=None, normal=None, face=None, **kwargs):
super().__init__(x,edge_index,None,y,pos,normal,face,**kwargs)
self.x_institution = x[x_type == 0,:]
self.x_author = x[x_type == 1,:]
self.x_paper = x[x_type == 2,:]
self.x_type = x_type
self.edge_type()
def edge_type(self):
ins_au = [[],[]]
au_pa = [[],[]]
for i in range(self.edge_index.shape[1]):
tail = self.edge_index[0,i]
head = self.edge_index[1,i]
if ((self.x_type[tail] == 0 and self.x_type[head] == 1) or
(self.x_type[tail] == 1 and self.x_type[head] == 0)):
ins_au[0].append(tail)
ins_au[1].append(head)
elif ((self.x_type[tail] == 1 and self.x_type[head] == 2) or
(self.x_type[tail] == 2 and self.x_type[head] == 1)):
au_pa[0].append(tail)
au_pa[1].append(head)
self.edge_au_pa = torch.tensor(au_pa)
self.edge_ins_au = torch.tensor(ins_au)
def num_edge_au_pa(self):
if self.is_undirected():
return int(self.edge_au_pa.shape[1]/2)
else:
return self.edge_au_pa.shape[1]
def num_edge_ins_au(self):
if self.is_undirected():
return int(self.edge_ins_au.shape[1]/2)
else:
return self.edge_ins_au.shape[1]
def num_node_au(self):
return self.x_author.shape[0]
def num_node_ins(self):
return self.x_institution.shape[0]
def num_node_pa(self):
return self.x_paper.shape[0]
则对于一个具体的图形
x = torch.tensor([[1,2,3],[2,3,4],[3,4,5],[1,4,6],[5,8,8],[3,6,7]])
x_type = torch.tensor([2,1,0,1,2,2])
edge_index = torch.tensor([[0,1,2,3,3,5,4,3,2,1],[1,2,3,4,5,3,3,2,1,0]])
#创建对象
a = Paper(x,x_type,edge_index)
其内容如下
{'edge_attr': None, 'edge_au_pa': tensor([[0, 3, 3, 5, 4, 1],
[1, 4, 5, 3, 3, 0]]), 'edge_index': tensor([[0, 1, 2, 3, 3, 5, 4, 3, 2, 1],
[1, 2, 3, 4, 5, 3, 3, 2, 1, 0]]), 'edge_ins_au': tensor([[1, 2, 3, 2],
[2, 3, 2, 1]]), 'face': None, 'normal': None, 'pos': None, 'x': tensor([[1, 2, 3],
[2, 3, 4],
[3, 4, 5],
[1, 4, 6],
[5, 8, 8],
[3, 6, 7]]), 'x_author': tensor([[2, 3, 4],
[1, 4, 6]]), 'x_institution': tensor([[3, 4, 5]]), 'x_paper': tensor([[1, 2, 3],
[5, 8, 8],
[3, 6, 7]]), 'x_type': tensor([2, 1, 0, 1, 2, 2]), 'y': None}
调用方法num_edge_au_pa()
,num_edge_ins_au()
,num_node_au()
,num_node_pa()
,num_node_ins()
,可以查看各属性边及节点的数量
a.num_edge_au_pa()
>>> 3
a.num_edge_ins_au()
>>> 2
a.num_node_au()
>>> 2
a.num_node_pa()
>>> 3
a.num_node_ins()
>>> 1
参考阅读: