赞
踩
GAT(Graph Attention Networks),即图注意力神经网络,根据名称,我们可以知道这个网络肯定是和注意力架构绑定的,那么为什么需要注意力架构呢?
在直推式模型如GCN中,使用拉普拉斯矩阵来获取顶点特征,但是,拉普拉斯矩阵存在着一些问题,在运算的时候,需要把整个图所有节点都放进模型中,这就导致无法预测新节点。而GAT采用Attention架构,只负责将该节点的邻居节点进行计算,也就是只计算子图的一部分,这样,就可以避免全图计算。
假设,图中有N个节点,每个节点都有F维特征,可以表示为如下:
h
=
{
h
1
⃗
,
h
2
⃗
,
.
.
.
h
N
⃗
}
,
h
i
⃗
∈
R
F
h=\{\vec{h_{1}},\vec{h_{2}},...\vec{h_{N}}\},\vec{h_{i}}\isin{R^F}
h={h1
,h2
,...hN
},hi
∈RF
为了能够保留足够的表达能力,将输入特征转为高阶特征,需要进行至少一次的线性变换,如对节点i,j进行如下转换:
e
i
j
=
a
(
W
h
i
⃗
,
W
h
j
⃗
)
e_{ij}=a(W\vec{h_{i}},W\vec{h_{j}})
eij=a(Whi
,Whj
)
其中,W是随机权重矩阵,
e
i
j
e_{ij}
eij是节点i对节点j的影响力系数。
如何计算节点i,j之间的相关度呢?论文中采用了一个单层的前馈神经网络,采用LeakyReLU作为非线性激活函数,公式如下:
e
i
j
=
L
e
a
k
y
R
e
L
U
(
a
⃗
T
[
W
h
i
⃗
∣
∣
W
h
j
⃗
]
)
e_{ij}=LeakyReLU(\vec{a}^T[W\vec{h_{i}}||W\vec{h_{j}}])
eij=LeakyReLU(a
T[Whi
∣∣Whj
])
其中,||表示拼接操作。
在计算节点之间的注意力系数时,往往可能会计算所有节点之间的系数,如果这样计算的话,就需要加载所有的图节点,和实际的目的不符。实际上,论文使用了masked attention,只需要计算当前节点和其邻居节点的系数即可。
为了更好的在不同节点之间分配权重,我们将目标节点与所有邻居节点计算出来的系数进行归一化处理,公式如下:
α
i
j
=
s
o
f
t
m
a
x
j
(
e
i
j
)
=
e
x
p
(
e
i
j
)
∑
k
∈
N
i
e
x
p
(
e
i
k
)
\alpha_{ij}=softmax_{j}(e_{ij})=\frac{exp(e_{ij})}{\sum_{k\isin{N_{i}}}exp(e_{ik})}
αij=softmaxj(eij)=∑k∈Niexp(eik)exp(eij)
其中,k是节点i的邻居节点。
完整的权重系数计算公式为:
α
i
j
=
s
o
f
t
m
a
x
k
(
e
i
j
)
=
e
x
p
(
L
e
a
k
y
R
e
L
U
(
a
⃗
T
[
W
h
i
⃗
∣
∣
W
h
j
⃗
]
)
)
∑
k
∈
N
i
e
x
p
(
L
e
a
k
y
R
e
L
U
(
a
⃗
T
[
W
h
i
⃗
∣
∣
W
h
j
⃗
]
)
)
\alpha_{ij}=softmax_{k}(e_{ij})=\frac{exp(LeakyReLU(\vec{a}^T[W\vec{h_{i}}||W\vec{h_{j}}]))}{\sum_{k\isin{N_{i}}}exp(LeakyReLU(\vec{a}^T[W\vec{h_{i}}||W\vec{h_{j}}]))}
αij=softmaxk(eij)=∑k∈Niexp(LeakyReLU(a
T[Whi
∣∣Whj
]))exp(LeakyReLU(a
T[Whi
∣∣Whj
]))
得到整体的归一化系数后,与节点对应的特征进行组合,经过非线性激活函数后,每个节点最终输出的特征向量如下所示:
h
i
⃗
′
=
σ
(
∑
j
∈
N
i
α
i
j
W
h
j
⃗
)
\vec{h_{i}}^{\prime}=\sigma(\displaystyle\sum_{j\isin{N_{i}}}\alpha_{ij}W\vec{h_{j}})
hi
′=σ(j∈Ni∑αijWhj
)
以上,就是如何计算每个节点和节点之间的注意力系数了。
论文中采用了多头注意力,图中显示有三条有颜色的线,对应本文中选取的K=3,即3个注意力机制,节点1和节点2-6分别计算各自的注意力系数,最终,将所有的系数矩阵进行拼接然后求平均操作,公式如下:
h
i
⃗
′
=
σ
(
1
K
∑
k
=
1
K
∑
j
∈
N
i
α
i
j
k
W
k
h
j
⃗
)
\vec{h_{i}}^{\prime}=\sigma(\frac{1}{K}\displaystyle\sum_{k=1}^{K}\sum_{j\isin{N_{i}}}\alpha_{ij}^{k}W^{k}\vec{h_{j}})
hi
′=σ(K1k=1∑Kj∈Ni∑αijkWkhj
)
其中,
α
i
j
k
\alpha_{ij}^{k}
αijk是第k组注意力机制计算出的权重系数,
W
k
W^{k}
Wk是对应的输入线性变换矩阵。
以上,就是GAT所有的理论知识点了。
GAT模型
class GAT(nn.Module): def __init__(self, nfeat, nhid, nclass, dropout, alpha, nheads): """Dense version of GAT.""" super(GAT, self).__init__() self.dropout = dropout #第一层多头注意力机制 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) #第二层多头注意力机制 self.out_att = GraphAttentionLayer(nhid * nheads, nclass, dropout=dropout, alpha=alpha, concat=False) def forward(self, x, adj): #对特征数据进行dropout x = F.dropout(x, self.dropout, training=self.training) #对8个注意力系数矩阵进行拼接,cat(2708*8)=2708*64 x = torch.cat([att(x, adj) for att in self.attentions], dim=1) #再次进行dropout操作 x = F.dropout(x, self.dropout, training=self.training) #第二层注意力模型2708*64×64*7=2708*7并进行激活 x = F.elu(self.out_att(x, adj)) #对每行特征进行softmax,获取对应概率标签 return F.log_softmax(x, dim=1)
1、执行模型,将输入的特征数据进行dropout=0.6;
2、第一层注意力为多头:遍历每个attentions,传进去的数据是特征矩阵x和邻接矩阵adj,然后对每个注意力矩阵进行拼接(调用方法GraphAttentionLayer(1433,8,0.6,0.2,8));
3、再次对特征数据进行dropout=0.6;
4、第二层注意力为单个:通过一个激活函数elu之后,进行softmax输出标签概率。
Attention模型
class GraphAttentionLayer(nn.Module): """ Simple GAT layer, similar to https://arxiv.org/abs/1710.10903 """ ''' in_features:特征数-1433 out_features:隐藏单元数-8 dropout:0.6 alpha:0.2 ''' def __init__(self, in_features, out_features, dropout, alpha, concat=True): super(GraphAttentionLayer, self).__init__() self.dropout = dropout self.in_features = in_features self.out_features = out_features self.alpha = alpha self.concat = concat #1433*8 self.W = nn.Parameter(torch.empty(size=(in_features, out_features))) nn.init.xavier_uniform_(self.W.data, gain=1.414) #16*1 self.a = nn.Parameter(torch.empty(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): #2708*1433×1433*8=2708*8 Wh = torch.mm(h, self.W) # h.shape: (N, in_features), Wh.shape: (N, out_features) #2708*2708 e = self._prepare_attentional_mechanism_input(Wh) #创建等大小的负无穷矩阵2708*2708 zero_vec = -9e15*torch.ones_like(e) #将邻接矩阵为0的地方进行更新 attention = torch.where(adj > 0, e, zero_vec) #对注意力系数矩阵归一化 attention = F.softmax(attention, dim=1) #再随机进行dropout操作 attention = F.dropout(attention, self.dropout, training=self.training) #获取注意力系数矩阵2708*2708×2708*8=2708*8 h_prime = torch.matmul(attention, Wh) if self.concat: #进行一次激活函数 return F.elu(h_prime) else: return h_prime #Wh为线性矩阵 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) #2708*8×8*1=2708*1 Wh1 = torch.matmul(Wh, self.a[:self.out_features, :]) Wh2 = torch.matmul(Wh, self.a[self.out_features:, :]) # broadcast add #2708*1+2708*1.T=2708*2708 e = Wh1 + Wh2.T return self.leakyrelu(e) def __repr__(self): return self.__class__.__name__ + ' (' + str(self.in_features) + ' -> ' + str(self.out_features) + ')'
以上代码为模型在直推学习下进行,模型代码以及后期所做的注释已经标注在以上信息中,请大家自行观看。
1、两层GAT模型,第一层多头注意力,输出特征维度(共64个特征),激活函数为指数线性单元(ELU);
2、第二层单头注意力,计算个特征(为分类数),接softmax激活函数;
3、为了处理小的训练集,模型中大量采用正则化方法,具体为L2正则化;
4、dropout;
1、三层GAT模型,前两层多头注意力,输出特征维度(共1024个特征),激活函数为指数非线性单元(ELU);
2、最后一层用于多标签分类,,每个头计算121个特征,后接logistic sigmoid激活函数;
3、不使用正则化和dropout;
4、使用了跨越中间注意力层的跳跃连接。
以上就是小编对GAT模型的一个理解,大家如果要有纠正或者补充的话,请留言或者加QQ:1143948594,随时联系啦!!!
附:论文链接:GRAPH ATTENTION NETWORKS
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。