🎯要点
- 谱图神经网络
- 计算注意力分数
- 对比图神经网络、卷积网络和图注意力网络
- 药物靶标建模学习和预测相互作用
- 腹侧和背侧皮质下结构
- 手写字体字符序列文本识别
- 组织病理学图像分析
- 长短期记忆财务模式预测相关性
- 生物医学图像特征学习和迭代纠正
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 = mergedef 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 nxdef 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 npg, 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)))