详解 Non-local 与 SENet、CBAM 模块融合:GCNet、DANet (视觉注意力机制 (三))

一、Global Context Network (GCNet)

论文地址:https://arxiv.org/search/?query=GCNet&searchtype=all&source=header
代码地址:https://github.com/xvjiarui/GCNet
为了捕获长距离依赖关系,产生了两类方法: 采用自注意力机制来建模query对的关系。
对query-independent(可以理解为无query依赖)的全局上下文建模。
NLNet 就是采用自注意力机制来建模像素对关系。然而NLNet对于每一个位置学习不受位置依赖的 attention map,造成了大量的计算浪费。SENet 用全局上下文对不同通道进行权值重标定,来调整通道依赖。然而,采用权值重标定的特征融合,不能充分利用全局上下文。
GCNet 模块
作者提出了一种新的全局上下文建模框架,global context block(简写GCNet),即能够像Non-local block一样建立有效的长距离依赖,又能够像SE block一样省计算量。
GC block的3个步骤为:
global attention pooling用于上下文建模。
bottleneck transform来捕获通道间依赖。
broadcast element-wise addition用于特征融合。
在简化版的 non-local block 中,transform 模块有大量的参数。为了获得 SE block 轻量的优点,1×1 卷积用 bottleneck transform 模块来取代,能够显著的降低参数量(其中r是降低率)。因为两层 bottleneck transform 增加了优化难度,所以在 ReLU前面增加一个 layer normalization 层(降低优化难度且作为正则提高了泛化性)。

import torch
from torch import nn

class ContextBlock(nn.Module):
    def __init__(self,inplanes,ratio,pooling_type='att',
                 fusion_types=('channel_add', )):
        super(ContextBlock, self).__init__()
        valid_fusion_types = ['channel_add', 'channel_mul']

        assert pooling_type in ['avg', 'att']
        assert isinstance(fusion_types, (list, tuple))
        assert all([f in valid_fusion_types for f in fusion_types])
        assert len(fusion_types) > 0, 'at least one fusion should be used'

        self.inplanes = inplanes
        self.ratio = ratio
        self.planes = int(inplanes * ratio)
        self.pooling_type = pooling_type
        self.fusion_types = fusion_types

        if pooling_type == 'att':
            self.conv_mask = nn.Conv2d(inplanes, 1, kernel_size=1)
            self.softmax = nn.Softmax(dim=2)
        else:
            self.avg_pool = nn.AdaptiveAvgPool2d(1)
        if 'channel_add' in fusion_types:
            self.channel_add_conv = nn.Sequential(
                nn.Conv2d(self.inplanes, self.planes, kernel_size=1),
                nn.LayerNorm([self.planes, 1, 1]),
                nn.ReLU(inplace=True),  # yapf: disable
                nn.Conv2d(self.planes, self.inplanes, kernel_size=1))
        else:
            self.channel_add_conv = None
        if 'channel_mul' in fusion_types:
            self.channel_mul_conv = nn.Sequential(
                nn.Conv2d(self.inplanes, self.planes, kernel_size=1),
                nn.LayerNorm([self.planes, 1, 1]),
                nn.ReLU(inplace=True),  # yapf: disable
                nn.Conv2d(self.planes, self.inplanes, kernel_size=1))
        else:
            self.channel_mul_conv = None

    def spatial_pool(self, x):
        batch, channel, height, width = x.size()
        if self.pooling_type == 'att':
            input_x = x
            # [N, C, H * W]
            input_x = input_x.view(batch, channel, height * width)
            # [N, 1, C, H * W]
            input_x = input_x.unsqueeze(1)
            # [N, 1, H, W]
            context_mask = self.conv_mask(x)
            # [N, 1, H * W]
            context_mask = context_mask.view(batch, 1, height * width)
            # [N, 1, H * W]
            context_mask = self.softmax(context_mask)
            # [N, 1, H * W, 1]
            context_mask = context_mask.unsqueeze(-1)
            # [N, 1, C, 1]
            context = torch.matmul(input_x, context_mask)
            # [N, C, 1, 1]
            context = context.view(batch, channel, 1, 1)
        else:
            # [N, C, 1, 1]
            context = self.avg_pool(x)
        return context

    def forward(self, x):
        # [N, C, 1, 1]
        context = self.spatial_pool(x)
        out = x
        if self.channel_mul_conv is not None:
            # [N, C, 1, 1]
            channel_mul_term = torch.sigmoid(self.channel_mul_conv(context))
            out = out * channel_mul_term
        if self.channel_add_conv is not None:
            # [N, C, 1, 1]
            channel_add_term = self.channel_add_conv(context)
            out = out + channel_add_term
        return out
if __name__ == "__main__":
    in_tensor = torch.ones((12, 64, 128, 128))
    cb = ContextBlock(inplanes=64, ratio=1./16.,pooling_type='att')
    out_tensor = cb(in_tensor)
    print(in_tensor.shape)
    print(out_tensor.shape)

二、Dual Attention Network for Scene Segmentation(DANet)

论文地址:https://arxiv.org/pdf/1809.02983.pdf​arxiv.org
代码地址:https://github.com/junfu1115/DANet​github.com
DANet 是一种经典的应用self-Attention的网络,它引入了一种自注意力机制来分别捕获空间维度和通道维度中的特征依赖关系。
场景分割需要预测出图像中的像素点属于某一目标类或场景类,其图像场景的复杂多样(光照,视角,尺度,遮挡等)对于场景的理解和像素点的判别造成很大困难。
主流场景分割方法大致可分为以下两种类型:一是通过使用多尺度特征融合的方式增强特别的表达,例如空间金字塔结构 (PSP,ASPP) 或者高层浅层特征融合 (RefineNet)。但是这些方式没有考虑到不同特征之间的关联依赖,而这对于场景的理解确实十分重要。另一是利用 RNN 网络构建特征长范围的特征关联,但这种关联往往受限于 RNN 的 long-term memorization。
双重注意网络(DANet)来自适应地集成局部特征和全局依赖。在传统的扩张 FCN 之上附加两种类型的注意力模块,分别模拟空间和通道维度中的语义相互依赖性。
从其结构图中可以看到,它由两个并列的 attention module 组成,第一个得到的是特征图中任意两个位置的依赖关系,称为Position Attention Module(PAM);第二个是任意两个通道间的依赖关系,称为 Channel Attention Module(CAM)。

从其具体的模块中来看,PAM中 的 attention_map 的大小为 B×(W×H)×(W×H),而 CAM 中的 attention_map 大小为B×C×C,这就是 PAM 与 CAM 的区别,他们所代表的一个是任意两个位置之间的依赖关系,一个代表的是任意两个通道之间的依赖关系。

位置注意力模块(PAM)通过所有位置处的特征的加权和来选择性地聚合每个位置的特征。无论距离如何,类似的特征都将彼此相关。
通道注意力模块(CAM)通过整合所有通道映射之间的相关特征来选择性地强调存在相互依赖的通道映射。
将两个注意模块的输出相加以进一步改进特征表示,这有助于更精确的分割结果。

位置注意力模块(PAM)
问题:传统FCNs生成的特征会导致对物体的错误分类。
解决:引入位置注意模块在局部特征上建立丰富的上下文关系,将更广泛的上下文信息编码为局部特征,进而增强他们的表示能力。

链接来源:https://blog.csdn.net/qq_39478403/article/details/105459001?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522164999496416780271535838%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=164999496416780271535838&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_ecpm_v1~rank_v31_ecpm-5-105459001.142v9pc_search_result_cache,157v4control&utm_term=Non-local%E7%BD%91%E7%BB%9C%E6%A8%A1%E5%9D%97&spm=1018.2226.3001.4187

Original: https://blog.csdn.net/walkinging1/article/details/126336218
Author: 正在搬砖嘤
Title: 详解 Non-local 与 SENet、CBAM 模块融合:GCNet、DANet (视觉注意力机制 (三))

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/651001/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球