DeepLearning | 图注意力网络Graph Attention Network(GAT)论文、模型、代码解析

本篇博客是对论文 Velikovi, Petar, Cucurull, Guillem, Casanova, Arantxa,et al. Graph Attention Networks, 2018, ICLR的解读与python复现, 全文阅读约10分钟。

博主关于图神经网络的文章
DeepLearning | 图卷积神经网络(GCN)解析(论文、算法、代码)
DeepLearning | 图注意力网络Graph Attention Network(GAT)论文、模型、代码解析
DeepLearning | 图卷积网络基于拓扑结构的分类(T-GCN)

关于半监督学习
基于分歧的半监督学习方法
机器学习教程 之 半监督学习 Tri-training方法 (论文、数据集、代码)
机器学习教程 之 半监督学习 Co-training 协同训练 (论文、算法、数据集、代码)
机器学习教程 之 半监督学习 Coreg 协同回归算法 (论文、算法、数据集、代码)
机器学习教程 之 半监督学习 基于图正则项的半监督极限学习机

这些博客都提供了算法的讲解和python的代码复现,感兴趣的可以了解一下

一、Graph Attention Network

1.1 GAT的优点

图注意力网络(GAT)是作者对图卷积网络(GCN)的改进。它的主要创新点在于利用了注意力机制(Attention Mechanism)来自动的学习和优化节点间的连接关系,这一作法有以下几个优点:

  1. 克服了GCN只适用于直推式学习的缺陷(在训练期间需要测试时的图数据),可以应用于我们熟悉的归纳式学习任务(在训练期间不需要测试时的图数据)。
  2. 使用注意力权重代替了原先非0即1的节点连接关系,即两个节点间的关系可以被优化为连续数值,从而获得更丰富的表达
  3. 由于attention值的计算是可以在节点间并行进行的,网络的计算相当高效

1.2 Graph Attention layer的输入输出

作为一层网络,图注意力层的输入为

h = { h 1 , . . . , h N } , h i ∈ R F h=\{h_{1},...,h_{N}\}, h_{i} \in \mathbb{R}^{F} h={h1,...,hN},hiRF
这里的 N N N是图的节点个数, h i h_{i} hi表示节点的特征向量, F F F表示特征维度。
图注意力层的输出为
h ′ = { h 1 ′ , . . . , h N ′ } , h i ′ ∈ R F ′ {h}'=\{{h}'_{1},...,{h}'_{N}\}, {h}'_{i} \in \mathbb{R}^{{F}'} h={h1,...,hN},hiRF

同样的, F ′ {F}' F表示输出的特征维度。
从图注意力层的输入输出可以看出,其本质上也是对特征的一种变换,和其余的网络层功能是类似的。

1.3 Graph Attention layer的attention机制

首先需要定义一个特征变换矩阵 W ∈ R F × F ′ W \in \mathbb{R}^{F \times {F}'} WRF×F 用于每一个节点从输入到输出的变换。

  1. GAT中的attention机制被称为self-attention,记为 f f f,其功能如下:

    e i j = f ( W h i , W h j ) e_{ij}=f(Wh_{i},Wh_{j}) eij=f(Whi,Whj)

    如图所示,该式表示了self-attention利用节点 i i i和节点 j j j的特征作为输入计算出了 e i j e_{ij} eij, 而 e i j e_{ij} eij则表示了节点 j j j对于节点 i i i的重要性。
    在这里插入图片描述

  2. 需要说明的是,这里的节点 j j j是节点 i i i的近邻,而节点 i i i可能是拥有多个近邻的,因此就有了下面的 s o f t m a x softmax softmax归一化操

    a i j = s o f t m a x ( e i j ) = e x p ( e i j ) ∑ k ∈ χ i e x p ( e i k ) a_{ij}=softmax(e_{ij})=\frac{exp(e_{ij})}{\sum_{k \in \chi_{i} }exp(e_{ik})} aij=softmax(eij)=kχiexp(eik)exp(eij)
    χ i \chi_{i} χi是节点 i i i 的近邻集合。

  3. 那么说了这么久,这个self-attention机制,也就是我们一开始提到的 a ( W h i , W h j ) a(Wh_{i},Wh_{j}) a(Whi,Whj)是怎么计算的呢?其实也很简单

    f ( W h i , W h j ) = L e a k y R e L U ( a [ W h i ∣ ∣ W h j ] ) f(Wh_{i},Wh_{j}) = LeakyReLU(a[Wh_{i} || Wh_{j}]) f(Whi,Whj)=LeakyReLU(a[WhiWhj])

    这里的 a ∈ R 2 F ′ a \in \mathbb{R}^{2{F}'} aR2F 表示需要训练的网络参数, ∣ ∣ || 表示的是矩阵拼接操作, L e a r k y R e L u LearkyReLu LearkyReLu则是一种激活函数,是 R e L u ReLu ReLu的一种改进。

  4. 最后给出图感知层的定义,即

    h i ′ = σ ( ∑ j ∈ χ i a i j W h j ) {h}'_{i}=\sigma(\sum_{j \in \chi_{i}}a_{ij}Wh_{j}) hi=σ(jχiaijWhj)

上面就是GAT的attention计算方法了,其中会有两个知识点会影响理解

  1. self-attention机制为什么可以表示节点间的重要性
  2. L e a r k y R e L u LearkyReLu LearkyReLu的定义

对于上面这两点,如果知道的话,再结合对GCN的理解,可以很容易的get到GAT的点和含义,如果不清楚的话可能会有点迷糊。

  1. attention机制实际上是在有监督的训练下计算两个向量的匹配程度,从而揭示其重要性和影响,由于本篇博客不是专门介绍attention的,这里不做多余的解释,日后会补上相应的博客。
  2. L e a r k y R e L u LearkyReLu LearkyReLu的定义如下:
    y = { x i f x > = 0 a x e l s e y=\left\{\begin{matrix} x & if x >=0 \\ ax & else \end{matrix}\right. y={xaxifx>=0else
    即引入了一个系数 a a a来取消 R e L U ReLU ReLU的死区。

1.4 多头attention机制

  1. 为了稳定self−attention的学习过程,GAT还采用了一种多头机制,即独立的计算K个attention,然后将其获得的特征拼接起来,获得一个更全面的表述,表示如下
    h i ′ = ∣ ∣ k = 1 K σ ( ∑ j ∈ χ i a i j k W k h j ) {h}'_{i}=||^{K}_{k=1} \sigma(\sum_{j \in \chi_{i}}a^{k}_{ij}W^{k}h_{j}) hi=k=1Kσ(jχiaijkWkhj)
    这里的 || 表示矩阵拼接的操作,其余的符号和上面描述的一致。
  2. 同时,考虑到在网络的最后一层输出层如果还采用这种拼接的方式扩大特征维度,可能不合理,因此,GAT又为输出层定义了平均的操作
    h i ′ = σ ( 1 K ∑ k = 1 K ∑ j ∈ χ i a i j k W k h j ) {h}'_{i}= \sigma(\frac{1}{K}\sum^{K}_{k=1}\sum_{j \in \chi_{i}}a^{k}_{ij}W^{k}h_{j}) hi=σ(K1k=1KjχiaijkWkhj)

多头attention机制如图所示
在这里插入图片描述

二、GAN的python复现

模型的核心代码如下

import numpy as np
import tensorflow as tf

from utils import layers
from models.base_gattn import BaseGAttN

class GAT(BaseGAttN):
    def inference(inputs, nb_classes, nb_nodes, training, attn_drop, ffd_drop,
            bias_mat, hid_units, n_heads, activation=tf.nn.elu, residual=False):
        attns = []
        for _ in range(n_heads[0]):
            attns.append(layers.attn_head(inputs, bias_mat=bias_mat,
                out_sz=hid_units[0], activation=activation,
                in_drop=ffd_drop, coef_drop=attn_drop, residual=False))
        h_1 = tf.concat(attns, axis=-1)
        for i in range(1, len(hid_units)):
            h_old = h_1
            attns = []
            for _ in range(n_heads[i]):
                attns.append(layers.attn_head(h_1, bias_mat=bias_mat,
                    out_sz=hid_units[i], activation=activation,
                    in_drop=ffd_drop, coef_drop=attn_drop, residual=residual))
            h_1 = tf.concat(attns, axis=-1)
        out = []
        for i in range(n_heads[-1]):
            out.append(layers.attn_head(h_1, bias_mat=bias_mat,
                out_sz=nb_classes, activation=lambda x: x,
                in_drop=ffd_drop, coef_drop=attn_drop, residual=False))
        logits = tf.add_n(out) / n_heads[-1]
    
        return logits

三、GAT代码、论文、数据集下载

微信搜索“老和山算法指南”获取下载链接与技术交流群
在这里插入图片描述
有问题可以私信博主,点赞关注的一般都会回复,一起努力,谢谢支持。

好的,关于注意力机制代码,我们先来了解一下注意力机制Graph Attention Network,简称GAT)。 GAT是一种基于结构的注意力机制,它在结构中进行节点分类、分类等任务时,能够自适应地学习每个节点在不同邻居节点上所占的重要性,从而更准确地进行分类。下面是一个简单的GAT实现: ```python import torch import torch.nn as nn import torch.nn.functional as F class GraphAttentionLayer(nn.Module): def __init__(self, in_features, out_features, dropout, alpha, concat=True): super(GraphAttentionLayer, self).__init__() self.in_features = in_features self.out_features = out_features self.dropout = dropout self.alpha = alpha self.concat = concat self.W = nn.Parameter(torch.zeros(size=(in_features, out_features))) nn.init.xavier_uniform_(self.W.data, gain=1.414) self.a = nn.Parameter(torch.zeros(size=(2*out_features, 1))) nn.init.xavier_uniform_(self.a.data, gain=1.414) self.leakyrelu = nn.LeakyReLU(self.alpha) def forward(self, h, adj): Wh = torch.mm(h, self.W) a_input = self._prepare_attentional_mechanism_input(Wh) e = self.leakyrelu(torch.matmul(a_input, self.a).squeeze(2)) zero_vec = -9e15*torch.ones_like(e) attention = torch.where(adj > 0, e, zero_vec) attention = F.softmax(attention, dim=1) attention = F.dropout(attention, self.dropout, training=self.training) h_prime = torch.matmul(attention, Wh) if self.concat: return F.elu(h_prime) else: return h_prime def _prepare_attentional_mechanism_input(self, Wh): N = Wh.size()[0] Wh_repeated_in_chunks = Wh.repeat_interleave(N, dim=0) Wh_repeated_alternating = Wh.repeat(N, 1) all_combinations_matrix = torch.cat([Wh_repeated_in_chunks, Wh_repeated_alternating], dim=1) return all_combinations_matrix.view(N, N, 2 * self.out_features) ``` 在这个代码中,我们定义了一个名为GraphAttentionLayer的类,它继承于nn.Module类。在它的__init__方法中,我们定义了一些必要的参数,包括输入特征维度、输出特征维度、dropout率、LeakyReLU函数的负斜率系数以及是否将节点特征与注意力机制的输出进行拼接。W和a是需要学习的参数,其中W是线性变换的权重矩阵,a是注意力机制的权重矩阵。我们使用xavier_uniform_方法对这两个参数进行初始化。 在forward方法中,我们首先将节点特征矩阵h与权重矩阵W相乘,得到Wh。然后,我们通过_prepare_attentional_mechanism_input方法将Wh转换为用于注意力计算的输入矩阵a_input。接着,我们将a_input与注意力权重矩阵a相乘,得到每个节点与其邻居之间的注意力系数e。我们使用LeakyReLU函数将e中的负值裁剪掉。然后,我们对每个节点的邻居节点计算softmax,得到它们之间的注意力权重。我们使用dropout对注意力权重进行随机失活,以防止过拟合。最后,我们将注意力权重与Wh相乘,得到每个节点的新特征h_prime。如果concat参数为True,我们将h_prime与原始节点特征进行拼接并使用ELU函数作为输出;否则,我们直接返回h_prime作为输出。 需要注意的是,这个代码中的实现是基于PyTorch框架的,如果你使用其他的深度学习框架,可能需要做一些调整。同时,这个代码只是GAT的一个简单实现,如果你想深入学习GAT,还需要阅读相关论文并了解更多细节。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Liangjun_Feng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值