简单图论与PyG库

简单图论与PyG库

  • 图结构数据上的机器学习
    • 应用神经网络于图面临的挑战
  • 环境配置与PyG中图与图数据集的表示和使用
    • 一、引言
    • 二、环境配置
    • 三、`Data`类——PyG中图的表示及其使用
      • `Data`对象的创建
        • 通过构造函数
        • 转`dict`对象为`Data`对象
      • `Data`对象转换成其他类型数据
      • 获取`Data`对象属性
      • 设置`Data`对象属性
      • 获取`Data`对象包含的属性的关键字
      • 对边排序并移除重复的边
      • `Data`对象的其他性质
    • 四、`Dataset`类——PyG中图数据集的表示及其使用
      • 生成数据集对象并分析数据集
      • 分析数据集中样本
      • 数据集的使用
    • 结语
    • 作业
    • 参考资料

本文主要为学习笔记,完整开源笔记见 Datawhale

图结构数据上的机器学习

简单图论与PyG库_第1张图片

  1. 节点预测:预测节点的类别或某类属性的取值
    1. 例子:对是否是潜在客户分类、对游戏玩家的消费能力做预测
  2. 边预测:预测两个节点间是否存在链接
    1. 例子:Knowledge graph completion、好友推荐、商品推荐
  3. 图的预测:对不同的图进行分类或预测图的属性
    1. 例子:分子属性预测
  4. 节点聚类:检测节点是否形成一个社区
    1. 例子:社交圈检测
  5. 其他任务
    1. 图生成:例如药物发现
    2. 图演变:例如物理模拟
    3. ……

应用神经网络于图面临的挑战

应用神经网络于图面临的挑战。

过去的深度学习应用中,主要接触的数据形式主要是这四种:矩阵、张量、序列(sequence)和时间序列(time series)它们都是规则的结构化的数据。然而图数据是非规则的非结构化的,它具有以下的特点:

  1. 任意的大小和复杂的拓扑结构;
  2. 没有固定的节点排序或参考点;
  3. 通常是动态的,并具有多模态的特征;
  4. 图的信息并非只蕴含在节点信息和边的信息中,图的信息还包括了图的拓扑结构。

简单图论与PyG库_第2张图片

以往的深度学习技术是为规则且结构化的数据设计的,无法直接用于图数据。应用于图数据的神经网络,要求

  • 适用于不同度的节点
  • 节点表征的计算与邻接节点的排序无关
  • 不但能够根据节点信息、邻接节点的信息和边的信息计算节点表征,还能根据图拓扑结构计算节点表征

环境配置与PyG中图与图数据集的表示和使用

一、引言

PyTorch Geometric (PyG)是面向几何深度学习的PyTorch的扩展库,几何深度学习指的是应用于图和其他不规则、非结构化数据的深度学习。基于PyG库,可以轻松地根据数据生成一个图对象,然后很方便的使用它;也可以容易地为一个图数据集构造一个数据集类,然后很方便的将它用于神经网络。

接下来主要完成以下任务:

  1. 首先学习程序运行环境的配置
  2. 接着学习PyG中图数据的表示及其使用,即学习PyG中Data类。
  3. 最后学习PyG中图数据集的表示及其使用,即学习PyG中Dataset类。

二、环境配置

  1. 使用nvidia-smi命令查询显卡驱动是否正确安装

  2. 安装正确版本的pytorch和cudatoolkit,此处安装1.8.1版本的pytorch和11.1版本的cudatoolkit

    1. conda install pytorch torchvision torchaudio cudatoolkit=11.1 -c pytorch -c nvidia
    2. 确认是否正确安装,正确的安装应出现下方的结果
    $ python -c "import torch; print(torch.__version__)"
    # 1.8.1
    $ python -c "import torch; print(torch.version.cuda)"
    # 11.1
    
  3. 安装正确版本的PyG

    pip install torch-scatter -f https://pytorch-geometric.com/whl/torch-1.8.0+cu111.html
    pip install torch-sparse -f https://pytorch-geometric.com/whl/torch-1.8.0+cu111.html
    pip install torch-cluster -f https://pytorch-geometric.com/whl/torch-1.8.0+cu111.html
    pip install torch-spline-conv -f https://pytorch-geometric.com/whl/torch-1.8.0+cu111.html
    pip install torch-geometric
    

其他版本的安装方法以及安装过程中出现的大部分问题的解决方案可以在Installation of of PyTorch Geometric 页面找到。

三、Data类——PyG中图的表示及其使用

Data对象的创建

Data类的官方文档为torch_geometric.data.Data。

通过构造函数

Data类的构造函数

class Data(object):

    def __init__(self, x=None, edge_index=None, edge_attr=None, y=None, **kwargs):
    r"""
    Args:
        x (Tensor, optional): 节点属性矩阵,大小为`[num_nodes, num_node_features]`
        edge_index (LongTensor, optional): 边索引矩阵,大小为`[2, num_edges]`,第0行为尾节点,第1行为头节点,头指向尾
        edge_attr (Tensor, optional): 边属性矩阵,大小为`[num_edges, num_edge_features]`
        y (Tensor, optional): 节点或图的标签,任意大小(,其实也可以是边的标签)
	
    """
    self.x = x
    self.edge_index = edge_index
    self.edge_attr = edge_attr
    self.y = y

    for key, item in kwargs.items():
        if key == 'num_nodes':
            self.__num_nodes__ = item
        else:
            self[key] = item

edge_index的每一列定义一条边,其中第一行为边起始节点的索引,第二行为边结束节点的索引。这种表示方法被称为COO格式(coordinate format),通常用于表示稀疏矩阵。PyG不是用稠密矩阵 A ∈ { 0 , 1 } ∣ V ∣ × ∣ V ∣ \mathbf{A} \in \{ 0, 1 \}^{|\mathcal{V}| \times |\mathcal{V}|} A{0,1}V×V来持有邻接矩阵的信息,而是用仅存储邻接矩阵 A \mathbf{A} A中非 0 0 0元素的稀疏矩阵来表示图。

通常,一个图至少包含x, edge_index, edge_attr, y, num_nodes5个属性,当图包含其他属性时,我们可以通过指定额外的参数使Data对象包含其他的属性

graph = Data(x=x, edge_index=edge_index, edge_attr=edge_attr, y=y, num_nodes=num_nodes, other_attr=other_attr)

dict对象为Data对象

我们也可以将一个dict对象转换为一个Data对象

graph_dict = {
    'x': x,
    'edge_index': edge_index,
    'edge_attr': edge_attr,
    'y': y,
    'num_nodes': num_nodes,
    'other_attr': other_attr
}
graph_data = Data.from_dict(graph_dict)

from_dict是一个类方法:

@classmethod
def from_dict(cls, dictionary):
    r"""Creates a data object from a python dictionary."""
    data = cls()
    for key, item in dictionary.items():
        data[key] = item

    return data

注意graph_dict中属性值的类型与大小的要求与Data类的构造函数的要求相同。

Data对象转换成其他类型数据

我们可以将Data对象转换为dict对象:

def to_dict(self):
    return {key: item for key, item in self}

或转换为namedtuple

def to_namedtuple(self):
    keys = self.keys
    DataTuple = collections.namedtuple('DataTuple', keys)
    return DataTuple(*[self[key] for key in keys])

获取Data对象属性

x = graph_data['x']

设置Data对象属性

graph_data['x'] = x

获取Data对象包含的属性的关键字

graph_data.keys()

对边排序并移除重复的边

graph_data.coalesce()

Data对象的其他性质

我们通过观察PyG中内置的一个图来查看Data对象的性质:

from torch_geometric.datasets import KarateClub

dataset = KarateClub()
data = dataset[0]  # Get the first graph object.
print(data)
print('==============================================================')

# 获取图的一些信息
print(f'Number of nodes: {data.num_nodes}') # 节点数量
print(f'Number of edges: {data.num_edges}') # 边数量
print(f'Number of node features: {data.num_node_features}') # 节点属性的维度
print(f'Number of node features: {data.num_features}') # 同样是节点属性的维度
print(f'Number of edge features: {data.num_edge_features}') # 边属性的维度
print(f'Average node degree: {data.num_edges / data.num_nodes:.2f}') # 平均节点度
print(f'if edge indices are ordered and do not contain duplicate entries.: {data.is_coalesced()}') # 是否边是有序的同时不含有重复的边
print(f'Number of training nodes: {data.train_mask.sum()}') # 用作训练集的节点
print(f'Training node label rate: {int(data.train_mask.sum()) / data.num_nodes:.2f}') # 用作训练集的节点数占比
print(f'Contains isolated nodes: {data.contains_isolated_nodes()}') # 此图是否包含孤立的节点
print(f'Contains self-loops: {data.contains_self_loops()}')  # 此图是否包含自环的边
print(f'Is undirected: {data.is_undirected()}')  # 此图是否是无向图

四、Dataset类——PyG中图数据集的表示及其使用

PyG内置了大量常用的基准数据集,接下来我们以PyG内置的Planetoid数据集为例,来学习PyG中图数据集的表示及使用

Planetoid数据集类的官方文档为torch_geometric.datasets.Planetoid。

生成数据集对象并分析数据集

如下方代码所示,在PyG中生成一个数据集是简单直接的。在第一次生成PyG内置的数据集时,程序首先下载原始文件,然后将原始文件处理成包含Data对象的Dataset对象并保存到文件。

from torch_geometric.datasets import Planetoid

dataset = Planetoid(root='/dataset/Cora', name='Cora')
# Cora()

len(dataset)
# 1

dataset.num_classes
# 7

dataset.num_node_features
# 1433

分析数据集中样本

可以看到该数据集只有一个图,包含7个分类任务,节点的属性为1433维度。

data = dataset[0]
# Data(edge_index=[2, 10556], test_mask=[2708],
#         train_mask=[2708], val_mask=[2708], x=[2708, 1433], y=[2708])

data.is_undirected()
# True

data.train_mask.sum().item()
# 140

data.val_mask.sum().item()
# 500

data.test_mask.sum().item()
# 1000

现在我们看到该数据集包含的唯一的图,有2708个节点,节点特征为1433维,有10556条边,有140个用作训练集的节点,有500个用作验证集的节点,有1000个用作测试集的节点。PyG内置的其他数据集,请小伙伴一一试验,以观察不同数据集的不同。

数据集的使用

假设我们定义好了一个图神经网络模型,其名为Net。在下方的代码中,我们展示了节点分类图数据集在训练过程中的使用。

model = Net().to(device)
data = dataset[0].to(device)
optimizer = torch.optim.Adam(model.parameters(), lr=0.01, weight_decay=5e-4)

model.train()
for epoch in range(200):
    optimizer.zero_grad()
    out = model(data)
    loss = F.nll_loss(out[data.train_mask], data.y[data.train_mask])
    loss.backward()
    optimizer.step()

结语

通过此实践环节,我们学习了程序运行环境的配置PyG中Data对象的生成与使用、以及PyG中Dataset对象的表示和使用。此节内容是图神经网络实践的基础,所涉及的内容是最常用、最基础的,在后面的内容中我们还将学到复杂Data类的构建,和复杂Dataset类的构建。

作业

  • 请通过继承Data类实现一个类,专门用于表示“机构-作者-论文”的网络。该网络包含“机构“、”作者“和”论文”三类节点,以及“作者-机构“和“作者-论文“两类边。对要实现的类的要求:1)用不同的属性存储不同节点的属性;2)用不同的属性存储不同的边(边没有属性);3)逐一实现获取不同节点数量的方法。
# 构造data类
from torch_geometric.data import Data
import torch

"论文-机构-作者网络"

class Triple_data(Data):
    "继承Data并重写一些函数"

    def __init__(self, x_p=None, x_i=None, x_a=None, edge_index_a2p=None, edge_index_a2i=None, edge_attr_a2p=None,
                 edge_attr_a2i=None, y=None, **kwargs):
        """
        :param x_p: 表示paper,节点属性矩阵,大小为[num_nodes,num_node_features]
        :param x_i: 表示instituion,节点属性矩阵,大小为[num_nodes,num_node_features]
        :param x_a: 表示author,节点属性矩阵,大小为[num_nodes,num_node_features]
        :param edge_index_a2p:表示author-paper的边,边索引矩阵,大小为`[2, num_edges]`,第0行为尾节点,第1行为头节点,头指向尾
        :param edge_index_a2i:表示author-institution的边,边索引矩阵,大小为`[2, num_edges]`,第0行为尾节点,第1行为头节点,头指向尾
        :param edge_attr_a2p:对应边的属性矩阵,大小为`[num_edges, num_edge_features]`,默认无属性None
        :param edge_attr_a2i:对应边的属性矩阵,大小为`[num_edges, num_edge_features]`,默认无属性None
        :param y:(Tensor, optional)节点、图或者是边的标签,任意大小
        :param kwargs:其他参数
        """
        super().__init__()
        # 用不同的属性存储不同节点的属性
        self.x_p = x_p
        self.x_i = x_i
        self.x_a = x_a
        # 用不同的属性存储不同的边
        self.edge_attr_a2p = edge_attr_a2p
        self.edge_attr_a2i = edge_attr_a2i
        self.edge_index_a2i = edge_index_a2i
        self.edge_index_a2p = edge_index_a2p
        self.y = y

        for key, item in kwargs.items():
            if key == 'num_nodes':
                self.__num_nodes__ = item
            else:
                self[key] = item

    def num_nodes_triple(self, key):
        if key == 'p':
            return self.x_p.size(0)
        elif key == 'i':
            return self.x_i.size(0)
        elif key == 'a':
            return self.x_a.size(0)
        else:
            pass

if __name__ == "__main__":
    # 假设作者为3,出版机构为4,论文共5篇
    x_a = torch.randn(3, 6)
    x_p = torch.randn(5, 7)
    x_i = torch.randn(4, 5)
    # 节点连接关系
    edge_index_a2p = torch.tensor([
        [0, 1, 2, 3, 4],
        [5, 5, 5, 6, 7],
    ])
    edge_index_a2i = torch.tensor([
        [8, 9, 10, 11],
        [5, 6, 7, 5],
    ])
    tripe_data = Triple_data(x_p, x_i, x_a, edge_index_a2p=edge_index_a2p, edge_index_a2i=edge_index_a2i)
    print(tripe_data)
    # 逐一获取不同节点数量
    for i in ['p', 'i', 'a']:
        tmp = tripe_data.num_nodes_triple(i)
        print("{}节点的个数是:{}".format(i, tmp))

参考资料

  • PyTorch Geometric
  • Installation of of PyTorch Geometric
  • torch_geometric.data.Data
  • torch_geometric.datasets.Planetoid

你可能感兴趣的:(自然语言处理,神经网络)