GAT

这是关于GAT的一些介绍,注意力机制在图神经网络中的应用;inductive learning;局部信息的聚合

相较于GCN,GAT更加的注重局部环境

图注意力层

注意力机制的三要素:query,source,attention value。可以设置如下

  • Query :设置成当前中心节点的特征向量
  • Source设置为所有邻居的特征向量
  • attention value:设置为中心节点经过聚合操作后的新的特征向量
    设图中任意节点viv_i在第l层所对应的特征向量,hiRd(l)h_{i}\in R^{d^{(l)}}d(l)d^{(l)}表示节点特征的长度;经过一个以注意力机制为核心的聚合操作之后,输出的是每个节点的新特征向量:hiRd(l+1)h_{i}'\in R^{d^{(l+1)}}d(l+1)d^{(l+1)}表示输出的特征向量的长度。这个聚合操作叫做:图注意力层(GAL)!


假设中心节点为viv_i,我们假设邻居节点vjv_jviv_i的权重系数为:

eij=a(Whi,Wj)e_{ij}=a(Wh_i,W_j)

WRd(l+1)×d(l))W\in R^{d^{(l+1)}\times d^{(l)})}是该层节点特征变换的权重参数。aa是相关度计算,只要满足Rd(l+1)×Rd(l)RR^{d^{(l+1)}}\times R^{d^{(l)}} \rightarrow R

论文采用一个单层全连接层来处理:其中权重参数:aR2d(l+1)a\in R^{2d^{(l+1)}}

eij=Leaky ReLU(aT[WhiWhj])e_{ij}=\text{Leaky ReLU}(a^T[Wh_i||Wh_j])

其实我认为这里有点问题,从计算(代码)来看更因该像这样eij=Leaky ReLU([WhiWhj]a)e_{ij}=\text{Leaky ReLU}([Wh_i||Wh_j]a),中间是拼接操作。代码采用采用了一种很神奇的方式:没有使用拼接技术将两个矩阵拼接起来,而是采用了——广播机制:

1
2
3
4
5
6
7
8
9
10
11
12
13
def _prepare_attentional_mechanism_input(self, Wh):
# Wh.shape (N, out_feature)
# self.a.shape (2 * out_feature, 1)
# Wh1&2.shape (N, 1)
# e.shape (N, N)#这样就构成了一个相似度矩阵
Wh1 = torch.matmul(Wh, self.a[:self.out_features, :])
Wh2 = torch.matmul(Wh, self.a[self.out_features:, :])
# broadcast add
e = Wh1 + Wh2.T# N×1+1×N
#通过广播机制得到一个相似度矩阵
#这里和拼接和在和a相乘在进行扩展的原理是一样的,只不过这里利用广播机制来进行计算。
# 而且这里的计算量更小由4O^2变成了2O^2
return self.leakyrelu(e)

代码原链接:https://github.com/Diego999/pyGAT

采用了广播机制减少了计算量:4O22O2,O(out_features)4O^2\rightarrow 2O^2,O(\text{out\_features})

最后的权重系数:对e进行归一化处理:

αij=softmaxj(eij)=exp(eij)vkN~(vi)exp(eik)\alpha_{ij}=softmax_j(e_{ij})=\frac{\exp(e_{ij})}{\sum\limits_{v_{k}\in \tilde{N}(v_{i})}\exp(e_{ik})}

这样就保证了所有邻居的权重系数的和为1.完成权重系数的计算对节点viv_{i}进行更新:

hi=σ(vkN~(vi)αijWhj)h_i'=\sigma(\sum\limits_{v_{k}\in \tilde{N}(v_{i})}\alpha_{ij}Wh_{j})

当然也可以采用多头注意力机制:

ht=k=1Kσ(vjN~(vI)αij(k)W(k)hj)h_t'=||_{k=1}^K\sigma(\sum\limits_{v_j\in\tilde N_{(v_I)}}\alpha_{ij}^{(k)}W^{(k)h_j})