ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

DGL 异构图(Heterogeneous Graph)完全指南:从构建、类型化特征到子图抽取与同质化转换

DGL 异构图(Heterogeneous Graph)完全指南:从构建、类型化特征到子图抽取与同质化转换 人工智能机器学习深度学习图计算【免费下载链接】dglPython package built to ease deep learning on graph, on top of existing DL frameworks.项目地址https://gitcode.com/gh_mirrors/dg/dgl点击查看免费下载本指南以 DGL 官方用户指南韩文版 docs/source/guide_ko/graph-heterogeneous.rst英文原版见 docs/source/guide/graph-heterogeneous.rst为核心骨架系统讲解 DGL 中异构图的完整技术栈如何用关系三元组创建异构图、如何理解元图metagraph、如何通过类型化语法读写节点/边特征、如何从 CSV 与 DGL 二进制格式加载、如何抽取边类型子图以及如何将其转换为同质图。读完本文你将掌握异构图在 DGL 中的全生命周期操作并理解其底层实现如特征合并规则、ID 重标号机制可直接运用于药物-基因-疾病、用户-商品-话题等真实异构场景的建模。什么是异构图异构图heterogeneous graph简称 heterograph是指节点和边可以具有不同类型的图。不同类型的节点/边拥有相互独立的 ID 空间和特征存储——例如一个同时包含user与game两类节点的图中user的节点 ID 和game的节点 ID 都从 0 开始编号且各自携带完全不同的特征字段如用户画像特征、游戏属性特征。这一点是异构图区别于同质图homogeneous graph的本质所在也是 DGL 为异构图提供独立 API 与语法支持的根本原因。创建异构图在 DGL 中异构图被表示为按关系relation拆分的一组图每个关系是一个字符串三元组(source node type, edge type, destination node type)即(源节点类型, 边类型, 目标节点类型)。由于这个三元组能唯一确定一种边类型DGL 将其称为规范边类型canonical edge type。下面的代码创建了一个包含 3 种节点类型、3 种边类型的异构图以 PyTorch 后端为例 import dgl import torch as th # Create a heterograph with 3 node types and 3 edges types. graph_data { ... (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 dgl.heterograph(graph_data) g.ntypes [disease, drug, gene] g.etypes [interacts, interacts, treats] g.canonical_etypes [(drug, interacts, drug), (drug, interacts, gene), (drug, treats, disease)]从输出可以看出g.ntypes列出全部节点类型注意顺序由 DGL 内部确定性算法决定不一定与graph_data中的书写顺序一致g.etypes只列出边类型名称因此重名的interacts会出现两次g.canonical_etypes以完整三元组形式列出规范边类型可以精确区分同名的不同边。heterograph 构造函数的完整签名从源码 python/dgl/convert.py 可以看到dgl.heterograph的完整签名是def heterograph(data_dict, num_nodes_dictNone, idtypeNone, deviceNone):各参数含义如下参数类型说明data_dictdict键为(src_type, edge_type, dst_type)三元组值为图数据(U, V)其中(U[i], V[i])构成第i条边num_nodes_dictdict[str, int]显式指定某节点类型的节点总数未指定的类型会取所有涉及该类型的数据中的最大 ID 加 1若给定的值不大于该类型最大 ID 会抛出错误idtypetorch.int32 / torch.int64图结构信息节点/边 ID的存储类型为None时根据data_dict推断节点张量格式沿用张量自身 dtype序列格式默认 int64devicetorch.device返回图的设备为None时使用输入张量的设备非节点张量格式默认 CPUdata_dict的值除了最常用的(Tensor, Tensor)节点张量元组外还支持多种稀疏格式源码 python/dgl/convert.py(Tensor, Tensor)两个一维张量dtype 必须一致int32 或 int64且设备上下文一致(coo, (Tensor, Tensor))等价于(Tensor, Tensor)(csr, (indptr, indices, eids))CSR 邻接矩阵表示eids可为空0 元素表示从 0 开始的连续 ID(csc, (indptr, indices, eids))CSC 邻接矩阵表示。张量也可以替换为任意整数可迭代对象list、tuple、numpy.ndarray 等。官方文档还提示最高效的构建方式是直接提供节点张量元组且不指定idtype/device此时返回图与输入张量共享存储此外 DGL 内部会按需维护多种稀疏格式副本大规模图内存紧张时可使用dgl.DGLGraph.formats限制存储格式见 python/dgl/convert.py 的 Notes。同质图与二分图只有一个关系的特殊异构图值得强调的是同质图和二分图都只是只有一个关系的特殊异构图 # A homogeneous graph dgl.heterograph({(node_type, edge_type, node_type): (u, v)}) # A bipartite graph dgl.heterograph({(source_type, edge_type, destination_type): (u, v)})这一设计让 DGL 的图 API 能够用一套统一的底层实现覆盖从同质图到多类型异构图的全部场景。元图Metagraph异构图的模式定义与异构图关联的元图metagraph是该图的模式schema它指定了节点集合以及节点间边的类型约束。元图中的节点u对应异构图中的一种节点类型元图中的边(u, v)表示异构图里存在从类型u的节点到类型v的节点的边。打印异构图对象或调用g.metagraph()即可查看其元图信息 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)]) g.metagraph().edges() OutMultiEdgeDataView([(drug, drug), (drug, gene), (drug, disease)])g.metagraph()返回的是以节点类型为节点的多重图结构实现见 python/dgl/heterograph.py从中可以直观看出异构图允许哪些类型之间的连接关系。相关 API 还包括dgl.heterograph、g.ntypes、g.etypes、g.canonical_etypes、g.metagraph()。处理多种类型类型化 API 与类型化特征语法当图中存在多种节点/边类型时调用 DGL 中与类型相关的 API 必须显式指定类型因为不同类型的节点拥有独立的 ID脱离类型谈 ID 是没有意义的。计数与取节点 # Get the number of all nodes in the graph g.num_nodes() 10 # Get the number of drug nodes g.num_nodes(drug) 3 # Nodes of different types have separate IDs, # hence not well-defined without a type specified g.nodes() DGLError: Node type name must be specified if there are more than one node types. g.nodes(drug) tensor([0, 1, 2])g.num_nodes()不带参数返回所有类型节点总数上例为 10 3 disease 3 drug 4 gene带类型参数则返回该类型节点数g.nodes()在多种类型存在时必须传入类型名否则抛出DGLError。类型化特征读写语法针对特定节点/边类型的特征设置与获取DGL 提供两种新语法g.nodes[node_type].data[feat_name]g.edges[edge_type].data[feat_name] # Set/get feature hv for nodes of type drug g.nodes[drug].data[hv] th.ones(3, 1) g.nodes[drug].data[hv] tensor([[1.], [1.], [1.]]) # Set/get feature he for edge of type treats g.edges[treats].data[he] th.zeros(1, 1) g.edges[treats].data[he] tensor([[0.]])这种语法将按类型的特征字典封装成与g.ndata[feat]一致的读写接口底层为每种节点/边类型维护独立的特征框架frame因此不同类型可以持有不同形状、不同 dtype的特征互不干扰。单一类型的简化写法如果图只有一种节点/边类型则无需指定类型直接使用传统语法 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() tensor([0, 1, 2, 3]) # To set/get feature with a single type, no need to use the new syntax g.ndata[hv] th.ones(4, 1)即使etypes有多个只要节点类型只有一种节点相关操作仍可省略类型边操作是否可省略取决于边类型是否能唯一确定三元组。边类型唯一确定时可用单字符串一个实用技巧当边类型名称能唯一确定其源/目标节点类型时可以用单字符串代替完整三元组。例如下图包含(user, plays, game)和(user, likes, game)两个关系时只需用plays或likes即可唯一指定对应关系无需再写(user, plays, game)这种完整三元组。从磁盘加载异构图方式一CSV及 GML/JSON 等常规格式存储异构图最常见的方式是将不同类型的节点与边分别存放到不同的 CSV 文件中例如# data folder data/ |-- drug.csv # drug nodes |-- gene.csv # gene nodes |-- disease.csv # disease nodes |-- drug-interact-drug.csv # drug-drug interaction edges |-- drug-interact-gene.csv # drug-gene interaction edges |-- drug-treat-disease.csv # drug-treat-disease edges与同质图的处理方式完全一致使用 Pandas 等包解析各 CSV 文件将数据存入 numpy 数组或框架张量再按(src_type, edge_type, dst_type)组织成关系字典最后调用dgl.heterograph构建异构图。该方法同样适用于 GML、JSON 等其他常见格式。方式二DGL 二进制格式DGL 提供了专有的二进制格式读写函数dgl.save_graphs与dgl.load_graphs用于保存和读取异构图。其实现位于 python/dgl/data/graph_serialize.py要点如下保存save_graphs(filename, g_list, labelsNone, formatsNone)同时保存图结构与节点/边特征labels用于保存图级别特征键为字符串、值为张量的字典formats可指定coo/csc/csr的任意组合未指定时按coo csc csr的优先级选择一种可用格式加载load_graphs(filename, idx_listNone)返回(graph_list, labels)元组文件含多张图时可用idx_list只加载部分图但返回的 labels 字典始终包含全部图的标签加载时自动检测文件版本v1/v2旧版本文件会给出升级提示除本地文件外DGL 还支持直接读写s3://...S3与hdfs://...HDFS路径方便大规模异构数据的分布式存取。边类型子图按关系抽取子图dgl.edge_type_subgraph用于保留指定关系、抽取异构图的子图抽取时特征会被一并复制复制是惰性的仅在需要时才发生数据搬运见 python/dgl/subgraph.py 的文档说明。 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) # Retain relations (drug, interacts, drug) and (drug, treats, disease) # All nodes for drug and disease will be retained eg dgl.edge_type_subgraph(g, [(drug, interacts, drug), ... (drug, treats, disease)]) eg Graph(num_nodes{disease: 3, drug: 3}, num_edges{(drug, interacts, drug): 2, (drug, treats, disease): 1}, metagraph[(drug, drug, interacts), (drug, disease, treats)]) # The associated features will be copied as well eg.nodes[drug].data[hv] tensor([[1.], [1.], [1.]])关键行为从源码 python/dgl/subgraph.py 可确认节点保留策略子图中会保留所有与被保留边关联的节点类型的全部节点。上例中drug与disease两类的所有节点都得以保留而gene类型则被剔除特征复制被保留节点/边的特征随子图一起保留上例中hv特征完整复制到eg参数格式etypes支持完整三元组(str, str, str)也支持能唯一标识三元组的单字符串配套 API还有node_type_subgraph按节点类型抽取见 python/dgl/subgraph.py可供组合使用注意该函数会丢弃 batch 信息如需保留需通过dgl.DGLGraph.set_batch_num_nodes/set_batch_num_edges手动设置。将异构图转换为同质图异构图在以下场景中具有明显优势不同类型的节点/边拥有不同的特征数据类型或形状需要对不同类型的节点/边应用不同的算子。如果上述条件不满足——即建模过程中不需要区分节点/边类型——就可以用dgl.to_homogeneous将异构图转换为同质图源码 python/dgl/convert.py。转换过程分两步重新标号将所有类型的节点/边重新标注为从 0 开始的连续整数合并特征将用户指定的特征跨节点/边类型进行拼接拼接要求各类型特征具有相同的 shape 与 dtype。默认行为不合并任何特征 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) # By default, it does not merge any features hg dgl.to_homogeneous(g) hv in hg.ndata False特征合并的约束shape 与 dtype 必须一致 # Copy edge features # For feature copy, it expects features to have # the same size and dtype across node/edge types hg dgl.to_homogeneous(g, edata[he]) DGLError: Cannot concatenate column he with shape Scheme(shape(2,), dtypetorch.float32) and shape Scheme(shape(1,), dtypetorch.float32)上例中interacts边的he特征形状为(2,)而treats边的he特征形状为(1,)二者 shape 不一致因此抛出DGLError。这说明特征合并要求同名特征在各类型间 shape 与 dtype 完全一致。 # Copy node features hg dgl.to_homogeneous(g, ndata[hv]) hg.ndata[hv] tensor([[1.], [1.], [1.], [0.], [0.], [0.]])合并顺序与g.ntypes一致本例中disease在前3 个节点特征为 1drug在后3 个节点特征为 0。原始类型与类型内 ID 的保留转换后原始节点/边类型及类型内 ID 会以特征形式存储在hg.ndata和hg.edata中对应的常量键为dgl.NTYPE、dgl.NID、dgl.ETYPE、dgl.EID # Order of node types in the heterograph g.ntypes [disease, drug] # Original node types hg.ndata[dgl.NTYPE] tensor([0, 0, 0, 1, 1, 1]) # Original type-specific node IDs hg.ndata[dgl.NID] tensor([0, 1, 2, 0, 1, 2]) # Order of edge types in the heterograph g.etypes [interacts, treats] # Original edge types hg.edata[dgl.ETYPE] tensor([0, 0, 1]) # Original type-specific edge IDs hg.edata[dgl.EID] tensor([0, 1, 0])从源码 python/dgl/convert.py 可以确认实现细节同质图节点按g.ntypes顺序连续排列第一种类型的节点占据 ID0 ~ num_nodes(ntypes[0])之后紧接第二种类型的节点以此类推ID 重标号通过offset_per_ntype前缀和偏移完成dgl.NTYPE/dgl.ETYPE始终以 int64 存储值为类型在g.ntypes/g.canonical_etypes中的下标dgl.NID/dgl.EID保存各节点/边在原类型内部的 ID因此可以实现从同质图反推原始异构身份。to_homogeneous 的完整参数dgl.to_homogeneous(G, ndataNone, edataNone, store_typeTrue, return_countFalse)中还有两个值得注意的选项store_typeTrue默认将类型信息以dgl.NTYPE/dgl.ETYPE特征形式存储若不需要可设为False节省开销return_countTrue以整数列表形式返回各类型节点/边数量第 i 个元素对应第 i 种类型的数量比逐节点存储类型特征更节省内存源码注释建议在不需要逐节点类型时优先使用store_typeFalsereturn_countTrue的组合官方文档还指出对转换结果再次调用to_heterogeneous可以还原出原图二者互为逆操作。实战组合先抽子图再同质化建模时经常需要把若干关系聚为一组、对其统一应用相同算子。此时的标准做法是先用edge_type_subgraph抽取目标关系子图再对该子图执行to_homogeneous 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) h_sub_g Graph(num_nodes7, num_edges4, ...)这个关系筛选 → 类型归一的两段式流程是 DGL 中处理只关心部分关系且不区分类型场景的推荐实践也常用于RelGraphConv等需要类型统计信息的模型return_count返回的ntype_count/etype_count列表可直接加速此类运算见 python/dgl/convert.py。总结DGL 的异构图体系可以归纳为一条清晰的脉络环节核心 API关键要点构建dgl.heterograph关系三元组键 节点张量/稀疏格式值支持num_nodes_dict、idtype、device结构信息g.ntypes/g.etypes/g.canonical_etypes/g.metagraph()类型列表、规范边类型、元图模式类型化访问g.num_nodes(T)、g.nodes(T)、g.nodes[T].data[feat]多类型必须显式指定类型单类型可省略持久化dgl.save_graphs/dgl.load_graphs二进制格式支持多图、labels、S3/HDFS子图抽取dgl.edge_type_subgraph/dgl.node_type_subgraph按关系/类型抽取惰性复制特征类型归一dgl.to_homogeneous连续重标号 特征拼接NTYPE/NID/ETYPE/EID保留原始身份掌握这套 API 与底层机制即可在 DGL 中自如处理真实世界最常见的多类型图数据。进一步学习可参考官方用户指南英文版 docs/source/guide/graph-heterogeneous.rst 与韩文原版 docs/source/guide_ko/graph-heterogeneous.rst并结合仓库中的 python/dgl/convert.py、python/dgl/subgraph.py、python/dgl/heterograph.py 源码深入理解实现细节。赞分享人工智能机器学习深度学习图计算【免费下载链接】dglPython package built to ease deep learning on graph, on top of existing DL frameworks.项目地址https://gitcode.com/gh_mirrors/dg/dgl点击查看免费下载相关推荐DGL 异构图Heterogeneous Graph完全指南从构造、特征管理到同质化转换DGL 异构图Heterogeneous Graph完全指南从构造、特征管理到同质化转换 导读 本指南基于 DGL 官方用户指南中「Heterogeneo人工智能机器学习深度学习图计算DGL 异构图Heterogeneous Graph完全指南创建、操作与同构化转换DGL 异构图Heterogeneous Graph完全指南创建、操作与同构化转换 本指南基于 DGL 官方中文用户手册 guide_cn/graph h人工智能机器学习深度学习图计算基于 DGL 实现 Heterogeneous Graph TransformerACM 异构图节点分类实战基于 DGL 实现 Heterogeneous Graph TransformerACM 异构图节点分类实战 Heterogeneous Graph Tran人工智能机器学习深度学习图计算创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表