《GRAPH ATTENTION NETWORKS》 
  
 
   
  
    解决GNN聚合邻居节点的时候没有考虑到不同的邻居节点重要性不同的问题,GAT借鉴了Transformer的idea,引入masked self-attention机制, 
   在计算图中的每个节点的表示的时候,会根据邻居节点特征的不同来为其分配不同的权值。 
  
 
   
  1.论文公式:
    注意力系数:代表节点j的特征对i的重要性。 
  
 
  
    经过归一化: 
  
 
  
 
  
    得到最终注意力系数: 
  
 
  
   a是 
   注意力机制权重, h是 
   节点特征,W是 
   节点特征的权重 
  
    如图: 
  
 
  
 
   
   将多头注意力得到的系数整合: 
  
 
  
 一共k个注意力头,求i节点聚合后的特征。 
  
    如果是到了最后一层: 
  
 
  
 使用avg去整合 
  
    如图: 
  
 
  
 
  2.论文导图:
 
  3.核心代码:
 
   从输入h节点特征 到 输出h`的过程: 
  
 
  -  
    数据处理方式和GCN一样,对边、点、labels编码,并得到h和adj矩阵。
 -  
    GraphAttention层的具体实现:
 
    定义权重W,作用是将input的维度转换,且是可学习参数。 
  
 
  self.W = nn.Parameter(torch.empty(size=(in_features, out_features))) #一开始定义的是空的nn.init.xavier_uniform_(self.W.data, gain=1.414) #随机初始化,然后在学习的过程中不断更新学习参数。
    定义 
   注意力机制权重a,用来求注意力分数,也是可学习参数。 
  
 
  self.a = nn.Parameter(torch.empty(size=(2*out_features, 1)))nn.init.xavier_uniform_(self.a.data, gain=1.414) #随机初始化
 
   求得注意力分数 
  
 
  Wh1 = torch.matmul(Wh, self.a[:self.out_features, :])Wh2 = torch.matmul(Wh, self.a[self.out_features:, :])e = Wh1 + Wh2.T
    然后进行softmax、dropout。 
  
 
  zero_vec = -9e15*torch.ones_like(e)##如果如果两个节点有边链接则使其注意力得分为e,如果没有则使用-9e15的mask作为其得分attention = torch.where(adj > 0, e, zero_vec)##使用 softmax 函数对注意力得分进行归一化attention = F.softmax(attention, dim=1)##dropout,为模型的训练增加了一些正则化attention = F.dropout(attention, self.dropout, training=self.training)
-  
    整体GAT实现:
 
 
   根据头数n,定义n个GraphAttention 
   层(每个头都具有相同的参 
   数,并且 
   共享输入特征 
   ),和一个 
   GraphAttention 
   输出层。 
  
 
  
    n个层各输出一个注意力分数,最后拼接成一个。 
   features( 
   features.shape[1] 
   ) --> hidden(自定义) --*n(自定义) --> n*hidden 
  
 
  self.attentions = [GraphAttentionLayer(nfeat, nhid, dropout=dropout, alpha=alpha, concat=True) for _ in range(nheads)]for i, attention in enumerate(self.attentions):self.add_module('attention_{}'.format(i), attention) #将层命名然后加到模块里
    输出层将 n*hidden --> 
   class( 
   int(labels.max()) + 1) 
  
 
  self.out_att = GraphAttentionLayer(nhid * nheads, nclass, dropout=dropout, alpha=alpha, concat=False)
 
   forward(x, adj)函数: 
  
 
  x = F.dropout(x, self.dropout, training=self.training)x = torch.cat([att(x, adj) for att in self.attentions], dim=1) #遍历每个层对象,传参去运行。重复n次后得到 n * hidden 维的拼接后的注意力分数x = F.dropout(x, self.dropout, training=self.training)x = F.elu(self.out_att(x, adj))return F.log_softmax(x, dim=1) #输出层最后得到class维的分类概率矩阵。
    整体是 
   通过 x 在层之间传递参数的,x一直在变,不断的dropout。 
  
 
   
 4.采取的正则化:
   正则化通过对算法的修改来减少泛化误差,目前在深度学习中使用较多的策略有参数范数惩罚,提前终止,DropOut等。 
 
 
 -  
   dropout:通过使其它隐藏层神经网络单元不可靠从而 阻止了共适应的发生。因此,一个隐藏层神经元不能依赖其它特定神经元去纠正其错误。因为dropout程序导致两个神经元不一定每次都在一个dropout网络中出现。这样权值的更新不再依赖于有固定关系的隐含节点的共同作用,阻止了某些特征仅仅在其它特定特征下才有效果的情况 。迫使网络去学习更加鲁棒的特征 ,这些特征在其它的神经元的随机子集中也存在。
 
    -  
   提前停止:是将一部分训练集作为验证集。 当 验证集的性能越来越差时或者性能不再提升,则立即停止对该模型的训练。 这被称为提前停止。
 
   在当前best的loss对应的epoch之后后,又跑了patience轮,但是新的loss没有比之前best loss小的,也就是loss越来越大了,出现过拟合了。这时就应该停止训练。 
 
 
 if loss_values[-1] < best:best = loss_values[-1]best_epoch = epochbad_counter = 0else:bad_counter += 1if bad_counter == args.patience:break
5.和GCN对比adj的用法:
  GCN通过变换(归一、正则)的邻接矩阵和节点特征矩阵相乘,得到了考虑邻接节点后的节点特征矩阵。通过两层卷积,得到output。 
 
  GAT利用Wh和注意力权重a计算出注意力分数,再用adj矩阵把不相邻的节点的分数mask掉。即,可以给不同节点分配不同权重。 
 
                









![[C++进阶]对于多态的底层逻辑](https://i-blog.csdnimg.cn/direct/0751c4d8747843328703ec7450fba2aa.png)




![[H并查集] lc100347. 判断矩形的两个角落是否可达(并查集+高质量+周赛408_4)](https://i-blog.csdnimg.cn/direct/c527738e837744aba3298e2272d78ac4.png)



