使用DGL构造和使用简单图。
参考:https://docs.dgl.ai/guide_cn/graph-graphs-nodes-edges.html
import dgl
import torch
import numpy as np
Using backend: pytorch
使用DGL建简单图
创建一个 DGLGraph 对象的一种方法是使用 dgl.graph() 函数。它接受一个边的集合作为输入。
DGL推荐使用Tensor作为DGL API的输入。不过大部分的DGL API也支持Python的可迭代类型(比如列表)或numpy.ndarray类型作为API的输入,方便用户快速进行开发验证。
g = dgl.graph((torch.tensor([0, 0, 0, 0, 0]), torch.tensor([1, 2, 3, 4, 5])))
print(g)
Graph(num_nodes=6, num_edges=5,
ndata_schemes={}
edata_schemes={})
DGL支持使用 32 位或 64 位的整数作为节点ID和边ID。节点和边ID的数据类型必须一致。如果使用 64 位整数, DGL可以处理最多 2 63 2^{63} 263−1 个节点或边。不过,如果图里的节点或者边的数量小于 2 63 2^{63} 263−1 ,用户最好使用 32 位整数。 这样不仅能提升速度,还能减少内存的使用。
print(g.idtype) # DGL默认使用int64
g = g.int()
print(g.idtype) # 转换成int32
torch.int32
torch.int32
获取图的属性信息
print(g)
print(g.nodes()) # 图中节点的数量是DGL通过给定的图的边列表中最大的点ID推断所得出的
print(g.edges())
# 获取边的对应端点和边ID
print(g.edges(form='all'))
Graph(num_nodes=6, num_edges=5,
ndata_schemes={}
edata_schemes={})
tensor([0, 1, 2, 3, 4, 5], dtype=torch.int32)
(tensor([0, 0, 0, 0, 0], dtype=torch.int32), tensor([1, 2, 3, 4, 5], dtype=torch.int32))
(tensor([0, 0, 0, 0, 0], dtype=torch.int32), tensor([1, 2, 3, 4, 5], dtype=torch.int32), tensor([0, 1, 2, 3, 4], dtype=torch.int32))
为图中的节点添加feat和label等特征或属性信息
- 仅允许使用数值类型(如单精度浮点型、双精度浮点型和整型)的特征。这些特征可以是标量、向量或多维张量。
- 每个节点特征具有唯一名称,每个边特征也具有唯一名称。节点和边的特征可以具有相同的名称。
- 通过张量分配创建特征时,DGL会将特征赋给图中的每个节点和每条边。该张量的第一维必须与图中节点或边的数量一致。 不能将特征赋给图中节点或边的子集。
g.ndata['feat'] = torch.randn(g.num_nodes(), 3)
# 不同名称的特征可以具有不同形状
g.ndata['x'] = torch.randn(g.num_nodes(), 5)
g.ndata['label'] = torch.tensor([0, 1, 2, 3, 4, 5])
# 边特征
g.edata['feat'] = torch.ones(g.num_edges(), dtype=torch.int32)
print(g)
print(g.ndata['feat'])
print(g.ndata['label'])
# 获取节点1的特征
print(g.ndata['feat'][1])
# 获取边0和3的特征
print(g.edata['feat'][torch.tensor([0, 3])])
Graph(num_nodes=6, num_edges=5,
ndata_schemes={'feat': Scheme(shape=(3,), dtype=torch.float32), 'label': Scheme(shape=(), dtype=torch.int64), 'x': Scheme(shape=(5,), dtype=torch.float32)}
edata_schemes={'feat': Scheme(shape=(), dtype=torch.int32)})
tensor([[-0.4046, -1.5649, -0.5510],
[-1.1374, -1.0940, 1.7150],
[ 0.2465, 0.0811, -1.9171],
[ 0.8162, 0.4573, -1.3169],
[ 0.3926, 0.2399, -1.2495],
[ 0.1937, 0.1652, 0.3655]])
tensor([0, 1, 2, 3, 4, 5])
tensor([-1.1374, -1.0940, 1.7150])
tensor([1, 1], dtype=torch.int32)
加权图
对于加权图,用户可以将权重储存为一个边特征w
g.edata['w'] = torch.tensor([0.1, 0.6, 0.9, 0.7, 0.2]) # 数量必须与边数一致
print(g)
Graph(num_nodes=6, num_edges=5,
ndata_schemes={'feat': Scheme(shape=(3,), dtype=torch.float32), 'label': Scheme(shape=(), dtype=torch.int64), 'x': Scheme(shape=(5,), dtype=torch.float32)}
edata_schemes={'feat': Scheme(shape=(), dtype=torch.int32), 'w': Scheme(shape=(), dtype=torch.float32)})
生成一个train_mask以及onhot的例子
train_mask = torch.zeros((g.ndata['feat'].shape[0],), dtype=torch.long)
train_mask[0] = train_mask[1] = 1
print(train_mask)
tensor([1, 1, 0, 0, 0, 0])
num_classes = 6
onehot = torch.zeros(g.ndata['feat'].shape[0], num_classes)
onehot[train_mask, g.ndata['label'][train_mask]] = 1
print(onehot)
tensor([[1., 0., 0., 0., 0., 0.],
[0., 1., 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.]])
子图的一些操作
print(g.out_degree(0))
print(g.in_degree(0))
5
0
print(g.subgraph([1, 2, 3]))
print(g.subgraph([0, 1, 3]))
Graph(num_nodes=3, num_edges=0,
ndata_schemes={'feat': Scheme(shape=(3,), dtype=torch.float32), 'label': Scheme(shape=(), dtype=torch.int64), 'x': Scheme(shape=(5,), dtype=torch.float32), '_ID': Scheme(shape=(), dtype=torch.int32)}
edata_schemes={'feat': Scheme(shape=(), dtype=torch.int32), 'w': Scheme(shape=(), dtype=torch.float32), '_ID': Scheme(shape=(), dtype=torch.int32)})
Graph(num_nodes=3, num_edges=2,
ndata_schemes={'feat': Scheme(shape=(3,), dtype=torch.float32), 'label': Scheme(shape=(), dtype=torch.int64), 'x': Scheme(shape=(5,), dtype=torch.float32), '_ID': Scheme(shape=(), dtype=torch.int32)}
edata_schemes={'feat': Scheme(shape=(), dtype=torch.int32), 'w': Scheme(shape=(), dtype=torch.float32), '_ID': Scheme(shape=(), dtype=torch.int32)})
sg1 = g.subgraph([0, 1, 3])
print(sg1.nodes())
print(sg1.ndata[dgl.NID]) #打印子图节点在原图中的nid
# 子图中的节点继承了原图中的ndata和edata
print(sg1.ndata['feat'])
print(sg1.ndata['label'])
tensor([0, 1, 2], dtype=torch.int32)
tensor([0, 1, 3], dtype=torch.int32)
tensor([[-0.4046, -1.5649, -0.5510],
[-1.1374, -1.0940, 1.7150],
[ 0.8162, 0.4573, -1.3169]])
tensor([0, 1, 3])
有向图到无向图的转换
newg = dgl.add_reverse_edges(g)
print(newg.edges())
newg = dgl.to_bidirected(g)
print(newg.edges())
(tensor([0, 0, 0, 0, 0, 1, 2, 3, 4, 5], dtype=torch.int32), tensor([1, 2, 3, 4, 5, 0, 0, 0, 0, 0], dtype=torch.int32))
(tensor([0, 0, 0, 0, 0, 1, 2, 3, 4, 5], dtype=torch.int32), tensor([1, 2, 3, 4, 5, 0, 0, 0, 0, 0], dtype=torch.int32))
保存图,从磁盘加载图
dgl.save_graphs('./graph.dgl', g)
(load_g,), _ = dgl.load_graphs('./graph.dgl')
print(load_g)
Graph(num_nodes=6, num_edges=5,
ndata_schemes={'x': Scheme(shape=(5,), dtype=torch.float32), 'label': Scheme(shape=(), dtype=torch.int64), 'feat': Scheme(shape=(3,), dtype=torch.float32)}
edata_schemes={'w': Scheme(shape=(), dtype=torch.float32), 'feat': Scheme(shape=(), dtype=torch.int32)})