YOLOv5改进系列(十二) 本文(5000字) | 引入密集连接卷积网络DenseNet思想 |

141 篇文章 7 订阅

已下架不支持订阅


点击进入专栏:
《人工智能专栏》 Python与Python | 机器学习 | 深度学习 | 目标检测 | YOLOv5及其改进 | YOLOv8及其改进 | 关键知识点 | 各种工具教程


代码函数调用关系图(全网最详尽-重要)

因文档特殊,不能在博客正确显示,请移步以下链接!

图解YOLOv5_v7.0代码结构与调用关系(点击进入可以放大缩小等操作)

预览:
在这里插入图片描述



YOLOv5引入密集连接卷积网络DenseNet思想

在这里插入图片描述

CVPR 2017最佳论文 D e n s e N e t DenseNet DenseNet 论文地址:https://arxiv.org/pdf/1608.06993.pdf

最近的研究表明,如果卷积网络在接近输入和接近输出的层之间包含更短的连接,那么卷积网络可以更深入、更准确、更有效地训练。在本文中,我们采用了这种观察结果,并引入了密集卷积网络(DenseNet),它以前馈的方式将每一层与每一层连接起来。传统的L层卷积网络有lconnections(每层和随后的层之间有一个连接),而我们的网络有L(L+1) 2个直接连接。对于每一层,前面所有层的特征映射用作输入,它自己的特征映射用作所有后续层的输入。DenseNets有几个令人信服的优点:它们缓解消失梯度问题,加强特征传播,鼓励特征重用,并大大减少参数的数量。我们在四个竞争激烈的目标识别基准任务(CIFAR-10、CIFAR-100、SVHN和ImageNet)上评估我们提出的架构。DenseNets在大多数技术上获得了显著的改进,同时需要更少的计算来实现高性能。代码和预先训练的模型可以通过https://github.com/liuzhuang13/DenseNet获得。


论文思想

DenseNet主要有以下三个优点:

  1. 缓解了梯度消失问题;
  2. 加强了特征传递;
  3. 减少参数数量。

Dense Block:像GoogLeNet网络由Inception模块组成、ResNet网络由残差块Residual Building Block组成一样,DenseNet网络由Dense Block组成,论文截图如下所示:每个层从前面的所有层获得额外的输入,并将自己的特征映射传递到后续的所有层,使用级联(Concatenation)方式,每一层都在接受来自前几层的”集体知识(collective knowledge)”
在这里插入图片描述


设计理念

既然 DenseNet DenseNet的效果这么好,那么为什么不能在YOLOv5中引入这个思想呢?
YOLOv5的网络结构非常简洁,能动的地方也就只有C3模块了,所以我就在C3模块中尝试了这个思想,搭建了一个Dense_C3模块。

  • 参数量方面:模块里面使用了大量的残差分支,引入到YOLOv5中参数量有略微的上涨,但是有很多的改进空间,因为我在原本C3Bottleneck里面加了两个3×3卷积,这几个卷积完全可以换成其它的变种卷积;也可以通过改变Bottleneck数量降低参数量。
  • 计算量方面:上涨了很多,目前没想出有什么好的解决方案,这种结构带来计算量的上涨是在所难免的。

请添加图片描述
请添加图片描述


参数量计算量对比
模型参数量parameters计算量GFLOPs
yolov5s723538916.5
yolov5s-Dense_C3976633335.5

源码

yolov5s-Dense_C3

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

# 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, 14], 1, Concat, [1]],  # cat head P4
   [-1, 3, C3, [512, False]],  # 20 (P4/16-medium)

   [-1, 1, Conv, [512, 3, 2]],
   [[-1, 10], 1, Concat, [1]],  # cat head P5
   [-1, 3, C3, [1024, False]],  # 23 (P5/32-large)

   [[17, 20, 23], 1, Detect, [nc, anchors]],  # Detect(P3, P4, P5)
  ]

common.py

class Dense_Bottleneck(nn.Module):
    def __init__(self, c1, c2, shortcut=True, g=1, e=0.5):
        super().__init__()
        c_ = int(c2 * e)  # 计算隐藏层通道数
        self.cv1 = Conv(c1, c_, 1, 1)  # 第一个卷积层,1x1卷积
        self.cv2 = Conv(c_, c_, 3, 1, g=g)  # 第二个卷积层,3x3卷积
        self.cv3 = Conv(c_, c_, 3, 1, g=g)  # 第三个卷积层,3x3卷积
        self.cv4 = Conv(c_, c2, 3, 1, g=g)  # 第四个卷积层,3x3卷积
        self.add = shortcut and c1 == c2  # 是否使用快捷连接(shortcut connection)

    def forward(self, x):
        x1 = self.cv1(x) + x
        x2 = self.cv2(x1) + x + self.cv1(x)
        x3 = self.cv3(x2) + x + self.cv1(x) + self.cv2(x1)
        x4 = self.cv4(x3) + x + self.cv1(x) + self.cv2(x1) + self.cv3(x2)
        return x4 if self.add else self.cv4(self.cv3(self.cv2(self.cv1(x))))

class Dense_C3(nn.Module):
    def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):
        super().__init__()
        c_ = int(c2 * e)  # 计算隐藏层通道数
        self.cv1 = Conv(c1, c_, 1, 1)  # 第一个卷积层,1x1卷积
        self.cv2 = Conv(c1, c_, 1, 1)  # 第二个卷积层,1x1卷积
        self.cv3 = Conv(2 * c_, c2, 1)  # 第三个卷积层,1x1卷积
        self.m = nn.Sequential(*(Dense_Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)))  # 创建包含多个Dense_Bottleneck的序列

    def forward(self, x):
        # 将输入分成两个分支,一个通过cv1和多个Dense_Bottleneck,另一个通过cv2
        return self.cv3(torch.cat((self.m(self.cv1(x)), self.cv2(x)), 1))

yolo.py

在这里插入图片描述
在这里插入图片描述


[点击查看专栏全部文章]


  • 0
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

已下架不支持订阅

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值