YOLOv5算法改进(15)— 更换Neck之AFPN

news2025/1/23 7:56:07

前言:Hello大家好,我是小哥谈。在YOLOv5中添加AFPN(Adaptive Feature Pyramid Network)可以提高目标检测的准确性。AFPN是一种用于目标检测任务的功能增强模块,它能够自适应地融合来自不同层级的特征图,以提供更好的目标定位和尺度适应能力。🌈  

前期回顾:

          YOLOv5算法改进(1)— 如何去改进YOLOv5算法

          YOLOv5算法改进(2)— 添加SE注意力机制

          YOLOv5算法改进(3)— 添加CBAM注意力机制

          YOLOv5算法改进(4)— 添加CA注意力机制

          YOLOv5算法改进(5)— 添加ECA注意力机制

          YOLOv5算法改进(6)— 添加SOCA注意力机制

          YOLOv5算法改进(7)— 添加SimAM注意力机制

          YOLOv5算法改进(8)— 替换主干网络之MobileNetV3

          YOLOv5算法改进(9)— 替换主干网络之ShuffleNetV2

          YOLOv5算法改进(10)— 替换主干网络之GhostNet

          YOLOv5算法改进(11)— 替换主干网络之EfficientNetv2

          YOLOv5算法改进(12)— 替换主干网络之Swin Transformer

          YOLOv5算法改进(13)— 替换主干网络之PP-LCNet

          YOLOv5算法改进(14)— 更换Neck之BiFPN

          目录

🚀1.论文

🚀2.AFPN网络架构及添加步骤

🚀3.更换AFPN的方法

💥💥步骤1:在common.py中添加AFPN模块

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

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

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

🚀1.论文

多尺度特征提取的一种常见策略是采用经典的自上而下和自下而上的特征金字塔网络。然而,这些方法遭受特征信息的丢失或退化,削弱了非相邻 Level 的融合效果。本文提出了一种渐近特征金字塔网络(AFPN)来支持非相邻层的直接交互。AFPN是通过融合两个相邻的Low-Level特征来启动的,并渐进地将High-Level特征纳入融合过程。通过这种方式,可以避免非相邻 Level 之间的较大语义差距。考虑到在每个空间位置的特征融合过程中可能出现多目标信息冲突,进一步利用自适应空间融合操作来缓解这些不一致。🌟

本文将所提出的AFPN纳入两阶段和一阶段目标检测框架,并使用MS-COCO 2017验证和测试数据集进行评估。实验评估表明,与其他最先进的特征金字塔网络相比,论文作者的方法获得了更具竞争力的结果。🌟

论文主要贡献:♨️♨️♨️

1.作者引入了一种渐进特征金字塔网络(AFPN),它有助于跨非相邻 Level 的直接特征融合,从而防止特征信息在传输和交互过程中的丢失或退化。

2.为了抑制不同层次特征之间的信息矛盾,作者在多层次特征融合过程中引入了自适应空间融合操作。

3.在MS COCO 2017验证和测试数据集上的大量实验表明,与其他特征金字塔网络相比,作者的方法表现出优越的计算效率,同时获得了更具竞争力的结果。

论文题目:《AFPN: Asymptotic Feature Pyramid Network for Object Detection》

论文地址:  https://arxiv.org/abs/2306.15988v1

代码实现:  GitHub - gyyang23/AFPN


🚀2.AFPN网络架构及添加步骤

论文所提出的AFPN架构如下图所示。在Backbone网络自下而上的特征提取过程中,AFPN渐进地集成了Low-LevelHigh-Level顶级特征。具体来说,AFPN最初融合了Low-Level特征,然后融合了深层特征,最后融合了最High-Level的特征,即最抽象的特征。非相邻层次特征之间的语义差距大于相邻层次特征间的语义差距,尤其是底部和顶部特征,这直接导致了非相邻层次特征的融合效果较差由于AFPN的架构是渐进的,这将使不同 Level 特征的语义信息在渐进融合过程中更加接近,从而缓解上述问题📚

为了对齐维度并为特征融合做准备,作者使用1×1卷积双线性插值方法对特征进行上采样。另一方面,作者根据所需的下采样率使用不同的卷积核Stride来执行下采样。例如,作者应用Stride为2的2×2卷积来实现2次下采样,应用Stride为4的4×4卷积来实现4次下采样以及应用Stride为8的8×8卷积来实现8倍下采样。在特征融合之后,作者使用4个残差单元继续学习特征,这些残差单元类似于ResNet。每个残差单元包括2个3×3卷积。由于YOLO中只使用了3个 Level 的特征,因此没有8次上采样和8次下采样。🌴

在YOLOv5中添加AFPN(Adaptive Feature Pyramid Network)可以提高目标检测的准确性。AFPN是一种用于目标检测任务的功能增强模块,它能够自适应地融合来自不同层级的特征图,以提供更好的目标定位和尺度适应能力。

要在YOLOv5中添加AFPN,可以按照以下步骤进行操作:👇

🍀(1)首先,在YOLOv5的模型架构中引入AFPN模块。为此,您可以修改模型的backbone部分。通常,YOLOv5使用CSPDarknet53作为backbone,您可以在该部分添加AFPN。

🍀(2)在引入AFPN之后,您需要确定要从backbone中选择哪些特征图进行融合。一般来说,可以选择较高层级和较低层级的特征图进行融合,以获得更好的目标定位和尺度适应。

🍀(3)接下来,您需要实现AFPN模块的具体结构。AFPN通常由上采样和特征图融合两部分组成。上采样部分可以使用双线性插值或者转置卷积等方法进行实现。特征图融合部分可以使用逐元素相加或者卷积等方法进行实现。

🍀(4)最后,在YOLOv5的检测头部之前,将AFPN模块的输出与backbone的输出进行融合。这样可以将更多信息引入到检测头部,提升目标检测的准确性。

说明:♨️♨️♨️

请注意,在实现AFPN时,您需要根据YOLOv5的具体版本和代码结构进行适当的修改。这里提供的步骤仅供参考,具体实现可能因版本而异。建议您查阅相关文档和代码,以了解如何在YOLOv5中添加AFPN的详细实现方法。另外,需要注意的是,添加AFPN可能会增加模型的计算和内存开销,因此在使用时需要根据实际情况进行权衡和优化。


🚀3.更换AFPN的方法

💥💥步骤1:在common.py中添加AFPN模块

 
class Upsample(nn.Module):
    """Applies convolution followed by upsampling."""
# ---1.渐进架构部分(融合前的准备)--- #
    def __init__(self, c1, c2, scale_factor=2):
        super().__init__()
        # self.cv1 = Conv(c1, c2, 1)
        # self.upsample = nn.Upsample(scale_factor=scale_factor, mode='nearest')  # or model='bilinear' non-deterministic
        if scale_factor == 2:
            self.cv1 = nn.ConvTranspose2d(c1, c2, 2, 2, 0, bias=True)  # 如果下采样率为2,就用Stride为2的2×2卷积来实现2次下采样
        elif scale_factor == 4:
            self.cv1 = nn.ConvTranspose2d(c1, c2, 4, 4, 0, bias=True)  # 如果下采样率为4,就用Stride为4的4×4卷积来实现4次下采样
 
    def forward(self, x):
        # return self.upsample(self.cv1(x))
        return self.cv1(x)
 
# ---2.自适应空间融合(ASFF)--- #
class ASFF2(nn.Module):
    """ASFF2 module for YOLO AFPN head https://arxiv.org/abs/2306.15988"""
 
    def __init__(self, c1, c2, level=0):
        super().__init__()
        c1_l, c1_h = c1[0], c1[1]
        self.level = level
        self.dim = c1_l, c1_h
        self.inter_dim = self.dim[self.level]
        compress_c = 8
       
#如果是第0层
        if level == 0:
# self.stride_level_1调整level-1出来的特征图,通道调整为和level-0出来的特征图一样大小
            self.stride_level_1 = Upsample(c1_h, self.inter_dim)
#如果是第1层
        if level == 1:
# self.stride_level_0通道调整为和level-1出来的特征图一样大小
            self.stride_level_0 = Conv(c1_l, self.inter_dim, 2, 2, 0)  # stride=2 下采样为2倍
 
 
# 两个卷积为了学习权重
        self.weight_level_0 = Conv(self.inter_dim, compress_c, 1, 1)
        self.weight_level_1 = Conv(self.inter_dim, compress_c, 1, 1)
# 用于调整拼接后的两个权重的通道
        self.weights_levels = nn.Conv2d(compress_c * 2, 2, kernel_size=1, stride=1, padding=0)
        self.conv = Conv(self.inter_dim, self.inter_dim, 3, 1)
 
    def forward(self, x):
        x_level_0, x_level_1 = x[0], x[1]
 
# 如果在第0层
# level-0出来的特征图保持不变
# 调整level-1的特征图,使得其channel、width、height与level-0一致
        if self.level == 0:
            level_0_resized = x_level_0
            level_1_resized = self.stride_level_1(x_level_1)
# 如果在第1层,同上
        elif self.level == 1:
            level_0_resized = self.stride_level_0(x_level_0)
            level_1_resized = x_level_1
 
# 将N*C*H*W的level-0特征图卷积得到权重,权重level_0_weight_v:N*256*H*W
        level_0_weight_v = self.weight_level_0(level_0_resized)
        level_1_weight_v = self.weight_level_1(level_1_resized)
 
# 将各个权重矩阵按照通道拼接
# levels_weight_v:N*3C*H*W
        levels_weight_v = torch.cat((level_0_weight_v, level_1_weight_v), 1)
 
# 将拼接后的矩阵调整,每个通道对应着不同的level_0_resized,level_1_resized的权重
        levels_weight = self.weights_levels(levels_weight_v)
 
# 在通道维度,对权重做归一化,也就是对于二通道tmp:tmp[0][0]+tmp[1][0]=1
        levels_weight = F.softmax(levels_weight, dim=1)
 
# 将levels_weight各个通道分别乘level_0_resized level_1_resized 
# 点乘用到了广播机制
        fused_out_reduced = level_0_resized * levels_weight[:, 0:1] + level_1_resized * levels_weight[:, 1:2]
        return self.conv(fused_out_reduced)
 
# ASFF3的运算流程同上
class ASFF3(nn.Module):
    """ASFF3 module for YOLO AFPN head https://arxiv.org/abs/2306.15988"""
 
    def __init__(self, c1, c2, level=0):
        super().__init__()
        c1_l, c1_m, c1_h = c1[0], c1[1], c1[2]
        self.level = level
        self.dim = c1_l, c1_m, c1_h
        self.inter_dim = self.dim[self.level]
        compress_c = 8
 
        if level == 0:
            self.stride_level_1 = Upsample(c1_m, self.inter_dim)
            self.stride_level_2 = Upsample(c1_h, self.inter_dim, scale_factor=4)
 
        if level == 1:
            self.stride_level_0 = Conv(c1_l, self.inter_dim, 2, 2, 0)  # downsample 2x
            self.stride_level_2 = Upsample(c1_h, self.inter_dim)
 
        if level == 2:
            self.stride_level_0 = Conv(c1_l, self.inter_dim, 4, 4, 0)  # downsample 4x
            self.stride_level_1 = Conv(c1_m, self.inter_dim, 2, 2, 0)  # downsample 2x
 
        self.weight_level_0 = Conv(self.inter_dim, compress_c, 1, 1)
        self.weight_level_1 = Conv(self.inter_dim, compress_c, 1, 1)
        self.weight_level_2 = Conv(self.inter_dim, compress_c, 1, 1)
 
        self.weights_levels = nn.Conv2d(compress_c * 3, 3, kernel_size=1, stride=1, padding=0)
        self.conv = Conv(self.inter_dim, self.inter_dim, 3, 1)
 
    def forward(self, x):
        x_level_0, x_level_1, x_level_2 = x[0], x[1], x[2]
 
        if self.level == 0:
            level_0_resized = x_level_0
            level_1_resized = self.stride_level_1(x_level_1)
            level_2_resized = self.stride_level_2(x_level_2)
 
        elif self.level == 1:
            level_0_resized = self.stride_level_0(x_level_0)
            level_1_resized = x_level_1
            level_2_resized = self.stride_level_2(x_level_2)
 
        elif self.level == 2:
            level_0_resized = self.stride_level_0(x_level_0)
            level_1_resized = self.stride_level_1(x_level_1)
            level_2_resized = x_level_2
 
        level_0_weight_v = self.weight_level_0(level_0_resized)
        level_1_weight_v = self.weight_level_1(level_1_resized)
        level_2_weight_v = self.weight_level_2(level_2_resized)
 
        levels_weight_v = torch.cat((level_0_weight_v, level_1_weight_v, level_2_weight_v), 1)
        w = self.weights_levels(levels_weight_v)
        w = F.softmax(w, dim=1)
 
        fused_out_reduced = level_0_resized * w[:, :1] + level_1_resized * w[:, 1:2] + level_2_resized * w[:, 2:]
        return self.conv(fused_out_reduced)
 

具体如下图所示:

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

修改yolo.py文件,在 parse_model函数中找到 elif m is Concat: 语句,在其后面加上下列代码:👇

        elif m is ASFF2:
            c1, c2 = [ch[f[0]], ch[f[1]]], args[0]
            c2 = make_divisible(c2 * gw, 8)
            args = [c1, c2, *args[1:]]
        elif m is ASFF3:
            c1, c2 = [ch[f[0]], ch[f[1]], ch[f[2]]], args[0]
            c2 = make_divisible(c2 * gw, 8)
            args = [c1, c2, *args[1:]]

具体如下图所示:

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

models文件夹中复制yolov5s.yaml,粘贴并重命名为yolov5s_AFPN.yaml

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.1 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, 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, 6, C3, [512]],
   [-1, 1, Conv, [1024, 3, 2]],  # 7-P5/32
   [-1, 3, C3, [1024]],
   [-1, 1, SPPF, [1024, 5]],
  ]  # 9
 
# YOLOv5 v6.1 head
head:
  [[4, 1, Conv, [128, 1, 1]], # 10 downsample backbone P3
   [6, 1, Conv, [256, 1, 1]], # 11 downsample backbone P4
 
   [[10, 11], 1, ASFF2, [128, 0]], # 12
   [[10, 11], 1, ASFF2, [256, 1]], # 13
 
   [-2, 1, C3, [128, False]], # 14
   [-2, 1, C3, [256, False]], # 15
 
   [9, 1, Conv, [512, 1, 1]], # 16 downsample backbone P5
 
   [[14, 15, 16], 1, ASFF3, [128, 0]], # 17
   [[14, 15, 16], 1, ASFF3, [256, 1]], # 18
   [[14, 15, 16], 1, ASFF3, [512, 2]], # 19
 
   [17, 1, C3, [256, False]],  # 20 (P3/8-small)
   [18, 1, C3, [512, False]],  # 21 (P4/16-medium)
   [19, 1, C3, [1024, False]],  # 22 (P5/32-large)
  [[20, 21, 22], 1, Detect, [nc, anchors]],  # Detect(P3, P4, P5)
  ]
 

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

yolo.py文件里,配置我们刚才自定义的yolov5s_AFPN.yaml

然后运行yolo.py我运行时出现报错信息。 ❌

报错信息如图所示:

解决方法:

需要导入包

import torch.nn.functional as F

导入后,则报错消失。然后继续运行yolo.py,得到运行结果。

这样就算添加成功了。🎉🎉🎉  

我没有试验过这种方法效果如何,有实验过的小伙伴可以把效果在评论区打出来!我们共同讨论下!🍉 🍓 🍑 🍈 🍌 🍐 


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

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

相关文章

scss文件自动导入

正常使用scss文件,需要先导入再使用内部变量,自动导入可以免除手动导入的步骤,直接使用内部的变量。 步骤:装包 -> scss文件 -> 配置 1. 装包 npm i sass -D // -D 仅在开发环境中依赖 2. 新增一个 scss文件&#xff…

设计模式之单列模式

单列模式是一种经典的设计模式,在校招中最乐意考的设计模式之一~ 设计模式就是软件开发中的棋谱,大佬们针对一些常见的场景,总结出来的代码的编写套路,按照套路来写,不说你写的多好,至少不会太差~ 在校招中…

Java的环境配置

目录 window系统安装java下载JDK配置环境变量JAVA_HOME 设置PATH设置CLASSPATH 设置测试JDK是否安装成功 Linux,UNIX,Solaris,FreeBSD环境变量设置流行JAVA开发工具使用 Eclipse 运行第一个 Java 程序 window系统安装java 下载JDK 首先我们…

OLED透明屏触控:引领未来科技革命的创新力量

OLED透明屏触控技术作为一项颠覆性的创新,正在引领新一轮科技革命。它将OLED显示技术与触摸技术相结合,实现了透明度和触控功能的完美融合。 在这篇文章中,尼伽将通过引用最新的市场数据、报告和行业动态,详细介绍OLED透明屏触控…

Java知识总结(持续更新)

一、JDK、JRE、JVM三者之间的关系? 1. **JDK (Java Development Kit)**: JDK 是 Java 开发工具包,它包含了用于开发 Java 应用程序的所有必要工具和库。这包括 Java 编译器(javac)、Java 核心类库、开发工具&#x…

【Day-33慢就是快】代码随想录-二叉树-二叉搜索树中的搜索

给定二叉搜索树(BST)的根节点和一个值。 你需要在BST中找到节点值等于给定值的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 NULL。 思路 什么是二叉搜索树: 二叉搜索树是一个有序树: 若它的左子树不空&a…

【NVIDIA CUDA】2023 CUDA夏令营编程模型(三)

博主未授权任何人或组织机构转载博主任何原创文章,感谢各位对原创的支持! 博主链接 本人就职于国际知名终端厂商,负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作,目前牵头6G算力网络技术标准研究。 博客…

yolov7增加mobileone

代码地址:GitHub - apple/ml-mobileone: This repository contains the official implementation of the research paper, "An Improved One millisecond Mobile Backbone". 论文地址:https://arxiv.org/abs/2206.04040 MobileOne出自Apple&am…

IP403参考资料和引脚图

特性 支持串并联双LED驱动和GPIO控制器 串并联LED驱动器 支持一个串行输入56个并行led或三串行输入16个并行ed为每个串行输入。 支持4-16mA可调电流源驱动,无限流电阻 支持LED熄灭模式,实现省电功能 提供级联能力,提供更多的LED驱动输出…

go语言基础---8

Http请求报文格式分析 package mainimport ("fmt""net" )func main() {//监听listener, err : net.Listen("tcp", ":8000")if err ! nil {fmt.Println("listener err", err)return}defer listener.Close()//阻塞等待用户的…

SAM + YOLOv8 图像分割及对象检测

SAM(Segment Anything Model)是由 Meta 的研究人员团队创建和训练的深度学习模型。该创新成果发表在 2023 年 4 月 5 日发表的一篇研究论文中,它立即引起了公众的广泛兴趣——相关的 Twitter 帖子迄今为止已累积超过 350 万次浏览: 计算机视…

接口文档生成工具JAPiDocs

效果如下: 相比Swagger要写一堆注解,Spring RestDocs需要写测试用例,才能生成API文档。 要使得JApiDcos正确工作,代码应该是像下面的样子的: /*** 用户接口*/ RequestMapping("/api/user/") RestControll…

YOLO目标检测——口罩规范佩戴数据集+已标注xml和txt格式标签下载分享

实际项目应用:目标检测口罩佩戴检测数据集的应用场景涵盖了公共场所监控、疫情防控管理、安全管理与控制以及人员统计和分析等领域。这些应用场景可以帮助相关部门和机构更好地管理口罩佩戴情况,提高公共卫生和安全水平,保障人们的健康和安全…

C++数据结构X篇_10_C++栈的应用-中缀转后缀

中缀表达式就是我们平时运算表达式,其特点是运算符总是处于两个运算对象之间。但是该表达式计算机处理起来较为麻烦,会将其转写成后缀表达式,后缀表达式也叫逆波兰表达式,后缀表达式的特点是每个运算符都置于两个运算对象之后。此…

微信小程序AI类目-深度合成-AI问答/AI绘画 互联网信息服务算法备案审核通过教程

近期小程序审核规则变化后,很多使用人类小徐提供的chatGPT系统的会员上传小程序无法通过审核,一直提示需要增加深度合成-AI问答、深度合成-AI绘画类目,该类目需要提供互联网信息服务算法备案并上传资质,一般对企业来说这种务很难实…

SSH是如何配置的

目录 什么是SSH SSH可以做什么其他用途? ssh有几种连接方法吗 我应该用哪种方法连接SSH1或SSH2? 每天都在用SSH你知道SSH的原理吗 开启ssh后telnet会关闭吗 SSH的优缺点 SSH和Telnet之间优缺点的对比 SSH的配置实验 ensp Cisco H3C 1、什么是…

linux内核模块编译方法详解

文章目录 前言一、静态加载法1.1 编写驱动程序1.2 将新功能配置在内核中1.3为新功能代码改写Makefile1.4 make menuconfig界面里将新功能对应的那项选择为<*> 二、动态加载法2.1 新功能源码与Linux内核源码在同一目录结构下2.2 新功能源码与Linux内核源码不在同一目录结构…

0003号因子测试结果、代码和数据

这篇文章共分为四个部分:第一个部分是因子测试结果,第二个部分是因子逻辑,第三个部分是因子代码,第四个部分是整个因子测试用的数据、代码、分析结果的下载地址。 因子测试结果: 因子描述 因子属性-量价因子因子构建:计算成交量的变化率和日振幅率,计算两者在过去一定…

Solidity 小白教程:12. 事件

Solidity 小白教程&#xff1a;12. 事件 这一讲&#xff0c;我们用转账 ERC20 代币为例来介绍solidity中的事件&#xff08;event&#xff09;。 事件 Solidity中的事件&#xff08;event&#xff09;是EVM上日志的抽象&#xff0c;它具有两个特点&#xff1a; 响应&#x…

外传-Midjourney的局部重绘功能

今天在抄袭。。。啊不&#xff0c;借鉴 midjourney 官网教程的时候&#xff0c;发现多了一个 局部重绘的功能&#xff0c;意外发觉还不错&#xff0c;分享一下用法。 先给大家说一下&#xff0c;我这段时间都在学习 SD&#xff0c;局部重绘是基操&#xff0c;而 MJ 一直是次次…