DGL如何表示图?

在本教程结束时,您将能够:

  • 在DGL中从头开始构建一个图。

  • 为图分配节点和边的特征。

  • 查询DGL图的属性,例如节点度和连通性。

  • 将DGL图转换为另一个图。

  • 加载和保存DGL图。

(预计时间:16分钟)

DGL 图构建

DGL 将一个有向图表示为一个 DGLGraph 对象。你可以通过指定图中的节点数量以及源节点和目标节点的列表来构建一个图。图中的节点具有从0开始的连续ID。

例如,以下代码构建了一个有5个叶子的有向星图。中心节点的ID是0。边从中心节点指向叶子节点。

import os

os.environ["DGLBACKEND"] = "pytorch"
import dgl
import numpy as np
import torch

g = dgl.graph(([0, 0, 0, 0, 0], [1, 2, 3, 4, 5]), num_nodes=6)
# Equivalently, PyTorch LongTensors also work.
g = dgl.graph(
    (torch.LongTensor([0, 0, 0, 0, 0]), torch.LongTensor([1, 2, 3, 4, 5])),
    num_nodes=6,
)

# You can omit the number of nodes argument if you can tell the number of nodes from the edge list alone.
g = dgl.graph(([0, 0, 0, 0, 0], [1, 2, 3, 4, 5]))

图中的边具有从0开始的连续ID,并且在创建时与源节点和目标节点的列表顺序相同。

# Print the source and destination nodes of every edge.
print(g.edges())
(tensor([0, 0, 0, 0, 0]), tensor([1, 2, 3, 4, 5]))

注意

DGLGraph 始终是有向的,以最好地适应图神经网络的计算模式,其中从一个节点发送到另一个节点的消息在两个方向上通常是不同的。如果你想处理无向图,你可以考虑将其视为双向图。有关创建双向图的示例,请参见 Graph Transformations

为图分配节点和边特征

许多图数据包含节点和边上的属性。 尽管在现实世界中节点和边属性的类型可以是任意的,但DGLGraph只接受存储在张量中的属性(具有数值内容)。因此,所有节点或边的属性必须具有相同的形状。在深度学习的背景下,这些属性通常被称为特征

您可以通过ndataedata接口分配和检索节点和边的特征。

# Assign a 3-dimensional node feature vector for each node.
g.ndata["x"] = torch.randn(6, 3)
# Assign a 4-dimensional edge feature vector for each edge.
g.edata["a"] = torch.randn(5, 4)
# Assign a 5x4 node feature matrix for each node.  Node and edge features in DGL can be multi-dimensional.
g.ndata["y"] = torch.randn(6, 5, 4)

print(g.edata["a"])
tensor([[-1.3337,  0.7580,  0.6050,  1.8967],
        [-0.0579,  0.5557, -0.0788, -0.2087],
        [ 0.4756,  0.7598,  0.3730, -0.8962],
        [-0.8344, -0.1920,  0.0165,  0.7171],
        [ 0.9992,  0.7447, -1.2148,  0.5741]])

注意

深度学习的巨大发展为我们提供了许多方法,将各种类型的属性编码为数值特征。以下是一些一般性建议:

  • 对于分类属性(例如性别、职业),考虑将它们转换为整数或进行独热编码。

  • 对于可变长度的字符串内容(例如新闻文章、引用),考虑应用语言模型。

  • 对于图像,考虑应用视觉模型,如CNNs。

你可以在PyTorch深度学习教程中找到大量关于如何将这些属性编码为张量的材料。

查询图结构

DGLGraph 对象提供了多种方法来查询图结构。

print(g.num_nodes())
print(g.num_edges())
# Out degrees of the center node
print(g.out_degrees(0))
# In degrees of the center node - note that the graph is directed so the in degree should be 0.
print(g.in_degrees(0))
6
5
5
0

图形变换

DGL 提供了许多 API 来将一个图转换为另一个图,例如提取子图:

# Induce a subgraph from node 0, node 1 and node 3 from the original graph.
sg1 = g.subgraph([0, 1, 3])
# Induce a subgraph from edge 0, edge 1 and edge 3 from the original graph.
sg2 = g.edge_subgraph([0, 1, 3])

您可以通过查看新图中的节点特征dgl.NID或边特征dgl.EID来获取从子图到原始图的节点/边映射。

# The original IDs of each node in sg1
print(sg1.ndata[dgl.NID])
# The original IDs of each edge in sg1
print(sg1.edata[dgl.EID])
# The original IDs of each node in sg2
print(sg2.ndata[dgl.NID])
# The original IDs of each edge in sg2
print(sg2.edata[dgl.EID])
tensor([0, 1, 3])
tensor([0, 2])
tensor([0, 1, 2, 4])
tensor([0, 1, 3])

subgraphedge_subgraph 也会将原始特征复制到子图中:

# The original node feature of each node in sg1
print(sg1.ndata["x"])
# The original edge feature of each node in sg1
print(sg1.edata["a"])
# The original node feature of each node in sg2
print(sg2.ndata["x"])
# The original edge feature of each node in sg2
print(sg2.edata["a"])
tensor([[ 2.6107, -0.8565, -0.2315],
        [-0.0494,  0.7228, -1.8882],
        [ 0.7597,  1.1079,  0.5933]])
tensor([[-1.3337,  0.7580,  0.6050,  1.8967],
        [ 0.4756,  0.7598,  0.3730, -0.8962]])
tensor([[ 2.6107, -0.8565, -0.2315],
        [-0.0494,  0.7228, -1.8882],
        [-0.2449,  0.1388,  0.7886],
        [-3.7515, -0.2419, -0.4272]])
tensor([[-1.3337,  0.7580,  0.6050,  1.8967],
        [-0.0579,  0.5557, -0.0788, -0.2087],
        [-0.8344, -0.1920,  0.0165,  0.7171]])

另一个常见的转换是为原始图中的每条边添加一条反向边,使用dgl.add_reverse_edges

注意

如果你有一个无向图,最好先通过添加反向边将其转换为双向图。

newg = dgl.add_reverse_edges(g)
print(newg.edges())
(tensor([0, 0, 0, 0, 0, 1, 2, 3, 4, 5]), tensor([1, 2, 3, 4, 5, 0, 0, 0, 0, 0]))

加载和保存图

您可以通过dgl.save_graphs保存一个图或图列表,并使用dgl.load_graphs将它们加载回来。

# Save graphs
dgl.save_graphs("graph.dgl", g)
dgl.save_graphs("graphs.dgl", [g, sg1, sg2])

# Load graphs
(g,), _ = dgl.load_graphs("graph.dgl")
print(g)
(g, sg1, sg2), _ = dgl.load_graphs("graphs.dgl")
print(g)
print(sg1)
print(sg2)
Graph(num_nodes=6, num_edges=5,
      ndata_schemes={'y': Scheme(shape=(5, 4), dtype=torch.float32), 'x': Scheme(shape=(3,), dtype=torch.float32)}
      edata_schemes={'a': Scheme(shape=(4,), dtype=torch.float32)})
Graph(num_nodes=6, num_edges=5,
      ndata_schemes={'y': Scheme(shape=(5, 4), dtype=torch.float32), 'x': Scheme(shape=(3,), dtype=torch.float32)}
      edata_schemes={'a': Scheme(shape=(4,), dtype=torch.float32)})
Graph(num_nodes=3, num_edges=2,
      ndata_schemes={'_ID': Scheme(shape=(), dtype=torch.int64), 'y': Scheme(shape=(5, 4), dtype=torch.float32), 'x': Scheme(shape=(3,), dtype=torch.float32)}
      edata_schemes={'_ID': Scheme(shape=(), dtype=torch.int64), 'a': Scheme(shape=(4,), dtype=torch.float32)})
Graph(num_nodes=4, num_edges=3,
      ndata_schemes={'_ID': Scheme(shape=(), dtype=torch.int64), 'y': Scheme(shape=(5, 4), dtype=torch.float32), 'x': Scheme(shape=(3,), dtype=torch.float32)}
      edata_schemes={'_ID': Scheme(shape=(), dtype=torch.int64), 'a': Scheme(shape=(4,), dtype=torch.float32)})

接下来是什么?

# Thumbnail credits: Wikipedia
# sphinx_gallery_thumbnail_path = '_static/blitz_2_dglgraph.png'

脚本的总运行时间: (0 分钟 0.030 秒)

Gallery generated by Sphinx-Gallery