Graph Attention Network 图注意力网络 (三) 更改邻接masked attention

背景:需要将GAT实现在resnet的预测score之上,并且将masked attention运用上。

相关内容:

图注意力网络(GAT) ICLR2018, Graph Attention Network论文详解

Graph Attention Network (一) 训练运行与代码概览

Graph Attention Network (二) 模型定义

博主代码地址:https://github.com/Xingxiangrui/masked_graph_attention_network/blob/master/masked_gat_after_resnet.py

目录

一、预测输出加上GAT

1.1 resnet结构

1.2 加入GAT

1.3 输出维度

二、masked attention推导

2.1 特征提取与注意力机制

2.2 邻接的选取

2.3 加入的位置

三、代码实现masked attention

3.1 矩阵对应位置相乘(哈达马积)

3.2 masked矩阵

3.3 masked attention


一、预测输出加上GAT

1.1 resnet结构

与resnet结构一致,最终根据需要输出的维度决定输出的fc维度。

class Head(nn.Module):
    def __init__(self, nclasses):
        super(Head, self).__init__()
        self.gmp = nn.AdaptiveMaxPool2d(1)
        self.fc = nn.Linear(2048, nclasses)

    def forward(self, x):
        B, C, _, _ = x.size()
        x = self.gmp(x).view(B, C)
        x = self.fc(x)
        return x


class Resnet(nn.Module):
    def __init__(self, nclasses, backbone):
        super(Resnet, self).__init__()
        if backbone == 'resnet101':
            model = models.resnet101(pretrained=False)
            print('load pretrained resnet101 model from local...')
            model.load_state_dict(torch.load('./resnet101-5d3b4d8f.pth'))
        else:
            raise Exception()
        self.features = nn.Sequential(
            model.conv1,
            model.bn1,
            model.relu,
            model.maxpool,
            model.layer1,
            model.layer2,
            model.layer3,
            model.layer4, )
        self.heads = Head(nclasses)

    def forward(self, x, embedding=None):
        x = self.features(x)  # [B,2048,H,W]
        x = self.heads(x)
        return x

1.2 加入GAT

将GATLayer加入

class Head(nn.Module):
    def __init__(self, nclasses):
        super(Head, self).__init__()
        self.gmp = nn.AdaptiveMaxPool2d(1)
        self.fc = nn.Linear(2048, nclasses)
        self.gat = BGATLayer(in_features=1, out_features=1, dropout=0, alpha=0.2)

需要保证输入的size为 [batch, n_classes, channels ],用view函数即可实现

    def forward(self, x):
        # size [batch, channels=2048, W=14 , H=14 ]
        B, C, _, _ = x.size()
        # output x [ Batch, channels , W]
        x = self.gmp(x).view(B, C)
        x = self.fc(x)
        x=x.view(x.size(0),x.size(1),1)
        residual=x
        output=residual+self.gat(x)

1.3 输出维度

输出为 [batch, n_classes=80, class_channels=1 ]

输出时需要将第三维度去掉,

参考:torch中维度相关变换

torch.squeeze() / torch.unsqueeze()
torch.squeeze(n)函数表示压缩tensor中第n维为1的维数,比如下面第一个,b.squeeze(2).size(),原始的b为上面的torch.Size([1, 3, 2]),第二维是2≠1,所以不压缩,尺寸保持不变;而若b.squeeze(0).size(),则发现第一维为1,因此压缩为3x2的tensor

        x = self.gmp(x).view(B, C)
        x = self.fc(x)
        x=x.view(x.size(0),x.size(1),1)
        residual=x
        output=residual+self.gat(x)
        output=output.squeeze(2)
        return output

二、masked attention推导

2.1 特征提取与注意力机制

为了得到相应的输入与输出的转换,我们需要根据输入的feature至少一次线性变换得到输出的feature,所以我们需要对所有节点训练一个权值矩阵:,这个权值矩阵就是输入与输出的F个feature与输出的F'个feature之间的关系。

We then perform self-attention on the nodes—a shared attentional mechanism,针对每个节点实行self-attention的注意力机制,机制为  

注意力互相关系数为attention coefficients:

  • 这个公式表示的节点 j 对于节点 i 的重要性,而不去考虑图结构性的信息
  • 如前面所言,向量h就是 feature向量
  • 下标i,j表示第i个节点和第j个节点

作者通过masked attention将这个注意力机制引入图结构之中,masked attention的含义 :只计算节点 i 的相邻的节点 j

 节点 j 为,其中Ni为 节点i的所有相邻节点。为了使得互相关系数更容易计算和便于比较,我们引入了softmax对所有的i的相邻节点j进行正则化:

实验之中,注意力机制a是一个单层的前馈神经网络,通过权值向量来确定,并且加入了 LeakyRelu的非线性激活,这里小于零斜率为0.2。(这里我们回顾下几种Relu函数,relu:小于0就是0,大于零斜率为1;LRelu:小于零斜率固定一个值,大于零斜率为1;PRelu:小于零斜率可变,大于零斜率为1;还有CRelu,Elu,SELU)。注意力机制如下:

,也是我们前面需要得到的注意力互相关系数

在模型中应用相互注意机制a(Whi,Whj),通过权重向量 a 参数化,应用 LeakyReLU 激活

  • 模型权重为
  • 转置表示为T
  • concatenation 用 || 表示
  • 公式含义就是权值矩阵与F'个特征相乘,然后节点相乘后并列在一起,与权重相乘,LRelu激活后指数操作得到softmax的分子

2.2 邻接的选取

实验之中,注意力机制a是一个单层的前馈神经网络,通过权值向量来确定,并且加入了 LeakyRelu的非线性激活,这里小于零斜率为0.2。(这里我们回顾下几种Relu函数,relu:小于0就是0,大于零斜率为1;LRelu:小于零斜率固定一个值,大于零斜率为1;PRelu:小于零斜率可变,大于零斜率为1;还有CRelu,Elu,SELU)。注意力机制如下:

,也是我们前面需要得到的注意力互相关系数

如果i,j之间没有连接,可以将LeakeyReLU之后的结果设为0,即softmax之后的结果设为1.

2.3 加入的位置

代码:

    def forward(self, x):
        # [B,N,C]
        B, N, C = x.size()
        # h = torch.bmm(x, self.W.expand(B, self.in_features, self.out_features))  # [B,N,C]
        h = torch.matmul(x, self.W)  # [B,N,C]
        a_input = torch.cat([h.repeat(1, 1, N).view(B, N * N, C), h.repeat(1, N, 1)], dim=2).view(B, N, N,
                                                                                                  2 * self.out_features)  # [B,N,N,2C]
        # temp = self.a.expand(B, self.out_features * 2, 1)
        # temp2 = torch.matmul(a_input, self.a)
        attention = self.leakyrelu(torch.matmul(a_input, self.a).squeeze(3))  # [B,N,N]

        attention = F.softmax(attention, dim=2)  # [B,N,N]
        attention = F.dropout(attention, self.dropout, training=self.training)
        h_prime = torch.bmm(attention, h)  # [B,N,N]*[B,N,C]-> [B,N,C]
        out = F.elu(h_prime + self.beta * h)
        return out

即在leakey_ReLU之后加入此矩阵。即attention,如果coco的互相关概率小于某个阈值,则此值置为0.

三、代码实现masked attention

3.1 矩阵对应位置相乘(哈达马积)

需要实现矩阵对应点的相乘。即aij*bij=cij,两个形状一样的矩阵,其中对应的点相乘。

对应点相乘,x.mul(y) ,即点乘操作,点乘不求和操作,又可以叫作Hadamard product;点乘再求和,即为卷积

    data = [[1,2], [3,4], [5, 6]]
    tensor = torch.FloatTensor(data)
     
    tensor
    Out[27]:
    tensor([[ 1.,  2.],
            [ 3.,  4.],
            [ 5.,  6.]])
     
    tensor.mul(tensor)
    Out[28]:
    tensor([[  1.,   4.],
            [  9.,  16.],
            [ 25.,  36.]])

即tensor.mul(another_tensor)

3.2 masked矩阵

即小于某值即为0,否则为1

需要用到np.where

        with open('coco_correlations.pkl', 'rb') as f:
            print("loading coco_correlations.pkl ")
            correlations = pickle.load(f)
        # with open('coco_names.pkl', 'rb') as f:
        #     print("loading coco_names.pkl")
        #     self.names = pickle.load(f)
        self.coco_correlation_A_B = correlations['pp']
        self.probability_filter_threshold=0.1
        self.mask=torch.FloatTensor(np.where(self.coco_correlation_A_B>self.probability_filter_threshold,1,0))

即将条件概率矩阵改为阈值相关,小于阈值设为0,大于阈值设为1.座位mask。

3.3 masked attention

leakeyReLU之后乘以masked attention

        attention = self.leakyrelu(torch.matmul(a_input, self.a).squeeze(3))  # [Batch,N_clsses,N_classes]

        # fixme add masked attention
        attention= attention.mul(self.mask)

        attention = F.softmax(attention, dim=2)  # [B,N,N]

四、运行与调试

4.1 显卡中的Tensor

为了保证mask能与相应的张量在GPU中运算,需要在forward之中进行相乘

self.mask=torch.FloatTensor(np.where(self.coco_correlation_A_B>self.probability_filter_threshold,1,0))
        # fixme add masked attention
        mask=self.mask.cuda()
        attention= attention.mul(mask)

init的时候不要加cuda,再forward的时候再加cuda。

  • 7
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 16
    评论
好的,关于注意力机制的代码,我们先来了解一下注意力机制(Graph Attention Network,简称GAT)。 GAT是一种基于结构的注意力机制,它在结构中进行节点分类、分类等任务时,能够自适应地学习每个节点在不同邻居节点上所占的重要性,从而更准确地进行分类。下面是一个简单的GAT实现: ```python import torch import torch.nn as nn import torch.nn.functional as F class GraphAttentionLayer(nn.Module): def __init__(self, in_features, out_features, dropout, alpha, concat=True): super(GraphAttentionLayer, self).__init__() self.in_features = in_features self.out_features = out_features self.dropout = dropout self.alpha = alpha self.concat = concat self.W = nn.Parameter(torch.zeros(size=(in_features, out_features))) nn.init.xavier_uniform_(self.W.data, gain=1.414) self.a = nn.Parameter(torch.zeros(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): Wh = torch.mm(h, self.W) a_input = self._prepare_attentional_mechanism_input(Wh) e = self.leakyrelu(torch.matmul(a_input, self.a).squeeze(2)) zero_vec = -9e15*torch.ones_like(e) attention = torch.where(adj > 0, e, zero_vec) attention = F.softmax(attention, dim=1) attention = F.dropout(attention, self.dropout, training=self.training) h_prime = torch.matmul(attention, Wh) if self.concat: return F.elu(h_prime) else: return h_prime def _prepare_attentional_mechanism_input(self, Wh): N = Wh.size()[0] Wh_repeated_in_chunks = Wh.repeat_interleave(N, dim=0) Wh_repeated_alternating = Wh.repeat(N, 1) all_combinations_matrix = torch.cat([Wh_repeated_in_chunks, Wh_repeated_alternating], dim=1) return all_combinations_matrix.view(N, N, 2 * self.out_features) ``` 在这个代码中,我们定义了一个名为GraphAttentionLayer的类,它继承于nn.Module类。在它的__init__方法中,我们定义了一些必要的参数,包括输入特征维度、输出特征维度、dropout率、LeakyReLU函数的负斜率系数以及是否将节点特征与注意力机制的输出进行拼接。W和a是需要学习的参数,其中W是线性变换的权重矩阵,a是注意力机制的权重矩阵。我们使用xavier_uniform_方法对这两个参数进行初始化。 在forward方法中,我们首先将节点特征矩阵h与权重矩阵W相乘,得到Wh。然后,我们通过_prepare_attentional_mechanism_input方法将Wh转换为用于注意力计算的输入矩阵a_input。接着,我们将a_input与注意力权重矩阵a相乘,得到每个节点与其邻居之间的注意力系数e。我们使用LeakyReLU函数将e中的负值裁剪掉。然后,我们对每个节点的邻居节点计算softmax,得到它们之间的注意力权重。我们使用dropout对注意力权重进行随机失活,以防止过拟合。最后,我们将注意力权重与Wh相乘,得到每个节点的新特征h_prime。如果concat参数为True,我们将h_prime与原始节点特征进行拼接并使用ELU函数作为输出;否则,我们直接返回h_prime作为输出。 需要注意的是,这个代码中的实现是基于PyTorch框架的,如果你使用其他的深度学习框架,可能需要做一些调整。同时,这个代码只是GAT的一个简单实现,如果你想深入学习GAT,还需要阅读相关论文并了解更多细节。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

祥瑞Coding

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值