数据挖掘期末-图注意力模型

news2024/11/19 21:29:00

PyGAT图注意力模型

​  PyGAT实现的分类器: https://www.aliyundrive.com/s/vfK8ndntpyc

  还在发烧,不是特别清醒,就简单写了写。用GAT进行关系预测,GAT可能是只做中间层,不过本来在GAT这一层就为了能懂就简化了很多地方了,如果再加别的,预测正确率大概率很低。尝试了直接用GAT预测边权(没用稀疏矩阵的版本),内存不够没办法跑(需要至少100G+),试了少一些节点,也基本预测不出来,所以这里只介绍GAT基本实现和GAT进行分类。

​   代码是用ANACONDA的IPYTHON虚拟环境里运行的。

​   需要安装pytorch,在conda prompt输入以下命令:

conda install pytorch torchvision torchaudio cpuonly -c pytorch

​   不用conda可以↓,选Pip,得到Pip的命令安装PyTorch。CUDA没有对应的版本就选CPU。

​   https://pytorch.org/
在这里插入图片描述

GAT

​   GAT对于一个图,按照其输入的节点特征预测输出新的节点的特征。

​   GAT是一种能够直接作用于图并且利用其结构信息的卷积神经网络,可用于网络中的半监督学习问题,学习网络中结点的特征与网络结构的信息。主要思想是对每个结点的邻居及其自身的信息作加权平均,按照其输入的节点特征输出新的节点特征。图注意力模型用注意力机制对邻近节点特征加权求和,每个节点可以根据邻节点的特征,为其分配不同的权值,将权重称为注意力系数,然后根据注意力系数进行加权求和,得到节点的新特征。

​   图注意力模型训练得到的是一个计算好节点的注意力权重的图,得到该图以后,就可以输入节点特征,得到新的节点特征。这个新的特征是什么取决于使用图注意力模型的目的。

GAT模型

  假设输入特征维度为x,输出特征维度为y。

  GAT是图注意力层+MUTIHEAD机制,MUTIHEAD机制相当于有多个图注意力层,每个层输出是n1,n2,n3…个新特征,最后再将这些特征转变为y个特征,也就是最终输出的特征

注意力系数计算

  构建图注意力层的第一步是计算注意力系数,对所有节点计算他的所有相邻节点的注意力系数。计算注意力系数公式如下:

在这里插入图片描述

•eij:节点i对邻居j的注意力系数

•W:可学习的参数矩阵

•a:可学习参数,一个向量,将多维特征转化为一个数

  得到注意力系数后,将该节点与所有邻居节点的注意力系数做归一化处理,就得到了注意力系数。这个归一化是指数归一化,并且归一化之前要使用L e a k y R e L U 进行非线性激活。归一后就得到了节点i和节点j的注意力系数。

在这里插入图片描述

聚合

  得到计算好的注意力系数,将特征进行加权求和,经激活函数激活后就得到了每个节点的新特征。

在这里插入图片描述

计算的例子

  https://zhuanlan.zhihu.com/p/412270208这里有一个图注意力模型的具体的计算的例子,可以看一下,就知道GAT是怎么由输入得到输出的了,这里的GAT代码是PyGAT的官方代码,对cora数据集分类,如果能看懂就知道GAT怎么用了,或者可以继续看下去,用了PyGAT的模型,训练的部分简化了,删除了稀疏矩阵运算的版本。

模型定义

  图注意力层的定义如下:

class GraphAttentionLayer(nn.Module):
    """
    Simple GAT layer, similar to https://arxiv.org/abs/1710.10903
    """
    def __init__(self, in_features, out_features, dropout, alpha, concat=True):
        super(GraphAttentionLayer, self).__init__()
        self.dropout = dropout								#dropout表示随机放弃多少邻节点,一般0.2
        self.in_features = in_features						#输入特征
        self.out_features = out_features				 	#输出特征
        self.alpha = alpha									#激活函数用的参数,0.2
        self.concat = concat
        self.W = nn.Parameter(torch.empty(size=(in_features, out_features)))	#参数矩阵W
        nn.init.xavier_uniform_(self.W.data, gain=1.414)						#初始化
        self.a = nn.Parameter(torch.empty(size=(2*out_features, 1)))			#参数向量a
        nn.init.xavier_uniform_(self.a.data, gain=1.414)						#初始化
        self.leakyrelu = nn.LeakyReLU(self.alpha)								#激活函数

  注意力层计算的过程在forward函数中,输入是h,原特征矩阵,输出是新特征矩阵。

    def forward(self, h, adj):
        Wh = torch.mm(h, self.W) # h.shape: (N, in_features), Wh.shape: (N, out_features)
        e = self._prepare_attentional_mechanism_input(Wh)						
        zero_vec = -9e15*torch.ones_like(e)
        attention = torch.where(adj > 0, e, zero_vec)							#邻接矩阵为0的位置表示没有边,注意力系数为0
        attention = F.softmax(attention, dim=1)									#指数归一化
        attention = F.dropout(attention, self.dropout, training=self.training)  #drop 0.2 的邻节点的注意力系数
        h_prime = torch.matmul(attention, Wh)									#输出特征
        if self.concat:
            return F.elu(h_prime)												#激活后的输出特征
        else:
            return h_prime

  GAT模型就是在图注意力层基础上MUTIHEAD机制的实现,但是MUTIHEAD机制不是图注意力模型必须的,只有1个图注意力层就可以算是一个图注意力模型了。
  下面看一下GAT模型的定义,MUTIHEAD机制的实现不重要,所以不看具体实现,只看参数。init中,nfeat是输入特征的维度,nhid是中间层,也就是如果有多个注意力层的情况下,注意力层的输出特征维度,nclass是最终的输出维度,因为这个GAT实现的是一个分类器,所以输出特征数就是类别数,即如果有n类,输出特征就应该有n个,每个输出值表示该样本是该类别的概率。例如每个样本都有一些自己的特征,一共有三类,把特征以及该样本和其他样本的关系输入到训练好的GAT,输出特征是0.1,0.5,0.4,就表示该样本是第一类,第二类,第三类的概率是0.1,0.5,0.4。至于为什么输出是概率,就涉及到模型训练的损失函数了,这部分内容可以看一下交叉熵相关的知识。顶着烧的我反正是没太看懂

class GAT(nn.Module):
    def __init__(self, nfeat, nhid, nclass, dropout, alpha, nheads):
    def forward(self, x, adj):

模型训练

  GAT能把输入特征变换为输出特征,是需要训练的。GAT一共两个可变参数,参数矩阵W和参数a,已经在模型定义时用nn.Parameter声明成参数了,接下来只要定义训练函数,就能训练出合适的模型了。

  怎么训练出合适的模型,让模型能够得到预期的输出,取决于模型的任务。对于分类任务来说,应该用已有数据的标签来训练模型,让模型得到的样本标签能够尽量贴近真实标签。

  首先要实例化一个模型,假设实例化一个三分类GAT模型:

hidden = int(labels.max()) + 1                  #直接把中间层特征数也设置成输出特征数了
model = GAT(nfeat=features.shape[1], 
            nhid=hidden,                        #中间层特征数,模型可以在每一层有不同的输出特征数
            nclass=int(labels.max()) + 1,       #类别即输出特征数
            dropout=0.2, 
            nheads= 1, 
            alpha=0.2)

  训练模型用的优化器也要实例化一个:

patience = 100 #100次LOSS没有下降,就停止训练
epochs = 1000
optimizer = optim.Adam(model.parameters(), 
                       lr=0.001,                #学习率,训练慢了可以调大一点 
                       weight_decay=5e-4)

  单轮次的训练是这样的,就是用GAT算输出,然后用输出和正确结果计算损失,然后使用优化器进行优化,优化器会会优化参数。损失函数就是上面提到的交叉熵。其中labels是ONEHOT编码,例如有三个类别type1,type2,type3,编码就是001,010,100,至于为什么使用ONEHOT编码,这也是和交叉熵有关的。

def train(epoch):
    t = time.time()
    model.train()
    optimizer.zero_grad()
    output = model(features, adj)										# 算输出
    loss_train = F.nll_loss(output[idx_train], labels[idx_train])		# nll_loss损失函数
    acc_train = accuracy(output[idx_train], labels[idx_train])
    loss_train.backward()
    optimizer.step()													# 使用优化器优化
    loss_val = F.nll_loss(output[idx_train], labels[idx_train])
    acc_val = accuracy(output[idx_train], labels[idx_train])
    # 输出单轮训练结果
    print('Epoch: {:04d}'.format(epoch+1),
          'loss_train: {:.4f}'.format(loss_train.data.item()),
          'acc_train: {:.4f}'.format(acc_train.data.item()),
          'loss_val: {:.4f}'.format(loss_val.data.item()),
          'acc_val: {:.4f}'.format(acc_val.data.item()),
          'time: {:.4f}s'.format(time.time() - t))
    return loss_val.data.item()

  这样的训练进行多轮,直到所有轮次训练完或者损失函数算的loss值不再变换。

t_total = time.time()
loss_values = []
bad_counter = 0
best = epochs + 1
best_epoch = 0
for epoch in range(epochs):
    loss_values.append(train(epoch))
    torch.save(model.state_dict(), '{}.pkl'.format(epoch))
    if loss_values[-1] < best:
        best = loss_values[-1]
        best_epoch = epoch
        bad_counter = 0
    else:
        bad_counter += 1

    if bad_counter == patience:
        break
    files = glob.glob('*.pkl')
    for file in files:
        epoch_nb = int(file.split('.')[0])
        if epoch_nb < best_epoch:
            os.remove(file)

  train.py里用随机生成的数据进行了训练,运行一下就可以看到模型训练的过程结果,训练好的最佳参数的模型会保存到一个pkl文件里,最后的测试部分,会读入文件中的模型,测试中有一个idx_test是用于测试的数据的下标,我没有定义测试数据,所以将测试的这部分注释掉了。

  (原来的数据集中标签是一整份的,idx_train是用于训练的样本下标,idx_test是用于测试的样本下标,例如idx_train是0-4,表示labels[0:4]都是训练用的)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/112349.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于双闭环PID控制器的永磁同步电机控制系统仿真

目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 永磁同步电机&#xff08;PMSM&#xff0c;permanent magnet synchronous motor&#xff09;的基本结构主要包括定子、转子以及端盖三个主要模块。其中转子磁路结构是永磁同步电机与其它电机最主…

Verilog刷题HDLBits——Exams/review2015 fsm

Verilog刷题HDLBits——Exams/review2015 fsm题目描述状态转换图代码结果题目描述 This is the fourth component in a series of five exercises that builds a complex counter out of several smaller circuits. See the final exercise for the overall design. You may …

人工智能期末复习:聚类(详细笔记)

文章目录聚类的概述常见的聚类算法原型聚类K均值聚类算法K均值聚类算法顺序前导聚类&#xff08;Sequential leader clustering&#xff09;高斯混合聚类&#xff08;KMM&#xff09;密度聚类DBSCAN聚类算法层次聚类AGNES聚类算法谱聚类聚类的评价&#xff08;轮廓系数&#xf…

深度学习—00入门 神经网络

1、深度学习简介 深度学习是机器学习的一个分支&#xff0c;简单来说就是通过人工神经网络&#xff0c;强行在业务的 输入 和 输出 之间&#xff0c;暴力耦合一个出一个数学模型。 1.1 深度学习特点 1、由于是暴力耦合出来的模型&#xff0c;自然模型可解释性很差&#xff0c…

kkfile在线文件预览部署(Linux服务器版本)

一&#xff1a;kkfile部署指南 KKfile文件预览是一款开源的文档在线预览项目。项目使用流行的spring boot搭建&#xff0c;易上手和部署&#xff0c;基本支持主流办公文档的在线预览&#xff0c;如doc,docx,Excel,pdf,txt,zip,rar,图片等等。 如果你是在windows系统中可以不用…

第二证券|锂离子聚合物电池的分类和使用注意事项

根据锂离子电池所用电解质资料的不同&#xff0c;锂离子电池分为液态锂离子电池和聚合物锂离子电池。聚合物锂离子电池所用的正负极资料与液态锂离子都是相同的&#xff0c;正极资料分为钴酸锂、锰酸锂、三元资料和磷酸铁锂资料&#xff0c;负极为石墨&#xff0c;电池工作原理…

RabbitMQ 第一天 基础 6 SpringBoot 整合RabbitMQ

RabbitMQ 【黑马程序员RabbitMQ全套教程&#xff0c;rabbitmq消息中间件到实战】 文章目录RabbitMQ第一天 基础6 SpringBoot 整合RabbitMQ6.1 SpringBoot 整合 RabbitMQ【生产者】6.1.1 生产者6.2 SpringBoot 整合 RabbitMQ【消费者】6.2.1 消费者6.3 小结第一天 基础 6 Spri…

ESP32-CAM 使用 MicroPython 进行开发

ESP32-CAM 开发工具 ESP32-CAM是安信可发布小尺寸的摄像头模组。该模块可以作为最小系统独立工作&#xff0c;尺寸仅为2740.54.5mm。 ESP32-CAM可广泛应用于各种物联网场合&#xff0c;适用于家庭智能设备、工业无线控制、无线监控、人脸识别以及其它物联网应用&#xff0c;是…

stm32f407VET6 系统学习 day02 GPIO 引脚的按键 中断,中断设置 (配置)

1.中断基本知识 1.知识点&#xff1a; STM32的所有中断&#xff08;内部或外部)都是由NVIC&#xff08;嵌套向量中断控制器&#xff09;控制 注意:在KEIL5工程中的fwlib分组中&#xff0c;misc.c文件提供了NVIC相关的固件库函数。 2.中断源 &#xff1a;引起CPU中断的根源&am…

DirectX12_API流程入门篇

本部分主要记录下使用D3D12入门所涉及到的API&#xff0c;记录简单使用方式供后期快速查找使用(数据参照龙书实现)。 首先看一下DX12中拥有的管线能力&#xff1a; Raster Graphics PipelineCompute Graphics PipelineRay Tracing PipelineMesh Geometry Pipeline 具体管线示…

【数据结构】链式二叉树的实现

作者&#xff1a;一个喜欢猫咪的的程序员 专栏&#xff1a;《数据结构》 喜欢的话&#xff1a;世间因为少年的挺身而出&#xff0c;而更加瑰丽。 ——《人民日报》 目录 1.二叉树的概念及结构 1.1二叉树的概念 1.2二叉树的类型分类&#xff…

腾讯焦虑了,一向温文尔雅的马化腾也发脾气了

大家好&#xff0c;我是校长。昨天小马哥内部讲话在互联网上疯传&#xff0c;这应该是&#xff0c;腾讯这家公司创办以来&#xff0c;马化腾最焦虑也最外露的一次讲话了&#xff0c;重点大概涉及 3 大方面&#xff0c;8 大项内容&#xff1a;1、所有业务线 ROI 化&#xff0c;再…

【关于时间序列的ML】项目 9 :机器学习中的 ARIMA 模型

&#x1f50e;大家好&#xff0c;我是Sonhhxg_柒&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流&#x1f50e; &#x1f4dd;个人主页&#xff0d;Sonhhxg_柒的博客_CSDN博客 &#x1f4c3; &#x1f381;欢迎各位→点赞…

<Linux线程概念及线程控制>——《Linux》

目录 1. Linux线程概念 什么是线程 线程的优点 线程的缺点 线程异常 线程用途 2. Linux进程VS线程 进程和线程 进程的多个线程共享 关于进程线程的问题 3. Linux线程控制 POSIX线程库 创建线程 线程ID及进程地址空间布局 线程终止 线程等待 4. 分离线程 后记&#xff1a;●由于…

ArcGIS 制作这种“清新设计风”的地图海报封面

这种图怎么做?下面是星球研究所制作的一张地图海报,那么究竟是如何制作的呢? 星球研究所 制作 一通鼓捣之下,使用 ArcGIS Pro 制作出了风格相近的成都市区位及地形示意图地图(其实 QGIS 也能做)。 下面教程一并分享给大家。 完全使用 ArcGIS Pro 制作的成果图 1.全国区位…

Python——几个常用的数学函数

1. min()函数&#xff1a;取出给定参数的最小值 说明&#xff1a;获取指定数值或者指定序列中最小值。 print(min(1, 5)) print(min(1, 2, 3, 4, 5, 6)) print(min([2, 3, 4, 5])) 2.max()函数&#xff1a;取出给定参数的最大值 说明&#xff1a;获取指定数值或者指定序列中…

CSS初级教程【第一天】

CSS初级教程【第一天】【1】CSS初识【2】CSS简介【3】CSS语法【4】CSS 选择器【5】CSS使用【6】CSS 注释【7】CSS 颜色【8】CSS RGB 颜色【9】CSS HEX 颜色【10】CSS HSL 颜色&#xff08;色相| 饱和度 | 明度&#xff09;【0】末尾声名【1】CSS初识 CSS 是一种描述 HTML 文档样…

【青岛大学·王卓】第3章_栈和队列

【青岛大学王卓】第3章_栈和队列 20221107-20221119 3.1 栈和队列的定义和特点 普通线性表插入和删除可以是线性表中的任意为位置&#xff1b; 3.1.1 栈 栈的概念 栈和队列是两种常用的、重要的数据结构。栈和队列是限定插入和删除只能在表的端点进行的线性表。 栈特点 后…

Anki学习之路

【常规操作】&#xff1a; 【自定义卡片进度】&#xff1a; [右键单击] -> [重设学习进度] //选择放到哪一个队列中&#xff08; 新卡片队列 / 复习队列 &#xff09;。 【重新学一遍】&#xff1a; //然后再进行上面的操作步骤。 【牌组齿轮按钮】&#xff1a; 【每日新…

Web前端105天-day62-HTML5_CORE

HTML5CORE02 目录 前言 一、复习 二、拖拽 三、上传服务器 四、Canvas 五、地图 总结 前言 HTML5CORE02学习开始 一、复习 跨域 浏览器的同源策略导致在网页中, 通过 AJAX 发送网络请求时, 默认只能向同源的服务器请求同源: 协议 端口号 域名 三者都相同产生跨域的原因…