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

简介: 本文详细讲解了如何在昇腾平台上使用PyTorch实现图神经网络(GCN)对Cora数据集进行分类训练。内容涵盖GCN背景、模型特点、网络架构剖析及实战分析。GCN通过聚合邻居节点信息实现“卷积”操作,适用于非欧氏结构数据。文章以两层GCN模型为例,结合Cora数据集(2708篇科学出版物,1433个特征,7种类别),展示了从数据加载到模型训练的完整流程。实验在NPU上运行,设置200个epoch,最终测试准确率达0.8040,内存占用约167M。

本文主要介绍了如何在昇腾上,使用pytorch对经典的图神经网络GCN在论文引用Cora数据集上进行分类训练的实战讲解。内容包括GCN背景介绍、模型特点介绍、GCN网络架构剖析与GCN网络模型代码实战分析等等。

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

  • GCN网络背景介绍

  • 模型特点介绍

  • GCN网络架构剖析

  • GCN网络用于Cora数据集分类实战

GCN网络背景介绍

001.jpg

多层感知机、卷积神经网络、循环神经网络和自编码器等深度学习模型通过对输入的数据进行逐层的特征提取和筛选,可以完成分类和预测等任务,在计算机视觉和语音识别等领域已被广泛应用。但上述模型只能处理具有固定排列规则和顺序的欧氏结构数据,对于一些非规则排布的非欧式数据显得有些则力不从心。随着非欧式数据结构被越来越多的实际问题应用所需要,针对处理非欧氏结构数据的深度学习模型图神经网络(Graph Neural Networks, GNN)应运而生。

图神经网络的本质就是:图中的任何一个节点,都受到和它相连的其他节点的影响,距离越近影响则越大。一个图中的所有节点间的互动关系和每个节点本身的信息,就构成了这整张图的全部信息。

由于CNN已经是个相当成熟的技术了,聚合“邻居”的信息并不是什么少见的思路。显然,从GNN出现开始,就必然会有人尝试在GNN上进行类似CNN的“聚合节点信息”操作。事实上在GCN之前,就已经有一些关于类似的研究了。但不外乎存在计算量大、聚合效果差、卷积核复杂的问题。

模型特点介绍

GCN是GNN的一个分支,全称为图卷积神经网络,顾名思义,GCN是在图上进行“卷积”操作的GNN,这里用引号是因为,GCN的操作并不是卷积神经网络里的那个卷积,这里的卷积,是因为GCN的运算是在聚合节点周围其他节点的信息,与卷积神经网络(CNN)的行为类似。不过话说回来,CNN里的“卷积”,也并不是不是数学意义上的卷积。
002.jpg

GCN的创新之处在于,提出了一种简化到在计算量上可行且效果好的“卷积”计算方案。GCN利用拉普拉斯变换变化,利用邻接矩阵算出了这个滤波矩阵,然后利用这个滤波矩阵进行层间传播。

其迭代间节点核心更新计算公式如下:

$ X' = \tilde{D}^{-1/2}\tilde{A}\tilde{D}^{1/2}*X{\Theta }$

其中$\tilde{D}$表示邻接矩阵的度矩阵,$\tilde{A}$表示整张图的邻接矩阵(含自回环,也就是加了单位矩阵),X表示节点在k-1层的特征向量,${\Theta }$是k-1层的卷积参数。

GCN的上述公式表达的是从整个图的角度来考虑和描述的。从单个节点来说,每个节点的特征向量可以表示为的变换 (前向传播) 的向量形式可以表示为如下:

$$ \mathbf{x}_{i}^{(k)}=\sum_{j \\in \mathcal{N}(i) \\cup\\{i\\}} \frac{1}{\sqrt{\operatorname{deg}(i)} \\cdot \sqrt{\operatorname{deg}(j)}} \\cdot\\left(\boldsymbol{\\Theta} \\cdot \mathbf{x}_{j}^{(k-1)}\\right) $$

其中${\Theta }$是权重矩阵 (即模型学习过程中要更新的参数),$\mathbf{x}_{i}^{(k)}$表示节点i在第k次迭代的特征向量,deg(i)表示节点i的度,N(i)表示节点i所有邻接节点的集合。

GCN网络架构剖析

003.jpg

GCN定义了一个两层的模型,中间隐藏的节点个数可以自设,后面输出层可以结合具体数据集类别设置使用,当然也可以跟训练类别设置不一致,只需要在后面接一个分类器即可(后再接一个全连接层)。

MessagePassing模块是图神经网络(Graph Neural Networks,GNNs)的一个基础组件,它被设计用来处理图形数据的问题。在图形数据中,数据点(节点)之间的关系(边)是非常重要的信息。MessagePassing通过在节点之间传递和聚合信息,使得每个节点都能获取其邻居节点的信息,从而更好地理解图形的结构和特性。里面'propagate'函数与'aggregate'函数用于实现节点之间的传播与聚合功能。


# 导入torch及相关库,便于后续搭建网络调用基础算子模块

import torch

import torch.nn.functional as F

from torch_geometric.nn import MessagePassing

在图神经网络(GNN)的实现中,对图结构的调整和优化是提升模型性能的关键手段之一。'add_self_loops'函数在PyTorch的图处理库中用于向图中添加自环(self-loops),即连接节点自身的边。'degree'用来计算一个无权图的入度矩阵。


from torch_geometric.utils import add_self_loops, degree

GCN实现继承了'MessagePassing'类,线性变换功能在'init'函数中通过'self.lin'为线性变换函数定义,具体特征维度的逻辑在'forward()'中实现,'init'函数中入参'in_channel'是每个节点输入特征的维度,'out_channels'是每个节点输出特征的维度,这一部分对应上述公式中的X${\Theta}$。输入的特征矩阵维度是(N, in_channels),输出的特征矩阵维度是(N, out_channels),其中 N 是节点个数。

在'forward()'函数中'edge_index, _ = add_self_loops(edge_index, num_nodes=x.size(0))'是给邻接矩阵加上self loops,也即构造出矩阵 $\hat{A} = A + I$ ,在torch geometric中,无权图的邻接矩阵表示为2维数组(COO存储格式),第1行表示边的起始节点(source 节点),第2行表示边的目标节点(target节点)。

对于'message()'函数而言,函数入参'x_j' 的形状是[E, out_channels],其中E表示边的数量。由上面可知,特征矩阵经过线性变换后的输出形状是(N, out_channels),边的矩阵的形状为 [2, E]。
'row, col = edge_index'表示取出所有边的起始节点和目标节点,row表示边的起始节点的结合,col表示边的目标节点的集合。在无向图中,这两者是等价的。以target节点作为索引,从线性变换后的特征矩阵中索引得到target节点的特征矩阵。


class  GCNConv(MessagePassing):

     def  __init__(self, in_channels, out_channels):

     # "Add" aggregation.

           super(GCNConv, self).__init__(aggr='add')

           self.lin = torch.nn.Linear(in_channels, out_channels)

     def  forward(self, x, edge_index):

         # x has shape \[N, in_channels\]

         # edge_index has shape \[2, E\]

         # Step 1: Add self-loops to the adjacency matrix.

         edge_index, _ = add_self_loops(edge_index, num_nodes=x.size(0))

         # Step 2: Linearly transform node feature matrix.

         x = self.lin(x)

         # Step 3-5: Start propagating messages.

         return  self.propagate(edge_index, size=(x.size(0), x.size(0)), x=x)

     def  message(self, x_j, edge_index, size):

         # x_j has shape [E, out_channels]
         # edge_index has shape [2, E]
         # Step 3: Normalize node features.
          row, col = edge_index
         # [N, ]
        deg = degree(row, size[0], dtype=x_j.dtype)
         # [N, ]
        deg_inv_sqrt = deg.pow(-0.5)
        norm = deg_inv_sqrt[row] * deg_inv_sqrt[col]

         return norm.view(-1, 1) * x_j

   def  update(self, aggr_out):
       # aggr_out has shape [N, out_channels]
       # Step 5: Return new node embeddings.
       return aggr_out

上述类目前已经集成在torch_geometric.nn模块中,也可以使用下述一行代码替换 'from torch_geometric.nn import GCNConv' 导入GCN层替换GCNConv类定义。

Planetoid集成了论文引用中Cora,CiteSeer,PubMed三个数据集,由于本实验需要用到Cora数据集,因此此处需要导入该模块用于加载数据集。


from torch_geometric.datasets import Planetoid

定义GCN_NET图网络,中间构造一个隐藏层用来辅助实现线性转换过程。


class  GCN_NET(torch.nn.Module):
   def  __init__(self, features, hidden, classes):
     super(GCN_NET, self).__init__()

     # shape(输入的节点特征维度 * 中间隐藏层的维度)
     self.conv1 = GCNConv(features, hidden)

     # shaape(中间隐藏层的维度 * 节点类别)
     self.conv2 = GCNConv(hidden, classes)

   def  forward(self, data):
     # 加载节点特征和邻接关系
     x, edge_index = data.x, data.edge_index

     # 传入卷积层
     x = self.conv1(x, edge_index)
     # 激活函数
     x = F.relu(x)

     # dropout层,防止过拟合
     x = F.dropout(x, training=self.training)
     # 第二层卷积层
     x = self.conv2(x, edge_index)
     # 将经过两层卷积得到的特征输入log_softmax函数得到概率分布
     return F.log_softmax(x, dim=1)

GCN网络用于Cora数据集分类实战

本实验需要跑在npu上,因此需要导入Npu相关库,以便于模型能够跑在Npu上。


import torch_npu

from torch_npu.contrib import transfer_to_npu

Cora数据集介绍与加载

Cora数据集包含2708篇科学出版物,10556条边,总共7种类别,数据集中的每个出版物都由一个 0/1 值的词向量描述,表示字典中相应词的缺失/存在。该词典由1433个独特的词组成,意思就是说每一个出版物都由1433个特征构成,每个特征仅由0/1表示。

如图,节点大小对应进出边的数量,节点越大表示进出边的数量越多,边的粗细反映对应两节点之间的相似或关联程度越高,也就是对彼此的影响力权重越大。
004.jpg

由于cora数据集处理的是无向图,所以'in degree count'与'out degree count'分布图一致,底部的'nodes for a given out-degree'与'node degree'图统计的是数据集中出边的分布情况,可以看到峰值点出现在[2, 4]范围内,说明大多数的节点之间之间与少量的边进行相连,相连节点最多的边是图中绿色节点,约有169个节点相连。
005.jpg

下载后的数据集总共包含8个文件分别是ind.cora.x、ind.cora.tx、ind.cora.all、ind.cora.y、ind.cora.ty、ind.cora.ally、ind.cora.graph与ind.cora.test.index。
006.jpg

其中:

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

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

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

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

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

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

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

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

上述介绍完cora数据集的基本组成情况,接下来我们通过Planetoid集成库来下载cora数据集,下载好以后对数据集中一些基本的信息进行打印。


import numpy as np

# 加载数据,出错可自行下载,解决方案见下文
print("===== begin Download Dadasat=====\\n")
dataset = Planetoid(root='/home/pengyongrong/workspace/data', name='Cora')

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:   1433

dataset.num\_classes is:   7

dataset.edge\_index is:   tensor(\[\[ 633, 1862, 2582,  ...,  598, 1473, 2706],

        \[   0,    0,    0,  ..., 2707, 2707, 2707]])

train data is:    Data(x=\[2708, 1433], edge\_index=\[2, 10556], y=\[2708], train\_mask=\[2708], val\_mask=\[2708], test\_mask=\[2708])

dataset0 is:   Data(x=\[2708, 1433], edge\_index=\[2, 10556], y=\[2708], train\_mask=\[2708], val\_mask=\[2708], test\_mask=\[2708])

train data mask is:    tensor(\[ True,  True,  True,  ..., False, False, False]) num train is:  140

val data mask is:    tensor(\[False, False, False,  ..., False, False, False]) num val is:  500

test data mask is:    tensor(\[False, False, False,  ...,  True,  True,  True]) num test is:  1000

本文用到的实验数据集这里已经下载好并保存在"/home/pengyongrong/workspace/data"目录下,若没有下载好启动该命令会自动下载数据集,整个下载过程可能会比较慢,也可以在https://github.com/D61-IA/FisherGCN/tree/a58c1613f1aca7077ef90af6e51a8021548cdb4c/data 自行选择下载。

从打印的信息可以看出来,每一个节点的特征维度为1433,也就是'datasat.numfeatures'的取值是1433;总的类别数是7,对应'datasat.numclasses';'dataset.edge_index'表示所有边与边之间的互联关系,采用coo存储格式,因为这里是无权边,因此只需要二维数组即可完成对应功能。

上述信息介绍完后就剩下训练集、验证集与测试集相关的信息,dataset[0]包含了所有信息,包括输入x,边信息、标签y及'train_mask'、'val_mask'与'test_mask'分别表示2708篇刊物中哪些用于训练,哪些用于验证及哪些用于测试。

开启cora数据训练过程

接下来就是将cora数据集送入搭建好的GCN网络模型中进行训练,训练过程中设置设备在npu上运行,并定义训练的'epoch=200',迭代次数可以根据需要自行更改,训练完成后对模型的效果进行评估并打印预测的准确率约为0.8。

#设置成npu
device = 'npu'
print("device is:  ", device)
# 构建模型,设置中间隐藏层维度为16
model = GCN_NET(dataset.num_node_features, 16, dataset.num_classes).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()
# 评估模型
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('GCN Accuracy: {:.4f}'.format(acc))
device is:   npu
GCN Accuracy: 0.8040

内存使用情况: 整个训练过程的内存使用情况可以通过"npu-smi info"命令在终端查看,因此本文实验只用到了单个npu卡(也就是chip 0),内存占用约167M,对内存、精度或性能优化有兴趣的可以自行尝试进行优化,这里运行过程中也有其他程序在运行,因此本实验用到的网络所需要的内存已单独框出来。

007.jpg

Reference

[1] Heidari, Negar , L. Hedegaard , and A. Iosifidis . "Graph convolutional networks." Deep Learning for Robot Perception and Cognition (2022).

相关文章
|
2月前
|
机器学习/深度学习 算法 PyTorch
【Pytorch框架搭建神经网络】基于DQN算法、优先级采样的DQN算法、DQN + 人工势场的避障控制研究(Python代码实现)
【Pytorch框架搭建神经网络】基于DQN算法、优先级采样的DQN算法、DQN + 人工势场的避障控制研究(Python代码实现)
|
2月前
|
机器学习/深度学习 算法 PyTorch
【DQN实现避障控制】使用Pytorch框架搭建神经网络,基于DQN算法、优先级采样的DQN算法、DQN + 人工势场实现避障控制研究(Matlab、Python实现)
【DQN实现避障控制】使用Pytorch框架搭建神经网络,基于DQN算法、优先级采样的DQN算法、DQN + 人工势场实现避障控制研究(Matlab、Python实现)
135 0
|
6月前
|
机器学习/深度学习 搜索推荐 PyTorch
基于昇腾用PyTorch实现CTR模型DIN(Deep interest Netwok)网络
本文详细讲解了如何在昇腾平台上使用PyTorch训练推荐系统中的经典模型DIN(Deep Interest Network)。主要内容包括:DIN网络的创新点与架构剖析、Activation Unit和Attention模块的实现、Amazon-book数据集的介绍与预处理、模型训练过程定义及性能评估。通过实战演示,利用Amazon-book数据集训练DIN模型,最终评估其点击率预测性能。文中还提供了代码示例,帮助读者更好地理解每个步骤的实现细节。
|
6月前
|
机器学习/深度学习 自然语言处理 PyTorch
基于Pytorch Gemotric在昇腾上实现GAT图神经网络
本实验基于昇腾平台,使用PyTorch实现图神经网络GAT(Graph Attention Networks)在Pubmed数据集上的分类任务。内容涵盖GAT网络的创新点分析、图注意力机制原理、多头注意力机制详解以及模型代码实战。实验通过两层GAT网络对Pubmed数据集进行训练,验证模型性能,并展示NPU上的内存使用情况。最终,模型在测试集上达到约36.60%的准确率。
|
6月前
|
算法 PyTorch 算法框架/工具
PyTorch 实现FCN网络用于图像语义分割
本文详细讲解了在昇腾平台上使用PyTorch实现FCN(Fully Convolutional Networks)网络在VOC2012数据集上的训练过程。内容涵盖FCN的创新点分析、网络架构解析、代码实现以及端到端训练流程。重点包括全卷积结构替换全连接层、多尺度特征融合、跳跃连接和反卷积操作等技术细节。通过定义VOCSegDataset类处理数据集,构建FCN8s模型并完成训练与测试。实验结果展示了模型在图像分割任务中的应用效果,同时提供了内存使用优化的参考。
|
6月前
|
机器学习/深度学习 算法 PyTorch
基于Pytorch Gemotric在昇腾上实现GraphSage图神经网络
本实验基于PyTorch Geometric,在昇腾平台上实现GraphSAGE图神经网络,使用CiteSeer数据集进行分类训练。内容涵盖GraphSAGE的创新点、算法原理、网络架构及实战分析。GraphSAGE通过采样和聚合节点邻居特征,支持归纳式学习,适用于未见节点的表征生成。实验包括模型搭建、训练与验证,并在NPU上运行,最终测试准确率达0.665。
|
11月前
|
SQL 安全 网络安全
网络安全与信息安全:知识分享####
【10月更文挑战第21天】 随着数字化时代的快速发展,网络安全和信息安全已成为个人和企业不可忽视的关键问题。本文将探讨网络安全漏洞、加密技术以及安全意识的重要性,并提供一些实用的建议,帮助读者提高自身的网络安全防护能力。 ####
261 17
|
11月前
|
SQL 安全 网络安全
网络安全与信息安全:关于网络安全漏洞、加密技术、安全意识等方面的知识分享
随着互联网的普及,网络安全问题日益突出。本文将从网络安全漏洞、加密技术和安全意识三个方面进行探讨,旨在提高读者对网络安全的认识和防范能力。通过分析常见的网络安全漏洞,介绍加密技术的基本原理和应用,以及强调安全意识的重要性,帮助读者更好地保护自己的网络信息安全。
220 10
|
11月前
|
存储 SQL 安全
网络安全与信息安全:关于网络安全漏洞、加密技术、安全意识等方面的知识分享
随着互联网的普及,网络安全问题日益突出。本文将介绍网络安全的重要性,分析常见的网络安全漏洞及其危害,探讨加密技术在保障网络安全中的作用,并强调提高安全意识的必要性。通过本文的学习,读者将了解网络安全的基本概念和应对策略,提升个人和组织的网络安全防护能力。
|
11月前
|
SQL 安全 网络安全
网络安全与信息安全:关于网络安全漏洞、加密技术、安全意识等方面的知识分享
在数字化时代,网络安全和信息安全已成为我们生活中不可或缺的一部分。本文将介绍网络安全漏洞、加密技术和安全意识等方面的内容,并提供一些实用的代码示例。通过阅读本文,您将了解到如何保护自己的网络安全,以及如何提高自己的信息安全意识。
232 10

热门文章

最新文章

推荐镜像

更多