YOLOv5改进 | 添加SE注意力机制 + 更换NMS之EIoU-NMS

2023-11-26 16:52

本文主要是介绍YOLOv5改进 | 添加SE注意力机制 + 更换NMS之EIoU-NMS,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言:Hello大家好,我是小哥谈。为提高算法模型在不同环境下的目标识别准确率,提出一种基于改进 YOLOv5 深度学习的识别方法(SE-NMS-YOLOv5),该方法融合SE(Squeeze-and-Excitation)注意力机制模块和改进非极大值抑制对数据集进行训练和测试。研究表明,SE-NMS-YOLOv5 目标识别方法有效地解决了不同场景下的检测准确率低的问题,提升了检测和识别的整体效果。🌈 

     目录

🚀1.基础概念

🚀2.添加位置

🚀3.添加步骤

🚀4.改进方法

💥💥步骤1:common.py文件修改

💥💥步骤2:yolo.py文件修改

💥💥步骤3:创建自定义yaml文件

💥💥步骤4:修改自定义yaml文件

💥💥步骤5:验证是否加入成功

💥💥步骤6:更改NMS

💥💥步骤7:修改默认参数

🚀1.基础概念

SE注意力机制:

SENet是由Momenta和牛津大学的胡杰等人提出的一种新的网络结构,目标是通过显式的建模卷积特征通道之间的相互依赖关系来提高网络的表示能力。在2017年最后一届ImageNet 比赛classification任务上获得第一名。SENet网络的创新点在于关注channel之间的关系,希望模型可以自动学习到不同channel特征的重要程度。为此,SENet提出了Squeeze-and-Excitation (SE)模块

SE模块首先对卷积得到的特征图进行Squeeze操作,得到channel级的全局特征,然后对全局特征进行Excitation操作,学习各个channel间的关系,也得到不同channel的权重,最后乘以原来的特征图得到最终特征。本质上,SE模块是在channel维度上做attention或者gating操作,这种注意力机制让模型可以更加关注信息量最大的channel特征,而抑制那些不重要的channel特征。另外一点是SE模块是通用的,这意味着其可以嵌入到现有的网络架构中。

SENet结构图如下图所示:

🍀步骤1:squeeze操作,将各通道的全局空间特征作为该通道的表示,形成一个通道描述符;

🍀步骤2:excitation操作,学习对各通道的依赖程度,并根据依赖程度的不同对特征图进行调整,调整后的特征图就是SE block的输出。

EIoU-NMS:

EIoU-NMS是一种新的非极大值抑制算法,它是YOLOv5中提出的一种改进算法。EIoU-NMS是在DIoU-NMS的基础上进行改进的。EIoU-NMS的主要思想是将检测框之间的距离嵌入到嵌入空间中,然后计算嵌入空间中的距离来代替传统的IoU计算。这种方法可以更好地处理检测框之间的重叠情况,从而提高目标检测的准确性。


🚀2.添加位置

本文的改进是基于YOLOv5-6.0版本,关于其网络结构具体如下图所示:

为了使网络能够更好地拟合通道之间的相关性,增加更重要的通道特征的权重,引入了SE模块,注意力机制是一种神经网络资源分配方案,用于将计算资源分配给更重要的任务,

本文的改进是将SE注意力机制添加在主干网络中,具体添加位置如下图所示:

关于NMS的改进,直接体现在代码中,所以,本节课改进后的网络结构图具体如下图所示:


🚀3.添加步骤

针对本文的改进,具体步骤如下所示:👇

步骤1:common.py文件修改

步骤2:yolo.py文件修改

步骤3:创建自定义yaml文件

步骤4:修改自定义yaml文件

步骤5:验证是否加入成功

步骤6:更改NMS

步骤7:修改默认参数


🚀4.改进方法

💥💥步骤1:common.py文件修改

common.py中添加SE注意力机制模块,所要添加模块的代码如下所示,将其复制粘贴到common.py文件末尾的位置。

SE注意力机制模块代码:

# SE
class SE(nn.Module):def __init__(self, c1, c2, ratio=16):super(SE, self).__init__()#c*1*1self.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:yolo.py文件修改

首先在yolo.py文件中找到parse_model函数这一行,加入SE。具体如下图所示:

💥💥步骤3:创建自定义yaml文件

models文件夹中复制yolov5s.yaml,粘贴并重命名为yolov5s_SE_ENMS.yaml具体如下图所示:

💥💥步骤4:修改自定义yaml文件

本步骤是修改yolov5s_SE_ENMS.yaml,根据改进后的网络结构图进行修改。

由下面这张图可知,当添加SE注意力机制之后,后面的层数会发生相应的变化,需要修改相关参数。

备注:层数从0开始计算,比如第0层、第1层、第2层......🍉 🍓 🍑 🍈 🍌 🍐  

综上所述,修改后的完整yaml文件如下所示:

# YOLOv5 🚀 by Ultralytics, GPL-3.0 license# Parameters
nc: 80  # number of classes
depth_multiple: 0.33  # model depth multiple
width_multiple: 0.50  # layer channel multiple
anchors:- [10,13, 16,30, 33,23]  # P3/8- [30,61, 62,45, 59,119]  # P4/16- [116,90, 156,198, 373,326]  # P5/32# YOLOv5 v6.0 backbone
backbone:# [from, number, module, args][[-1, 1, Conv, [64, 6, 2, 2]],  # 0-P1/2[-1, 1, Conv, [128, 3, 2]],  # 1-P2/4[-1, 3, C3, [128]],[-1, 1, Conv, [256, 3, 2]],  # 3-P3/8[-1, 6, C3, [256]],[-1, 1, Conv, [512, 3, 2]],  # 5-P4/16[-1, 9, C3, [512]],[-1, 1, Conv, [1024, 3, 2]],  # 7-P5/32[-1, 3, C3, [1024]],[-1, 1, SE, [1024]],[-1, 1, SPPF, [1024, 5]],  # 9]# YOLOv5 v6.0 head
head:[[-1, 1, Conv, [512, 1, 1]],[-1, 1, nn.Upsample, [None, 2, 'nearest']],[[-1, 6], 1, Concat, [1]],  # cat backbone P4[-1, 3, C3, [512, False]],  # 13[-1, 1, Conv, [256, 1, 1]],[-1, 1, nn.Upsample, [None, 2, 'nearest']],[[-1, 4], 1, Concat, [1]],  # cat backbone P3[-1, 3, C3, [256, False]],  # 17 (P3/8-small)[-1, 1, Conv, [256, 3, 2]],[[-1, 15], 1, Concat, [1]],  # cat head P4[-1, 3, C3, [512, False]],  # 20 (P4/16-medium)[-1, 1, Conv, [512, 3, 2]],[[-1, 11], 1, Concat, [1]],  # cat head P5[-1, 3, C3, [1024, False]],  # 23 (P5/32-large)[[18, 21, 24], 1, Detect, [nc, anchors]],  # Detect(P3, P4, P5)]

💥💥步骤5:验证是否加入成功

yolo.py文件里,将配置改为我们刚才自定义的yolov5s_SE_ENMS.yaml

修改1,位置位于yolo.py文件165行左右,具体如图所示:

修改2,位置位于yolo.py文件363行左右,具体如下图所示:

配置完毕之后,点击“运行”,结果如下图所示:

由运行结果可知,与我们前面更改后的网络结构图相一致,证明添加成功了!✅

💥💥步骤6:更改NMS

本文需要更改NMS为EIoU-NMS

将下面非极大值抑制NMS核心代码复制粘贴到 utils / general.py 的末尾位置。当复制粘贴后,会有报错提示,具体如下图所示:

# NMS实现代码
def NMS(boxes, scores, iou_thres, GIoU=False, DIoU=True, CIoU=False, EIoU=False, SIoU=False):B = torch.argsort(scores, dim=-1, descending=True)keep = []while B.numel() > 0:index = B[0]keep.append(index)if B.numel() == 1: breakiou = bbox_iou(boxes[index, :], boxes[B[1:], :], GIoU=GIoU, DIoU=DIoU, CIoU=CIoU, EIoU=EIoU, SIoU=SIoU)inds = torch.nonzero(iou <= iou_thres).reshape(-1)B = B[inds + 1]return torch.tensor(keep)def soft_nms(bboxes, scores, iou_thresh=0.5, sigma=0.5, score_threshold=0.25):order = scores.argsort(descending=True).to(bboxes.device)keep = []while order.numel() > 1:if order.numel() == 1:keep.append(order[0])breakelse:i = order[0]keep.append(i)iou = bbox_iou(bboxes[i], bboxes[order[1:]]).squeeze()idx = (iou > iou_thresh).nonzero().squeeze()if idx.numel() > 0:iou = iou[idx]new_scores = torch.exp(-torch.pow(iou, 2) / sigma)scores[order[idx + 1]] *= new_scoresnew_order = (scores[order[1:]] > score_threshold).nonzero().squeeze()if new_order.numel() == 0:breakelse:max_score_index = torch.argmax(scores[order[new_order + 1]])if max_score_index != 0:new_order[[0, max_score_index],] = new_order[[max_score_index, 0],]order = order[new_order + 1]return torch.LongTensor(keep)

然后,解决报错提示,需要导入下列代码:

from utils.metrics import box_iou, fitness, bbox_iou

最后,在utils / general.py中找到non_max_suppression函数(大约885行左右),将non_max_suppression函数中的代码:

替换为:

 i = NMS(boxes, scores, iou_thres, class_nms='EIoU')

💥💥步骤7:修改默认参数

train.py文件中找到parse_opt函数,然后将第二行 '--cfg的default改为 'models/yolov5s_SE_ENMS.yaml',然后就可以开始进行训练了。🎈🎈🎈 

结束语:关于更多YOLOv5学习知识,可参考专栏:《YOLOv5:从入门到实战》🍉 🍓 🍑 🍈 🍌 🍐

这篇关于YOLOv5改进 | 添加SE注意力机制 + 更换NMS之EIoU-NMS的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/426043

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

基于 YOLOv5 的积水检测系统:打造高效智能的智慧城市应用

在城市发展中,积水问题日益严重,特别是在大雨过后,积水往往会影响交通甚至威胁人们的安全。通过现代计算机视觉技术,我们能够智能化地检测和识别积水区域,减少潜在危险。本文将介绍如何使用 YOLOv5 和 PyQt5 搭建一个积水检测系统,结合深度学习和直观的图形界面,为用户提供高效的解决方案。 源码地址: PyQt5+YoloV5 实现积水检测系统 预览: 项目背景

【编程底层思考】垃圾收集机制,GC算法,垃圾收集器类型概述

Java的垃圾收集(Garbage Collection,GC)机制是Java语言的一大特色,它负责自动管理内存的回收,释放不再使用的对象所占用的内存。以下是对Java垃圾收集机制的详细介绍: 一、垃圾收集机制概述: 对象存活判断:垃圾收集器定期检查堆内存中的对象,判断哪些对象是“垃圾”,即不再被任何引用链直接或间接引用的对象。内存回收:将判断为垃圾的对象占用的内存进行回收,以便重新使用。

【Tools】大模型中的自注意力机制

摇来摇去摇碎点点的金黄 伸手牵来一片梦的霞光 南方的小巷推开多情的门窗 年轻和我们歌唱 摇来摇去摇着温柔的阳光 轻轻托起一件梦的衣裳 古老的都市每天都改变模样                      🎵 方芳《摇太阳》 自注意力机制(Self-Attention)是一种在Transformer等大模型中经常使用的注意力机制。该机制通过对输入序列中的每个元素计算与其他元素之间的相似性,

如何通俗理解注意力机制?

1、注意力机制(Attention Mechanism)是机器学习和深度学习中一种模拟人类注意力的方法,用于提高模型在处理大量信息时的效率和效果。通俗地理解,它就像是在一堆信息中找到最重要的部分,把注意力集中在这些关键点上,从而更好地完成任务。以下是几个简单的比喻来帮助理解注意力机制: 2、寻找重点:想象一下,你在阅读一篇文章的时候,有些段落特别重要,你会特别注意这些段落,反复阅读,而对其他部分

JavaSE——封装、继承和多态

1. 封装 1.1 概念      面向对象程序三大特性:封装、继承、多态 。而类和对象阶段,主要研究的就是封装特性。何为封装呢?简单来说就是套壳屏蔽细节 。     比如:对于电脑这样一个复杂的设备,提供给用户的就只是:开关机、通过键盘输入,显示器, USB 插孔等,让用户来和计算机进行交互,完成日常事务。但实际上:电脑真正工作的却是CPU 、显卡、内存等一些硬件元件。

【Tools】大模型中的注意力机制

摇来摇去摇碎点点的金黄 伸手牵来一片梦的霞光 南方的小巷推开多情的门窗 年轻和我们歌唱 摇来摇去摇着温柔的阳光 轻轻托起一件梦的衣裳 古老的都市每天都改变模样                      🎵 方芳《摇太阳》 在大模型中,注意力机制是一种重要的技术,它被广泛应用于自然语言处理领域,特别是在机器翻译和语言模型中。 注意力机制的基本思想是通过计算输入序列中各个位置的权重,以确

JavaSE-易错题集-002

1. 下面有关java基本类型的默认值和取值范围,说法错误的是? A 字节型的类型默认值是0,取值范围是-2^7—2^7-1 B boolean类型默认值是false,取值范围是true\false C 字符型类型默认是0,取值范围是-2^15 —2^15-1 D long类型默认是0,取值范围是-2^63—2^63-1 答案:C 题解:注意字符型(char) char 占16位,

FreeRTOS内部机制学习03(事件组内部机制)

文章目录 事件组使用的场景事件组的核心以及Set事件API做的事情事件组的特殊之处事件组为什么不关闭中断xEventGroupSetBitsFromISR内部是怎么做的? 事件组使用的场景 学校组织秋游,组长在等待: 张三:我到了 李四:我到了 王五:我到了 组长说:好,大家都到齐了,出发! 秋游回来第二天就要提交一篇心得报告,组长在焦急等待:张三、李四、王五谁先写好就交谁的