利用DGL创建具有3种节点类型和3种边类型的异构图
graph_data = {# (src_type, edge_type, dst_type)('drug', 'interacts', 'drug'): (th.tensor([0, 1]), th.tensor([1, 2])),('drug', 'interacts',, 'disease'): (th.tensor([1]), th.tensor([2]))
}g = dgl.heterograph(graph_data)
上述代码相当于创建了一张异构图,其中:
0 号 drug interacts了 1号 drug
1 号 drug interacts了 2号 drug
0 号 drug interacts了 1号 gene
2 号 drug interacts了 3号 gene
1 号 drug treats 了 2号disease
对于上图,我们可以通过g.ntypes
和g.etypes
查看节点和边的类型,同时可以查看规范边的类型,即g.canonical_etypes
。
(规范边:在DGL中,一个异构图由一系列子图构成,一个子图对应一种关系。每个关系由一个字符串三元组 定义 (源节点类型, 边类型, 目标节点类型)
。由于这里的关系定义消除了边类型的歧义,DGL称它们为规范边类型。)
同构图和二分图只是一种特殊的异构图,它们只包括一种关系。
一个同构图的伪代码:
dgl.heterograph({('node_type', 'edge_type', 'node_type'): (u, v)})
一个二分图的伪代码:
dgl.heterograph({('source_type', 'edge_type', 'destination_type'): (u, v)})
同时异构图使用metagraph呈现点与点之间的关系的,下面是一个例子:
print('g:',g)
out: g: Graph(
num_nodes={‘disease’: 3, ‘drug’: 3, ‘gene’: 4},
num_edges={(‘drug’, ‘interacts’, ‘drug’): 2, (‘drug’, ‘interacts’, ‘gene’): 2, (‘drug’, ‘treats’, ‘disease’): 1},
metagraph=[(‘drug’, ‘drug’, ‘interacts’), (‘drug’, ‘gene’, ‘interacts’), (‘drug’, ‘disease’, ‘treats’)])
使用多种类型节点和边的异构图
当引入多种节点和边类型后,用户在调用DGLGraph API以获取特定类型的信息时,需要指定具体的节点和边类型。此外,不同类型的节点和边具有单独的ID。
# 获取图中所有节点的数量
g.num_nodes()
# 获取drug节点的数量
g.num_nodes('drug')
# 不同类型的节点有单独的ID。因此,没有指定节点类型就没有明确的返回值。
# g.nodes()---》会报错
# DGLError: Node type name must be specified if there are more than one node types.
g.nodes('drug')
为了设置/获取特定节点和边类型的特征,DGL提供了两种新类型的语法: (伪代码)
g.nodes[‘node_type’].data[‘feat_name’]
和
g.edges[‘edge_type’].data[‘feat_name’] 。
以下是该代码的应用:
# 设置/获取"drug"类型的节点的"hv"特征
g.nodes['drug'].data['hv'] = th.ones(3, 1)
print('获取已经设置的drug类型的节点的hv属性:',g.nodes['drug'].data['hv'])# 设置/获取"treats"类型的边的"he"特征
g.edges['treats'].data['he'] = th.zeros(1, 1)
print('获取已经设置的treats类型的节点的he属性:',g.edges['treats'].data['he'])
但是当图中仅有一种节点或者边的类型时,则不需要指定节点或者边的类型:
g = dgl.heterograph({('drug', 'interacts', 'drug'): (th.tensor([0, 1]), th.tensor([1, 2])),('drug', 'is similar', 'drug'): (th.tensor([0, 1]), th.tensor([2, 3]))
})
g.nodes()
# 设置/获取单一类型的节点或边特征,不必使用新的语法
g.ndata['hv'] = th.ones(4, 1)
边类型子图
用户可以通过指定要保留的关系来创建异构图的子图,相关的特征也会被拷贝。
g = dgl.heterograph({('drug', 'interacts', 'drug'): (th.tensor([0, 1]), th.tensor([1, 2])),('drug', 'interacts', 'gene'): (th.tensor([0, 1]), th.tensor([2, 3])),('drug', 'treats', 'disease'): (th.tensor([1]), th.tensor([2]))
})
g.nodes['drug'].data['hv'] = th.ones(3, 1)
# 保留关系 ('drug', 'interacts', 'drug') 和 ('drug', 'treats', 'disease') 。
# 'drug' 和 'disease' 类型的节点也会被保留
eg = dgl.edge_type_subgraph(g, [('drug', 'interacts', 'drug'),('drug', 'treats', 'disease')])
print('形成的子图:',eg)
同时相关的特征也会被保存:
# 相关的特征也会被拷贝
print(eg.nodes['drug'].data['hv'])
DGL提供了 dgl.save_graphs() 和 dgl.load_graphs() 函数,分别用于以二进制格式保存异构图和加载它们。
DGL提供了 dgl.save_graphs() 和 dgl.load_graphs() 函数,分别用于以二进制格式保存异构图和加载它们。
将异构图转成同构图
异构图有如下的优点:
- 不同类型的节点和边的特征具有不同的数据类型或大小
- 用户希望对不同类型的节点和边应用不同的操作
但是如果不希望区分节点和边的类型可以使用dgl.DGLGraph.to_homogeneous() 将异构图转成同构图,具体包括以下步骤:
- 用从0开始的连续整数重新标记所有类型的节点和边。
- 对所有的节点和边合并用户指定的特征
g = dgl.heterograph({('drug', 'interacts', 'drug'): (th.tensor([0, 1]), th.tensor([1, 2])),('drug', 'treats', 'disease'): (th.tensor([1]), th.tensor([2]))})
g.nodes['drug'].data['hv'] = th.zeros(3, 1)
g.nodes['disease'].data['hv'] = th.ones(3, 1)
g.edges['interacts'].data['he'] = th.zeros(2, 1)
g.edges['treats'].data['he'] = th.zeros(1, 2)
# 默认情况下不进行特征合并
hg = dgl.to_homogeneous(g)
# 查看原始的节点特征是否在创建的同构图中:
print('hv' in hg.ndata)
# out: False
拷贝边的特征:对于要拷贝的特征,DGL假定不同类型的节点或边的需要合并的特征具有相同的大小和数据类型。执行hg = dgl.to_homogeneous(g, edata=['he'])
会报错:
因为
g.edges['interacts'].data['he'] = th.zeros(2, 1)
和
g.edges['treats'].data['he'] = th.zeros(1, 2)
具有不同的shape因此会报错(DGLError: Cannot concatenate column ‘he’ with shape Scheme(shape=(2,), dtype=torch.float32) and shape Scheme(shape=(1,), dtype=torch.float32)
)
而节点的特征是有一致的shape所以可以进行拷贝,例如这里的节点的特征的shape是一致的,所以可以进行拷贝。
# 拷贝节点特征
hg = dgl.to_homogeneous(g, ndata=['hv'])
print('拷贝后节点的特征:',hg.ndata['hv'])
原始的节点和边的类型以及对应的ID被保存在ndata和edata中。
节点特征:
# 异构图中节点类型的顺序
print('原始异构图中节点类型的顺序:',g.ntypes)
# 原始节点类型
print('原始异构图中节点类型:',hg.ndata[dgl.NTYPE])
# 原始的特定类型节点ID
print('原始的特定类型节点ID:',hg.ndata[dgl.NID])
边特征:
# 异构图中边类型的顺序
print('异构图中边类型的顺序:',g.etypes)
# 原始边类型
print('原始边类型:',hg.edata[dgl.ETYPE])
# 原始的特定类型边ID
print('原始的特定类型边ID:',hg.edata[dgl.EID])
子图合并
出于建模的目的,用户也可以只对其中的子图进行关系的合并:
g = dgl.heterograph({('drug', 'interacts', 'drug'): (th.tensor([0, 1]), th.tensor([1, 2])),('drug', 'interacts', 'gene'): (th.tensor([0, 1]), th.tensor([2, 3])),('drug', 'treats', 'disease'): (th.tensor([1]), th.tensor([2]))
})
sub_g = dgl.edge_type_subgraph(g, [('drug', 'interacts', 'drug'),('drug', 'interacts', 'gene')])
h_sub_g = dgl.to_homogeneous(sub_g)
print(h_sub_g)