🎯要点
- 谱图神经网络
- 计算注意力分数
- 对比图神经网络、卷积网络和图注意力网络
- 药物靶标建模学习和预测相互作用
- 腹侧和背侧皮质下结构
- 手写字体字符序列文本识别
- 组织病理学图像分析
- 长短期记忆财务模式预测相关性
- 生物医学图像特征学习和迭代纠正
Python注意力机制
对于图卷积网络,图卷积运算产生邻居节点特征的归一化和。
h
i
(
l
+
1
)
=
σ
(
∑
j
∈
N
(
i
)
1
c
i
j
W
(
l
)
h
j
(
l
)
)
h_i^{(l+1)}=\sigma\left(\sum_{j \in N (i)} \frac{1}{c_{i j}} W^{(l)} h_j^{(l)}\right)
hi(l+1)=σ
j∈N(i)∑cij1W(l)hj(l)
其中
N
(
i
)
N (i)
N(i) 是其一跳邻居的集合(要在集合中包含
v
i
v_i
vi,只需向每个节点添加一个自循环),
c
i
j
=
∣
N
(
i
)
∣
∣
N
(
j
)
∣
c_{i j}=\sqrt{| N (i)|} \sqrt{| N (j)|}
cij=∣N(i)∣∣N(j)∣ 是基于图结构的归一化常数,
σ
\sigma
σ 是激活函数(图卷积网络使用 ReLU),
W
(
l
)
W^{(l)}
W(l) 是节点级特征的共享权重矩阵转变。
图注意力网络引入了注意力机制来替代静态归一化卷积运算。下面是根据层
l
l
l 的嵌入计算层
l
+
1
l+1
l+1 的节点嵌入
h
i
(
l
+
1
)
h_i^{(l+1)}
hi(l+1) 的方程。
z
i
(
l
)
=
W
(
l
)
h
i
(
l
)
(
1
)
z_i^{(l)}=W^{(l)} h_i^{(l)}\qquad(1)
zi(l)=W(l)hi(l)(1)
e i j ( l ) = LeakyReLU ( a ⃗ ( l ) T ( z i ( l ) ∥ z j ( l ) ) ) ( 2 ) e_{i j}^{(l)}=\operatorname{LeakyReLU}\left(\vec{a}^{(l)^T}\left(z_i^{(l)} \| z_j^{(l)}\right)\right)\qquad(2) eij(l)=LeakyReLU(a(l)T(zi(l)∥zj(l)))(2)
α i j ( l ) = exp ( e i j ( l ) ) ∑ k ∈ N ( i ) exp ( e i k ( l ) ) ( 3 ) \alpha_{i j}^{(l)}=\frac{\exp \left(e_{i j}^{(l)}\right)}{\sum_{k \in N (i)} \exp \left(e_{i k}^{(l)}\right)}\qquad(3) αij(l)=∑k∈N(i)exp(eik(l))exp(eij(l))(3)
h i ( l + 1 ) = σ ( ∑ j ∈ N ( i ) α i j ( l ) z j ( l ) ) ( 4 ) h_i^{(l+1)}=\sigma\left(\sum_{j \in N (i)} \alpha_{i j}^{(l)} z_j^{(l)}\right)\qquad(4) hi(l+1)=σ j∈N(i)∑αij(l)zj(l) (4)
方程(1)是下层嵌入 h i ( l ) h_i^{(l)} hi(l)的线性变换, W ( l ) W^{(l)} W(l)是其可学习的权重矩阵。方程(2)计算两个邻居之间的成对非标准化注意力得分。
方程 1:
def edge_attention(self, edges):
z2 = torch.cat([edges.src['z'], edges.dst['z']], dim=1)
a = self.attn_fc(z2)
return {'e' : F.leaky_relu(a)}
方程 2:
def edge_attention(self, edges):
z2 = torch.cat([edges.src['z'], edges.dst['z']], dim=1)
a = self.attn_fc(z2)
return {'e' : F.leaky_relu(a)}
在这里,它首先连接两个节点的 z z z 嵌入,其中 ||表示串联,然后取它和可学习权重向量 a ⃗ ( l ) \vec{a}^{(l)} a(l) 的点积,最后应用 LeakyReLU。这种形式的注意力通常称为附加注意力,与 Transformer 模型中的点积注意力形成对比。方程(3)应用 softmax 来标准化每个节点传入边上的注意力分数。方程(4)与图卷积网络类似。来自邻居的嵌入被聚合在一起,并按注意力分数进行缩放。
方程 3 和 4:
def reduce_func(self, nodes):
alpha = F.softmax(nodes.mailbox['e'], dim=1)
h = torch.sum(alpha * nodes.mailbox['z'], dim=1)
return {'h' : h}
图注意力网络引入多头注意力来丰富模型容量并稳定学习过程。每个注意力头都有自己的参数,它们的输出可以通过两种方式合并:
h
i
(
l
+
1
)
=
∥
k
=
1
K
σ
(
∑
j
∈
N
(
i
)
α
i
j
k
W
k
h
j
(
l
)
)
h_i^{(l+1)}=\|_{k=1}^K \sigma\left(\sum_{j \in N (i)} \alpha_{i j}^k W^k h_j^{(l)}\right)
hi(l+1)=∥k=1Kσ
j∈N(i)∑αijkWkhj(l)
或
h
i
(
l
+
1
)
=
σ
(
1
K
∑
k
=
1
K
∑
j
∈
N
(
i
)
α
i
j
k
W
k
h
j
(
l
)
)
h_i^{(l+1)}=\sigma\left(\frac{1}{K} \sum_{k=1}^K \sum_{j \in N (i)} \alpha_{i j}^k W^k h_j^{(l)}\right)
hi(l+1)=σ
K1k=1∑Kj∈N(i)∑αijkWkhj(l)
class MultiHeadLayer(nn.Module):
def __init__(self, g, in_dim, out_dim, num_heads, merge='cat'):
super(MultiHeadLayer, self).__init__()
self.heads = nn.ModuleList()
for i in range(num_heads):
self.heads.append(Layer(g, in_dim, out_dim))
self.merge = merge
def forward(self, h):
head_outs = [attn_head(h) for attn_head in self.heads]
if self.merge == 'cat':
return torch.cat(head_outs, dim=1)
else:
return torch.mean(torch.stack(head_outs))
定义两层注意力模型
class TAM(nn.Module):
def __init__(self, g, in_dim, hidden_dim, out_dim, num_heads):
super(TAM, self).__init__()
self.layer1 = MultiHeadLayer(g, in_dim, hidden_dim, num_heads)
self.layer2 = MultiHeadLayer(g, hidden_dim * num_heads, out_dim, 1)
def forward(self, h):
h = self.layer1(h)
h = F.elu(h)
h = self.layer2(h)
return h
加载数据集
from xl import Graph
from xl.data import citation_graph as citegrh
import networkx as nx
def load_cora_data():
data = citegrh.load_cora()
features = torch.FloatTensor(data.features)
labels = torch.LongTensor(data.labels)
mask = torch.BoolTensor(data.train_mask)
g = Graph(data.graph)
return g, features, labels, mask
训练
import time
import numpy as np
g, features, labels, mask = load_cora_data()
net = TAM(g,
in_dim=features.size()[1],
hidden_dim=8,
out_dim=7,
num_heads=2)
optimizer = torch.optim.Adam(net.parameters(), lr=1e-3)
dur = []
for epoch in range(30):
if epoch >= 3:
t0 = time.time()
logits = net(features)
logp = F.log_softmax(logits, 1)
loss = F.nll_loss(logp[mask], labels[mask])
optimizer.zero_grad()
loss.backward()
optimizer.step()
if epoch >= 3:
dur.append(time.time() - t0)
print("Epoch {:05d} | Loss {:.4f} | Time(s) {:.4f}".format(
epoch, loss.item(), np.mean(dur)))