注意力机制(Attention Mechanism)源于对人类视觉的研究。 在认知科学中,由于信息处理的瓶颈,人类会选择性地关注所有信息的一部分,同时忽略其他可见的信息。 上述机制通常被称为注意力机制。 人类视网膜不同的部位具有不同程度的信息处理能力,即敏锐度(Acuity),只有视网膜中央凹部位具有最强的敏锐度。
简而言之,注意力机制源于自然界人类视觉的研究。人类的视觉会天然地进行一个抉择,就是选择性地关注所有信息的一个部分,同事就会忽略其他可见的信息。就属于是合理的利用有限的信息处理资源。

注意力机制的分类示意图

在这里插入图片描述

1、SE注意力模块

SEnet(Squeeze-and-Excitation Network)考虑了特征通道之间的关系,在特征通道之间的关系,在特征通道之间引入了注意力机制这么个东西。
在这里插入图片描述

# SE
class SE(nn.Module):
    def __init__(self, c1, ratio=16):
        super(SE, self).__init__()
        self.avgpool = nn.AdaptiveAvgPool2d(1)
        self.l1 = nn.Linear(c1, c1 // ratio, bias=False)
        self.relu = nn.ReLU(inplace=True)
        self.l2 = nn.Linear(c1 // ratio, c1, bias=False)
        self.sig = nn.Sigmoid()

    def forward(self, x):
        b, c, _, _ = x.size()
        y = self.avgpool(x).view(b, c)
        y = self.l1(y)
        y = self.relu(y)
        y = self.l2(y)
        y = self.sig(y)
        y = y.view(b, c, 1, 1)
        return x * y.expand_as(x)

2、CBAM注意力模块

CBAM(Convolutional Block Attention Module ) 集合了特征通道和特征空间两个维度的注意力机制。

在这里插入图片描述
CBAM通过学习的方式自动获得每个特征通道的重要程度,和SEnet相似。此外还可以通过类似的学习方式自动获取每个特征空间的重要程度。并且利用得到的重要程度来提升特征并且抑制对当前任务不重要的特征。
在这里插入图片描述
CBAM提取特征通道注意力的方式和SEnet类似,如下channel Attention中的代码所示,其在SEnet的基础上增加了max_pool的特征提取方式,其余步骤是一样的。将通道注意力提取后的特征作为空间注意力模块的输入。
在这里插入图片描述
而CBAM提取空间注意力的方式是: 经过ChannelAttention后, 最终将经过通道重要性选择后的特征图送入特征空间注意力模块,和通道注意力模块相似,空间注意力是以通道为单位进行最大池化合平均池化,并将两者的结果进行concat,之后再进行一个卷积降成 1wh的特征空间权重,在讲该权重和输入特征进行点积,从而实现空间注意力机制。

# CBAM
class ChannelAttention(nn.Module):
    # Channel-attention module https://github.com/open-mmlab/mmdetection/tree/v3.0.0rc1/configs/rtmdet
    def __init__(self, channels: int) -> None:
        super().__init__()
        self.pool = nn.AdaptiveAvgPool2d(1)
        self.fc = nn.Conv2d(channels, channels, 1, 1, 0, bias=True)
        self.act = nn.Sigmoid()

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        return x * self.act(self.fc(self.pool(x)))


class SpatialAttention(nn.Module):
    # Spatial-attention module
    def __init__(self, kernel_size=7):
        super().__init__()
        assert kernel_size in (3, 7), 'kernel size must be 3 or 7'
        padding = 3 if kernel_size == 7 else 1
        self.cv1 = nn.Conv2d(2, 1, kernel_size, padding=padding, bias=False)
        self.act = nn.Sigmoid()

    def forward(self, x):
        return x * self.act(self.cv1(torch.cat([torch.mean(x, 1, keepdim=True), torch.max(x, 1, keepdim=True)[0]], 1)))


class CBAM(nn.Module):
    # Convolutional Block Attention Module
    def __init__(self, c1, kernel_size=7):  # ch_in, kernels
        super().__init__()
        self.channel_attention = ChannelAttention(c1)
        self.spatial_attention = SpatialAttention(kernel_size)

    def forward(self, x):
        return self.spatial_attention(self.channel_attention(x))


3、 ECA注意力模块

先前的方法大多致力于开发更复杂的注意力模块,以实现更好的性能,这不可避免地增加了模型的复杂性。为了克服性能和复杂性之间的矛盾,作者提出了一种有效的通道关注(ECA)模块,该模块只增加了少量的参数,却能获得明显的性能增益。
在这里插入图片描述

# ECA
class ECA(nn.Module):

    def __init__(self, k_size=3):
        super(ECA, self).__init__()
        self.avg_pool = nn.AdaptiveAvgPool2d(1)
        self.conv = nn.Conv1d(1, 1, kernel_size=k_size, padding=(k_size - 1) // 2, bias=False)
        self.sigmoid = nn.Sigmoid()

    def forward(self, x):
        y = self.avg_pool(x)
        y = self.conv(y.squeeze(-1).transpose(-1, -2)).transpose(-1, -2).unsqueeze(-1)
        y = self.sigmoid(y)
        return x * y.expand_as(x)


4、CA注意力模块

先前的轻量网络的注意力机制大多数采用了SE模块,仅仅考虑了通道之间的信息,忽略了位置信息。尽管后来的CBAM和BAM尝试在降低通道数后通过聚氨基来提取位置注意力信息,但卷积只能提取局部信息,缺乏长距离关系提取的能力。 为此,论文提出了新的高效注意力机制 coordinate attention(CA),能够将横向和纵向的位置信息编码到channel attention中, 使得移动网络能够关注大范围的位置信息有不会带来过多的计算量。

在这里插入图片描述

# CA
class h_sigmoid(nn.Module):
    def __init__(self, inplace=True):
        super(h_sigmoid, self).__init__()
        self.relu = nn.ReLU6(inplace=inplace)

    def forward(self, x):
        return self.relu(x + 3) / 6


class h_swish(nn.Module):
    def __init__(self, inplace=True):
        super(h_swish, self).__init__()
        self.sigmoid = h_sigmoid(inplace=inplace)

    def forward(self, x):
        return x * self.sigmoid(x)


class CoordAtt(nn.Module):
    def __init__(self, inp, oup, reduction=32):
        super(CoordAtt, self).__init__()
        self.pool_h = nn.AdaptiveAvgPool2d((None, 1))
        self.pool_w = nn.AdaptiveAvgPool2d((1, None))
        mip = max(8, inp // reduction)
        self.conv1 = nn.Conv2d(inp, mip, kernel_size=1, stride=1, padding=0)
        self.bn1 = nn.BatchNorm2d(mip)
        self.act = h_swish()
        self.conv_h = nn.Conv2d(mip, oup, kernel_size=1, stride=1, padding=0)
        self.conv_w = nn.Conv2d(mip, oup, kernel_size=1, stride=1, padding=0)

    def forward(self, x):
        identity = x
        n, c, h, w = x.size()
        x_h = self.pool_h(x)
        x_w = self.pool_w(x).permute(0, 1, 3, 2)
        y = torch.cat([x_h, x_w], dim=2)
        y = self.conv1(y)
        y = self.bn1(y)
        y = self.act(y)
        x_h, x_w = torch.split(y, [h, w], dim=2)
        x_w = x_w.permute(0, 1, 3, 2)
        a_h = self.conv_h(x_h).sigmoid()
        a_w = self.conv_w(x_w).sigmoid()
        out = identity * a_w * a_h
        return out

5、 注意力机制的添加方式

5.1 、检测 分割 关键点任务

大致的修改方法:
在YOLOv8中添加注意力机制主要分为以下5步,以在yolov8.yaml中添加SE注意力机制为例:

1、在ultralytics/models/v8 文件夹下新建一个yolov8-SE.yaml 文件;
2、 将文中上面提供的SE注意力代码添加到 ultralytics/nn/modules.py 中;
3、 将SE这个类的名字加入到 ultralytics/nn/tasks.py中;
4、修改yolov8-SE.yaml , 将SE注意力机制加入到需要的位置上去;
5、 修改 ultralytics/yolo/cfg/default.yaml 文件中的 ‘–model’ 默认参数,或者直接使用指令,随后就可以开始训练网络模型了。

详细的修改方法如下所示:

  • 第1步、在ultralytics/models/v8 文件夹下新建一个yolov8-SE.yaml
    文件,将yolov8.yaml文件内容拷贝粘贴到新建的这个文件当中来等待第4步对它进行改造;
  • 第2步、 将文中上面提供的SE注意力代码添加到 ultralytics/nn/modules.py 中(文件末尾即可);
# SE
class SE(nn.Module):
    def __init__(self, c1, ratio=16):
        super(SE, self).__init__()
        self.avgpool = nn.AdaptiveAvgPool2d(1)
        self.l1 = nn.Linear(c1, c1 // ratio, bias=False)
        self.relu = nn.ReLU(inplace=True)
        self.l2 = nn.Linear(c1 // ratio, c1, bias=False)
        self.sig = nn.Sigmoid()

    def forward(self, x):
        b, c, _, _ = x.size()
        y = self.avgpool(x).view(b, c)
        y = self.l1(y)
        y = self.relu(y)
        y = self.l2(y)
        y = self.sig(y)
        y = y.view(b, c, 1, 1)
        return x * y.expand_as(x)

  • 第3步:首先将SE模块import一下, 然后将SE这个类的名字加入到
    ultralytics/nn/tasks.py中,如下图所示的位置处;

在这里插入图片描述
在这里插入图片描述
4、修改yolov8-SE.yaml , 将SE注意力机制加入到需要的位置上去;
常见的位置有C2f模块的后面 , Neck脖子部分中, 也可以在主干部分的SPPF前面添加一层; (如下图所展示的样子),那么随后,后面的每一层的编号也就改变了,加1,记得concat那里输入的参数from就需要随机应变;

在这里插入图片描述
比如后面的detec检测头的输入也会随之改变:
左侧是原始的yolov8.yaml, 右侧为修改后的yolov8-SE.yaml
5、 修改 ultralytics/yolo/cfg/default.yaml 文件中的 ‘–model’ 默认参数,在’–model’后面加上刚创建好的yolov8-SE.yaml文件的路径,或者直接使用指令,随后就可以开始训练网络模型了。
在这里插入图片描述

或者使用如下指令:

yolo detect train data=coco128.yaml model=D:\Pycharm_Projects\ultralytics\ultralytics\models\v8\yolov8-SE.yaml  epochs=200 imgsz=640

在这里插入图片描述
完整的yaml文件如下:

# Ultralytics YOLO 🚀, GPL-3.0 license
# YOLOv8 object detection model with P3-P5 outputs. 

# Parameters
nc: 80  # number of classes
scales: # model compound scaling constants, i.e. 'model=yolov8n.yaml' will call yolov8.yaml with scale 'n'
  # [depth, width, max_channels]
  n: [0.33, 0.25, 1024]  # YOLOv8n summary: 225 layers,  3157200 parameters,  3157184 gradients,   8.9 GFLOPs
  s: [0.33, 0.50, 1024]  # YOLOv8s summary: 225 layers, 11166560 parameters, 11166544 gradients,  28.8 GFLOPs
  m: [0.67, 0.75, 768]   # YOLOv8m summary: 295 layers, 25902640 parameters, 25902624 gradients,  79.3 GFLOPs
  l: [1.00, 1.00, 512]   # YOLOv8l summary: 365 layers, 43691520 parameters, 43691504 gradients, 165.7 GFLOPs
  x: [1.00, 1.25, 512]   # YOLOv8x summary: 365 layers, 68229648 parameters, 68229632 gradients, 258.5 GFLOPs

# YOLOv8.0n backbone
backbone:
  # [from, repeats, module, args]
  - [-1, 1, Conv, [64, 3, 2]]  # 0-P1/2
  - [-1, 1, Conv, [128, 3, 2]]  # 1-P2/4
  - [-1, 3, C2f, [128, True]]
  - [-1, 1, Conv, [256, 3, 2]]  # 3-P3/8
  - [-1, 6, C2f, [256, True]]
  - [-1, 1, Conv, [512, 3, 2]]  # 5-P4/16
  - [-1, 6, C2f, [512, True]]
  - [-1, 1, Conv, [1024, 3, 2]]  # 7-P5/32
  - [-1, 3, C2f, [1024, True]]
  - [-1, 1, SE, [16]]
  - [-1, 1, SPPF, [1024, 5]]  # 9

# YOLOv8.0n head
head:
  - [-1, 1, nn.Upsample, [None, 2, 'nearest']]
  - [[-1, 6], 1, Concat, [1]]  # cat backbone P4
  - [-1, 3, C2f, [512]]  # 12

  - [-1, 1, nn.Upsample, [None, 2, 'nearest']]
  - [[-1, 4], 1, Concat, [1]]  # cat backbone P3
  - [-1, 3, C2f, [256]]  # 15 (P3/8-small)

  - [-1, 1, Conv, [256, 3, 2]]
  - [[-1, 13], 1, Concat, [1]]  # cat head P4
  - [-1, 3, C2f, [512]]  # 18 (P4/16-medium)

  - [-1, 1, Conv, [512, 3, 2]]
  - [[-1, 10], 1, Concat, [1]]  # cat head P5
  - [-1, 3, C2f, [1024]]  # 21 (P5/32-large)

  - [[16, 19, 22], 1, Detect, [nc]]  # Detect(P3, P4, P5)


5.2、分类任务

分类任务的区别和检测任务相比,就是在于yaml文件上,并且分类任务的yaml文件不需要修改from参数,所以其实是更加简单方便的。

# Ultralytics YOLO 🚀, AGPL-3.0 license
# YOLOv8-cls image classification model. For Usage examples see https://docs.ultralytics.com/tasks/classify

# Parameters
nc: 1000  # number of classes
scales: # model compound scaling constants, i.e. 'model=yolov8n-cls.yaml' will call yolov8-cls.yaml with scale 'n'
  # [depth, width, max_channels]
  n: [0.33, 0.25, 1024]
  s: [0.33, 0.50, 1024]
  m: [0.67, 0.75, 1024]
  l: [1.00, 1.00, 1024]
  x: [1.00, 1.25, 1024]

# YOLOv8.0n backbone
backbone:
  # [from, repeats, module, args]
  - [-1, 1, Conv, [64, 3, 2]]  # 0-P1/2
  - [-1, 1, Conv, [128, 3, 2]]  # 1-P2/4
  - [-1, 3, C2f, [128, True]]
  - [-1, 1, Conv, [256, 3, 2]]  # 3-P3/8
  - [-1, 6, C2f, [256, True]]
  - [-1, 1, SE, [16]]
  - [-1, 1, Conv, [512, 3, 2]]  # 5-P4/16
  - [-1, 6, C2f, [512, True]]
  - [-1, 1, SE, [16]]
  - [-1, 1, Conv, [1024, 3, 2]]  # 7-P5/32
  - [-1, 3, C2f, [1024, True]]

# YOLOv8.0n head
head:
  - [-1, 1, Classify, [nc]]  # Classify

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐