【图神经网络DGL】数据封装和栗子

简介: 一、DGL简介和安装DGL支持深度学习框架Pytorch、MXNet、tensorflow等,如果使用Pytorch则需要1.5.0版本以上。

一、DGL简介和安装

DGL支持深度学习框架Pytorch、MXNet、tensorflow等,如果使用Pytorch则需要1.5.0版本以上。

image.png

安装就一句:

conda install -c dglteam dgl-cuda10.1

如果遇到报错:

An unexpected error has occurred. Conda has prepared the above report.

可以使用conda clean -i后再下载。

ps:如果使用torch框架,则一定要在下载DGL前下载好pytorch。

如果遇到其他报错可以在github上issue:https://github.com/dmlc/dgl/issues

使用不同的深度学习框架,Use the DGLBACKEND environment variable:

You can use DGLBACKEND=[BACKEND] python gcn.py ... to specify the backend

Or export DGLBACKEND=[BACKEND] to set the global environment variable

Modify the config.json file under “~/.dgl”:

You can use python -m dgl.backend.set_default_backend [BACKEND] to set the default backend

二、基础教程

官方文档的中文版:https://docs.dgl.ai/en/latest/guide_cn/index.html。分为以下章节:

第1章:图

第2章:消息传递范式

第3章:构建图神经网络(GNN)模块

第4章:图数据处理管道

第5章:训练图神经网络

第6章:在大图上的随机(批次)训练

第7章:分布式训练

三、图

关于图,各个节点的边可以是有向,或者无向,每条边还能有属性,如在多重图中,同一对节点之间可以有多条(有向)边,包括自循环的边。例如,两名作者可以在不同年份共同署名文章, 这就带来了具有不同特征的多条边。

3.1 创建图形

# -*- coding: utf-8 -*-
"""
Created on Mon Dec 13 15:10:42 2021
@author: 86493
"""
import dgl
def build_karate_club_graph():
    g = dgl.DGLGraph()
    # 在图中添加34个节点,分别标记为0至333
    g.add_nodes(34)
    # 所有78条边组成一个元组列表
    edge_list = [(1, 0), (2, 0), (2, 1), (3, 0), (3, 1), (3, 2),
        (4, 0), (5, 0), (6, 0), (6, 4), (6, 5), (7, 0), (7, 1),
        (7, 2), (7, 3), (8, 0), (8, 2), (9, 2), (10, 0), (10, 4),
        (10, 5), (11, 0), (12, 0), (12, 3), (13, 0), (13, 1), (13, 2),
        (13, 3), (16, 5), (16, 6), (17, 0), (17, 1), (19, 0), (19, 1),
        (21, 0), (21, 1), (25, 23), (25, 24), (27, 2), (27, 23),
        (27, 24), (28, 2), (29, 23), (29, 26), (30, 1), (30, 8),
        (31, 0), (31, 24), (31, 25), (31, 28), (32, 2), (32, 8),
        (32, 14), (32, 15), (32, 18), (32, 20), (32, 22), (32, 23),
        (32, 29), (32, 30), (32, 31), (33, 8), (33, 9), (33, 13),
        (33, 14), (33, 15), (33, 18), (33, 19), (33, 20), (33, 22),
        (33, 23), (33, 26), (33, 27), (33, 28), (33, 29), (33, 30),
        (33, 31), (33, 32)]
    #为边添加两个列表:src and dst
    src, dst = tuple(zip(*edge_list)) # 这句重点
    g.add_edges(src, dst)
    # 边是有方向的,并使他们双向
    g.add_edges(dst, src)
    return g
G = build_karate_club_graph() 
print('We have %d nodes.' % G.number_of_nodes()) 
print('We have %d edges.' % G.number_of_edges())
# We have 34 nodes.
# We have 156 edges.
import networkx as nx
# 由于实际图形是无向的,因此我们去掉边的方向,以达到可视化的目的
nx_G = G.to_networkx().to_undirected()
# 为了图更加美观,我们使用Kamada-Kawaii layout 
pos = nx.kamada_kawai_layout(nx_G)
nx.draw(nx_G, pos, with_labels=True, node_color=[[.7, .7, .7]])

image.png

当然也可以这样写:

def build_karate_club_graph():
    src = np.array([1, 2, 2, 3, 3, 3, 4, 5, 6, 6, 6, 7, 7, 7, 7, 8, 8, 9, 10, 10,
                    10, 11, 12, 12, 13, 13, 13, 13, 16, 16, 17, 17, 19, 19, 21, 21,
                    25, 25, 27, 27, 27, 28, 29, 29, 30, 30, 31, 31, 31, 31, 32, 32,
                    32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33,
                    33, 33, 33, 33, 33, 33, 33, 33, 33, 33])
    dst = np.array([0, 0, 1, 0, 1, 2, 0, 0, 0, 4, 5, 0, 1, 2, 3, 0, 2, 2, 0, 4,
                    5, 0, 0, 3, 0, 1, 2, 3, 5, 6, 0, 1, 0, 1, 0, 1, 23, 24, 2, 23,
                    24, 2, 23, 26, 1, 8, 0, 24, 25, 28, 2, 8, 14, 15, 18, 20, 22, 23,
                    29, 30, 31, 8, 9, 13, 14, 15, 18, 19, 20, 22, 23, 26, 27, 28, 29, 30,
                    31, 32])
    # Edges are directional in DGL; Make them bi-directional.
    u = np.concatenate([src, dst])
    v = np.concatenate([dst, src])
    # Construct a DGLGraph
    return dgl.DGLGraph((u, v))

如果不用Kamada-Kawaii layout:

# 不用Kamada-Kawaii layout
import networkx as nx 
nx_G = G.to_networkx().to_undirected() 
nx.draw(nx_G, with_labels=True, node_color=[[.7, .7, .7]])

image.png

3.2 图、节点、边

点ID:是一个整数,表示一个节点。对应两个端点ID表示一条边。

边ID:根据边被添加的顺序,给每条边进行编号。和点ID一样都是从0开始编号的。(注意在DGL中的图,所有的边都是有方向的)

节点张量:对于多个节点,图的点ID保存在一个一维的整型张量(如pytorch的tensor类,tensorflow中的tensor类或mxnet框架的ndarray类)。

多条边的指代:一个包含2个节点张量的元组(U,V),其中( U [ i ] , V [ i ] ) (U[i], V[i])(U[i],V[i])指一条从U [ i ] U[i]U[i]到V [ i ] V[i]V[i]的边。

dgl.graph()函数:可以创建一个DGLGraph对象。input为一个边的结合,也支持其他数据源来创建图对象。

image.png

example:下图含有4个节点的图,对应创建代码如下所示:

import dgl
import torch as th
# 边 0->1, 0->2, 0->3, 1->3
u, v = th.tensor([0, 0, 0, 1]), th.tensor([1, 2, 3, 3])
g = dgl.graph((u, v))
print(g) # 图中节点的数量是DGL通过给定的图的边列表中最大的点ID推断所得出的
# 获取节点的ID
print(g.nodes())
# 获取边的对应端点
print(g.edges())
# 获取边的对应端点和边ID
print(g.edges(form='all'))
# 如果具有最大ID的节点没有边,在创建图的时候,用户需要明确地指明节点的数量。
g = dgl.graph((u, v), num_nodes=8)

结果如下,根据上图,每条边的编号和我们边添加的顺序是一样的。

Graph(num_nodes=4, num_edges=4,
      ndata_schemes={}
      edata_schemes={})
tensor([0, 1, 2, 3])
(tensor([0, 0, 0, 1]), tensor([1, 2, 3, 3]))
(tensor([0, 0, 0, 1]), tensor([1, 2, 3, 3]), tensor([0, 1, 2, 3]))

无向图,每条边则都需要创建两个方向,通过dgl.to_bidirected()函数实现。如果在上面的基础上,加上这句:

bg = dgl.to_bidirected(g)
print(bg.edges())
# (tensor([0, 0, 0, 1, 1, 2, 3, 3]), tensor([1, 2, 3, 0, 3, 0, 0, 1]))

由于Tensor类内部使用C来存储,且显性定义了数据类型以及存储的设备信息,DGL推荐使用Tensor作为DGL API的输入。 不过大部分的DGL API也支持Python的可迭代类型(比如列表)或numpy.ndarray类型作为API的输入,方便用户快速进行开发验证。

3.3 节点和边的特征

DGLGraph对象的节点和边可具有多个用户定义、可命名的特征(存储图的节点和边的属性),并且可分别通过ndata和edata进行访问。

栗子:下面创建了2个节点特征,和1个边特征。

# -*- coding: utf-8 -*-
"""
Created on Mon Dec 13 20:14:41 2021
@author: 86493
"""
import dgl
import torch as th
g = dgl.graph(([0, 0, 1, 5], [1, 2, 2, 0])) # 6个节点,4条边
print(g)
# 长度为3的节点特征
g.ndata['x'] = th.ones(g.num_nodes(), 3)               
# 标量整型特征
g.edata['x'] = th.ones(g.num_edges(), dtype=th.int32)  
print(g)
# 不同名称的特征可以具有不同形状
g.ndata['y'] = th.randn(g.num_nodes(), 5)
# 获取节点1的特征
g.ndata['x'][1]                  
# 获取边0和3的特征
g.edata['x'][th.tensor([0, 3])]  
Graph(num_nodes=6, num_edges=4,
      ndata_schemes={}
      edata_schemes={})
Graph(num_nodes=6, num_edges=4,
      ndata_schemes={'x': Scheme(shape=(3,), dtype=torch.float32)}
      edata_schemes={'x': Scheme(shape=(), dtype=torch.int32)})

如果是加权图,可以就将边权值存储在一个边特征:

import torch as th
import dgl
# 边 0->1, 0->2, 0->3, 1->3
edges = th.tensor([0, 0, 0, 1]), th.tensor([1, 2, 3, 3])
weights = th.tensor([0.1, 0.6, 0.9, 0.7])  # 每条边的权重
g = dgl.graph(edges)
g.edata['w'] = weights  # 将其命名为 'w'
print(g)
"""
打印结果为:
Graph(num_nodes=4, num_edges=4,
      ndata_schemes={}
      edata_schemes={'w': Scheme(shape=(), dtype=torch.float32)})
"""

3.4 从外部源创建图

从外部构造一个DGLGraph对象:

  • 从用于图(NetworkX)和稀疏矩阵(SciPy)的外部Python库(NetworkX 和 SciPy)创建而来。
  • 从磁盘加载图数据。
import dgl
import torch as th
import scipy.sparse as sp
spmat = sp.rand(100, 100, density=0.05) # 5%非零项
dgl.from_scipy(spmat)                   # 来自SciPy
"""
Graph(num_nodes=100, num_edges=500,
      ndata_schemes={}
      edata_schemes={})
"""
import networkx as nx
nx_g = nx.path_graph(5) # 一条链路0-1-2-3-4
dgl.from_networkx(nx_g) # 来自NetworkX
"""
Graph(num_nodes=5, num_edges=8,
      ndata_schemes={}
      edata_schemes={})
"""

当无向图的NetworkX图转为DGLGraph对象时,DGL会在内部将1条无向边转为2条有向边,使用networkx.DiGraph则不会这样转换(如下面,还是4条边,没变):

nxg = nx.DiGraph([(2, 1), (1, 2), (2, 3), (0, 0)])
dgl.from_networkx(nxg)
"""
Graph(num_nodes=4, num_edges=4,
      ndata_schemes={}
      edata_schemes={})
"""

DGL在内部将SciPy矩阵和NetworkX图转换为张量来创建图。因此,这些构建方法并不适用于重视性能的场景。

还有从磁盘中加载图。

3.5 异构图

(1)在同构图中,所有节点表示同一类型的实体,所有边表示同一类型的关系。 例如,社交网络的图由表示同一实体类型的人及其相互之间的社交关系组成。

(2)在异构图中,节点和边的类型可以是不同的。

例如,编码市场的图可以有表示”顾客”、”商家”和”商品”的节点, 它们通过“想购买”、“已经购买”、“是顾客”和“正在销售”的边互相连接。

PS:二分图是一类特殊的、常用的异构图, 其中的边连接两类不同类型的节点。例如,在推荐系统中,可以使用二分图表示”用户”和”物品”之间的关系。

(1)创建异构图

一个子图对应一种关系(1个异构图由一坨子图构成)。每个关系由三元组定义:(源节点类型, 边类型, 目标节点类型)。DGL中也讲这种关系成为规范边类型。

image.png

import dgl
import torch as th
# 创建一个具有3种节点类型和3种边类型的异构图
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')]
"""

上面的异构图(节点类型不同,边类型不同,每种类型可以有独立的ID及特征)如下:

image.png

同构图和二分图是一种特殊的异构图(即只包含一种关系)。

# 一个同构图
dgl.heterograph({('node_type', 'edge_type', 'node_type'): (u, v)})
# 一个二分图
dgl.heterograph({('source_type', 'edge_type', 'destination_type'): (u, v)})

图的模式:与异构图相关联的 metagraph 。

它指定节点集和节点之间的边的类型约束。 metagraph 中的一个节点 u 对应于相关异构图中的一个节点类型。 metagraph 中的边 (u,v) 表示在相关异构图中存在从 u 型节点到 v 型节点的边。

g = dgl.heterograph(graph_data)
"""
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')])
"""

(2)使用多种类型

不同类型的节点和边有单独的ID。获取对应类型的边or点:

# 获取图中所有节点的数量
g.num_nodes()# 10
# 获取drug节点的数量
g.num_nodes('drug') # 3
# 不同类型的节点有单独的ID。因此,没有指定节点类型就没有明确的返回值。
g.nodes()
# DGLError: Node type name must be specified if there are more than one node types.
g.nodes('drug')
# tensor([0, 1, 2])

为了设置/获取特定节点和边类型的特征,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)
g.nodes['drug'].data['hv']
"""
tensor([[1.],
        [1.],
        [1.]])
"""
# 设置/获取"treats"类型的边的"he"特征
g.edges['treats'].data['he'] = th.zeros(1, 1)
g.edges['treats'].data['he']
"""
tensor([[0.]])
"""

注意:如果图里只有一种节点或边类型,则不需要指定节点或边的类型。

当边类型唯一地确定了源节点和目标节点的类型时,用户可以只使用一个字符串而不是字符串三元组来指定边类型。例如, 对于具有两个关系 (‘user’, ‘plays’, ‘game’) 和 (‘user’, ‘likes’, ‘game’) 的异构图, 只使用 ‘plays’ 或 ‘like’ 来指代这两个关系是可以的

相关文章
|
6月前
|
机器学习/深度学习 人工智能 自然语言处理
ICLR 2024 Spotlight:训练一个图神经网络即可解决图领域所有分类问题!
【2月更文挑战第17天】ICLR 2024 Spotlight:训练一个图神经网络即可解决图领域所有分类问题!
195 2
ICLR 2024 Spotlight:训练一个图神经网络即可解决图领域所有分类问题!
|
5月前
|
机器学习/深度学习 人工智能 自然语言处理
Transformer 能代替图神经网络吗?
Transformer模型的革新性在于其自注意力机制,广泛应用于多种任务,包括非原始设计领域。近期研究专注于Transformer的推理能力,特别是在图神经网络(GNN)上下文中。
96 5
|
4月前
|
机器学习/深度学习 搜索推荐 知识图谱
图神经网络加持,突破传统推荐系统局限!北大港大联合提出SelfGNN:有效降低信息过载与数据噪声影响
【7月更文挑战第22天】北大港大联手打造SelfGNN,一种结合图神经网络与自监督学习的推荐系统,专攻信息过载及数据噪声难题。SelfGNN通过短期图捕获实时用户兴趣,利用自增强学习提升模型鲁棒性,实现多时间尺度动态行为建模,大幅优化推荐准确度与时效性。经四大真实数据集测试,SelfGNN在准确性和抗噪能力上超越现有模型。尽管如此,高计算复杂度及对图构建质量的依赖仍是待克服挑战。[详细论文](https://arxiv.org/abs/2405.20878)。
80 5
|
4月前
|
机器学习/深度学习 PyTorch 算法框架/工具
图神经网络是一类用于处理图结构数据的神经网络。与传统的深度学习模型(如卷积神经网络CNN和循环神经网络RNN)不同,
图神经网络是一类用于处理图结构数据的神经网络。与传统的深度学习模型(如卷积神经网络CNN和循环神经网络RNN)不同,
|
4月前
|
机器学习/深度学习 编解码 数据可视化
图神经网络版本的Kolmogorov Arnold(KAN)代码实现和效果对比
目前我们看到有很多使用KAN替代MLP的实验,但是目前来说对于图神经网络来说还没有类似的实验,今天我们就来使用KAN创建一个图神经网络Graph Kolmogorov Arnold(GKAN),来测试下KAN是否可以在图神经网络方面有所作为。
187 0
|
5月前
|
机器学习/深度学习 数据采集 TensorFlow
使用Python实现深度学习模型:图神经网络(GNN)
使用Python实现深度学习模型:图神经网络(GNN)
264 1
|
6月前
|
机器学习/深度学习 自然语言处理 搜索推荐
【传知代码】图神经网络长对话理解-论文复现
在ACL2023会议上发表的论文《使用带有辅助跨模态交互的关系时态图神经网络进行对话理解》提出了一种新方法,名为correct,用于多模态情感识别。correct框架通过全局和局部上下文信息捕捉对话情感,同时有效处理跨模态交互和时间依赖。模型利用图神经网络结构,通过构建图来表示对话中的交互和时间关系,提高了情感预测的准确性。在IEMOCAP和CMU-MOSEI数据集上的实验结果证明了correct的有效性。源码和更多细节可在文章链接提供的附件中获取。
【传知代码】图神经网络长对话理解-论文复现
|
5月前
|
机器学习/深度学习 搜索推荐 PyTorch
【机器学习】图神经网络:深度解析图神经网络的基本构成和原理以及关键技术
【机器学习】图神经网络:深度解析图神经网络的基本构成和原理以及关键技术
1135 2
|
6月前
|
机器学习/深度学习 JSON PyTorch
图神经网络入门示例:使用PyTorch Geometric 进行节点分类
本文介绍了如何使用PyTorch处理同构图数据进行节点分类。首先,数据集来自Facebook Large Page-Page Network,包含22,470个页面,分为四类,具有不同大小的特征向量。为训练神经网络,需创建PyTorch Data对象,涉及读取CSV和JSON文件,处理不一致的特征向量大小并进行归一化。接着,加载边数据以构建图。通过`Data`对象创建同构图,之后数据被分为70%训练集和30%测试集。训练了两种模型:MLP和GCN。GCN在测试集上实现了80%的准确率,优于MLP的46%,展示了利用图信息的优势。
90 1
|
6月前
|
机器学习/深度学习 数据挖掘 算法框架/工具
想要了解图或图神经网络?没有比看论文更好的方式,面试阿里国际站运营一般会问什么
想要了解图或图神经网络?没有比看论文更好的方式,面试阿里国际站运营一般会问什么