YOLOv5/v7 添加注意力机制,30多种模块分析③,GCN模块,DAN模块

news2024/11/15 17:24:58

目录

    • 一、注意力机制介绍
      • 1、什么是注意力机制?
      • 2、注意力机制的分类
      • 3、注意力机制的核心
    • 二、GCN 模块
      • 1、GCN 模块的原理
      • 2、实验结果
      • 3、应用示例
    • 三、DAN模块
      • 1、DAN模块的原理
      • 2、实验结果
      • 3、应用示例

大家好,我是哪吒。

🏆本文收录于,目标检测YOLO改进指南。

本专栏均为全网独家首发,内附代码,可直接使用,改进的方法均是2023年最近的模型、方法和注意力机制。每一篇都做了实验,并附有实验结果分析,模型对比。


在机器学习和自然语言处理领域,随着数据的不断增长和任务的复杂性提高,传统的模型在处理长序列或大型输入时面临一些困难。传统模型无法有效地区分每个输入的重要性,导致模型难以捕捉到与当前任务相关的关键信息。为了解决这个问题,注意力机制(Attention Mechanism)应运而生。

一、注意力机制介绍

1、什么是注意力机制?

注意力机制(Attention Mechanism)是一种在机器学习和自然语言处理领域中广泛应用的重要概念。它的出现解决了模型在处理长序列或大型输入时的困难,使得模型能够更加关注与当前任务相关的信息,从而提高模型的性能和效果。

本文将详细介绍注意力机制的原理、应用示例以及应用示例。

2、注意力机制的分类

类别描述
全局注意力机制(Global Attention)在计算注意力权重时,考虑输入序列中的所有位置或元素,适用于需要全局信息的任务。
局部注意力机制(Local Attention)在计算注意力权重时,只考虑输入序列中的局部区域或邻近元素,适用于需要关注局部信息的任务。
自注意力机制(Self Attention)在计算注意力权重时,根据输入序列内部的关系来决定每个位置的注意力权重,适用于序列中元素之间存在依赖关系的任务。
Bahdanau 注意力机制全局注意力机制的一种变体,通过引入可学习的对齐模型,对输入序列的每个位置计算注意力权重。
Luong 注意力机制全局注意力机制的另一种变体,通过引入不同的计算方式,对输入序列的每个位置计算注意力权重。
Transformer 注意力机制自注意力机制在Transformer模型中的具体实现,用于对输入序列中的元素进行关联建模和特征提取。

3、注意力机制的核心

注意力机制的核心思想是根据输入的上下文信息来动态地计算每个输入的权重。这个过程可以分为三个关键步骤:计算注意力权重、对输入进行加权和输出。首先,计算注意力权重是通过将输入与模型的当前状态进行比较,从而得到每个输入的注意力分数。这些注意力分数反映了每个输入对当前任务的重要性。对输入进行加权是将每个输入乘以其对应的注意力分数,从而根据其重要性对输入进行加权。最后,将加权后的输入进行求和或者拼接,得到最终的输出。注意力机制的关键之处在于它允许模型在不同的时间步或位置上关注不同的输入,从而捕捉到与任务相关的信息。

🏆YOLOv5/v7 添加注意力机制,30多种模块分析①,SE模块,SK模块

🏆YOLOv5/v7 添加注意力机制,30多种模块分析②,BAM模块,CBAM模块

二、GCN 模块

1、GCN 模块的原理

GCN(Global Context Network)模块是一种用于计算机视觉领域的深度学习模型中的注意力机制。它由 Tsinghua 大学的 Cao et al. 在 2019 年提出,旨在通过给神经网络提供全局上下文信息来提高图像分类、分割、检测等任务的性能。

GCN模块的核心思想是利用自适应的全局平均池化(Adaptive Global Average Pooling),根据每个通道的重要性对其进行加权,将全局范围内的并行卷积特征映射融合成一个全局语义向量,从而增强模型对局部和全局特征的感知能力。

GCN模块的具体实现如下所示:

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

class ContextBlock2d(nn.Module):

    def __init__(self, in_channels, ratio, pooling_type='att', fusion_types=('channel_add', )):
        super(ContextBlock2d, self).__init__()
        assert pooling_type in ['avg', 'att']
        assert isinstance(fusion_types, (list, tuple))
        
        self.in_channels = in_channels
        self.ratio = ratio
        self.planes = int(in_channels // ratio)
        self.pooling_type = pooling_type
        self.fusion_types = fusion_types

        if 'channel_add' in fusion_types:
            self.channel_add_conv = nn.Sequential(
                nn.Conv2d(self.in_channels, self.in_channels, kernel_size=1),
                nn.LayerNorm([self.in_channels, 1, 1])
            )
        
        if 'channel_mul' in fusion_types:
            self.channel_mul_conv = nn.Sequential(
                nn.Conv2d(self.in_channels, self.in_channels, kernel_size=1),
                nn.LayerNorm([self.in_channels, 1, 1]),
                nn.Sigmoid()
            )

        if 'spatial' in fusion_types:
            self.spatial_conv = nn.Sequential(
                nn.Conv2d(self.in_channels, self.planes, kernel_size=1),
                nn.LayerNorm([self.planes, 1, 1])
            )
        
        if pooling_type == 'att':
            self.conv_mask = nn.Conv2d(self.in_channels, 1, kernel_size=1)

    def spatial_pool(self, x):
        batch, channel, height, width = x.size()
        input_x = x
        # [N, C, H * W]
        input_x = input_x.view(batch, channel, height * width)
        # [N, C, 1, 1]
        spatial_output = F.adaptive_avg_pool2d(x, output_size=(1, 1))
        # [N, C, 1, 1]
        spatial_output = self.spatial_conv(spatial_output)
        # [N, C, 1, 1]
        spatial_output = F.relu(spatial_output, inplace=True)
        # [N, C, 1, 1]
        spatial_output = F.interpolate(spatial_output, size=(height, width), mode='nearest')
        # [N, 1, H, W]
        output = F.softmax(spatial_output, dim=1)
        return output

    def forward(self, x):
        batch, channel, height, width = x.size()
        
        # calculate the input tensor for calculating correlation matrix
        input_x = x
        
        if self.pooling_type == 'avg':
            # N x C x 1 x 1
            context_mask = F.adaptive_avg_pool2d(x, output_size=(1, 1))
        elif self.pooling_type == 'att':
            # N x C x 1 x 1
            context_mask = F.relu(self.conv_mask(x))
        
        # N x C x 1 x 1
        context_mask = F.interpolate(context_mask, size=(height, width), mode='nearest')

        # N x C x H x W
        context = x * context_mask
        if 'channel_mul' in self.fusion_types:
            # N x C x 1 x 1
            avg_context = torch.sum(context, dim=(2, 3), keepdim=True) / (height * width)
        # N x C x H x W
        context = context * avg_context
         if 'channel_add' in self.fusion_types:
        # N x C x H x W
        channel_add_term = self.channel_add_conv(context)
        # N x C x H x W
        context = context + channel_add_term
    
        output = context

        if 'spatial' in self.fusion_types:
        # N x 1 x H x W
        spatial_attention = self.spatial_pool(x)
        # N x C x H x W
        output = output * spatial_attention
    
        return output

在这个实现中,ContextBlock2d 类接受输入张量 x,并提供了以下四种融合策略:

  • channel_add:通过一个卷积层和 LayerNorm 实现的通道级别加法操作。

  • channel_mul:通过一个卷积层、Sigmoid 激活函数和 LayerNorm 实现的通道级别乘法操作。

  • spatial:通过自适应平均池化和一组卷积层实现的空间级别的特征融合。

  • att:通过一组卷积层和 Softmax 函数实现的注意力机制。

GCN模块的实现中用到了以下技巧:

  • 自适应平均池化:对于不同大小的输入,使用自适应的池化核大小以得到固定大小的输出。
  • Sigmoid 激活函数:对于 channel_mul 融合策略,使用 Sigmoid 函数将权重限制在 (0, 1) 范围内。
  • LayerNorm:对于 channel_add 和 channel_mul 融合策略,使用 LayerNorm 对特征图进行归一化操作。
  • Softmax 函数:对于 att 融合策略,使用 Softmax 函数计算注意力值。

2、实验结果

在Kinetics验证集上,使用R50作为骨干的Slow-only基线下,GCNet和NLNet的结果如下:

methodTop-1 AccTop-5 Acc#params(M)FLOPs(G)
baseline74.9491.9032.4539.29
+5 NL75.9592.2939.8159.60
+5 SNL75.7692.4436.1339.32
+5 GC75.8592.2534.3039.31
+all GC76.0092.3442.4539.35

3、应用示例

在 YOLOv5 中,GCNet 模块被应用于 CSPDarknet53 特征提取器中,以增强模型的感受野和上下文信息。具体来说,GCNet 模块是通过在通道维度上进行全局上下文编码来实现的。

下面是在 YOLOv5 中使用 GCNet 模块的应用示例:

import torch.nn as nn
from models.common import Conv

class GCNet(nn.Module):
    def __init__(self, in_channels, reduction=16):
        super(GCNet, self).__init__()
        self.conv1x1 = Conv(in_channels, in_channels // reduction, 1)
        self.conv3x3 = Conv(in_channels, in_channels // reduction, 3, padding=1)
        self.global_context = nn.Sequential(
            nn.AdaptiveAvgPool2d(1),
            Conv(in_channels, in_channels // reduction, 1),
            nn.ReLU(inplace=True),
            Conv(in_channels // reduction, in_channels, 1),
            nn.Sigmoid()
        )

    def forward(self, x):
        feat1 = self.conv1x1(x)
        feat2 = self.conv3x3(x)
        gc = self.global_context(feat2)
        feat = feat1 * gc.expand_as(feat1) + feat2
        return feat

class CSPBlock(nn.Module):
    def __init__(self, in_channels, out_channels, num_blocks, darknet_lite=False):
        super(CSPBlock, self).__init__()
        self.conv1 = Conv(in_channels, out_channels, 1)
        self.conv2 = Conv(in_channels, out_channels, 1)
        self.conv3 = Conv(out_channels * 2, out_channels, 1)
        self.conv4 = Conv(out_channels * 2, out_channels * 2, 3, padding=1, groups=out_channels * 2)
        self.conv5 = Conv(out_channels * 2, out_channels, 1)
        self.layers = nn.Sequential(*[
            ResBlock(out_channels, darknet_lite) for _ in range(num_blocks)
        ])
        self.gc_block = nn.Sequential(
            Conv(out_channels, out_channels // 2, 1),
            GCNet(out_channels // 2),
            Conv(out_channels, out_channels, 1)
        )

    def forward(self, x):
        feat1 = self.conv1(x)
        feat2 = self.conv2(x)
        feat2 = self.layers(feat2)
        feat2 = self.gc_block(feat2)
        feat2 = torch.cat([feat2, feat1], dim=1)
        feat2 = self.conv3(feat2)
        feat2 = self.conv4(feat2)
        feat2 = self.conv5(feat2)
        return feat2

在上述代码中,我们首先定义了一个 GCNet 类和一个 CSPBlock 类。GCNet 类是实现全局上下文编码的模块,而 CSPBlock 是 YOLOv5 中的一个基本块。

在 CSPBlock 中,我们使用 GCNet 模块来增强模型的感受野和上下文信息。具体来说,我们将 GCNet 模块放置于 CSPBlock 的末尾,并将其输入特征图和经过卷积操作的另一份特征图进行拼接,最后再通过几个卷积层输出特征图。这样做可以使模型更好地处理不同尺度的物体。

三、DAN模块

1、DAN模块的原理

在这里插入图片描述

DANet(Dual Attention Network)模块是一种新型的注意力机制,广泛应用于计算机视觉领域中的图像分割任务。该模块由位置注意力模块和通道注意力模块组成,能够自适应地对输入图像中的关键区域进行加强,从而提高了图像分割的精度。

在这里插入图片描述

在位置注意力模块中,通过构建一个全局上下文信息嵌入层来获取位置感知初始特征,然后使用空间转换网络(Spatial Transform Network,STN)来自适应地调整这些特征的空间位置,以使其更好地匹配目标对象的形状和大小。进一步地,通过使用一个门控方案,位置注意力模块可以选择性地增强或抑制每个特征通道的激活,以便更好地突出目标对象。

在这里插入图片描述

在通道注意力模块中,首先提取特征图的全局信息,并通过一个门控方案将其与特征图中每个通道的激活相乘,以得到通道加权的响应。接着,在通道特征响应映射(Channel Feature Response Map,CFRM)中,使用类似于SENet(Squeeze and Excitation Network)的方式来生成通道注意力图。最后,将通道注意力图与特征图相乘,以获得增强后的特征响应。

2、实验结果

在PASCAL VOC 2012和Cityscapes等多个数据集上进行的大量实验表明,DANet模块相对于其他主流方法具有更好的图像分割性能。例如,在Cityscapes数据集中,使用DANet模块的分割网络在Mean IoU指标上达到了81.5%,优于其他方法。

在这里插入图片描述

3、应用示例

以下是一个使用DANet模块的应用示例片段,其中包括了DANet模块的定义和在YOLOv5中的应用:

import torch.nn as nn

class DANet(nn.Module):
    def __init__(self, in_channels):
        super(DANet, self).__init__()
        self.conv1 = nn.Conv2d(in_channels, in_channels // 4, kernel_size=1)
        self.conv2 = nn.Conv2d(in_channels, in_channels // 4, kernel_size=1)
        self.conv3 = nn.Conv2d(in_channels, in_channels, kernel_size=1)
        self.softmax = nn.Softmax(dim=-1)
        self.gamma = nn.Parameter(torch.zeros(1))

    def forward(self, x):
        batch_size, channels, height, width = x.size()
        proj_query = self.conv1(x).view(batch_size, -1, width * height).permute(0, 2, 1)
        proj_key = self.conv2(x).view(batch_size, -1, width * height)
        energy = torch.bmm(proj_query, proj_key)
        attention = self.softmax(energy)
        proj_value = self.conv3(x).view(batch_size, -1, width * height)

        out = torch.bmm(proj_value, attention.permute(0, 2, 1))
        out = out.view(batch_size, channels, height, width)
        out = self.gamma * out + x

        return out

# 在YOLOv5中使用DANet模块
class YOLOv5(nn.Module):
    def __init__(self):
        super(YOLOv5, self).__init__()
        # ... 略去其他层的定义
        self.da_conv1 = nn.Sequential(
            nn.Conv2d(256, 512, kernel_size=3, stride=2, padding=1),
            nn.BatchNorm2d(512),
            nn.LeakyReLU(negative_slope=0.1),
            DANet(in_channels=512),  # 添加DANet模块
        )
        # ... 略去其他层的定义

    def forward(self, x):
        # ... 略去前面的网络部分
        x = self.da_conv1(x)
        # ... 略去后续的网络部分
        return x

在上述代码中,我们首先定义了一个DANet模块。在该模块中,将输入张量x通过三个卷积层,得到了三个特征张量:proj_queryproj_key和proj_value。其中,proj_queryproj_key用来计算注意力权重矩阵(attention),而proj_value作为输出特征的候选。

在DANet模块的forward函数中,我们首先计算了proj_query和proj_key之间的点积,得到了能量矩阵energy,然后对energy进行softmax操作,得到了注意力权重矩阵attention。最后,我们将proj_valueattention进行矩阵乘法操作,得到了加权后的输出特征张量out。

在YOLOv5模型中,我们通过在卷积层之间插入DANet模块来提高特征表示能力。例如,在上述代码中,我们定义了一个包含DANet模块的卷积层da_conv1,并在其中使用了DANet模块来增强特征表示能力。

参考论文:

  1. https://arxiv.org/abs/1904.11492
  2. https://arxiv.org/abs/1809.02983

在这里插入图片描述

🏆本文收录于,目标检测YOLO改进指南。

本专栏均为全网独家首发,🚀内附代码,可直接使用,改进的方法均是2023年最近的模型、方法和注意力机制。每一篇都做了实验,并附有实验结果分析,模型对比。

🏆华为OD机试(JAVA)真题(A卷+B卷)

每一题都有详细的答题思路、详细的代码注释、样例测试,订阅后,专栏内的文章都可看,可加入华为OD刷题群(私信即可),发现新题目,随时更新,全天CSDN在线答疑。

🏆哪吒多年工作总结:Java学习路线总结,搬砖工逆袭Java架构师。

🏆往期回顾:

1、YOLOv5/v7 添加注意力机制,30多种模块分析①,SE模块,SK模块

2、YOLOv5/v7 添加注意力机制,30多种模块分析②,BAM模块,CBAM模块

3、YOLOv5结合BiFPN,如何替换YOLOv5的Neck实现更强的检测能力?

4、YOLOv5结合BiFPN:BiFPN网络结构调整,BiFPN训练模型训练技巧

5、YOLOv7升级换代:EfficientNet骨干网络助力更精准目标检测

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

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

相关文章

扫雷——C语言实现

扫雷 文章目录 扫雷实现代码什么是扫雷基本功能实现显示选择菜单定义几个二维数组?确定数组大小初始化数组布置地雷打印展示数组排查地雷记录指定区域周围地雷的个数判断排雷成功排查地雷实现代码 基本功能的实现代码和效果展示 拓展功能简化游戏界面改变字体颜色实…

[创业之路-73] :如何判断一个公司或团队是熵减:凝聚力强、上下一心,还是,熵增:一盘散沙、乌合之众?

目录 前言: 一盘散沙、乌合之众: 凝聚力强、上下一心: 一、股权结构与利益分配 一盘散沙、乌合之众 凝聚力强、上下一心 二、组织架构与岗位职责 一盘散沙、乌合之众 凝聚力强、上下一心 三、战略目标 一盘散沙、乌合之众 凝聚力…

碳排放预测模型 | Python实现基于MLP多层感知机的碳排放预测模型(预测未来发展趋势)

文章目录 效果一览文章概述研究内容环境准备源码设计学习总结参考资料效果一览 ![1](https://img-blog.csdnimg.cn/34c113bde2 文章概述 碳排放预测模型 | Python实现基于MLP多层感知机的碳排放预测模型(预测未来发展趋势) 研究内容 这是数据集的链接:https://github.com/…

【伏羲八卦图】(PythonMatlab实现)

目录 1 与达尔文对话 2 与老子对话 2.1 Python实现 2.2 Matlab实现 1 与达尔文对话 140年前,1858年7月1日,达尔文在英伦岛发表了自己有关自然选择的杰出论文。他提出,生物的发展规律是物竞天择。经过物竞,自然界选择并存留最具…

【CAD】【动态块】CAD设置动态块

文章目录 1 CAD的动态块及应用2 块的相关概念2.1 块的相关命令2.1.1 创建块BLOCK2.1.2 插入块INSERT2.1.3 编辑块BEDIT2.1.4 重命名块RENAME 2.2 CAD组(group)和块(block)的区别2.3 “块”(block)和“写块”…

CMake学习(6): 打印日志信息及宏定义

1. message 打印日志 介绍CMake中的调试打印命令Message, 可以为用户显示一条消息,并在终端上打印显示。假如,我们通过File命令对文件进行了搜索,但是不能确定搜索到的文件就是我们需要的文件。此时,可以利用message将搜索到的变…

PS 套索选区工具(2) 多边形套索工具 磁性套索工具使用技巧

上文PS 套索选区工具(1) 套索工具基础使用带大家了解了套索工具的基本操作 这边 我们右键套索工具 它还有个 多边形套索工具 多边形套索工具是用来画直线的 我们选中它 然后 我们鼠标点击一下 然后 移动到指定位置 从你点的位置开始 到结束的地方 就会形成一个直线的选区 点…

SpringBoot项目编译运行时提示“程序包xxx不存在,找不到符号”

今天想要在虚拟机上部署自己的前后端项目,在系统打包时碰到了这个问题,记录一下 1. 项目结构,启动程序再pub-oa-web项目中 2、编译异常报错分析 编译中报错是在其他子项目中都配置了如下代码,导致项目每个子项目都是可执行的&am…

第五章数组

我们之前学习,如果我们存储一个值(或者说只有一个值在改变)那么我们使用变量; 但是如果我们需要存储多个值,因该怎么办呢! 1 数组 1.1 概念 数组Array,标志是[ ] ,用于储存多个相同类型数据的集…

JDBC 连接池 详解(通俗易懂)

目录 一、前言 二、传统连接方式的弊端分析 1.局限性 : 2.几个弊端 : 三、数据库连接池 1.基本介绍 : 2.示意图如下 : 3.连接池种类 : 四、C3P0连接池 0.准备工作 : 1.方式一 —— 程序中指定相关参数 : 2.方式二 —— 通过配置文件 : 五、Druid(德鲁伊…

Vue中如何进行路由懒加载

Vue中如何进行路由懒加载 路由懒加载是一种优化Vue应用程序性能的技术。它可以延迟加载路由组件,从而减少应用程序的初始加载时间,提高应用程序的性能。本文将介绍Vue中如何进行路由懒加载,包括使用Vue异步组件和Webpack代码分割。 使用Vue异…

Docker本地私有仓库、harbor私有仓库部署与管理

目录 一、本地私有仓库1、本地私有仓库简介2、搭建本地私有仓库3、容器重启策略介绍 二、harbor私有仓库部署与管理1、什么是harbor2、Harbor的特性3、Harbor的构成4、harbor部署及配置①部署docker-compose②部署Harbor服务③登录创建项目④登录仓库并上传镜像 5、客户端测试①…

Python 基于 Django 的学生成绩管理系统,可视化界面

1简介 对于学生成绩管理系统,充分运用现代化的信息技术手段,对于学生成绩信息管理发展的趋势就是信息化,信息化时代下的信息管理,需要深化信息管理体制与手段的改革,充分运用信息化手段来全方位的进行学生成绩管理系统…

Vue中如何进行数据响应式更新?

Vue中如何进行数据响应式更新? Vue是一款流行的JavaScript框架,它提供了数据响应式更新的能力,可以让我们轻松地更新数据,并自动更新视图。本文将介绍Vue中如何进行数据响应式更新,包括使用Vue的响应式系统、使用计算…

nacos-sdk-rust binding for Python

广告时间 nacos-sdk-rust-binding-py : nacos-sdk-rust binding for Python with PyO3. Tip: nacos-sdk-python 仓库暂未提供 2.x gRPC 交互模式,为了能升级它,故而通过 ffi 方式调用 nacos-sdk-rust py 包 -> https://pypi.org/project/nacos-sdk-r…

【Java代码的运行过程】 ——每天一点小知识

💧 J a v a 代码的运行过程 \color{#FF1493}{Java代码的运行过程} Java代码的运行过程💧 🌷 仰望天空,妳我亦是行人.✨ 🦄 个人主页——微风撞见云的博客🎐 🐳 《数据结构与算法》专栏…

Vue中如何处理日期与时间

Vue中如何处理日期与时间 日期和时间处理是Web应用中常见的需求,Vue作为一款流行的前端框架,提供了很多方便的工具和库,以满足不同业务场景下的需求。本文将介绍如何在Vue中处理日期和时间,包括使用原生JavaScript、Moment.js和D…

useMemo和useCallback

上述代码中,useSate 用于定义了三个状态:supNum、oppNum、x。在组件函数中,我们根据这些状态来计算投票的支持率,并将其渲染到视图中。但是,每次状态变化时,投票支持率的计算都会重新执行,即使其…

c++深搜1-迷宫类问题

目录 1.问题引入 2.知识讲解 3.例题解析 【例题1】迷宫的第一条出路。 题目描述 输入格式 输出格式 样例 输入数据#1 输出数据#1 输入数据#2 输出数据#2 【例题2】迷宫的所有路径。 【例题3】走出迷宫的最少步数。 1.问题引入 拓拓小时候玩迷宫游戏时&#xff…

小酷智慧地图3D导公众号小程序 v1.0.50+前端

🎈 限时活动领体验会员:可下载程序网创项目短视频素材 🎈 🎉 有需要的朋友记得关赞评,阅读文章底部来交流!!! 🎉 ✨ 源码介绍 小酷智慧地图3D导览系统适用于城市或乡镇全…