我的编程空间,编程开发者的网络收藏夹
学习永远不晚

YOLOv5的Backbone源码分析

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

YOLOv5的Backbone源码分析

本篇内容主要讲解“YOLOv5的Backbone源码分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“YOLOv5的Backbone源码分析”吧!

    1 Backbone概览及参数

    # Parametersnc: 80  # number of classesdepth_multiple: 0.33  # model depth multiplewidth_multiple: 0.50  # layer channel multiple# YOLOv5 v6.0 backbonebackbone:  # [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, SPPF, [1024, 5]],  # 9  ]

    yolov5s的backbone部分如上,其网络结构使用yaml文件配置,通过./models/yolo.py解析文件加了一个输入构成的网络模块。与v3和v4所使用的config设置的网络不同,yaml文件中的网络组件不需要进行叠加,只需要在配置文件中设置number即可。

    1.1 Param

    # Parametersnc: 80  # number of classesdepth_multiple: 0.33  # model depth multiplewidth_multiple: 0.50  # layer channel multiple

    nc: 8

    代表数据集中的类别数目,例如MNIST中含有0-9共10个类.

    depth_multiple: 0.33

    用来控制模型的深度,仅在number≠1时启用。 如第一个C3层(c3具体是什么后续介绍)的参数设置为[-1, 3, C3, [128]],其中number=3,表示在v5s中含有1个C3(3*0.33);同理,v5l中的C3个数就是3(v5l的depth_multiple参数为1)。

    width_multiple: 0.50

    用来控制模型的宽度,主要作用于args中的ch_out。如第一个Conv层,ch_out=64,那么在v5s实际运算过程中,会将卷积过程中的卷积核设为64x0.5,所以会输出32通道的特征图。

    1.2 backbone

    # YOLOv5 v6.0 backbonebackbone:  # [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, SPPF, [1024, 5]],  # 9  ]
    1. from:-n代表是从前n层获得的输入,如-1表示从前一层获得输入

    2. number:表示网络模块的数目,如[-1, 3, C3, [128]]表示含有3个C3模块

    3. model:表示网络模块的名称,具体细节可以在./models/common.py查看,如Conv、C3、SPPF都是已经在common中定义好的模块

    4. args:表示向不同模块内传递的参数,即[ch_out, kernel, stride, padding, groups],这里连ch_in都省去了,因为输入都是上层的输出(初始ch_in为3)。为了修改过于麻烦,这里输入的获取是从./models/yolo.py的def parse_model(md, ch)函数中解析得到的。

    1.3 Exp

    [-1, 1, Conv, [64, 6, 2, 2]],  # 0-P1/2

    input:3x640x640

    [ch_out, kernel, stride, padding]=[64, 6, 2, 2]

    故新的通道数为64x0.5=32

    根据特征图计算公式:Feature_new=(Feature_old-kernel+2xpadding)/stride+1可得:

    新的特征图尺寸为:Feature_new=(640-6+2x2)/2+1=320

    [-1, 1, Conv, [128, 3, 2]],  # 1-P2/4

    input:32x320x320

    [ch_out, kernel, stride]=[128, 3, 2]

    同理可得:新的通道数为64,新的特征图尺寸为160

    2 Backbone组成

    v6.0版本的Backbone去除了Focus模块(便于模型导出部署),Backbone主要由CBL、BottleneckCSP/C3以及SPP/SPPF等组成,具体如下图所示:

    YOLOv5的Backbone源码分析

    3.1 CBS

    YOLOv5的Backbone源码分析

    CBS模块其实没什么好稀奇的,就是Conv+BatchNorm+SiLU,这里着重讲一下Conv的参数,就当复习pytorch的卷积操作了,先上CBL源码:

    class Conv(nn.Module):    # Standard convolution    def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True):  # ch_in, ch_out, kernel, stride, padding, groups        super().__init__()        self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False)        self.bn = nn.BatchNorm2d(c2)        #其中nn.Identity()是网络中的占位符,并没有实际操作,在增减网络过程中,可以使得整个网络层数据不变,便于迁移权重数据;nn.SiLU()一种激活函数(S形加权线性单元)。        self.act = nn.SiLU() if act is True else (act if isinstance(act, nn.Module) else nn.Identity())    def forward(self, x):#正态分布型的前向传播        return self.act(self.bn(self.conv(x)))    def forward_fuse(self, x):#普通前向传播        return self.act(self.conv(x))

    由源码可知:Conv()包含7个参数,这些参数也是二维卷积Conv2d()中的重要参数。ch_in, ch_out, kernel, stride没什么好说的,展开说一下后三个参数:

    padding

    从我现在看到的主流卷积操作来看,大多数的研究者不会通过kernel来改变特征图的尺寸,如googlenet中3x3的kernel设定了padding=1,所以当kernel≠1时需要对输入特征图进行填充。当指定p值时按照p值进行填充,当p值为默认时则通过autopad函数进行填充:

    def autopad(k, p=None):  # kernel, padding    # Pad to 'same'    if p is None:        p = k // 2 if isinstance(k, int) else [x // 2 for x in k]  # auto-pad        #如果k是整数,p为k与2整除后向下取整;如果k是列表等,p对应的是列表中每个元素整除2。    return p

    这里作者考虑到对不同的卷积操作使用不同大小的卷积核时padding也需要做出改变,所以这里在为p赋值时会首先检查k是否为int,如果k为列表则对列表中的每个元素整除。

    groups

    代表分组卷积,如下图所示

    YOLOv5的Backbone源码分析

    groups – Number of blocked connections from input channels to output

    • At groups=1, all inputs are convolved to all outputs.

    • At groups=2, the operation becomes equivalent to having two conv layers side by side, each seeing half the input channels, and producing half the output channels, and both subsequently concatenated.

    • At groups= in_channels, each input channel is convolved with its own set of filters, of size: ⌊(out_channels)/(in_channels)⌋.

    act

    决定是否对特征图进行激活操作,SiLU表示使用Sigmoid进行激活。

    one more thing:dilation

    Conv2d中还有一个重要的参数就是空洞卷积dilation,通俗解释就是控制kernel点(卷积核点)间距的参数,通过改变卷积核间距实现特征图及特征信息的保留,在语义分割任务中空洞卷积比较有效。

    YOLOv5的Backbone源码分析

    3.2 CSP/C3

    CSP即backbone中的C3,因为在backbone中C3存在shortcut,而在neck中C3不使用shortcut,所以backbone中的C3层使用CSP1_x表示,neck中的C3使用CSP2_x表示。

    3.2.1 CSP结构

    接下来让我们来好好梳理一下backbone中的C3层的模块组成。先上源码:

    class C3(nn.Module):    # CSP Bottleneck with 3 convolutions    def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):  # ch_in, ch_out, number, shortcut, groups, expansion        super().__init__()        c_ = int(c2 * e)  # hidden channels        self.cv1 = Conv(c1, c_, 1, 1)        self.cv2 = Conv(c1, c_, 1, 1)        self.cv3 = Conv(2 * c_, c2, 1)  # act=FReLU(c2)        self.m = nn.Sequential(*[Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)])        # self.m = nn.Sequential(*[CrossConv(c_, c_, 3, 1, g, 1.0, shortcut) for _ in range(n)])    def forward(self, x):        return self.cv3(torch.cat((self.m(self.cv1(x)), self.cv2(x)), dim=1))

    从源码中可以看出:输入特征图一条分支先经过.cv1,再经过.m,得到子特征图1;另一分支经过.cv2后得到子特征图2。最后将子特征图1和子特征图2拼接后输入.cv3得到C3层的输出,如下图所示。 这里的CV操作容易理解,就是前面的Conv2d+BN+SiLU,关键是.m操作。

    YOLOv5的Backbone源码分析

    .m操作使用nn.Sequential将多个Bottleneck(图示中我以Resx命名)串接到网络中,for loop中的n即网络配置文件args中的number,也就是将number×depth_multiple个Bottleneck串接到网络中。那么,Bottleneck又是个什么玩意呢?

    3.2.2 Bottleneck

    要想了解Bottleneck,还要从Resnet说起。在Resnet出现之前,人们的普遍为网络越深获取信息也越多,模型泛化效果越好。然而随后大量的研究表明,网络深度到达一定的程度后,模型的准确率反而大大降低。这并不是过拟合造成的,而是由于反向传播过程中的梯度爆炸和梯度消失。也就是说,网络越深,模型越难优化,而不是学习不到更多的特征。

    YOLOv5的Backbone源码分析

    为了能让深层次的网络模型达到更好的训练效果,残差网络中提出的残差映射替换了以往的基础映射。对于输入x,期望输出H(x),网络利用恒等映射将x作为初始结果,将原来的映射关系变成F(x)+x。与其让多层卷积去近似估计H(x) ,不如近似估计H(x)-x,即近似估计残差F(x)。因此,ResNet相当于将学习目标改变为目标值H(x)和x的差值,后面的训练目标就是要将残差结果逼近于0。

    残差模块有什么好处呢?

    梯度弥散方面。加入ResNet中的shortcut结构之后,在反传时,每两个block之间不仅传递了梯度,还加上了求导之前的梯度,这相当于把每一个block中向前传递的梯度人为加大了,也就会减小梯度弥散的可能性。
    2.特征冗余方面。正向卷积时,对每一层做卷积其实只提取了图像的一部分信息,这样一来,越到深层,原始图像信息的丢失越严重,而仅仅是对原始图像中的一小部分特征做提取。这显然会发生类似欠拟合的现象。加入shortcut结构,相当于在每个block中又加入了上一层图像的全部信息,一定程度上保留了更多的原始信息。

    在resnet中,人们可以使用带有shortcut的残差模块搭建几百层甚至上千层的网络,而浅层的残差模块被命名为Basicblock(18、34),深层网络所使用的的残差模块,就被命名为了Bottleneck(50+)。

    YOLOv5的Backbone源码分析


    Bottleneck与Basicblock最大的区别是卷积核的组成。 Basicblock由两个3x3的卷积层组成,Bottleneck由两个1x1卷积层夹一个3x3卷积层组成:其中1x1卷积层降维后再恢复维数,让3x3卷积在计算过程中的参数量更少、速度更快。

    第一个1x1的卷积把256维channel降到64维,然后在最后通过1x1卷积恢复,整体上用的参数数目:1x1x256x64 + 3x3x64x64 + 1x1x64x256 = 69632,而不使用bottleneck的话就是两个3x3x256的卷积,参数数目: 3x3x256x256x2 = 1179648,差了16.94倍。

    Bottleneck减少了参数量,优化了计算,保持了原有的精度。

    说了这么多,都是为了给CSP中的Bottleneck做前情提要,我们再回头看CSP中的Bottleneck其实就更清楚了:

    class Bottleneck(nn.Module):    # Standard bottleneck    def __init__(self, c1, c2, shortcut=True, g=1, e=0.5):  # ch_in, ch_out, shortcut, groups, expansion        super().__init__()        c_ = int(c2 * e)  # hidden channels        self.cv1 = Conv(c1, c_, 1, 1)        self.cv2 = Conv(c_, c2, 3, 1, g=g)        self.add = shortcut and c1 == c2    def forward(self, x):        return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x))

    可以看到,CSP中的Bottleneck同resnet模块中的类似,先是1x1的卷积层(CBS),然后再是3x3的卷积层,最后通过shortcut与初始输入相加。但是这里与resnet的不通点在于:CSP将输入维度减半运算后并未再使用1x1卷积核进行升维,而是将原始输入x也降了维,采取concat的方法进行张量的拼接,得到与原始输入相同维度的输出。其实这里能区分一点就够了:resnet中的shortcut通过add实现,是特征图对应位置相加而通道数不变;而CSP中的shortcut通过concat实现,是通道数的增加。二者虽然都是信息融合的主要方式,但是对张量的具体操作又不相同.

    YOLOv5的Backbone源码分析

    其次,对于shortcut是可根据任务要求设置的,比如在backbone中shortcut=True,neck中shortcut=False。
    当shortcut=True时,Resx如图:

    YOLOv5的Backbone源码分析

    当shortcut=False时,Resx如图:

    YOLOv5的Backbone源码分析

    这其实也是YOLOv5为人称赞的地方,代码更体系、代码冗余更少,仅需要指定一个参数便可以将Bottleneck和普通卷积联合在一起使用,减少了代码量的同时也使整体感观得到提升。

    3.3 SSPF

    class SPPF(nn.Module):    # Spatial Pyramid Pooling - Fast (SPPF) layer for YOLOv5 by Glenn Jocher    def __init__(self, c1, c2, k=5):  # equivalent to SPP(k=(5, 9, 13))        super().__init__()        c_ = c1 // 2  # hidden channels        self.cv1 = Conv(c1, c_, 1, 1)        self.cv2 = Conv(c_ * 4, c2, 1, 1)        self.m = nn.MaxPool2d(kernel_size=k, stride=1, padding=k // 2)    def forward(self, x):        x = self.cv1(x)        with warnings.catch_warnings():            warnings.simplefilter('ignore')  # suppress torch 1.9.0 max_pool2d() warning            y1 = self.m(x)            y2 = self.m(y1)            return self.cv2(torch.cat([x, y1, y2, self.m(y2)], 1))

    SSPF模块将经过CBS的x、一次池化后的y1、两次池化后的y2和3次池化后的self.m(y2)先进行拼接,然后再CBS提取特征。 仔细观察不难发现,虽然SSPF对特征图进行了多次池化,但是特征图尺寸并未发生变化,通道数更不会变化,所以后续的4个输出能够在channel维度进行融合。这一模块的主要作用是对高层特征进行提取并融合,在融合的过程中作者多次运用最大池化,尽可能多的去提取高层次的语义特征。

    YOLOv5的Backbone源码分析

    YOLOv5s的Backbone总览

    最后,结合上述的讲解应该就不难理解v5s的backbone了

    YOLOv5的Backbone源码分析

    到此,相信大家对“YOLOv5的Backbone源码分析”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

    免责声明:

    ① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

    ② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

    YOLOv5的Backbone源码分析

    下载Word文档到电脑,方便收藏和打印~

    下载Word文档

    猜你喜欢

    YOLOv5的Backbone源码分析

    本篇内容主要讲解“YOLOv5的Backbone源码分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“YOLOv5的Backbone源码分析”吧!1 Backbone概览及参数# Paramet
    2023-06-30

    Backbone前端框架核心及源码解析

    这篇文章主要为大家介绍了Backbone前端框架核心及源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-02-07

    YOLOv5模型训练与评估源码分析

    本篇内容介绍了“YOLOv5模型训练与评估源码分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1 搭建训练环境首先,我们需要搭建YOLOv
    2023-07-05

    YOLOv5车牌识别案例代码分析

    本文小编为大家详细介绍“YOLOv5车牌识别案例代码分析”,内容详细,步骤清晰,细节处理妥当,希望这篇“YOLOv5车牌识别案例代码分析”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。1 目标检测概念目标检测是计算
    2023-07-05

    SocketServer 源码分析

    Creating network servers.contentsSocketServer.pycontentsfile headBaseServerBaseServer.serve_foreverBaseServer.shutdownBa
    2023-01-31

    CesiumJS源码分析

    这篇文章主要介绍“CesiumJS源码分析”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“CesiumJS源码分析”文章能帮助大家解决问题。1. 有什么光CesiumJS 支持的光的类型比较少,默认场
    2023-07-06

    RateLimiter 源码分析

    俗话说得好,缓存,限流和降级是系统的三把利剑。刚好项目中每天早上导出数据时因调订单接口频率过高,订单系统担心会对用户侧的使用造成影响,让我们对调用限速一下,所以就正好用上了。 常用的限流算法有2种:漏桶算法和令牌桶算法。漏桶算法漏桶算法:请
    2023-05-31

    MyBatis的SqlSession.getMapper()源码分析

    今天小编给大家分享一下MyBatis的SqlSession.getMapper()源码分析的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来
    2023-07-05

    Android AsyncTask源码分析

    Android中只能在主线程中进行UI操作,如果是其它子线程,需要借助异步消息处理机制Handler。除此之外,还有个非常方便的AsyncTask类,这个类内部封装了Handler和线程池。本文先简要介绍AsyncTask的用法,然后分析具
    2022-06-06

    Pythonkeras.metrics源代码分析

    最近在用keras写模型的时候,参考别人代码时,经常能看到各种不同的metrics,因此会产生几个问题,下面主要介绍了Pythonkeras.metrics源代码分析
    2022-11-13

    Java中的CyclicBarrier源码分析

    这篇文章主要介绍了Java中的CyclicBarrier源码分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Java中的CyclicBarrier源码分析文章都会有所收获,下面我们一起来看看吧。CyclicB
    2023-06-30

    Kafka源码分析(一)

    Apache Kafka® 是 一个分布式流处理平台. 这到底意味着什么呢?我们知道流处理平台有以下三种特性:可以让你发布和订阅流式的记录。这一方面与消息队列或者企业消息系统类似。可以储存流式的记录,并且有较好的容错性。可以在流式记录产生时就进行处理。Kafk
    Kafka源码分析(一)
    2019-10-17

    Spring refresh()源码分析

    今天小编给大家分享一下Spring refresh()源码分析的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。正文public
    2023-07-05

    Python中的jieba源码分析

    本篇内容主要讲解“Python中的jieba源码分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Python中的jieba源码分析”吧!前言jieba分词是Python 里面几个比较流行的中文
    2023-06-02

    编程热搜

    • Python 学习之路 - Python
      一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
      Python 学习之路 - Python
    • chatgpt的中文全称是什么
      chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
      chatgpt的中文全称是什么
    • C/C++中extern函数使用详解
    • C/C++可变参数的使用
      可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
      C/C++可变参数的使用
    • css样式文件该放在哪里
    • php中数组下标必须是连续的吗
    • Python 3 教程
      Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
      Python 3 教程
    • Python pip包管理
      一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
      Python pip包管理
    • ubuntu如何重新编译内核
    • 改善Java代码之慎用java动态编译

    目录