神经网络多种注意力机制原理和代码讲解

news2024/11/24 21:09:37

多种注意力表格:
大神参考仓库链接: 魔鬼面具
对应 name 就是目录,点击即可跳转到对应学习。

nameneed_chaneelpaper
SE (2017) Truehttps://arxiv.org/abs/1709.01507
BAM (2018) Truehttps://arxiv.org/pdf/1807.06514.pdf
CBAM (2018) Truehttps://openaccess.thecvf.com/content_ECCV_2018/papers/Sanghyun_Woo_Convolutional_Block_Attention_ECCV_2018_paper.pdf
CoTAttention (2021) Truehttps://arxiv.org/abs/2107.12292
MobileViTAttention (2021) Truehttps://arxiv.org/abs/2110.02178
SK Truehttps://arxiv.org/pdf/1903.06586.pdf
ShuffleAttention Truehttps://arxiv.org/pdf/2102.00240.pdf
S2Attention Truehttps://arxiv.org/abs/2108.01072
ParNetAttention Truehttps://arxiv.org/abs/2110.07641
CoordAttention Truehttps://arxiv.org/abs/2103.02907
MHSA
Multi-Head-Self-Attention
Truehttps://wuch15.github.io/paper/EMNLP2019-NRMS.pdf
A2Attention Truehttps://arxiv.org/pdf/1810.11579.pdf
GC
Global Context Attention
Truehttps://arxiv.org/abs/1904.11492
EffectiveSE
Effective Squeeze-Excitation
Truehttps://arxiv.org/abs/1911.06667
GE
Gather-Excite Attention
Truehttps://arxiv.org/abs/1810.12348
CrissCrossAttention Truehttps://arxiv.org/abs/1811.11721
Polarized Self-Attention Truehttps://arxiv.org/abs/2107.00782
Sequential Self-Attention Truehttps://arxiv.org/abs/2107.00782
GAM Truehttps://arxiv.org/pdf/2112.05561v1.pdf
Biformer Truehttps://arxiv.org/abs/2303.08810
EMA Truehttps://arxiv.org/abs/2305.13563v2
CloAttention Truehttps://arxiv.org/abs/2303.17803
LSKBlock Truehttps://arxiv.org/pdf/2303.09030.pdf
ECA (2020) Falsehttps://arxiv.org/pdf/1910.03151.pdf
SimAM (2021) Falsehttp://proceedings.mlr.press/v139/yang21o/yang21o.pdf
SGE Falsehttps://arxiv.org/pdf/1905.09646.pdf
TripletAttention Falsehttps://arxiv.org/abs/2010.03045

  注意:博客尽可能的简单的介绍各个注意力主要的思想和图示,并通过代码的解释和测试来详细的理解各个注意力机制的使用。(内容可能不全,希望各位理解)

SE

全称:Squeeze-and-Excitation 挤压和激励
1、主要思想:想通过全局池化和全链接,形成通道上的注意力。然后从通道域的角度赋予图像不同位置不同的权重,得到更重要的特征信息。
简单来说:将原始特征图H 、W 维度上压缩为为1、1(长条形状),再进行一些全链接层, 得到1 * 1 * C的条形,再和原始的特征图进行通道上的相称,得到最终的注意力特征层。
在这里插入图片描述
对应公式具体流程如下:
X ^ = F s c a l e ( ) ( U , F e x ( F s q ( U ) , W ) ) F s q ( U ) = A v g P o o l ( U ) ,   F e x = M L P . \hat{X} = F_{scale()}(U, F_{ex}(F_{sq}(U), W)) \\ F_{sq}(U) = AvgPool(U) , \ F_{ex} = MLP. X^=Fscale()(U,Fex(Fsq(U),W))Fsq(U)=AvgPool(U), Fex=MLP.

值得注意的地方:1、论文中对比了平均池化与最大池化的实验结果,发现平均池化效果稍好一些(后面一些注意力机制,两者都用)。2、SE 的如何防止才可以使得网络的性能最优,论文中也在探讨。

2、模块使用地方:SE模块是一个即插即用的模块,可以在卷积模块之后直接插入SE模块,也可以在残差结构里面添加了SE模块。
在这里插入图片描述
3、代码

import numpy as np
import torch
from torch import nn
from torch.nn import init

class SEAttention(nn.Module):

    def __init__(self, channel=512, reduction=16):
        super().__init__()
        # 对应全局平均池化
        self.avg_pool = nn.AdaptiveAvgPool2d(1)
        
        # 两层全连接层,用于获取通道注意力机制,最后用 Sigmoid 激活函数,输出 0~1 之间的权重
        self.fc = nn.Sequential(
            nn.Linear(channel, channel // reduction, bias=False),
            nn.ReLU(inplace=True),
            nn.Linear(channel // reduction, channel, bias=False),
            nn.Sigmoid()
        )

    def init_weights(self):
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                init.kaiming_normal_(m.weight, mode='fan_out')
                if m.bias is not None:
                    init.constant_(m.bias, 0)
            elif isinstance(m, nn.BatchNorm2d):
                init.constant_(m.weight, 1)
                init.constant_(m.bias, 0)
            elif isinstance(m, nn.Linear):
                init.normal_(m.weight, std=0.001)
                if m.bias is not None:
                    init.constant_(m.bias, 0)

    def forward(self, x):
        b, c, _, _ = x.size()
        y = self.avg_pool(x).view(b, c)
        y = self.fc(y).view(b, c, 1, 1)
        return x * y.expand_as(x)


if __name__ == '__main__':
    input=torch.randn(50,512,7,7)
    se = SEAttention(channel=512,reduction=8)
    output=se(input)
    print(output.shape)

BAM

全称:Bottleneck Attention Module 瓶颈注意模块
1、主要思想:沿着两个不同的路径(通道和空间)推断注意力映射,通道注意力使得网络关注那个通道上的数据更加重要,空间注意力机制使得网络关注那个位置上的数据更加重要。
简单来说:一个分支将原始特征图H 、W 维度上压缩为为1、1(长条形状),再进行一些全链接层, 得到C * 1 * 1的条形。另一个分支将在通道C上进行压缩,压缩为C/R (R为比例)(拍扁一些),再经过一些卷积层,得到1 * H * W的特征图。将C * 1 * 1 对应乘上1 * H * W, 就变成 C * H * W 的 BAM 注意力。再和原始特征图对应相乘,经过残差层即可。对应图示如下:
在这里插入图片描述
对应公式具体流程如下:
M c ( F ) = B N ( M L P ( A v g P o o l ( F ) ) ) = B N ( W 1 ( W 0 A v g P o o l ( F ) + b 0 ) + b 1 ) , M s ( F ) = B N ( f 3 1 × 1 ( f 2 3 × 3 ( f 1 3 × 3 ( f 0 1 × 1 ( F ) ) ) ) ) , M ( F ) = σ ( M c ( F ) + M s ( F ) ) , F ′ = F + F ⊗ M ( F ) , 1 、其中, F ∈ R C ∗ H ∗ W , A v g P o o l ( F ) ∈ R C ∗ 1 ∗ 1 , W 0 ∈ R C / r × C , b 0 ∈ R C / r , W 1 ∈ R C × C / r , b 1 ∈ R C . 2 、其中, f 0 1 × 1 ∈ R C ∗ 1 ∗ 1 , f 1 3 × 3 ∈ R C / r ∗ 3 ∗ 3 , f 2 3 × 3 ∈ R C / r ∗ 3 ∗ 3 , f 3 1 × 1 ∈ R C / r ∗ 1 ∗ 1 , f 0 1 × 1 ∗ C / r 个, f 1 3 × 3 ∗ C / r 个, f 2 3 × 3 ∗ C / r 个, f 0 1 × 1 ∗ 1 个, 3 、其中, M c ( F ) + M s ( F ) 在相加之前都被调整为 R C × H × W 大小。 4 、其中, ⊗ 表示逐元素乘法。 M_c(F) = BN(MLP(AvgPool(F))) = BN(W_1(W_0AvgPool(F) + b_0) + b_1), \\ M_s(F) = BN(f^{1×1}_3(f^{3×3}_2(f^{3×3}_1(f^{1×1}_0(F))))), \\ M(F) = σ(M_c(F) +M_s(F)), \\ F' = F+F⊗M(F), \\ 1、其中,F \in R^{C*H*W},AvgPool(F) \in R^{C*1*1},W_0 ∈ R^{C/r×C}, b_0 ∈ R^{C/r}, W_1 ∈ R^{C×C/r}, b_1 ∈ R^C. \\ 2、其中,f^{1×1}_0 \in R^{C*1*1},f^{3×3}_1 \in R^{C/r*3*3},f^{3×3}_2 \in R^{C/r*3*3}, f^{1×1}_3 \in R^{C/r*1*1}, \\ f^{1×1}_0 * C/r 个, f^{3×3}_1 * C/r 个,f^{3×3}_2 * C/r 个,f^{1×1}_0 * 1 个, \\ 3、其中, M_c(F) +M_s(F)在相加之前都被调整为R^{C×H×W}大小。 \\ 4、其中,⊗表示逐元素乘法。 Mc(F)=BN(MLP(AvgPool(F)))=BN(W1(W0AvgPool(F)+b0)+b1),Ms(F)=BN(f31×1(f23×3(f13×3(f01×1(F))))),M(F)=σ(Mc(F)+Ms(F)),F=F+FM(F),1、其中,FRCHW,AvgPool(F)RC11,W0RC/r×C,b0RC/r,W1RC×C/r,b1RC.2、其中,f01×1RC11,f13×3RC/r33,f23×3RC/r33,f31×1RC/r11,f01×1C/r个,f13×3C/r个,f23×3C/r个,f01×11个,3、其中,Mc(F)+Ms(F)在相加之前都被调整为RC×H×W大小。4、其中,表示逐元素乘法。
值得注意的点是:模块有两个超参数:膨胀值 d 和收缩比 r。膨胀值决定了接受域的大小,有助于空间分支的上下文信息聚集。收缩比控制着两个注意力分支的能力和开销。 通过实验验证,设 d = 4, r = 16。

2、模块使用地方:将模块放在模型的每个瓶颈处(特征映射产生降采样的地方),构建一个具有多个参数的分层注意,可以与任何前馈模型以端到端方式进行训练。
在这里插入图片描述
3、代码

import numpy as np
import torch
from torch import nn
from torch.nn import init

def autopad(k, p=None, d=1):  # kernel, padding, dilation
    """Pad to 'same' shape outputs."""
    if d > 1:
        k = d * (k - 1) + 1 if isinstance(k, int) else [d * (x - 1) + 1 for x in k]  # actual kernel-size
    if p is None:
        p = k // 2 if isinstance(k, int) else [x // 2 for x in k]  # auto-pad
    return p

class Flatten(nn.Module):
    def forward(self, x):
        return x.view(x.shape[0], -1)

class ChannelAttention(nn.Module):
    def __init__(self, channel, reduction=16, num_layers=3):
        super().__init__()
        # 添加通道注意力机制的通道数,方便后面的全连接层
        gate_channels = [channel]
        gate_channels += [channel // reduction] * num_layers
        gate_channels += [channel]
        
        # 全局平均池化,用于降维为 B * C * 1 * 1
        self.avgpool = nn.AdaptiveAvgPool2d(1)
        
        self.ca = nn.Sequential()
        
        # 展平,用于全连接层 B * C * 1 * 1 -> B * C
        self.ca.add_module('flatten', Flatten())
        
        # 连续的全链接层,来获取通道注意力机制 
        for i in range(len(gate_channels) - 2):
            self.ca.add_module('fc%d' % i, nn.Linear(gate_channels[i], gate_channels[i + 1]))
            self.ca.add_module('bn%d' % i, nn.BatchNorm1d(gate_channels[i + 1]))
            self.ca.add_module('relu%d' % i, nn.ReLU())
            
        # 最后的全连接层,用于恢复通道数为 B * C.
        self.ca.add_module('last_fc', nn.Linear(gate_channels[-2], gate_channels[-1]))

    def forward(self, x):
        res = self.avgpool(x)
        res = self.ca(res)
        # unsqueeze(-1).unsqueeze(-1) 的作用是为了将 B * C 转换为 B * C * 1 * 1,方便后面的广播。
        # expand_as(x) 的作用是为了将 B * C * 1 * 1 广播为 B * C * H * W
        res = res.unsqueeze(-1).unsqueeze(-1).expand_as(x)
        return res


class SpatialAttention(nn.Module):
    def __init__(self, channel, reduction=16, num_layers=3, dia_val=2):
        super().__init__()
        self.sa = nn.Sequential()
        # 最开始的 1x1 卷积,用于降维为原来的 1/r
        self.sa.add_module('conv_reduce1',
                           nn.Conv2d(kernel_size=1, in_channels=channel, out_channels=channel // reduction))
        self.sa.add_module('bn_reduce1', nn.BatchNorm2d(channel // reduction))
        self.sa.add_module('relu_reduce1', nn.ReLU())
        
        # 连续的 3x3 的空洞卷积, 用于捕捉空间信息
        for i in range(num_layers):
            self.sa.add_module('conv_%d' % i, nn.Conv2d(kernel_size=3, in_channels=channel // reduction,
                                                        out_channels=channel // reduction, padding=autopad(3, None, dia_val), dilation=dia_val))
            self.sa.add_module('bn_%d' % i, nn.BatchNorm2d(channel // reduction))
            self.sa.add_module('relu_%d' % i, nn.ReLU())
            
        # 最后的 1x1 卷积,用于恢复通道数
        self.sa.add_module('last_conv', nn.Conv2d(channel // reduction, 1, kernel_size=1))

    def forward(self, x):
        res = self.sa(x)
        res = res.expand_as(x)
        return res


class BAMBlock(nn.Module):
    def __init__(self, channel=512, reduction=16, dia_val=2):
        super().__init__()
        self.ca = ChannelAttention(channel=channel, reduction=reduction)
        self.sa = SpatialAttention(channel=channel, reduction=reduction, dia_val=dia_val)
        self.sigmoid = nn.Sigmoid()

    def init_weights(self):
        # 根据不同的网络初始化权重
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                init.kaiming_normal_(m.weight, mode='fan_out')
                if m.bias is not None:
                    init.constant_(m.bias, 0)
            elif isinstance(m, nn.BatchNorm2d):
                init.constant_(m.weight, 1)
                init.constant_(m.bias, 0)
            elif isinstance(m, nn.Linear):
                init.normal_(m.weight, std=0.001)
                if m.bias is not None:
                    init.constant_(m.bias, 0)

    def forward(self, x):
        # b, c, _, _ = x.size()
        sa_out = self.sa(x)
        ca_out = self.ca(x)
        weight = self.sigmoid(sa_out + ca_out)
        out = (1 + weight) * x
        return out

if __name__ == '__main__':
    input = torch.randn(50, 512, 7, 7)
    bam = BAMBlock(channel=512, reduction=16, dia_val=2)
    output = bam(input)
    print(output.shape)

CBAM

注意:和BAM的不一样,BAM模块是放在网络的每个瓶颈处,CBAM放在每个卷积块上。
全称:Convolutional Block Attention Module 卷积块注意力模块
1、主要思想:沿着一条路径路径推断注意力映射,先执行通道注意力模块,和原始特征图相乘之后,再执行空间注意力模块,再和原始特征层相乘。通道注意力使得网络关注那个通道上的数据更加重要,空间注意力机制使得网络关注那个位置上的数据更加重要。
简单来说,
在这里插入图片描述

对应公式流程如下:
F ′ = M c ( F ) ⊗ F , F ′ ′ = M s ( F ′ ) ⊗ F ′ , 其中, F 、 F ′ 、 F ′ ′ ∈ R C × H × W , M c ( F ) ∈ R 1 × H × W , M s ( F ′ ) ∈ R C × 1 × 1 , F' =M_c(F) ⊗F, \\ F'' =M_s(F') ⊗F', \\ 其中, F、F'、F'' \in R^{C×H×W}, M_c(F) \in R^{1×H×W}, M_s(F') \in R^{C×1×1}, F=Mc(F)F,F′′=Ms(F)F,其中,FFF′′RC×H×W,Mc(F)R1×H×W,Ms(F)RC×1×1,
其中⊗表示基于元素的乘法。在乘法过程中,注意值被广播(复制):通道注意值沿着空间维度广播,反之亦然。F”是最终的精炼输出。

细化两个注意力模块:
1、通道注意力模块:在 H * W 的维度上,利用平均池化和最大池化操作聚合特征映射的空间信息,生成两种不同的空间上下文描述符,经过共享MLP 之后,再相加经过 sigmoid,得到最终的通道注意力机制。(SE 注意力中只要的全局平均池化,这里全都要)
也就是说:由于feature map的每个通道都被认为是一个特征检测器,所以通道的注意力集中在给定一个输入图像的“什么”是有意义的。

2、空间注意力机制:在 C 的维度上,分别得到全局平均池化和平均池化的结果,进行叠加,为B * 2 * H * W,再经过卷积提取、压缩一些信息,通过 sigmoid 之后得到 空间注意力机制。(和 BAM 不一样的地方,没有使用 1 * 1 的卷积来降维,而是采用了 池化的方式)

在这里插入图片描述

对应公式流程:
通道注意力   M c ( F ) = σ ( M L P ( A v g P o o l ( F ) ) + M L P ( M a x P o o l ( F ) ) ) = σ ( W 1 ( W 0 ( F a v g c ) ) + W 1 ( W 0 ( F m a x c ) ) ) , 其中, σ 为 s i g m o i d 函数, W 0 ∈ R C / r × C , W 1 ∈ R C × C / r , 空间注意力 M s ( F ) = σ ( f 7 × 7 ( [ A v g P o o l ( F ) ; M a x P o o l ( F ) ] ) ) = σ ( f 7 × 7 ( [ F a v g s ; F m a x s ] ) ) , 其中,其中 σ 为 s i g m o i d 函数, f 7 × 7 为为 7 × 7 的卷积。 通道注意力\\\ M_c(F) = σ(MLP(AvgPool(F)) +MLP(MaxPool(F))) \\ = σ(W_1(W_0(F^c_{avg})) +W_1(W_0(F^c_{max}))), \\ 其中,σ为sigmoid函数,W_0∈R^{C/r×C}, W_1∈R^{C×C/r},\\ 空间注意力\\ M_s(F) = σ(f^{7×7}([AvgPool(F);MaxPool(F)])) \\ = σ(f^{7×7}([F^s_{avg}; F^s_{max}])), \\ 其中,其中σ为sigmoid函数,f^{7×7}为为7×7的卷积。 通道注意力 Mc(F)=σ(MLP(AvgPool(F))+MLP(MaxPool(F)))=σ(W1(W0(Favgc))+W1(W0(Fmaxc))),其中,σsigmoid函数,W0RC/r×C,W1RC×C/r空间注意力Ms(F)=σ(f7×7([AvgPool(F);MaxPool(F)]))=σ(f7×7([Favgs;Fmaxs])),其中,其中σsigmoid函数,f7×7为为7×7的卷积。

值得注意的是:
1、空间注意力的时候,使用的为 7 * 7 的卷积,r 为 16 。
2、在实际实现通道注意力的时候,因为 MLP 是共享的,我们输入有两个,所以这里可以使用 1 * 1 的卷积来实现 MLP 的效果,但是可以很方便的实现参数共享。其次为了减少参数开销,隐藏的激活大小设置为 R C / r × 1 × 1 R^{C/r×1×1} RC/r×1×1,其中r是减少率。
3、论文实验证实,同时使用平均池和最大池特性这两个特性比单独使用它们大大提高了网络的表示能力。
4、注意模块的安排。给定一个输入图像,两个注意模块,通道和空间,计算互补注意,分别关注“什么”和“在哪里”。考虑到这一点,两个模块可以以并行或顺序的方式放置。我们发现序列排列比并行排列有更好的结果。对于顺序过程的排列,我们的实验结果表明,信道优先顺序略好于空间优先顺序。
5、在VOC数据集上,作者将SE和CBAM放在每个分类器的前面,在预测之前细化由上采样的全局特征和相应的局部特征组成的最终特征,强制模型自适应地只选择有意义的特征。

2、模块使用地方:CBAM放在每个卷积块上,可以使用在网络的任何地方。
下面的方法是在残差结构内部使用:
在这里插入图片描述

3、代码

import numpy as np
import torch
from torch import nn
from torch.nn import init


class ChannelAttention(nn.Module):
    def __init__(self, channel, reduction=16):
        super().__init__()
        # 两种全局池化方式 + 共享的两层全连接层(1 * 1 卷积相当于全连接层)
        self.maxpool = nn.AdaptiveMaxPool2d(1)
        self.avgpool = nn.AdaptiveAvgPool2d(1)
        self.se = nn.Sequential(
            nn.Conv2d(channel, channel // reduction, 1, bias=False),
            nn.ReLU(),
            nn.Conv2d(channel // reduction, channel, 1, bias=False)
        )
        self.sigmoid = nn.Sigmoid()

    def forward(self, x):
        max_result = self.maxpool(x)
        avg_result = self.avgpool(x)
        max_out = self.se(max_result)
        avg_out = self.se(avg_result)
        output = self.sigmoid(max_out + avg_out)
        return output


class SpatialAttention(nn.Module):
    def __init__(self, kernel_size=7):
        super().__init__()
        self.conv = nn.Conv2d(2, 1, kernel_size=kernel_size, padding=kernel_size // 2)
        self.sigmoid = nn.Sigmoid()

    def forward(self, x):
        # 1 代表通道维度,输入为 B * C * H * W
        max_result, _ = torch.max(x, dim=1, keepdim=True)
        avg_result = torch.mean(x, dim=1, keepdim=True)
        result = torch.cat([max_result, avg_result], 1)
        output = self.conv(result)
        output = self.sigmoid(output)
        return output


class CBAMBlock(nn.Module):

    def __init__(self, channel=512, reduction=16, kernel_size=7):
        super().__init__()
        self.ca = ChannelAttention(channel=channel, reduction=reduction)
        self.sa = SpatialAttention(kernel_size=kernel_size)

    def init_weights(self):
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                init.kaiming_normal_(m.weight, mode='fan_out')
                if m.bias is not None:
                    init.constant_(m.bias, 0)
            elif isinstance(m, nn.BatchNorm2d):
                init.constant_(m.weight, 1)
                init.constant_(m.bias, 0)
            elif isinstance(m, nn.Linear):
                init.normal_(m.weight, std=0.001)
                if m.bias is not None:
                    init.constant_(m.bias, 0)

    def forward(self, x):
        b, c, _, _ = x.size()
        out = x * self.ca(x)
        out = out * self.sa(out)
        return out


if __name__ == '__main__':
    input = torch.randn(50, 512, 7, 7)
    kernel_size = input.shape[2]
    cbam = CBAMBlock(channel=512, reduction=16, kernel_size=kernel_size)
    output = cbam(input)
    print(output.shape)

CoTAttention

全称:Contextual Transformer Attention 上下文自注意力

背景提要:传统的用于视觉任务的transformer结构大多是利用 1 ∗ 1 1 * 1 11 卷积得到 Q u e r y 、 K e y 、 V a l u e Query 、Key 、 Value QueryKeyValue,,然后通过简单的相乘,得到注意力矩阵,这种构造方法并并没有考虑到 K e y Key Key 矩阵相邻位置之间丰富的上下文.( 1 ∗ 1 1 * 1 11 矩阵没有空间上的上下文)

1、主要思想:首先通过3×3卷积上下文编码得到 k e y key key,得到静态的上下文表示。我们进一步将编码后的 K e y Key Key 与原始特征图上的 Q u e r y Query Query 连接起来,通过两个连续的 1 ∗ 1 1 * 1 11 卷积学习动态多头注意矩阵 W e i g h t s Weights Weights。对于 V a l u e Value Value,我们通过 1 ∗ 1 1 * 1 11 的卷积获得,然后将学习到的注意矩阵 W e i g h t s Weights Weights 与输入值 V a l u e s Values Values 相乘,实现输入的动态上下文表示。最后将静态和动态语境表示的相加融合作为输出。

简单图示如下:
在这里插入图片描述
细节图示对比如下:
在这里插入图片描述

上图(a)是作者介绍的传统的局部多头自注意力机制, 这里插播一下传统的局部多头自注意力机制的计算过程:

假设:输入: X ∈ R H ∗ W ∗ C X \in R^{H *W *C} XRHWC, 有 C h C_h Ch 个注意力头, 每个特征点和 k ∗ k k * k kk 范围的点做注意力。

  • 第一步:先得到 Query、Key、Value。 Q = X W q , K = X W k , V = X W v Q = XW_q, K = XW_k, V = XW_v Q=XWq,K=XWk,V=XWv
  • 第二步:得到 注意力。 R = K ⊗ Q R = K⊗Q R=KQ
  • 第三步:得到 注意力 + 位置编码。 R ^ = R + P ⊗ Q \hat{R}= R+P⊗Q R^=R+PQ 。其中, P ∈ R k × k × C k P ∈ R^{k×k×C_k} PRk×k×Ck 表示每个 k × k k × k k×k 网格内的 2D 相对位置嵌入,并且在所有 C h C_h Ch 头之间共享。
  • 第四步:得到归一化之后的注意力机制。 A = S o f t m a x ( R ^ ) A = Softmax( \hat{R}) A=Softmax(R^)
  • 第五步:乘上 Value 得到最终的输出。 Y = V ⊗ A Y = V⊗A Y=VA

注意,论文中 ⊗ ⊗ 被定义为:每个查询与空间中局部 k×k 网格内相应键之间的关系。我这里暂时理解为 每个特征点的描述子为 C k C_k Ck 维度,对应位置相乘为 C k ∗ C k C_k * C_k CkCk,然后加在一起。

上图(b)即为作者提出的Cot模块的实现过程:

  • 第一步:获取 Q 、 K 、 V Q、K、V QKV。-> K = X , Q = X , V = X W v K = X, Q = X, V = XW_v K=X,Q=X,V=XWv
  • 第二步:获取静态上下文 K 1 K^1 K1。不同于图(a)采用1x1的卷积生成key矩阵,CoT模块首先采用 k ∗ k k * k kk 的卷积提取上下文信息,这样得到的 K 1 K^1 K1自然的可以反映近邻间的上下文信息,我们将其作为输入X的 静态上下文 表示。
  • 第三步:获取 A A A 注意力矩阵。将 K 1 K^1 K1 Q Q Q 拼接起来,并且通过两个 1 ∗ 1 1* 1 11 的卷积计算注意力矩阵 A = [ K 1 , Q ] W θ W δ A = [K^1, Q]W_θW_δ A=[K1,Q]WθWδ。这样得到的注意力矩阵 A A A 是通过 query 与 提取了上下文信息的 k 1 k^1 k1 学习所得到的,并不是仅仅通过独立的query-key对得到的。也就是通过引入静态上下文表示增强了自注意力机制。
  • 第四步:获取 动态上下文表示 : K 2 = A ⊗ V K^2 = A ⊗ V K2=AV
  • 第五步:CoT 模块 最终输出:CoT模块最终的输出为静态上下文表示 K 1 K^1 K1 和动态上下文表示 K 2 K^2 K2 的融合,

值得注意的是:
1、 K 1 K^1 K1 K 2 K^2 K2 的融合,在代码里作者采用的是SE注意力模块进行特征的融合,也可以使用相加的方式。
2、 W θ W_θ Wθ带ReLU激活函数, W δ W_δ Wδ 不带激活函数.

2、模块使用地方:
作者设计的CoT模块是一个统一的 self-attention 块,可以直接用来代替 3 ∗ 3 3* 3 33卷积。也就是只要是 3 ∗ 3 3* 3 33卷积 出现的地方,你都可以替换。

3、代码

import torch
from torch import nn
from torch.nn import init
from torch.nn import functional as F


class CoTAttention(nn.Module):

    def __init__(self, dim=512, kernel_size=3):
        super().__init__()
        self.dim = dim
        self.kernel_size = kernel_size

        # 用来生成静态上下文 K^1
        self.key_embed = nn.Sequential(
            nn.Conv2d(dim, dim, kernel_size=kernel_size, padding=kernel_size // 2, groups=4, bias=False),
            nn.BatchNorm2d(dim),
            nn.ReLU()
        )
        
        # 用来生成 V
        self.value_embed = nn.Sequential(
            nn.Conv2d(dim, dim, 1, bias=False),
            nn.BatchNorm2d(dim)
        )

        # 用来对 K^1 + Q 进行注意力计算,也就是使用 1 * 1的卷积来计算注意力
        # 这里和传统的自注意力采用q和k相乘不一样,这里采用q和k相加,然后再计算注意力
        factor = 4
        self.attention_embed = nn.Sequential(
            nn.Conv2d(2 * dim, 2 * dim // factor, 1, bias=False),
            nn.BatchNorm2d(2 * dim // factor),
            nn.ReLU(),
            nn.Conv2d(2 * dim // factor, kernel_size * kernel_size * dim, 1)
        )

        def init_weights(self):
            for m in self.modules():
                if isinstance(m, nn.Conv2d):
                    init.kaiming_normal_(m.weight, mode='fan_out')
                    if m.bias is not None:
                        init.constant_(m.bias, 0)
                elif isinstance(m, nn.BatchNorm2d):
                    init.constant_(m.weight, 1)
                    init.constant_(m.bias, 0)
                elif isinstance(m, nn.Linear):
                    init.normal_(m.weight, std=0.001)
                    if m.bias is not None:
                        init.constant_(m.bias, 0)

    def forward(self, x):
        bs, c, h, w = x.shape
        k1 = self.key_embed(x)  # bs,c,h,w
        v = self.value_embed(x).view(bs, c, -1)  # bs,c,h*w
        y = torch.cat([k1, x], dim=1)  # bs,2c,h,w
        att = self.attention_embed(y)  # bs,c*k*k,h,w
        att = att.reshape(bs, c, self.kernel_size * self.kernel_size, h, w)
        att = att.mean(2, keepdim=False).view(bs, c, -1)  # bs,c,h*w
        k2 = F.softmax(att, dim=-1) * v
        k2 = k2.view(bs, c, h, w)

        return k1 + k2


if __name__ == '__main__':
    input = torch.randn(50, 512, 7, 7)
    cot = CoTAttention(dim=512, kernel_size=3)
    output = cot(input)
    print(output.shape)

MobileViTAttention

全称:MobileViT 这个轻量型网络中所提出来的概念。

1、主要思想:使用CNN和transformer混合的方式,使用CNN提取局部特征,使用transformer提取全局特征。理论上网络的感受野为H*W,即全感受野。

具体来说,我们引入了 MobileViT 块,它可以有效地在张量中编码局部和全局信息。与 ViT 及其变体(带或不带卷积)不同,MobileViT 提出了学习全局表示的不同视角。标准卷积涉及三个操作:展开、局部处理和折叠。 MobileViT 块用使用转换器的全局处理取代了卷积中的局部处理。这使得 MobileViT 块具有类似 CNN 和 ViT 的属性,这有助于它用更少的参数和简单的训练方法(例如,基本增强)学习更好的表示.

图示如下:

所提出模型的优点:1、 更好的性能。2、泛化能力强。3、鲁棒性强

值得注意的是:对transformer的输入保留了维度patch,每个patch包含所有像素的位置顺序。这使得encoder的输入包含了每个patch的顺序以及每个patch中所有像素的顺序。

2、模块使用地方:

3、代码

from torch import nn
import torch
from einops import rearrange


class PreNorm(nn.Module):
    def __init__(self, dim, fn):
        super().__init__()
        self.ln = nn.LayerNorm(dim)
        self.fn = fn

    def forward(self, x, **kwargs):
        return self.fn(self.ln(x), **kwargs)


class FeedForward(nn.Module):
    def __init__(self, dim, mlp_dim, dropout):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(dim, mlp_dim),
            nn.SiLU(),
            nn.Dropout(dropout),
            nn.Linear(mlp_dim, dim),
            nn.Dropout(dropout)
        )

    def forward(self, x):
        return self.net(x)


class Attention(nn.Module):
    def __init__(self, dim, heads, head_dim, dropout):
        super().__init__()
        inner_dim = heads * head_dim
        project_out = not (heads == 1 and head_dim == dim)

        self.heads = heads
        self.scale = head_dim ** -0.5

        self.attend = nn.Softmax(dim=-1)
        self.to_qkv = nn.Linear(dim, inner_dim * 3, bias=False)

        self.to_out = nn.Sequential(
            nn.Linear(inner_dim, dim),
            nn.Dropout(dropout)
        ) if project_out else nn.Identity()

    def forward(self, x):
        qkv = self.to_qkv(x).chunk(3, dim=-1)
        q, k, v = map(lambda t: rearrange(t, 'b p n (h d) -> b p h n d', h=self.heads), qkv)
        dots = torch.matmul(q, k.transpose(-1, -2)) * self.scale
        attn = self.attend(dots)
        out = torch.matmul(attn, v)
        out = rearrange(out, 'b p h n d -> b p n (h d)')
        return self.to_out(out)


class Transformer(nn.Module):
    def __init__(self, dim, depth, heads, head_dim, mlp_dim, dropout=0.):
        super().__init__()
        self.layers = nn.ModuleList([])
        for _ in range(depth):
            self.layers.append(nn.ModuleList([
                PreNorm(dim, Attention(dim, heads, head_dim, dropout)),
                PreNorm(dim, FeedForward(dim, mlp_dim, dropout))
            ]))

    def forward(self, x):
        out = x
        for att, ffn in self.layers:
            out = out + att(out)
            out = out + ffn(out)
        return out


class MobileViTAttention(nn.Module):
    def __init__(self, in_channel=3, dim=512, kernel_size=3, patch_size=7):
        super().__init__()
        self.ph, self.pw = patch_size, patch_size
        self.conv1 = nn.Conv2d(in_channel, in_channel, kernel_size=kernel_size, padding=kernel_size // 2)
        self.conv2 = nn.Conv2d(in_channel, dim, kernel_size=1)

        self.trans = Transformer(dim=dim, depth=3, heads=8, head_dim=64, mlp_dim=1024)

        self.conv3 = nn.Conv2d(dim, in_channel, kernel_size=1)
        self.conv4 = nn.Conv2d(2 * in_channel, in_channel, kernel_size=kernel_size, padding=kernel_size // 2)

    def forward(self, x):
        y = x.clone()  # bs,c,h,w

        ## Local Representation
        y = self.conv2(self.conv1(x))  # bs,dim,h,w

        ## Global Representation
        _, _, h, w = y.shape
        y = rearrange(y, 'bs dim (nh ph) (nw pw) -> bs (ph pw) (nh nw) dim', ph=self.ph, pw=self.pw)  # bs,h,w,dim
        y = self.trans(y)
        y = rearrange(y, 'bs (ph pw) (nh nw) dim -> bs dim (nh ph) (nw pw)', ph=self.ph, pw=self.pw, nh=h // self.ph,
                      nw=w // self.pw)  # bs,dim,h,w

        ## Fusion
        y = self.conv3(y)  # bs,dim,h,w
        y = torch.cat([x, y], 1)  # bs,2*dim,h,w
        y = self.conv4(y)  # bs,c,h,w

        return y


if __name__ == '__main__':
    m = MobileViTAttention(in_channel=512)
    input = torch.randn(1, 512, 49, 49)
    output = m(input)
    print(output.shape)

SK

ShuffleAttention

S2Attention

ParNetAttention

CoordAttention

MHSA

A2Attention

GCAttention

EffectiveSE

GEAttention

CrissCrossAttention

Polarized Self-Attention

Sequential Self-Attention

GAM

Biformer

EMA

CloAttention

LSKBlock

ECA

全称:Efficient Channel Attention

背景提要:更复杂的注意力模块不可避免地增加了模型的复杂性。通过剖析 SENet 中的通道注意力模块,我们凭经验证明避免降维对于学习通道注意力非常重要,适当的跨通道交互可以保持性能,同时显着降低模型复杂性。因此,我们提出了一种无需降维的局部跨通道交互策略,可以通过一维卷积有效地实现。此外,我们开发了一种自适应选择一维卷积核大小的方法,确定局部跨通道交互的覆盖范围。

1、主要思想:使用 1 ∗ 1 ∗ k 1 * 1 * k 11k 的卷积代替 SE 注意力机制中的全连接和降维操作,将全连接变成卷积操作,减少了计算量。并提出了 自适应 k 值的选取策略,通过考虑每个通道及其 k 个邻居来捕获局部跨通道交互。

图示如下:
在这里插入图片描述

思想步骤解释:
第一步:作者从 SE 出发,说明避免降维。

SE模块的通道注意力通过下式计算:
ω = σ ( f { w 1 , w 2 } ( g ( X ) ) ) , \omega = \sigma(f_{\{w_1, w_2\}}(g(X))), ω=σ(f{w1,w2}(g(X))),
其中, g ( x ) = 1 W H ∑ i = 1 , j = 1 W , H X i , j g(x) = \frac{1}{WH}\sum_{i =1, j = 1}^{W, H}X_{i,j} g(x)=WH1i=1,j=1W,HXi,j 为全局平均池化。如果 y = g ( x ) y = g(x) y=g(x), 则 f { w 1 , w 2 } ( y ) f_{\{w_1, w_2\}}(y) f{w1,w2}(y) 可以表示为 f { w 1 , w 2 } ( y ) = W 2 R e L U ( W 1 y ) f_{\{w_1, w_2\}}(y) = W_2ReLU(W_1y) f{w1,w2}(y)=W2ReLU(W1y),也就是两层全连接。 W 1 W_1 W1 的尺度为 R C ∗ C / r R^{C*C/r} RCC/r W 2 W_2 W2 的尺度为 R C / r ∗ C R^{C/r*C} RC/rC

作者阐述重新说明 SE 的原理,主要想给出下面的观点: C / r C/r C/r 使得维数降低。 可以降低模型复杂度,但它破坏了通道与其权重之间的直接对应关系。例如,单个 FC 层使用所有通道的线性组合来预测每个通道的权重。但是 SE 中两层全连接首先将通道特征投影到低维空间,然后将其映射回来,使得通道与其权重之间的对应关系是间接的(没有一一对应)。

为了说明 SE 中通道与其权重之间的对应关系是间接的。为了验证效果,论文中将原始 SE 块与其三个变体(即 SE-Var1、SE-Var2 和 SE-Var3)进行比较,所有变体均不执行降维。比较结果如下:

可以从实验对比上看得出,无参数的SE-Var1仍然优于原始网络,表明通道注意力有能力提高深度CNN的性能。同时,SE-Var2独立学习每个通道的权重,在涉及的参数较少的情况下略优于SE块。这可能表明通道及其权重需要直接对应,同时避免降维比考虑非线性通道依赖性更重要。此外,采用单个 FC 层的 SEVar3 的性能优于在 SE 块中进行降维的两个 FC 层。所有上述结果都清楚地表明,避免降维有助于学习有效的通道注意力。因此,我们开发了没有通道降维的 ECA 模块。

==>>> 所以通道降维不行

第二步:局部跨通道交互的方法

给定没有降维的聚合特征 y ∈ R C y ∈ R^C yRC,通道注意力可以通过以下方式学习:
ω = σ ( W y ) \omega = \sigma(Wy) ω=σ(Wy)
其中, W W W C ∗ C C * C CC 的参数矩阵,在 SE-Var2 和 SE-Var3 中, W W W 矩阵的形式如下:(SE-Var2 为 1 对 1 全连接,SE-Var3 为 C 对 1 的全连接)
W = { W var  2 = [ w 1 , 1 … 0 ⋮ ⋱ ⋮ 0 … w C , C ] W var  3 = [ w 1 , 1 … w 1 , C ⋮ ⋱ ⋮ w 1 , C … w C , C ] \mathbf{W}=\left\{\begin{array}{c} \mathbf{W}_{\text {var } 2}=\left[\begin{array}{ccc} w^{1,1} & \ldots & 0 \\ \vdots & \ddots & \vdots \\ 0 & \ldots & w^{C, C} \end{array}\right] \\ \mathbf{W}_{\text {var } 3}=\left[\begin{array}{ccc} w^{1,1} & \ldots & w^{1, C} \\ \vdots & \ddots & \vdots \\ w^{1, C} & \ldots & w^{C, C} \end{array}\right] \end{array}\right. W= Wvar 2= w1,100wC,C Wvar 3= w1,1w1,Cw1,CwC,C
其中 SE-Var2 的 W v a r 2 W_{var2} Wvar2 是对角矩阵,涉及C个参数; SE-Var3 的 W v a r 3 W_{var3} Wvar3 是一个全矩阵,涉及 C×C 参数。如方程式所示。主要区别在于SE-Var3考虑了跨渠道交互,而SEVar2则没有,因此SE-Var3取得了更好的性能。这一结果表明跨渠道交互有利于学习渠道注意力。然而,SEVar3 需要大量参数,导致模型复杂度较高,特别是对于大通道数。

所以作者想折中两种方式:SE-Var2 和 SE-Var3 之间可能的折衷方案是将 W v a r 2 W_{var2} Wvar2 扩展到块对角矩阵,即
W G = [ W G 1 ⋯ 0 ⋮ ⋱ ⋮ 0 ⋯ W G G ] \mathbf{W}_{G}=\left[\begin{array}{ccc} \mathbf{W}_{G}^{1} & \cdots & \mathbf{0} \\ \vdots & \ddots & \vdots \\ \mathbf{0} & \cdots & \mathbf{W}_{G}^{G} \end{array}\right] WG= WG100WGG
是将通道分为 G G G组,每个组包括 C / G C/G C/G通道,并独立学习每个组中的通道注意力,以局部方式捕获跨通道交互。相应地,涉及到 C 2 / G C^2/G C2/G参数。从卷积的角度来看,SE-Var2、SEVar3和上诉方式可以分别视为深度可分离卷积、FC层和组卷积。这里,具有组卷积的 SE 块(SE-GC)由 σ ( G C G ( y ) ) = σ ( W G y ) σ(GC_G(y)) = σ(W_Gy) σ(GCG(y))=σ(WGy) 表示。然而,过多的组卷积会增加内存访问成本,从而降低计算效率。

此外,如实验对比表所示,具有不同组的 SE-GC 没有比 SE-Var2 带来任何增益,这表明它不是捕获本地跨通道交互的有效方案。原因可能是SE-GC完全抛弃了不同群体之间的依赖关系。

==>>> 所以组卷积不行。

结合上面的说明:作者采用带状矩阵 W k W_k Wk 来学习通道注意力, W k W_k Wk 形式如下:
[ w 1 , 1 ⋯ w 1 , k 0 0 ⋯ ⋯ 0 0 w 2 , 2 ⋯ w 2 , k + 1 0 ⋯ ⋯ 0 ⋮ ⋮ ⋮ ⋮ ⋱ ⋮ ⋮ ⋮ 0 ⋯ 0 0 ⋯ w C , C − k + 1 ⋯ w C , C ] \begin{bmatrix}w^{1,1}&\cdots&w^{1,k}&0&0&\cdots&\cdots&0\\0&w^{2,2}&\cdots&w^{2,k+1}&0&\cdots&\cdots&0\\\vdots&\vdots&\vdots&\vdots&\ddots&\vdots&\vdots&\vdots\\0&\cdots&0&0&\cdots&w^{C,C-k+1}&\cdots&w^{C,C}\end{bmatrix} w1,100w2,2w1,k00w2,k+1000wC,Ck+100wC,C
第一:避免了组卷积之间的完全独立。
第二:参数比较小。
第三:移动的考虑多个通道之间的关系。

具体公式如: ω i = σ ( ∑ j = 1 k w i j y i j ) , y i j ∈ Ω i k \omega_{i}=\sigma\bigg(\sum_{j=1}^{k}w_{i}^{j}y_{i}^{j}\bigg),y_{i}^{j}\in\Omega_{i}^{k} ωi=σ(j=1kwijyij),yijΩik
其中, Ω i k Ω^k_i Ωik 表示 y i y_i yi k k k 个相邻通道的集合。

更有效的方法是让所有通道共享相同的学习参数,即:
ω i = σ ( ∑ j = 1 k w j y i j ) , y i j ∈ Ω i k \omega_{i}=\sigma\bigg(\sum_{j=1}^{k}w^{j}y_{i}^{j}\bigg),y_{i}^{j}\in\Omega_{i}^{k} ωi=σ(j=1kwjyij),yijΩik
请注意,这种策略可以通过内核大小为 k 的快速一维卷积轻松实现,即:
ω = σ ( C 1 D k ( y ) ) , \omega=\sigma(\mathrm{C1D}_k(\mathbf{y})), ω=σ(C1Dk(y)),
其中 C 1 D C1D C1D 表示一维卷积,仅涉及k个参数。实验证明,我们的 k = 3 的 ECA 模块实现了与 SE-var3 类似的结果,同时模型复杂度低得多,通过适当捕获本地跨渠道交互来保证效率和有效性。

第三步,确定 k 如何选取:局部跨通道交互覆盖
上述ECA模块旨在适当捕获局部跨道信息交互,因此需要确定通道交互信息的大致范围(即1D卷积的卷积核大小k)。虽然可以针对各种CNN架构中具有不同通道数的卷积块进行手动优化设置信息交互的最佳范围,但是通过手动进行交叉验证调整将花费大量计算资源。而且分组卷积已成功地用于改善CNN架构,在固定group数量的情况下,高维(低维)通道与长距离(短距离)卷积成正比。同理,跨通道信息交互作用的覆盖范围(即一维卷积的内核大小k)与通道维数C应该也是成正比的。换句话说,在k和C之间可能存在映射 ϕ \phi ϕ
C = ϕ ( k ) . C=\phi(k). C=ϕ(k).
最简单的映射是线性函数,即 ϕ ( k ) = γ ∗ k − b \phi(k) = γ ∗ k − b ϕ(k)=γkb。然而,以线性函数为特征的关系过于有限。另一方面,众所周知,通道维度 C(即滤波器的数量)通常设置为 2 的幂。因此,我们通过扩展线性函数 ϕ ( k ) = γ ∗ k − b \phi(k) = γ ∗ k − b ϕ(k)=γkb 来引入一种可能的解决方案为非线性的,即
C = ϕ ( k ) = 2 ( γ ∗ k − b ) , C=\phi(k)=2^{(\gamma*k-b)}, C=ϕ(k)=2(γkb),
所以,给定通道维度 C,内核大小 k 可以自适应地确定为
k = ψ ( C ) = ∣ l o g 2 ( C ) γ + b γ ∣ o d d , k=\psi(C)=\left|\frac{log_2(C)}{\gamma}+\frac{b}{\gamma}\right|_{odd}, k=ψ(C)= γlog2(C)+γb odd,
其中 ∣ t ∣ o d d |t|_{odd} todd 表示 t 最接近的奇数。在本文中,我们在所有实验中将 γ γ γ b b b 分别设置为 2 和 1。显然,通过映射 ψ ψ ψ,高维通道具有较长范围的相互作用,而低维通道通过使用非线性映射进行较短范围的相互作用。

2、模块使用地方:ECA模块是一个即插即用的模块,可以在卷积模块之后直接插入ECA模块,也可以在残差结构里面添加了ECA模块。

3、代码

import numpy as np
import torch
from torch import nn
from torch.nn import init
from collections import OrderedDict


class ECAAttention(nn.Module):

    def __init__(self, kernel_size=3):
        super().__init__()
        self.gap = nn.AdaptiveAvgPool2d(1)
        self.conv = nn.Conv1d(1, 1, kernel_size=kernel_size, padding=(kernel_size - 1) // 2)
        self.sigmoid = nn.Sigmoid()
        
    def init_weights(self):
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                init.kaiming_normal_(m.weight, mode='fan_out')
                if m.bias is not None:
                    init.constant_(m.bias, 0)
            elif isinstance(m, nn.BatchNorm2d):
                init.constant_(m.weight, 1)
                init.constant_(m.bias, 0)
            elif isinstance(m, nn.Linear):
                init.normal_(m.weight, std=0.001)
                if m.bias is not None:
                    init.constant_(m.bias, 0)

    def forward(self, x):
        y = self.gap(x)  # bs,c,1,1
        y = y.squeeze(-1).permute(0, 2, 1)  # bs,1,c
        y = self.conv(y)  # bs,1,c
        y = self.sigmoid(y)  # bs,1,c
        y = y.permute(0, 2, 1).unsqueeze(-1)  # bs,c,1,1
        return x * y.expand_as(x)


if __name__ == '__main__':
    input = torch.randn(50, 512, 7, 7)
    eca = ECAAttention(kernel_size=3)
    output = eca(input)
    print(output.shape)

SimAM

全称: Simple, Parameter-Free Attention Module

背景提要:现有的注意力模块通常被继承到每个块中,以改进来自先前层的输出。这种细化步骤通常沿着通道维度(a)或空间维度(b)操作,这些方法生成一维或二维权重,并平等对待每个通道或空间位置中的神经元,
  通道注意力:1D注意力,它对不同通道区别对待,对所有位置同等对待;
  空域注意力:2D注意力,它对不同位置区别对待,对所有通道同等对待。
这可能会限制他们学习更多辨别线索的能力。因此三维权重(本文)优于传统的一维和二维权重注意力

1、主要思想:基于一些著名的神经科学理论,提出优化能量函数来找出每个神经元的重要性(三维权重)。

思想步骤解释:
  为更好的实现注意力,我们需要评估每个神经元的重要性。在神经科学中,信息丰富的神经元通常表现出与周围神经元不同的放电模式。而且,激活神经元通常会抑制周围神经元,即空域抑制。换句话说,具有空域抑制效应的神经元应当赋予更高的重要性,找到这些神经元最简单的实现:测量一个目标神经元和其他神经元之间的线性可分性

如何理解这个线性可分性:如果当前神经元和其他神经元差异性很明显的话,通过一层 y = w x + b y= wx + b y=wx+b的分类器,我们能够很容易的将其区分开,结果的均值和方差就很小。要是差异不大,很难分开,那结果的均值和方差就比较大。

基于这些神经科学发现,作者为每个神经元定义了以下能量函数:
e t ( w t , b t , y , x i ) = ( y t − t ^ ) 2 + 1 M − 1 ∑ i = 1 M − 1 ( y o − x ^ i ) 2 \begin{align} e_{t}\left(w_{t}, b_{t}, \mathbf{y}, x_{i}\right) = \left(y_{t}-\hat{t}\right)^{2}+\frac{1}{M-1} \sum_{i = 1}^{M-1}\left(y_{o}-\hat{x}_{i}\right)^{2} \end{align} et(wt,bt,y,xi)=(ytt^)2+M11i=1M1(yox^i)2
其中: t ^ = w t t + b t \hat{t} = w_tt + b_t t^=wtt+bt x ^ i = w t x i + b t \hat{x}_i = w_tx_i + b_t x^i=wtxi+bt t t t x i x_i xi 的线性变换,其中 t t t x i x_i xi 是输入特征 X ∈ R C × H × W X ∈ R^{C×H×W} XRC×H×W 的单个通道中的目标神经元和其他神经元。 i i i 是空间维度上的索引, M = H × W M = H×W M=H×W 是该通道上的神经元数量。 w t w_t wt b t b_t bt 是变换的权重和偏置,我们想要求解的就是 w t w_t wt b t b_t bt
方程(1)中的所有值都是标量。当 t ^ \hat{t} t^ 等于 y t y_t yt 且所有其他 x ^ i \hat{x}_i x^i 均为 y o y_o yo 时,方程(1)达到最小值,其中 y t y_t yt y o y_o yo 是两个不同的值。

通过最小化方程,方程(1)相当于找到目标神经元 t 与同一通道中所有其他神经元之间的线性可分离性。为了简单起见,我们对 y t y_t yt y o y_o yo 采用二进制标签(即 1 和 -1),并在方程(1)中添加权重正则化器。最终的能量函数由下式给出:
e t ( w t , b t , y , x i ) = 1 M − 1 ∑ i = 1 M − 1 ( − 1 − ( w t x i + b t ) ) 2 + ( 1 − ( w t t + b t ) ) 2 + λ w t 2 \begin{align} e_{t}\left(w_{t}, b_{t}, \mathbf{y}, x_{i}\right) & =\frac{1}{M-1} \sum_{i=1}^{M-1}\left(-1-\left(w_{t} x_{i}+b_{t}\right)\right)^{2} & +\left(1-\left(w_{t} t+b_{t}\right)\right)^{2}+\lambda w_{t}^{2} \end{align} et(wt,bt,y,xi)=M11i=1M1(1(wtxi+bt))2+(1(wtt+bt))2+λwt2

理论上,每个通道都有 M 个能量函数 (每个神经元一个)。通过 SGD 等迭代求解器求解所有这些方程的计算量非常大。幸运的是,方程(2)对于 w t w_t wt b t b_t bt 有一个快速闭式解,可以通过以下方式轻松获得:
w t = − 2 ( t − µ t ) ( t − µ t ) 2 + 2 σ t 2 + 2 λ , b t = − 1 2 ( t + µ t ) w t . \begin{align} w_t = −\frac{2(t − µ_t)}{(t − µ_t)^2 + 2σ^2_t + 2λ}, \\ b_t = −\frac{1}{2}(t + µ_t)w_t. \end{align} wt=(tµt)2+2σt2+2λ2(tµt),bt=21(t+µt)wt.
其中: µ t = 1 M − 1 ∑ i = 1 M − 1 x i µ_t = \frac{1}{M−1} \sum^{M−1}_{i=1}x_i µt=M11i=1M1xi σ t 2 = 1 M − 1 ∑ i M − 1 ( x i − μ t ) 2 σ^2_t = \frac{1}{M−1}\sum^{M−1}_i (x_i−μ_t)^2 σt2=M11iM1(xiμt)2 是对该通道中除 t 之外的所有神经元计算的平均值和方差。

由于方程(3)和方程(4)所示的现有解是在单个通道上获得的,因此可以合理地假设单个通道中的所有像素遵循相同的分布(均值和方差一样)。考虑到这一假设,可以计算所有神经元的平均值和方差,并为该通道上的所有神经元重复使用。它可以显着降低计算成本,避免迭代计算每个位置的 µ µ µ σ σ σ
因此,最小能量可以通过以下公式计算:
e t ∗ = 4 ( σ ^ 2 + λ ) ( t − μ ^ ) 2 + 2 σ ^ 2 + 2 λ , \begin{align} e_{t}^{*}=\frac{4\left(\hat{\sigma}^{2}+\lambda\right)}{(t-\hat{\mu})^{2}+2 \hat{\sigma}^{2}+2 \lambda}, \end{align} et=(tμ^)2+2σ^2+2λ4(σ^2+λ),
其中: µ ^ = 1 M ∑ i = 1 M x i \hat{µ} = \frac{1}{M} \sum^{M}_{i=1}x_i µ^=M1i=1Mxi σ ^ 2 = 1 M ∑ i M ( x i − μ ^ ) 2 \hat{σ}^2 = \frac{1}{M}\sum^{M}_i (x_i−\hat{μ})^2 σ^2=M1iM(xiμ^)2 是该通道所有神经元的平均值和方差。

公式(5)表明能量 e t ∗ e^*_t et 越低,神经元 t t t 与周围神经元的区别越明显,对视觉处理越重要。因此,每个神经元的重要性可以通过 1 / e t ∗ 1/e^*_t 1/et 得到。

哺乳动物大脑中的注意力调节通常表现为对神经元反应的增益(即缩放)效应。因此,我们使用缩放运算符而不是加法来进行特征细化。我们模块的整个细化阶段是:
X ^ = s i g m o i d ( 1 E ) ⊙ X = σ ( ( t − μ ^ ) 2 + 2 σ ^ 2 + 2 λ 4 ( σ ^ 2 + λ ) ) ⊙ X = σ ( ( t − μ ^ ) 2 4 ( σ ^ 2 + λ ) + 0.5 ) ⊙ X \begin{align} \hat{X} = sigmoid(\frac{1}{E}) \odot X = \sigma(\frac{(t-\hat{\mu})^{2}+2 \hat{\sigma}^{2}+2 \lambda}{4\left(\hat{\sigma}^{2}+\lambda\right)}) \odot X \\ = \sigma(\frac{(t-\hat{\mu})^{2}}{4\left(\hat{\sigma}^{2}+\lambda\right)} + 0.5) \odot X \end{align} X^=sigmoid(E1)X=σ(4(σ^2+λ)(tμ^)2+2σ^2+2λ)X=σ(4(σ^2+λ)(tμ^)2+0.5)X
其中: E E E 将所有 e t ∗ e^*_t et 跨通道和空间维度进行分组。添加 s i g m o i d sigmoid sigmoid 是为了限制 E E E 值过大,不会影响每个神经元的相对重要性,因为 s i g m o i d sigmoid sigmoid 是单峰函数。

论文给出的代码流程图如下:

值得注意的是:论文中唯一的超参数 λ \lambda λ 在实验的时候为 1 e − 4 1e-4 1e4

2、模块使用地方:一个即插即用的模块,可以在卷积模块之后直接插入simAM模块。

3、代码

import torch
import torch.nn as nn


class SimAM(torch.nn.Module):
    def __init__(self, e_lambda=1e-4):
        super(SimAM, self).__init__()

        self.activaton = nn.Sigmoid()
        self.e_lambda = e_lambda

    def __repr__(self):
        s = self.__class__.__name__ + '('
        s += ('lambda=%f)' % self.e_lambda)
        return s

    @staticmethod
    def get_module_name():
        return "simam"

    def forward(self, x):
        b, c, h, w = x.size()
        n = w * h - 1
        # 计算(t-u)^2
        x_minus_mu_square = (x - x.mean(dim=[2, 3], keepdim=True)).pow(2)
        y = x_minus_mu_square / (4 * (x_minus_mu_square.sum(dim=[2, 3], keepdim=True) / n + self.e_lambda)) + 0.5

        return x * self.activaton(y)


if __name__ == '__main__':
    input = torch.randn(3, 64, 7, 7)
    model = SimAM()
    print(model)
    outputs = model(input)
    print(outputs.shape)

TripletAttention

SGE

全称:

背景提要:

1、主要思想:

简单图示如下:

思想步骤解释:

值得注意的是:

2、模块使用地方:

3、代码

import torch

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1164505.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

百度竞价排名推广对比自然排名哪一个更具优势-华媒舍

在搜索引擎结论网页页面(SERP)中,我们经常会看到一些网站链接及其广告栏。这种连接一般分为两种类型:百度竞价推广排名推广与自然排名。究竟哪个更有优势?本文将对这几种排名形式进行科谱详细介绍。 什么叫百度竞价推广…

YOLOv8将注意力机制融合进入C2f模块

1. 引言 1.1 YOLOv8添加注意力机制方法 yolov8添加注意力机制是一个非常常见的操作,常见的操作直接将注意力机制添加至YOLOv8的某一层之后,这种改进特别常见。 示例如下: 新版yolov8添加注意力机制(以NAMAttention注意力机制为例…

BulkInsert in Entity Framework

实体框架中的 BulkInsert 扩展方法 安装 Z.EntityFramework.Extensions: 现在,我将向您展示如何使用 Z.EntityFramework.Extensions 包,以及如何通过 Entity Framework 执行批量插入、更新和删除操作。首先,打开“NuGet 包管理器…

Linux驱动 编译乱序和执行乱序

编译乱序 现代的高性能编译器在目标码优化上都具备对指令进行乱序优化的能力。编译器可以对访存的指令进行乱序,减少逻辑上不必要的访存,以及尽量提高Cache命中率和CPU的Load/Store单元的工作效率。 因此在打开编译器优化以后,看到生成的汇编…

JAVA亡了?那么多岗位去哪了?

1.java现在有多卷? 虽然近年来出现了许多其他编程语言和技术。但JAVA依旧是热度最高的。它仍然被广泛用于大型企业应用、后端开发、Android应用开发以及嵌入式系统等领域。此外,Java在大数据、云计算和物联网等新兴领域也有着重要的地位。 因此&#x…

MySQL数据库入门到大牛_01_内容简介

在企业中高级程序员以上级别常常要求是精通MySQL。任何一项技术一旦深入,体系都是繁杂的,想要真正掌握,需要掌握底层的逻辑,梳理清知识脉络,能够以架构师的思路学习MySQL,才能以不变应万变。此篇开始介绍My…

Linux-固定usb转网口名称

参考链接 https://www.cnblogs.com/WCH-SoftGroup/p/16516383.htmludev简介 udev 是一个用户空间系统,它使操作系统管理员能够为事件注册用户空间处理程序。 udev 守护程序接收的事件主要由 (Linux) 内核生成,以响应与外围设备相…

便携式燃料容器上亚马逊加拿大站合规标准是什么?如何办理?

便携式燃料容器 便携式燃料容器是预填充或设计用于容纳易燃液体燃料的一次性或可重复使用的容器。该政策还涵盖用于便携式燃料容器的随附组件,包括用于储存或分配易燃液体燃料的密封罩。 便携式燃料容器亚马逊政策 根据亚马逊政策的要求,所有便携式燃料…

香港「加密货币新政」一周年回顾:怀疑、亢奋和审慎乐观的发展历程

香港作为国际金融中心,一直以来都在追求创新和发展新兴市场。然而,在虚拟资产领域,香港经历了一段怀疑、亢奋和审慎乐观的过程。如今,回顾香港虚拟资产宣言一周年,可以看到这个领域正逐步稳定发展,并得到了…

使用C++的QT框架实现贪吃蛇

最近刷抖音经常看到别人使用类似chatGPT的al工具实现这个贪吃蛇游戏,正好我之前也写过,那么今天看看怎么去实现这个简单的游戏 我这边使用的是C的QT框架,当然用哪些框架都可以,主要是逻辑思路 1.生成画布,开始是一些…

MySQL(6):多表查询

多表查询,也称为关联查询,指两个或更多个表一起完成查询操作。 前提条件: 这些一起查询的表之间是有关系的(一对一、一对多),它们之间一定是有关联字段,这个关联字段可能建立了外键,…

医院小程序解决方案:让医疗服务触手可及

随着移动互联网的发展,小程序已经成为各行各业进行营销和提供服务的重要工具。医疗陪诊行业也不例外。本教程将带领大家了解如何快速掌握医疗陪诊小程序的搭建技巧,帮助大家轻松搭建自己的医疗陪诊小程序。 步骤一:登录乔拓云平台进入商城后台…

分类预测 | Matlab实现SMA-KELM黏菌优化算法优化核极限学习机分类预测

分类预测 | Matlab实现SMA-KELM黏菌优化算法优化核极限学习机分类预测 目录 分类预测 | Matlab实现SMA-KELM黏菌优化算法优化核极限学习机分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 1.MATLAB实现SMA-KELM黏菌优化算法优化核极限学习机分类预测(完整源码和数…

进程空间管理:用户态和内核态

用户态虚拟空间里面有几类数据,例如代码、全局变量、堆、栈、内存映射区等。在 struct mm_struct 里面,有下面这些变量定义了这些区域的统计信息和位置。 unsigned long mmap_base; /* base of mmap area */ unsigned long total_vm; /* Total page…

sql根据同一字段不同值时间升序将序

SELECT ID,T_STATUS,T_TIME FROMs order by CASE WHEN T_STATUS 0 THEN 100 ELSE 1000 END,case when T_STATUS 0 then T_TIME end ,case when T_STATUS ! 0 then T_TIME end desc;

Java规则引擎2.1.8版本新增功能说明

规则引擎更新功能 新增: 1.决策结束节点新增结果导出excel功能; 在决策流程的结束节点,可以将决策结果导出为Excel文件。这个功能为用户提供了更多的灵活性和便利性,使他们能够轻松地将决策结果数据进行进一步的分析和处理。 2.新增公有变…

【教3妹学编程-算法题】2913. 子数组不同元素数目的平方和 I

-----------------第二天------------------------ 面试官 : 好的, 我们再来做个算法题吧。平时工作中会尝试用算法吗, 用到了什么数据结构? 3妹 : 有用到, 用到了 bla bla… 面试官 : 好的, 题目是这样的&#xff1…

kali搭建Cobalt strike挂马

APT攻击是高级可持续化攻击,一般是团队分工合作 Cobalt strike红队用的渗透工具。多协议,端口,插件 服务端运行 cobaltstrike4提示java异常,改用cobaltstrike 3.14版本 服务端搭建:./teamserver 服务器IP地址 密码 …

2G-GCN:Multi-person HOI Recognition in Videos

Geometric Features Informed Multi-person Human-object Interaction Recognition in Videos解读 摘要简介 2. Related Work2.1 图像中的HOI检测2.2 视频中的HOI识别2.3 HOI识别数据集2.4 几何特征为HOI分析提供信息 3. 多人HOI数据集(MPHOI-72)4. Two-level Geometric Featur…

MySQL -- 复合查询及内外连接

MySQL – 复合查询及内外连接 文章目录 MySQL -- 复合查询及内外连接一、基本查询回顾1.单表查询 二、多表查询1.表的组合2.案例 三、自连接1.案例四、子查询1.单行子查询2.多行子查询3.多列子查询4.在from子句中使用子查询5.合并查询 五、内连接六、外连接1.左外连接2.右外连接…