Graph attention network
论文链接:https://arxiv.org/abs/1710.10903
图注意力:图上的某一个节点A和其他的节点去做了attention计算来更新A的特征。
global graph attention
计算节点A与其他所有节点的attention ,但是会丢失图的结构特征,并且造成计算量很大。
为什么会丢失结构特征?因为节点A与外围节点不是直接相连的。A的一阶连接节点其实只有橘黄色的节点,但是global graph attention还是会计算A与空白节点的关系,那我们只需要A与橘黄色节点的attention,该怎么办?
mask graph attention
只计算A节点与一阶邻居节点的 attention,此时获取到的就是关于节点A 的局部结构特征,大大减少了计算量。
论文中说到,共享的可训练权重矩阵W的用处是为了将输入特征转换成高维特征以此获取足够的表达能力,这个权重矩阵是作用在每个节点上的。a是共享的注意力系数,就是说每个节点的注意力系数都一样。在节点上做self-attention。这个公式的输出表示节点j对节点i的重要性。在这里,允许节点彼此学习,丢弃所有结构信息,毕竟self-attention本身也不会学习位置信息。
论文中说到,公式1中的注意力机制a是一个单层的前馈神经网络,由权重向量参数化
作者将图结构引入注意力机制中,只计算节点i的一阶邻居节点。
论文中说到,softmax的目的是为了方便在不同的节点之间比较系数。
以节点1为例,计算节点2对节点1的影响,节点3对节点1的影响,并更新节点1,下述代码只用于理解公式:
import torch
import numpy as np
import torch.nn as nn
import torch.nn.functional as F
leaky_relu = nn.LeakyReLU(negative_slope=0.2)
node_1 = np.array([[1., 2., 3., 4., 5.]])
node_1_tensor = torch.from_numpy(node_1) # N x dim:N是节点数量,这里是1,dim是节点维度,这里是5
node_2 = np.array([[3., 4., 5., 6., 7.]])
node_2_tensor = torch.from_numpy(node_2) # N x dim:N是节点数量,这里是1,dim是节点维度,这里是5
node_3 = np.array([[4., 5., 6., 7., 8.]])
node_3_tensor = torch.from_numpy(node_3) # N x dim:N是节点数量,这里是1,dim是节点维度,这里是5
node_w = np.array([[1., 2., 3., 4., 5., 6.],
[4., 5., 6., 7., 8., 7.],
[7., 8., 9., 10., 11., 8.],
[7., 8., 9., 10., 11., 9.],
[7., 8., 9., 10., 11., 10.]]) # 5 x 6:假设6为最后输出的维度
node_w_tensor = torch.from_numpy(node_w) # torch.Size([3, 5])
node_a = np.array([[1., 2., 3., 4., 5., 6., 1., 2., 3., 4., 5., 6.]]) # 2倍的F撇 torch.Size([1, 10])
node_a_tensor = torch.from_numpy(node_a)
# 线性变换
w_h1 = torch.mm(node_1_tensor, node_w_tensor) # tensor([[ 93., 108., 123., 138., 153., 130.]], dtype=torch.float64)
w_h2 = torch.mm(node_2_tensor, node_w_tensor) # tensor([[145., 170., 195., 220., 245., 210.]], dtype=torch.float64)
w_h1_w_h2 = torch.concat((w_h1, w_h2), dim=1) # tensor([[ 93., 108., 123., 138., 153., 130., 145., 170., 195., 220., 245., 210.]], dtype=torch.float64)
e_1_2 = torch.matmul(w_h1_w_h2, node_a_tensor.T).squeeze() # tensor(7210., dtype=torch.float64) 节点2对节点1的重要性
w_h1 = torch.mm(node_1_tensor, node_w_tensor) # tensor([[ 93., 108., 123., 138., 153., 130.]], dtype=torch.float64)
w_h3 = torch.mm(node_3_tensor, node_w_tensor) # tensor([[171., 201., 231., 261., 291., 250.]], dtype=torch.float64)
w_h1_w_h3 = torch.concat((w_h1, w_h3), dim=1) # tensor([[ 93., 108., 123., 138., 153., 130., 171., 201., 231., 261., 291., 250.]], dtype=torch.float64)
e_1_3 = torch.matmul(w_h1_w_h3, node_a_tensor.T).squeeze() # tensor(8040., dtype=torch.float64) 节点3对节点1的重要性
e_1_2_e_1_3 = torch.stack([leaky_relu(e_1_2), leaky_relu(e_1_3)]) # tensor([7210., 8040.], dtype=torch.float64)
a_1_2_a_1_3 = F.softmax(e_1_2_e_1_3, dim=0) # tensor([0., 1.], dtype=torch.float64) 获得归一化注意力系数a_1_2_a_1_3
w_h1_2_w_h1_3 = torch.stack([w_h2, w_h3]).squeeze() # torch.Size([2, 5])
# 更新了节点1的特征,该张量包含了节点2对节点1的影响和节点3对节点1的影响
node_1_tensor = F.sigmoid(torch.matmul(a_1_2_a_1_3.unsqueeze(0), w_h1_2_w_h1_3)) # torch.Size([1, 6])
Tips:
如有问题,麻烦指正,谢谢各位!
如有疑问,请留言,谢谢!
持续更新…