YOLOv10改进系列,YOLOv10添加MLCA注意力机制(混合局部信道注意)


在这里插入图片描述

原论文摘要

注意力机制是计算机视觉中最广泛使用的组件之一,能够帮助神经网络突出重要元素并抑制不相关的部分。然而,大多数通道注意力机制只包含通道特征信息,忽略了空间特征信息,导致模型的表示效果较差或目标检测性能不佳,并且空间注意力模块往往复杂且代价高昂。为了在性能和复杂性之间取得平衡,本文提出了一种轻量级的混合局部通道注意力(MLCA)模块,以提高目标检测网络的性能。该模块能够同时结合通道信息和空间信息,以及局部信息和全局信息,从而提升网络的表示效果。

MLCA介绍

MLCA是一种即插即用的可扩展混合局部注意力机制,以在检测效果、速度和模型参数数量之间实现平衡,并使注意力机制同时包含通道信息、空间信息、局部通道信息和全局通道信息。
具体来说:
1.局部性:关注特征图的局部区域,而非全局特征,避免无关区域的干扰。
2.通道性:同时对不同的通道进行加权,以突出最相关的特征。
3.混合性:将局部通道注意力与全局注意力或其他类型的注意力结合,增强模型的表达能力。
混合局部信道注意(MLCA)结构图如下:
在这里插入图片描述

MLCA理论详解可以参考链接:论文地址
MLCA代码可在这个链接找到:代码地址

本文在YOLOv10中引入MLCA(混合局部信道注意),代码已经整理好了,跟着文章复制粘贴,即可直接运行


🎓一、YOLOv10原始版本代码下载

如果之前有在我的网盘下载的YOLOv10源码的就不需要重新下载了,没有下载从我的网盘下载,链接: YOLOv10原始版本源码下载
提取码: js2i

注意注意注意:如果在我之前的文章下载过YOLOv10源码,不用重新下载了,没有特殊说明都是用同一个版本的源码

🍀🍀1.YOLOv10模型结构图

根据yolov10n.yaml画出yolo整体结构图,如下图所示
在这里插入图片描述

🍀🍀2.环境配置

环境配置参考教程链接:链接: 环境配置链接如果已经配置好环境可以忽略此步骤

🎓二、MLCA代码

# -*- coding: utf-8 -*-
"""
@Auth : 挂科边缘
@File :MLCA.py
@IDE :PyCharm
@Motto:学习新思想,争做新青年
@Email :179958974@qq.com
@qq :179958974
"""

import torch
import torch.nn as nn
import torch.nn.functional as F
import math


class MLCA(nn.Module):
    def __init__(self, in_size, local_size=5, gamma=2, b=1, local_weight=0.5):
        super(MLCA, self).__init__()

        self.local_size = local_size
        self.gamma = gamma
        self.b = b
        t = int(abs(math.log(in_size, 2) + self.b) / self.gamma)  # eca  gamma=2
        k = t if t % 2 else t + 1

        self.conv = nn.Conv1d(1, 1, kernel_size=k, padding=(k - 1) // 2, bias=False)
        self.conv_local = nn.Conv1d(1, 1, kernel_size=k, padding=(k - 1) // 2, bias=False)

        self.local_weight = local_weight

        self.local_arv_pool = nn.AdaptiveAvgPool2d(local_size)
        self.global_arv_pool = nn.AdaptiveAvgPool2d(1)

    def forward(self, x):
        local_arv = self.local_arv_pool(x)
        global_arv = self.global_arv_pool(local_arv)

        b, c, m, n = x.shape
        b_local, c_local, m_local, n_local = local_arv.shape

        # (b,c,local_size,local_size) -> (b,c,local_size*local_size)-> (b,local_size*local_size,c)-> (b,1,local_size*local_size*c)
        temp_local = local_arv.view(b, c_local, -1).transpose(-1, -2).reshape(b, 1, -1)
        temp_global = global_arv.view(b, c, -1).transpose(-1, -2)

        y_local = self.conv_local(temp_local)
        y_global = self.conv(temp_global)

        # (b,c,local_size,local_size) <- (b,c,local_size*local_size)<-(b,local_size*local_size,c) <- (b,1,local_size*local_size*c)
        y_local_transpose = y_local.reshape(b, self.local_size * self.local_size, c).transpose(-1, -2).view(b, c,
                                                                                                            self.local_size,
                                                                                                            self.local_size)
        # y_global_transpose = y_global.view(b, -1).transpose(-1, -2).unsqueeze(-1)
        y_global_transpose = y_global.view(b, -1).unsqueeze(-1).unsqueeze(-1)  # 代码修正
        # print(y_global_transpose.size())
        # 反池化
        att_local = y_local_transpose.sigmoid()
        att_global = F.adaptive_avg_pool2d(y_global_transpose.sigmoid(), [self.local_size, self.local_size])
        # print(att_local.size())
        # print(att_global.size())
        att_all = F.adaptive_avg_pool2d(att_global * (1 - self.local_weight) + (att_local * self.local_weight), [m, n])
        # print(att_all.size())
        x = x * att_all
        return x


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 Conv(nn.Module):
    """Standard convolution with args(ch_in, ch_out, kernel, stride, padding, groups, dilation, activation)."""
    default_act = nn.SiLU()  # default activation

    def __init__(self, c1, c2, k=1, s=1, p=None, g=1, d=1, act=True):
        """Initialize Conv layer with given arguments including activation."""
        super().__init__()
        self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p, d), groups=g, dilation=d, bias=False)
        self.bn = nn.BatchNorm2d(c2)
        self.act = self.default_act if act is True else act if isinstance(act, nn.Module) else nn.Identity()

    def forward(self, x):
        """Apply convolution, batch normalization and activation to input tensor."""
        return self.act(self.bn(self.conv(x)))

    def forward_fuse(self, x):
        """Perform transposed convolution of 2D data."""
        return self.act(self.conv(x))


class C2f_MLCA(nn.Module):
    """Faster Implementation of CSP Bottleneck with 2 convolutions."""

    def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5):
        """Initialize CSP bottleneck layer with two convolutions with arguments ch_in, ch_out, number, shortcut, groups,
        expansion.
        """
        super().__init__()
        self.c = int(c2 * e)  # hidden channels
        self.cv1 = Conv(c1, 2 * self.c, 1, 1)
        self.cv2 = Conv((2 + n) * self.c, c2, 1)  # optional act=FReLU(c2)
        self.m = nn.ModuleList(Bottleneck(self.c, self.c, shortcut, g, k=((3, 3), (3, 3)), e=1.0) for _ in range(n))

    def forward(self, x):
        """Forward pass through C2f layer."""
        y = list(self.cv1(x).chunk(2, 1))
        y.extend(m(y[-1]) for m in self.m)
        return self.cv2(torch.cat(y, 1))

    def forward_split(self, x):
        """Forward pass using split() instead of chunk()."""
        y = list(self.cv1(x).split((self.c, self.c), 1))
        y.extend(m(y[-1]) for m in self.m)
        return self.cv2(torch.cat(y, 1))


class Bottleneck(nn.Module):
    """Standard bottleneck."""

    def __init__(self, c1, c2, shortcut=True, g=1, k=(3, 3), e=0.5):
        """Initializes a bottleneck module with given input/output channels, shortcut option, group, kernels, and
        expansion.
        """
        super().__init__()
        c_ = int(c2 * e)  # hidden channels
        self.cv1 = Conv(c1, c_, k[0], 1)
        self.cv2 = Conv(c_, c2, k[1], 1, g=g)
        self.add = shortcut and c1 == c2
        self.MLCA = MLCA(c2)

    def forward(self, x):
        """'forward()' applies the YOLO FPN to input data."""
        return x + self.MLCA(self.cv2(self.cv1(x))) if self.add else self.MLCA(self.cv2(self.cv1(x)))

🎓三、在YOLOv10添加MLCA代码

🍀🍀1.在modules目录下添加第二章的MLCA代码

(1).在ultralytics/nn/modules目录下,新建一个文件名,我这里取名为MLCA.py,操作如以下截图:

在这里插入图片描述

(2).把第二章的代码复制进去,如下图所示:
在这里插入图片描述

🍀🍀2.在__init__.py文件导入MLCA模块

文件路径为:ultralytics/nn/modules/init.py
(1)在__init__.py开头注释下面代码,这个步骤注释了以后可以跳过这个步骤
在这里插入图片描述
(2)之后开头导入iRMB,导入截图所示

from .MLCA import C2f_MLCA,MLCA

在这里插入图片描述

🍀🍀3.在tasks.py文件注册MLCA

(1)在tasks.py文件开头导入所有模块,该文件路径为:ultralytics/nn/tasks.py
在这个文件修改导入方法,注释掉下图红色圈出来的部分,改成*号导入,以后无需手动一个个导入新的模块,方便很多,一次性导完,这个步骤改完了,往后的文章就可以跳过这个步骤了,添加如截图所示

from ultralytics.nn.modules import *

在这里插入图片描述

(2)之后在这个文件的parse_model方法,添加MLCA,添加如截图所示
在这里插入图片描述

看到这里已经成功把改进的模块添加进YOLOv10源码了,接下来配置yaml文件调用改进的模块就行了


🎓四、yaml文件修改

在ultralytics/cfg/models/v10目录下,新建一个yaml文件,复制yolov10n.yaml文件,然后取名为yolov10n-MLCA.yaml

🍀🍀1.第一种添加方法

yolov10n-MLCA.yaml全部代码如下:

# 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] 

# 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, SCDown, [512, 3, 2]] # 5-P4/16
  - [-1, 6, C2f, [512, True]]
  - [-1, 1, SCDown, [1024, 3, 2]] # 7-P5/32
  - [-1, 3, C2f, [1024, True]]
  - [-1, 1, SPPF, [1024, 5]] # 9
  - [-1, 1, PSA, [1024]] # 10

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

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

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

  - [-1, 1, SCDown, [512, 3, 2]]
  - [[-1, 10], 1, Concat, [1]] # cat head P5
  - [-1, 3, C2fCIB, [1024, True, True]] # 22 (P5/32-large)
  - [-1, 1, MLCA, []]  # 23

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

🍀🍀2.第二种添加方法

yolov10n-iRMB.yaml全部代码如下:

# 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] 

# 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_MLCA, [128, True]]
  - [-1, 1, Conv, [256, 3, 2]] # 3-P3/8
  - [-1, 6, C2f_MLCA, [256, True]]
  - [-1, 1, SCDown, [512, 3, 2]] # 5-P4/16
  - [-1, 6, C2f_MLCA, [512, True]]
  - [-1, 1, SCDown, [1024, 3, 2]] # 7-P5/32
  - [-1, 3, C2f_MLCA, [1024, True]]
  - [-1, 1, SPPF, [1024, 5]] # 9
  - [-1, 1, PSA, [1024]] # 10

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

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

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

  - [-1, 1, SCDown, [512, 3, 2]]
  - [[-1, 10], 1, Concat, [1]] # cat head P5
  - [-1, 3, C2fCIB, [1024, True, True]] # 22 (P5/32-large)


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


🎓五、训练文件修改

🍀🍀1.新建训练文件

在根目录新建一个python文件,取名为:train.py,如果之前看过我的文章,已经新建过就不用重新新建了
在这里插入图片描述

🍀🍀2.修改训练文件

YOLOv10训练方式跟YOLOv5是有区别的,但是训练数据集格式跟YOLOv5一样的,你只需把处理好的数据集就行,这里就不在阐述了,废话不多说。

把训练代码复制到train.py文件,如果之前看过我的文章,已经复制过了就不用重新复制了,只需修改参数就行,训练的代码如下:

# -*- coding: utf-8 -*-
"""
@Auth : 挂科边缘
@File :train.py
@IDE :PyCharm
@Motto:学习新思想,争做新青年
@Email :179958974@qq.com
@qq :179958974
"""


import warnings
warnings.filterwarnings('ignore')
from ultralytics import YOLOv10

if __name__ == '__main__':
    # model.load('yolov8n.pt') # 加载预训练权重,改进或者做对比实验时候不建议打开,因为用预训练模型整体精度没有很明显的提升
    model = YOLOv10(model=r'D:\2-Python\1-YOLO\YOLOv10\yolov10-main\ultralytics\cfg\models\v10\yolov10n.yaml')
    model.train(data=r'data.yaml',
                imgsz=640,
                epochs=200,
                batch=4,
                workers=8,
                device='',
                optimizer='SGD',
                close_mosaic=10,
                resume=False,
                project='runs/train',
                name='exp',
                single_cls=False,
                cache=False,
                )

根据你训练需求修改指定参数就行,其中圈起来的参数需要你修改的,其他参数根据自己需求选择改或者不改就行。

在这里插入图片描述

训练代码的参数解释,标蓝色的参数为常用参数:
1.model参数:该参数填入模型配置文件的路径,改进的话建议不需要预训练模型权重来训练
2.data参数:该参数可以填入训练数据集配置文件的路径
3.imgsz参数:该参数代表输入图像的尺寸,指定为 640x640 像素
4.epochs参数:该参数代表训练的轮数
5.batch参数:该参数代表批处理大小,电脑显存越大,就设置越大,根据自己电脑性能设置
6.workers参数:该参数代表数据加载的工作线程数,出现显存爆了的话可以设置为0。默认填8,可以加快训练
7.device参数:该参数代表用哪个显卡训练,留空表示自动选择可用的GPU或CPU
8.optimizer参数:该参数代表优化器类型
9.close_mosaic参数:该参数代表在多少个 epoch 后关闭 mosaic 数据增强
10.resume参数:该参数代表是否从上一次中断的训练状态继续训练。设置为False表示从头开始新的训练。如果设置为True,则会加载上一次训练的模型权重和优化器状态,继续训练。这在训练被中断或在已有模型的基础上进行进一步训练时非常有用。
11.project参数:该参数代表项目文件夹,用于保存训练结果
12.name参数:该参数代表命名保存的结果文件夹
13.single_cls参数:该参数代表是否将所有类别视为一个类别,设置为False表示保留原有类别
14.cache参数:该参数代表是否缓存数据,设置为False表示不缓存。

训练报错常见的错误:
训练显存报错,出现以下报错情况,把workers参数改为0
(1)OSError: [WinError 1455] 页面文件太小,无法完成操作。 Error loading “D:\1-ProgramFiles\Anaconda\envs\torch\lib\site-packages\torch\lib\caffe2_detectron_ops_gpu.dll” or one of its dependencies.
在这里插入图片描述
(2)RuntimeError: CUDA out of memory. Tried to allocate 2.00 MiB (GPU 0; 8.00 GiB total capacity; 530.52 MiB already allocated; 4.87 GiB free; 558.00 MiB reserved in total by PyTorch) If reserved memory is >> allocated memory try setting max_split_size_mb to avoid fragmentation. See documentation for Memory Management and PYTORCH_CUDA_ALLOC_CONF

在这里插入图片描述

测试一下训练,打印出来的YOLOv10结构可以看到添加改进的模块成功
在这里插入图片描述

总结

请在我提供的YOLOv10代码修改,把环境配置好,数据集处理好,训练基本能成功,创作不易,请帮忙点一个爱心,谢谢观看

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

挂科边缘(毕业版)

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

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

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

打赏作者

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

抵扣说明:

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

余额充值