pytorch实现gat(基于pytorch实现)(代码片段)

海洋.之心 海洋.之心     2023-04-14     590

关键词:

文章目录


前言

大家好,我是阿光。

本专栏整理了《图神经网络代码实战》,内包含了不同图神经网络的相关代码实现(PyG以及自实现),理论与实践相结合,如GCN、GAT、GraphSAGE等经典图网络,每一个代码实例都附带有完整的代码。

正在更新中~ ✨

🚨 我的项目环境:

  • 平台:Windows10
  • 语言环境:python3.7
  • 编译器:PyCharm
  • PyTorch版本:1.11.0
  • PyG版本:2.1.0

💥 项目专栏:【图神经网络代码实战目录】


本文我们将使用PyTorch来简易实现一个GAT(图注意力网络),不使用PyG库,让新手可以理解如何PyTorch来搭建一个简易的图网络实例demo。

一、导入相关库

本项目是采用自己实现的GAT,并没有使用 PyG 库,原因是为了帮助新手朋友们能够对GAT的原理有个更深刻的理解,如果熟悉之后可以尝试使用PyG库直接调用 GATConv 这个图层即可。

import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
from scipy.sparse import coo_matrix
from torch_geometric.datasets import Planetoid

二、加载Cora数据集

本文使用的数据集是比较经典的Cora数据集,它是一个根据科学论文之间相互引用关系而构建的Graph数据集合,论文分为7类,共2708篇。

  • Genetic_Algorithms
  • Neural_Networks
  • Probabilistic_Methods
  • Reinforcement_Learning
  • Rule_Learning
  • Theory

这个数据集是一个用于图节点分类的任务,数据集中只有一张图,这张图中含有2708个节点,10556条边,每个节点的特征维度为1433。

# 1.加载Cora数据集
dataset = Planetoid(root='./data/Cora', name='Cora')

三、定义GAT网络

3.1 定义GAT层

这里我们就不重点介绍GCN网络了,相信大家能够掌握基本原理,本文我们使用的是PyTorch定义网络层。

对于GATConv的常用参数:

  • in_channels:每个样本的输入维度,就是每个节点的特征维度
  • out_channels:经过注意力机制后映射成的新的维度,就是经过GAT后每个节点的维度长度
  • add_self_loops:为图添加自环,是否考虑自身节点的信息
  • bias:训练一个偏置b

我们在实现时也是考虑这几个常见参数

对于GAT的传播公式为:

x i ′ = α i , i θ x i + ∑ j ∈ N ( i ) α i , j θ x j x_i'=\\alpha_i,i\\theta x_i+ \\sum_j\\in N(i)\\alpha_i,j\\theta x_j xi=αi,iθxi+jN(i)αi,jθxj

上式子的意思就是对自己和邻居的特征进行按照权重聚合,其中的 α \\alpha α 代表注意力分数, θ \\theta θ 代表可学习参数, x j x_j xj 代表邻居节点的特征向量。

其中注意力分数的计算方式如下:

α i , j = e x p ( L e a k y R e L U ( α T [ θ x i ∣ ∣ θ x j ] ) ) ∑ k ∈ N ( i ) ∪ i e x p ( L e a k y R e L U ( α T [ θ x i ∣ ∣ θ x j ] ) ) \\alpha_i,j=\\fracexp(LeakyReLU(\\alpha^T[\\theta x_i||\\theta x_j]))\\sum_k\\in N(i)\\cupiexp(LeakyReLU(\\alpha^T[\\theta x_i||\\theta x_j])) αi,j=kN(i)iexp(LeakyReLU(αT[θxi∣∣θxj]))exp(LeakyReLU(αT[θxi∣∣θxj]))

所以我们的任务无非就是获取这几个变量,然后进行传播计算即可

3.1.1 将节点信息进行空间映射

在注意力公式中,它是首先对邻居节点先进行空间上的映射,实现代码如下:

# 1.计算wh,进行节点空间映射
wh = torch.mm(x, self.weight_w)

3.1.2 注意力分数

第二步就是计算注意力分数,注意一点这个分数并没有被激活,实现的部分就是 LeakyReLU 括号内的部分。

# 2.计算注意力分数
e = torch.mm(wh, self.weight_a[: self.out_channels]) + torch.matmul(wh, self.weight_a[self.out_channels:]).T

3.1.3 获取邻接矩阵

由于我们使用的是内置数据集 Cora,他给出的数据集并没有给出对应的邻接矩阵,所以我们需要手动实现获取该图对应的邻接矩阵。

# 4.获取邻接矩阵
if self.adj == None:
    self.adj = to_dense_adj(edge_index).squeeze()
    
    # 5.添加自环,考虑自身加权
    if self.add_self_loops:
        self.adj += torch.eye(x.shape[0])

3.1.4 获得注意力分数矩阵

在上述GAT的传播公式中我们可以看到,每次加权的节点信息为自身和其邻居节点,所以为了实现非邻居节点不参与加权,我们需要对注意力分数矩阵非邻居节点的位置将其置为一个很小的值,这样在矩阵乘法时就不会发挥什么作用。

# 6.获得注意力分数矩阵
attention = torch.where(self.adj > 0, e, -1e9 * torch.ones_like(e))

该代码的意思就是如果邻接矩阵中位置大于0,也就是该条边存在,那么注意力矩阵对应的位置分数不变,否则将其置为 -1e9 这个很小的数。

3.1.5 加权融合特征

这个部分就是将获得的注意力分数进行归一化,然后将这个矩阵和映射后的特征矩阵进行相乘,实现聚合操作,最终在结果上面添加偏置信息。

# 7.归一化注意力分数
attention = F.softmax(attention, dim=1)

# 8.加权融合特征
output = torch.mm(attention, wh)

# 9.添加偏置
if self.bias != None:
    return output + self.bias.flatten()
else:
    return output         

3.1.6 GATConv层

接下来就可以定义GATConv层了,该层实现了2个函数,分别是 init_parameters()forward()

  • init_parameters():初始化可学习参数
  • forward():这个函数定义模型的传播过程,也就是上面公式的 x i ′ = α i , i θ x i + ∑ j ∈ N ( i ) α i , j θ x j x_i'=\\alpha_i,i\\theta x_i+ \\sum_j\\in N(i)\\alpha_i,j\\theta x_j xi=αi,iθxi+jN(i)αi,jθxj,如果设置了偏置在加上偏置返回即可
# 2.定义GATConv层
class GATConv(nn.Module):
    def __init__(self, in_channels, out_channels, heads=1, add_self_loops=True, bias=True):
        super(GATConv, self).__init__()
        self.in_channels = in_channels # 输入图节点的特征数
        self.out_channels = out_channels # 输出图节点的特征数
        self.adj = None
        self.add_self_loops = add_self_loops
        
        
        # 定义参数 θ
        self.weight_w = nn.Parameter(torch.FloatTensor(in_channels, out_channels))
        self.weight_a = nn.Parameter(torch.FloatTensor(out_channels * 2, 1))
        
        if bias:
            self.bias = nn.Parameter(torch.FloatTensor(out_channels, 1))
        else:
            self.register_parameter('bias', None)
        
        self.leakyrelu = nn.LeakyReLU()
        self.init_parameters()
    
    # 初始化可学习参数
    def init_parameters(self):
        nn.init.xavier_uniform_(self.weight_w)
        nn.init.xavier_uniform_(self.weight_a)
        
        if self.bias != None:
            nn.init.zeros_(self.bias)
        
    def forward(self, x, edge_index):
        # 1.计算wh,进行节点空间映射
        wh = torch.mm(x, self.weight_w)
                                  
        # 2.计算注意力分数
        e = torch.mm(wh, self.weight_a[: self.out_channels]) + torch.matmul(wh, self.weight_a[self.out_channels:]).T

        # 3.激活
        e = self.leakyrelu(e)
        
        # 4.获取邻接矩阵
        if self.adj == None:
            self.adj = to_dense_adj(edge_index).squeeze()
            
            # 5.添加自环,考虑自身加权
            if self.add_self_loops:
                self.adj += torch.eye(x.shape[0])
        
        # 6.获得注意力分数矩阵
        attention = torch.where(self.adj > 0, e, -1e9 * torch.ones_like(e))
        
        # 7.归一化注意力分数
        attention = F.softmax(attention, dim=1)
        
        # 8.加权融合特征
        output = torch.mm(attention, wh)
        
        # 9.添加偏置
        if self.bias != None:
            return output + self.bias.flatten()
        else:
            return output                                   

对于我们实现这个网络的实现效率上来讲比PyG框架内置的 GCNConv 层稍差一点,因为我们是按照公式来一步一步利用矩阵计算得到,没有对矩阵计算以及算法进行优化,不然初学者可能看不太懂,不利于理解GCN公式的传播过程,有能力的小伙伴可以看下官方源码学习一下。

3.2 定义GAT网络

上面我们已经实现好了 GATConv 的网络层,之后就可以调用这个层来搭建 GAT 网络。

# 3.定义GAT网络
class GAT(nn.Module):
    def __init__(self, num_node_features, num_classes):
        super(GAT, self).__init__()
        self.conv1 = GATConv(in_channels=num_node_features,
                                    out_channels=16,
                                    heads=2)
        self.conv2 = GATConv(in_channels=16,
                                    out_channels=num_classes,
                                    heads=1)
        
    def forward(self, data):
        x, edge_index = data.x, data.edge_index
        
        x = self.conv1(x, edge_index)
        x = F.relu(x)
        x = F.dropout(x, training=self.training)
        x = self.conv2(x, edge_index)
        
        return F.log_softmax(x, dim=1)

上面网络我们定义了两个GATConv层,第一层的参数的输入维度就是初始每个节点的特征维度,输出维度是16。

第二个层的输入维度为16,输出维度为分类个数,因为我们需要对每个节点进行分类,最终加上softmax操作。

四、定义模型

下面就是定义了一些模型需要的参数,像学习率、迭代次数这些超参数,然后是模型的定义以及优化器及损失函数的定义,和pytorch定义网络是一样的。

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') # 设备
epochs = 10 # 学习轮数
lr = 0.003 # 学习率
num_node_features = dataset.num_node_features # 每个节点的特征数
num_classes = dataset.num_classes # 每个节点的类别数
data = dataset[0].to(device) # Cora的一张图

# 3.定义模型
model = GAT(num_node_features, num_classes).to(device)
optimizer = torch.optim.Adam(model.parameters(), lr=lr) # 优化器
loss_function = nn.NLLLoss() # 损失函数

五、模型训练

模型训练部分也是和pytorch定义网络一样,因为都是需要经过前向传播、反向传播这些过程,对于损失、精度这些指标可以自己添加。

# 训练模式
model.train()

for epoch in range(epochs):
    optimizer.zero_grad()
    pred = model(data)
    
    loss = loss_function(pred[data.train_mask], data.y[data.train_mask]) # 损失
    correct_count_train = pred.argmax(axis=1)[data.train_mask].eq(data.y[data.train_mask]).sum().item() # epoch正确分类数目
    acc_train = correct_count_train / data.train_mask.sum().item() # epoch训练精度
    
    loss.backward()
    optimizer.step()
    
    if epoch % 20 == 0:
        print("【EPOCH: 】%s" % str(epoch + 1))
        print('训练损失为::.4f'.format(loss.item()), '训练精度为::.4f'.format(acc_train))

print('【Finished Training!】')

六、模型验证

下面就是模型验证阶段,在训练时我们是只使用了训练集,测试的时候我们使用的是测试集,注意这和传统网络测试不太一样,在图像分类一些经典任务中,我们是把数据集分成了两份,分别是训练集、测试集,但是在Cora这个数据集中并没有这样,它区分训练集还是测试集使用的是掩码机制,就是定义了一个和节点长度相同纬度的数组,该数组的每个位置为True或者False,标记着是否使用该节点的数据进行训练。

# 模型验证
model.eval()
pred = model(data)

# 训练集(使用了掩码)
correct_count_train = pred.argmax(axis=1)[data.train_mask].eq(data.y[data.train_mask]).sum().item()
acc_train = correct_count_train / data.train_mask.sum().item()
loss_train = loss_function(pred[data.train_mask], datapytorch实现mlp(基于pytorch实现)

文章目录前言一、导入相关库二、加载Cora数据集三、定义MLP网络3.1定义MLP层3.1.1定义参数WWW和bb 查看详情

基于pytorch使用实现cnn如何使用pytorch构建cnn卷积神经网络(代码片段)

基于pytorch使用实现CNN如何使用pytorch构建CNN卷积神经网络本文是一个基于pytorch使用CNN在生物信息学上进行位点预测的例子基于pytorch实现CNN,基于CNN进行位点预测,将CNN代码进行封装,可以非常简单的使用代码,基... 查看详情

pytorch实战用pytorch实现基于神经网络的图像风格迁移(代码片段)

用PyTorch实现基于神经网络的图像风格迁移1.风格迁移原理介绍2.FastNeuralStyle网络结构3.用PyTorch实现风格迁移3.1首先看看如何使用预训练的VGG。3.2接下来要实现风格迁移网络参考资料风格迁移,又称为风格转换。只需要给定原... 查看详情

pytorch实现基于charrnn的文本分类与生成(代码片段)

Pytorch实现基于CharRNN的文本分类与生成标签:deep-learningpytorchnlp1简介本篇主要介绍使用pytorch实现基于CharRNN来进行文本分类与内容生成所需要的相关知识,并最终给出完整的实现代码。2相关API的说明pytorch框架中每种网络... 查看详情

pytorch实现gcn(基于messagepassing消息传递机制实现)(代码片段)

...台:Windows10语言环境:python3.7编译器:PyCharmPyTorch版本:1.11.0PyG版本:2.1.0💥项目专栏:【图神经网络代码实战目录】本文我们将使用PyTorch来简易实现一个GCN(图卷积网络)&# 查看详情

基于pytorch使用实现cnn如何使用pytorch构建cnn卷积神经网络(代码片段)

基于pytorch使用实现CNN如何使用pytorch构建CNN卷积神经网络本文是一个基于pytorch使用CNN在生物信息学上进行位点预测的例子基于pytorch实现CNN,基于CNN进行位点预测,将CNN代码进行封装,可以非常简单的使用代码,基... 查看详情

pytorch深度学习实践入门01(代码片段)

文章目录基于PyTorch的两层神经网络一、基于numpy的两层神经网络实现:二、基于PyTorch的两层神经网络实现:三、使用nn库实现两层神经网络四、自定义nnModules实现两层神经网络总结基于PyTorch的两层神经网络提示:在... 查看详情

基于pytorch实现图片去模糊降噪,超详细,有代码,数据,可直接运行。

教大家一下,图片去模糊降噪的基本方法和完整的实验流程。目录简介:环境配置 参数设置数据显示 查看详情

基于深度学习的语义分割初探fcn以及pytorch代码实现(代码片段)

基于深度学习的语义分割初探FCN以及pytorch代码实现FCN论文论文地址:https://arxiv.org/abs/1411.4038FCN是基于深度学习方法的第一篇关于语义分割的开山之作,虽然这篇文章的分割结果现在看起来并不是目前最好的,但其意... 查看详情

基于pytorch平台实现对mnist数据集的分类分析(前馈神经网络softmax)基础版(代码片段)

基于pytorch平台实现对MNIST数据集的分类分析(前馈神经网络、softmax)基础版文章目录基于pytorch平台实现对MNIST数据集的分类分析(前馈神经网络、softmax)基础版前言一、基于“前馈神经网络”模型,分类分析... 查看详情

pytorch深度学习项目实战100例——基于transformer实现twitter文本隐喻二分类|第43例

前言大家好,我是阿光。本专栏整理了《PyTorch深度学习项目实战100例》,内包含了各种不同的深度学习项目,包含项目原理以及源码,每一个项目实例都附带有完整的代码+数据集。正在更新中~✨ 查看详情

ai常用框架和工具丨13.pytorch实现基于cnn的手写数字识别

代码实例,PyTorch实现基于CNN的手写数字识别,希望对您有所帮助。文章目录环境说明一、模型训练1.1导入相关依赖1.2选择使用的硬件1.3超参数配置1.4准备训练集和测试集1.5模型创建1.6模型评估指标1.7模型训练1.8模型测试1.9模型... 查看详情

ai常用框架和工具丨13.pytorch实现基于cnn的手写数字识别

代码实例,PyTorch实现基于CNN的手写数字识别,希望对您有所帮助。文章目录环境说明一、模型训练1.1导入相关依赖1.2选择使用的硬件1.3超参数配置1.4准备训练集和测试集1.5模型创建1.6模型评估指标1.7模型训练1.8模型测试1.9模型... 查看详情

ai常用框架和工具丨13.pytorch实现基于cnn的手写数字识别

代码实例,PyTorch实现基于CNN的手写数字识别,希望对您有所帮助。文章目录环境说明一、模型训练1.1导入相关依赖1.2选择使用的硬件1.3超参数配置1.4准备训练集和测试集1.5模型创建1.6模型评估指标1.7模型训练1.8模型测试1.9模型... 查看详情

基于pytorch模型剪枝的实现(极大的减少模型计算参数加快模型运行速度)(代码片段)

深度模型剪枝实现以及一些网络优化技巧模型剪枝:LearningEfficientConvolutionalNetworksThroughNetworkSlimming(ICCV2017).基于论文的代码复现以及拓展:在网络上中加入其它优化方法最强深度学习优化器Rangerwarmup与consinelearningrate为什... 查看详情

pytorch深度学习项目实战100例——基于lenet5实现交通标志分类任务|第50例

前言大家好,我是阿光。本专栏整理了《PyTorch深度学习项目实战100例》,内包含了各种不同的深度学习项目,包含项目原理以及源码,每一个项目实例都附带有完整的代码+数据集。正在更新中~✨ 查看详情

基于pytorch实现模型剪枝(代码片段)

...以在每层(局部)或多层/所有层(全局)上进行。二,PyTorch的剪枝目前PyTorch框架支持的权重剪枝方法有:Random:简单地修剪随机参数。Magnitude:修剪权重最小的参数(例如它们的L2范数)以上两种方法实现简单、计算容易,且可以... 查看详情

「深度学习一遍过」必修18:基于pytorch的语义分割模型实现(代码片段)

...型基础上实现膨胀卷积 1自定义5层普通卷积 模型结构 pytorch代码 fromtor 查看详情