写点什么

基于 Pytorch Gemotric 在昇腾上实现 GAT 图神经网络

  • 2025-05-26
    上海
  • 本文字数:4582 字

    阅读完需:约 15 分钟

本实验主要介绍了如何在昇腾上,使用 pytorch 对经典的图神经网络 GAT 在论文引用数据集 Pubmed 上进行分类训练的实战讲解。内容包括 GAT 网络创新点分析、图注意力机制原理与架构剖析、多头注意力机制分析与 GAT 网络模型代码实战分析等等。


本实验的目录结构安排如下所示:


  • GAT 网络创新点分析

  • 图注意力机制原理与架构分析

  • 多头注意力机制分析

  • GAT 网络用于 Pubmed 数据集分类实战

GAT 网络创新点分析

注意机制已成功用于许多基于序列的任务,例如机器翻译,机器阅读等等。与 GCN 平等对待节点的所有邻居相比,注意力机制可以为每个邻居分配不同的注意力得分,从而识别出更重要的邻居。将注意力机制纳入图谱神经网络的传播步骤是很直观的。图注意力网络也可以看作是图卷积网络家族中的一种方法。其创新点如下:


  • GAT 是一种图结构化数据上操作的新型神经网络架构,利用掩码自注意力层来解决基于图卷积或其近似值的现有方法的缺点。

  • GAT 网络对不同的相邻节点分配相应的权重,既不需要矩阵运算,也不需要事先知道图结构。

  • GAT 网络克服了基于谱神经网络的几个关键挑战,使得模型更加适用于归纳问题以及转导问题。

  • 在 Cora、Citeseer 和 Pubmed 引文网络数据集上取得了非常好的效果。

图注意力机制原理与架构分析

图中输入由 N 个节点组成,每个节点 feature 数为 F(也就是 feature vector 长度),下述两个公式分别表示输入的节点向量通过 GAT 网络进行注意力计算后得到的输出。


Input:


Output:


为了将输入的特征转换为高维的特征,这里至少需要一个科学系的线性转换。在 (Velickovic et al.,2017)中,作者对于每一个点使用了一个共享的线性转换方式,同时介绍了一个权重矩阵来参数化线性转换。对于节点中的每两个 node 间都有了相互关注机制(用来做加权平均,卷积时每个 node 的更新是其他的加权平均)。


计算节点与节点的相关性系数(注意力值):


先初步计算节点 i 与节点 j 的相关性:其中 a 是一个共享的权重系数,执行的是


计算节点 i 与节点 j 的相关性(归一化)


论文中采取取的计算 attention coefficient 的函数 a 是一个单层的前馈网络,经 LeakyReLU 处理得最终的


式中 || 表示串联/连接,一旦获得,归一化的相互注意系数用来计算对应特征的线性组合,以用作每个节点的最终输出特征。



左图表示在模型中应用注意机制 a(W * h_i ,W * h_j) 通过权重向量参数化,应用 LeakyReLU 激活输出。


右图表示在邻域中具有多端连接,不同的箭头样式表示独立的注意力计算,通过直连 concat 或平均 avg 获取

多头注意力机制分析

对于图中多头注意力情况,对应上图中右图情景,不只用一个函数进行 attention coefficient 的计算,而是设置 K 个函数,每一个函数都能计算出一组 attention coefficient,并能计算出一组加权求和用的系数,每一个卷积层中,K 个 attention 机制独立的工作,分别计算出自己的结果后连接在一起,得到卷积的结果。


我们知道对于单个注意力层输出计算如下:


对于有 k 个独立的相互注意机制同时计算,则集中其特征,可得到特征表示如下:


分别遍历 1~k 个头,每一个上按照下述公式计算:


对于网络的最后一层卷积层,如果还是使用 multi-head attention 机制,那么就不采取连接的方式合并不同的 attention 机制的结果了,而是采用求平均的方式进行处理。


GAT 网络用于 Pubmed 数据集分类实战

#导入torch相关库import torchimport torch.nn.functional as F
复制代码


该实验需要跑在 npu 上,因此需要导入 Npu 相关库使得模型快速迁移到 npu 上运行


import torch_npufrom torch_npu.contrib import transfer_to_npu
复制代码


由于 torch_geometric 中集成了单层的 GATConv 模块,这里直接进行导入,若有兴趣可以自行实现该类,注意输入与输出对齐即可。此外,数据集用的是 Pubmed,该数据集也直接集成在 Planetoid 模块中,这里也需要将其 import 进来。


from torch_geometric.nn import GATConvfrom torch_geometric.datasets import Planetoid
复制代码

Pubmed 数据集介绍

PubMed 数据集是一个广泛用于图神经网络(GNN)研究的基准数据集,主要用于节点分类任务。其由生物医学文献组成,每篇文献被视为一个节点,引用关系被视为边。该数据集包含三类糖尿病相关的论文,每个节点都带有特征向量和标签。


数据集一共有 19717 个节点 ,每一个节点代表一篇生物医学文献,每个节点有一个 500 维的特征向量,用来表示该医学文献的内容。总共含 44338 条边 ,每条边表示一篇文献对另一篇文献的引用关系,边与边之间是无向的,因此可以看做是对称的。总类别数包含 Diabetes Mellitus Experiment、Diabetes Mellitus Type 1 与 Diabetes Mellitus Type2 三类


# 加载数据print("===== begin Download Dadasat=====\n")dataset = Planetoid(root='/home/pengyongrong/workspace/data', name='PubMed')print("===== Download Dadasat finished=====\n")print("dataset num_features  is:  ", dataset.num_features)print("dataset.num_classes is:  ", dataset.num_classes)
print("dataset.edge_index is: ", dataset.edge_index)
print("train data is: ", dataset.data)print("dataset0 is: ", dataset[0])
print("train data mask is: ", dataset.train_mask, "num train is: ", (dataset.train_mask ==True).sum().item())print("val data mask is: ",dataset.val_mask, "num val is: ", (dataset.val_mask ==True).sum().item())print("test data mask is: ",dataset.test_mask, "num test is: ", (dataset.test_mask ==True).sum().item())
复制代码


===== begin Download Dadasat=====
===== Download Dadasat finished=====
dataset num_features is: 500dataset.num_classes is: 3dataset.edge_index is: tensor([[ 1378, 1544, 6092, ..., 12278, 4284, 16030], [ 0, 0, 0, ..., 19714, 19715, 19716]])train data is: Data(x=[19717, 500], edge_index=[2, 88648], y=[19717], train_mask=[19717], val_mask=[19717], test_mask=[19717])dataset0 is: Data(x=[19717, 500], edge_index=[2, 88648], y=[19717], train_mask=[19717], val_mask=[19717], test_mask=[19717])train data mask is: tensor([ True, True, True, ..., False, False, False]) num train is: 60val data mask is: tensor([False, False, False, ..., False, False, False]) num val is: 500test data mask is: tensor([False, False, False, ..., True, True, True]) num test is: 1000

复制代码


下载后的 pubmed 数据集总共包含 8 个文件,分别是 ind.pubmed.x、ind.pubmed.tx、ind.pubmed.all、ind.pubmed.y、ind.pubmed.ty、ind.pubmed.ally、ind.pubmed.graph 与 ind.pubmed.test.index。每个文件的作用说明如下:


ind.pubmed.x:训练集节点特征向量,大小(140,1433)


ind.pubmed.tx:测试集节点特征向量,实际展开后大小为(1000,1433)


ind.pubmed.allx:包含标签核无标签的训练节点特征向量(1708,1433)


ind.pubmed.y:one-hot 表示的训练节点的标签


ind.pubmed.ty:one-hot 表示的测试节点的标签


ind.pubmed.ally:one-hot 表示的 ind.cora.allx 对应的标签


ind.pubmed.graph:保存节点之间边的信息


ind.pubmed.test.index:保存测试集节点的索引,用于后面的归纳学习设置



从打印结果可以看出,数据集的特点与上述描述的相对应,GAT_NET 网络定义了一个两层的 GAT 网络,heads 的数量设置成 4。

开启 Pubmed 数据训练过程

class GAT_NET(torch.nn.Module):    def __init__(self, features, hidden, classes, heads=4):        super(GAT_NET, self).__init__()        # 定义GAT层,使用多头注意力机制        self.gat1 = GATConv(features, hidden, heads=4)         # 因为多头注意力是将向量拼接,所以维度乘以头数。        self.gat2 = GATConv(hidden*heads, classes) 
def forward(self, data): # 从输入数据集中获取x与边集相关信息 x, edge_index = data.x, data.edge_index # 将输入传入GAT层中,获得第一层Gat层的输出 x = self.gat1(x, edge_index) # 经过非线性激活与dropout,减少过拟合现象,增加模型的泛化能力 x = F.relu(x) x = F.dropout(x, training=self.training) # 第二层GAT层,得到整个网络的输出送给分类器进行分类 x = self.gat2(x, edge_index) return F.log_softmax(x, dim=1)
复制代码


定义设备跑在 Npu 上,这里如果需要替换成 Gpu 或 Cpu,则替换成'cuda'或'cpu'即可。


device = 'npu'
复制代码


定义 GAT_NET 网络,中间隐藏层节点个数定义为 16,'dataset.num_classes'为先前数据集中总的类别数,这里是 7 类。'to()'的作用是将该加载到指定模型设备上。优化器用的是'optim'中的'Adam'。


model = GAT_NET(dataset.num_node_features, 16, dataset.num_classes).to(device)  # 定义GraphSAGEdata = dataset[0].to(device)optimizer = torch.optim.Adam(model.parameters(), lr=0.01, weight_decay=5e-4)
复制代码


开始训练模型,指定训练次数 200 次,训练后采用极大似然用作损失函数计算损失,然后进行反向传播更新模型的参数,训练完成后,用验证集中的数据对模型效果进行验证,最后打印模型的准确率。


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()
# 模型验证过程,对训练得到的模型效果进行评估,并打印准确率。model.eval()_, pred = model(data).max(dim=1)correct = int(pred[data.test_mask].eq(data.y[data.test_mask]).sum().item())acc = correct / int(data.test_mask.sum())print('GAT Accuracy: {:.4f}'.format(acc))
复制代码


[W VariableFallbackKernel.cpp:51] Warning: CAUTION: The operator 'aten::scatter_reduce.two_out' is not currently supported on the NPU backend and will fall back to run on the CPU. This may have performance implications. (function npu_cpu_fallback)

GAT Accuracy: 0.3660
复制代码


内存使用情况: 整个训练过程的内存使用情况可以通过"npu-smi info"命令在终端查看,因此本文实验只用到了单个 npu 卡(也就是 chip 0),内存占用约 573M,对内存、精度或性能优化有兴趣的可以自行尝试进行优化。


Reference

[1] Velikovi, Petar , et al. "Graph Attention Networks." (2017).

用户头像

还未添加个人签名 2024-12-19 加入

还未添加个人简介

评论

发布
暂无评论
基于Pytorch Gemotric在昇腾上实现GAT图神经网络_永荣带你玩转昇腾_InfoQ写作社区