欢迎光临
我们一直在努力

YOLOv8改进 - 注意力机制 | SKAttention:选择性核注意力通过多分支融合与自适应感受野调整优化多尺度目标检测

前言

本文介绍了SKAttention(选择性核注意力)机制,这是一种受生物视觉皮层启发的动态多尺度特征提取方法。该技术通过Split、Fuse和Select三个算子,使神经元能根据输入自适应调整感受野大小,通过不同卷积核分支的软注意力融合来捕捉复杂的空间特征。我们将SKAttention集成进YOLOv11(基于YOLOv8架构演进),在保持计算效率的同时显著增强了模型对多尺度目标的检测能力。实验表明,该改进能有效提升模型在复杂场景下的特征聚合与理解能力。

文章目录: YOLOv8改进大全:卷积层、轻量化、注意力机制、损失函数、Backbone、SPPF、Neck、检测头全方位优化汇总

专栏链接: YOLOv8改进专栏

文章目录

  • 前言
  • 介绍
    • 摘要
  • 文章链接
  • 基本原理
    • 概述
    • 选择性内核(SK)
    • Split
      • Fuse
      • Select
  • 核心代码
  • 引入代码
  • 注册
    • 步骤1:
    • 步骤2
  • 配置yolov8_SKAttention.yaml
  • 实验
    • 脚本
    • 结果

介绍

image-20240630105831864

摘要

在标准卷积神经网络(CNNs)中,每一层的人工神经元的感受野大小被设计为相同。然而,神经科学界已知视觉皮层神经元的感受野大小会受到刺激的调节,这一点在构建CNN时很少被考虑。我们提出了一种动态选择机制,使每个神经元能够基于多尺度的输入信息自适应地调整其感受野大小。我们设计了一个称为选择性卷积(Selective Kernel,SK)单元的构建块,在该单元中,具有不同卷积核大小的多个分支通过受这些分支信息引导的softmax注意力融合。在融合层中,这些分支上的不同注意力产生了神经元的不同有效感受野大小。多个SK单元被堆叠形成一个称为选择性卷积网络(SKNets)的深层网络。在ImageNet和CIFAR基准测试中,我们实验证明SKNet在模型复杂度较低的情况下优于现有的最先进架构。详细分析显示,SKNet中的神经元能够捕捉不同尺度的目标对象,这验证了神经元根据输入自适应调整其感受野大小的能力。代码和模型可在https://github.com/implus/SKNet获取。

文章链接

论文地址:论文地址

代码地址:代码地址

基本原理

概述

SKAttention是一种用于深度学习中的注意力机制,特别是在卷积神经网络(CNN)中。它通过动态选择不同大小的卷积核来提高网络对多尺度特征的捕捉能力。SKAttention的设计灵感来源于视觉皮层神经元,这些神经元能够根据刺激自适应地调整其感受野大小。在CNN中实现这种机制可以帮助网络更好地捕捉复杂图像空间的多尺度特征,同时减少计算资源的浪费。

SKAttention的核心是“选择性核(Selective Kernel)”单元,它允许多个具有不同内核大小的分支在信息指导下使用SoftMax进行融合。这些分支中的每个卷积核都会对输入图像进行处理,产生不同尺寸的特征图。然后,通过融合操作将这些不同尺寸的特征图结合起来,生成用于选择权重的全局和综合表示。最后,根据这些权重对不同大小内核的特征图进行聚合,从而得到最终的输出特征。

SKAttention的主要优势在于它可以更有效地捕捉图像空间的多尺度特征,提高模型在处理不同尺度目标时的性能。此外,SKAttention还可以聚合深度特征,使模型更容易理解,同时也允许更好的可解释性。

SKAttention模块可以灵活地集成到各种深度学习模型中,特别是在目标检测领域,如YOLOv5和YOLOv7等模型,已经成功地集成了SKAttention来提升检测效果。在集成时,SKAttention可以作为即插即用的注意力模块添加到网络的任何合适位置。

SKAttention的实现涉及到多个卷积层、全连接层和softmax激活函数。在模型训练过程中,通过反向传播和梯度下降方法不断更新网络参数,优化模型性能。

选择性内核(SK)

“Selective Kernel” (SK) convolution使神经元能够自适应地调整其 RF 大小。

具体来说,我们通过三个算子——Split、Fuse 和 Select 来实现 SK 卷积。如下图所示是一个两分支的情况。

image-20240630110236019

Split

对输入特征图 X 进行两次变换 ~F 和 ^F,以输出 ~U 和 ^U,核大小分别为 3 和 5

~F 和 ^F 均由高效的分组/深度卷积、批量归一化(BN)和 ReLU 依次组成。

为了进一步提高效率,具有 5×5 内核的传统卷积被替换为具有 3×3 内核和扩张大小 2 的扩张卷积。

Fuse

  • 首先,多个(上图中两个)分支的结果通过逐元素求和进行融合:

    U

    =

    U

    ~

    +

    U

    ^

    ,

    \\mathrm{U}=\\tilde{\\mathrm{U}}+\\hat{\\mathrm{U}},

    U=U~+U^,

  • 应用全局平均池化来获取全局信息。具体来说,s 的第 c 个元素是通过空间维度 H×W 缩小 U 来计算的:

    s

    c

    =

    F

    g

    p

    (

    U

    c

    )

    =

    1

    H

    ×

    W

    i

    =

    1

    H

    j

    =

    1

    W

    U

    c

    (

    i

    ,

    j

    )

    .

    s_c=\\mathcal{F}_{gp}(\\mathbf{U}_c)=\\frac{1}{H\\times W}\\sum_{i=1}^H\\sum_{j=1}^W\\mathbf{U}_c(i,j).

    sc=Fgp(Uc)=H×W1i=1Hj=1WUc(i,j).

  • 然后,sc被压缩为紧凑特征z。这是通过一个简单的全连接(fc)层来实现的,并通过降低维度来提高效率:

    其中 δ 是 ReLU,β 是批量范数(BN)。

    z

    =

    F

    f

    c

    (

    s

    )

    =

    δ

    (

    B

    (

    W

    s

    )

    )

    ,

    \\mathbf{z}=\\mathcal{F}_{fc}(\\mathbf{s})=\\delta(\\mathcal{B}(\\mathbf{Ws})),

    z=Ffc(s)=δ(B(Ws)),

  • 为了研究d对模型效率的影响,我们使用一个减速比r来控制其值:

    d

    =

    max

    (

    C

    /

    r

    ,

    L

    )

    ,

    d=\\max(C/r,L),

    d=max(C/r,L),

Select

跨通道的软注意力用于自适应地选择不同空间尺度的信息,这是由紧凑特征描述符 z 引导的。具体来说,softmax 运算符应用于通道数字:

a

c

=

e

A

c

z

e

A

c

z

+

e

B

c

z

,

b

c

=

e

B

c

z

e

A

c

z

+

e

B

a

c

z

,

a_c=\\frac{e^{\\mathbf{A}_c\\mathbf{z}}}{e^{\\mathbf{A}_c\\mathbf{z}}+e^{\\mathbf{B}_c\\mathbf{z}}},b_c=\\frac{e^{\\mathbf{B}_c\\mathbf{z}}}{e^{\\mathbf{A}_c\\mathbf{z}}+e^{\\mathbf{B}_ac\\mathbf{z}}},

ac=eAcz+eBczeAcz,bc=eAcz+eBaczeBcz,

  • 在有两个分支的情况下,矩阵B是冗余的,因为ac+bc=1。

  • 最终的特征图V是通过各个内核上的注意力权重获得的:

    V

    c

    =

    a

    c

    U

    ~

    c

    +

    b

    c

    U

    ^

    c

    ,

    a

    c

    +

    b

    c

    =

    1

    ,

    \\mathbf{V}_c=a_c\\cdot\\tilde{\\mathbf{U}}_c+b_c\\cdot\\widehat{\\mathbf{U}}_c, a_c+b_c=1,

    Vc=acU~c+bcU

    c,ac+bc=1,

  • 上面的公式是针对两分支的情况,可以很容易地推导出更多分支的情况。

核心代码

引入代码

在根目录下的ultralytics/nn/目录,新建一个 attention目录,然后新建一个以 SKAttention为文件名的py文件, 把代码拷贝进去。

import torch
import torch.nn as nn
from collections import OrderedDict

class SKAttention(nn.Module):
def __init__(self, channel=512, kernels=[1, 3, 5, 7], reduction=16, group=1, L=32):
super().__init__()
self.d = max(L, channel // reduction)
self.convs = nn.ModuleList([])
for k in kernels:
self.convs.append(
nn.Sequential(
OrderedDict(
[
(
"conv",
nn.Conv2d(
channel,
channel,
kernel_size=k,
padding=k // 2,
groups=group,
),
),
("bn", nn.BatchNorm2d(channel)),
("relu", nn.ReLU()),
]
)
)
)
self.fc = nn.Linear(channel, self.d)
self.fcs = nn.ModuleList([])
for i in range(len(kernels)):
self.fcs.append(nn.Linear(self.d, channel))
self.softmax = nn.Softmax(dim=0)

def forward(self, x):
bs, c, _, _ = x.size()
conv_outs = []
for conv in self.convs:
conv_outs.append(conv(x))
feats = torch.stack(conv_outs, 0) # k,bs,channel,h,w
U = sum(conv_outs) # bs,c,h,w
S = U.mean(1).mean(1) # bs,c
Z = self.fc(S) # bs,d
weights = []
for fc in self.fcs:
weight = fc(Z)
weights.append(weight.view(bs, c, 1, 1)) # bs,channel
attention_weughts = torch.stack(weights, 0) # k,bs,channel,1,1
attention_weughts = self.softmax(attention_weughts) # k,bs,channel,1,1
V = (attention_weughts * feats).sum(0)
return V

注册

在ultralytics/nn/tasks.py中进行如下操作:

步骤1:

from ultralytics.nn.attention.SKAttention import SKAttention

步骤2

修改def parse_model(d, ch, verbose=True):

elif m is SKAttention:
c1, c2 = ch[f], args[0]
if c2 != nc:
c2 = make_divisible(min(c2, max_channels) * width, 8)
args = [c1, *args[1:]]

image-20240630113115019

配置yolov8_SKAttention.yaml

ultralytics/cfg/models/v8/yolov8_SKAttention.yaml

# Ultralytics YOLO 🚀, GPL-3.0 license
# YOLOv8 object detection model with P3-P5 outputs. For Usage examples see https://docs.ultralytics.com/tasks/detect

# Parameters
nc: 2 # 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, 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, 12], 1, Concat, [1]] # cat head P4
[1, 3, C2f, [512]] # 18 (P4/16-medium)

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

[[15, 18, 22], 1, Detect, [nc]] # Detect(P3, P4, P5)

实验

脚本

import os
from ultralytics import YOLO

# Define the configuration options directly
yaml = 'ultralytics/cfg/models/v8/yolov8_SKAttention.yaml'

# Initialize the YOLO model with the specified YAML file
model = YOLO(yaml)

# Print model information
model.info()
if __name__ == "__main__":
# Train the model with the specified parameters
results = model.train(data='ultralytics/datasets/original-license-plates.yaml',
name='yolov8_SKAttention',
epochs=10,
workers=8,
batch=1)

结果

在这里插入图片描述

赞(0)
未经允许不得转载:171主机测评 » YOLOv8改进 - 注意力机制 | SKAttention:选择性核注意力通过多分支融合与自适应感受野调整优化多尺度目标检测
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址