深度学习Pytorch检测实战 - Notes - 第6章 单阶经典检测器:YOLO

2020-08-27 15:07:55 浏览数 (1)

6.1 无锚框预测:YOLO v1

相比起Faster RCNN的两阶结构,2015年诞生的YOLO v1创造性地使用一阶结构完成了物体检测任务,直接预测物体的类别与位置,没有RPN网络,也没有类似于Anchor的预选框,因此速度很快。

6.1.1 网络结构

与其他物体检测算法一样,YOLO v1首先利用卷积神经网络进行了特征提取,具体结构如图6.1所示,该结构与GoogLeNet模型有些类似。

在该结构中,输出图像的尺寸固定为448×448,经过24个卷积层与两个全连接层后,最后输出的特征图大小为7×7×30。

关于YOLO v1的网络结构,有以下3个细节:

  • 在3×3的卷积后通常会接一个通道数更低的1×1卷积,这种方式既降低了计算量,同时也提升了模型的非线性能力。
  • 除了最后一层使用了线性激活函数外,其余层的激活函数为LeakyReLU。
  • 在训练中使用了Dropout与数据增强的方法来防止过拟合。
图6.1 YOLO v1网络结构图6.1 YOLO v1网络结构

6.1.2 特征图的意义

YOLO v1的网络结构并无太多创新之处,其精髓主要在最后7×7×30大小的特征图中。如图6.2所示,YOLO v1将输入图像划分成7×7的区域,每一个区域对应于最后特征图上的一个点,该点的通道数为30,代表了预测的30个特征。

YOLO v1在每一个区域内预测两个边框,如图6.2中的预测框A与B,这样整个图上一共预测7×7×2=98个框,这些边框大小与位置各不相同,基本可以覆盖整个图上可能出现的物体。

YOLO v1检测原理图YOLO v1检测原理图

如果一个物体的中心点落在了某个区域内,则该区域就负责检测该物体。图6.2中真实物体框的中心点在当前区域内,该区域就负责检测该物体,具体是将该区域的两个框与真实物体框进行匹配,IoU更大的框负责回归该真实物体框,在此A框更接近真实物体。最终的预测特征由类别概率、边框的置信度及边框的位置组成。

  • 类别概率:由于PASCAL VOC数据集一共有20个物体类别,因此这里预测的是边框属于哪一个类别。
  • 置信度:表示该区域内是否包含物体的概率,类似于Faster RCNN中是前景还是背景。由于有两个边框,因此会存在两个置信度预测值。
  • 边框位置:对每一个边框需要预测其中心坐标及宽、高这4个量,两个边框共计8个预测值。
预测特征的30维通道含义预测特征的30维通道含义

这里有以下3点值得注意的细节:

  • YOLO v1并没有先验框,而是直接在每个区域预测框的大小与位置,是一个回归问题。这样做能够成功检测的原因在于,区域本身就包含了一定的位置信息,另外被检测物体的尺度在一个可以回归的范围内。
  • 从图6.3中可以看出,一个区域内的两个边框共用一个类别预测,在训练时会选取与物体IoU更大的一个边框,在测试时会选取置信度更高的一个边框,另一个会被舍弃,因此整张图最多检测出49个物体。
  • YOLO v1采用了物体类别与置信度分开的预测方法,这点与Faster RCNN不同。Faster RCNN将背景也当做了一个类别,共计21种,在类别预测中包含了置信度的预测。

6.1.3 损失计算

通过卷积网络得到每个边框的预测值后,为了进一步计算网络训练的损失,还需要确定每一个边框是对应着真实物体还是背景框,即区分开正、负样本。YOLO v1在确定正负样本时,有以下两个原则:

  • 当一个真实物体的中心点落在了某个区域内时,该区域就负责检测该物体。具体做法是将与该真实物体有最大IoU的边框设为正样本,这个区域的类别真值为该真实物体的类别,该边框的置信度真值为1。
  • 除了上述被赋予正样本的边框,其余边框都为负样本。负样本没有类别损失与边框位置损失,只有置信度损失,其真值为0。

YOLO v1的损失一共由5部分组成,均使用了均方差损失,

公式中i代表第几个区域,一共有S2个区域,在此为49;j代表某个区域的第几个预测边框,一共有B个预测框,在此为2;obj代表该框对应了真实物体;noobj代表该框没有对应真实物体。这5项损失的意义如下:

  • 第一项为正样本中心点坐标的损失。λcoord的目的是为了调节位置损失的权重,YOLO v1设置λcoord为5,调高了位置损失的权重。
  • 第二项为正样本宽高的损失。由于宽高差值受物体尺度的影响,因此这里先对宽高进行了平方根处理,在一定程度上降低对尺度的敏感,强化了小物体的损失权重。
  • 第三、四项分别为正样本与负样本的置信度损失,正样本置信度真值为1,负样本置信度为0。λnoobj默认为0.5,目的是调低负样本置信度损失的权重。
  • 最后一项为正样本的类别损失。

总体上,YOLO v1利用了回归的思想,使用轻量化的一阶网络同时完成了物体的定位与分类,处理速度极快,可以达到45 FPS,当使用更轻量的网络时甚至可以达到155 FPS。得益于其出色的处理速度,YOLOv1被广泛应用在实际的工业场景中,尤其是追求实时处理的场景。当然,YOLO v1也有一些不足之处,主要有如下3点:

  • 由于每一个区域默认只有两个边框做预测,并且只有一个类别,因此YOLO v1有着天然的检测限制。这种限制会导致模型对于小物体,以及靠得特别近的物体检测效果不好。
  • 由于没有类似于Anchor的先验框,模型对于新的或者不常见宽高比例的物体检测效果不好。另外,由于下采样率较大,边框的检测精度不高。
  • 在损失函数中,大物体的位置损失权重与小物体的位置损失权重是一样的,这会导致同等比例的位置误差,大物体的损失会比小物体大,小物体的损失在总损失中占比较小,会带来物体定位的不准确。

6.2 依赖锚框:YOLO v2

6.2.1 网络结构的改善

首先,YOLO v2对于基础网络结构进行了多种优化,提出了一个全新的网络结构,称之为DarkNet。原始的DarkNet拥有19个卷积层与5个池化层,在增加了一个Passthrough层后一共拥有22个卷积层,精度与VGGNet相当,但浮点运算量只有VGGNet的1/5左右,因此速度极快。

相比起v1版本的基础网络,DarkNet进行了以下几点改进:

  • BN层:DarkNet使用了BN层,这一点带来了2%以上的性能提升。BN层有助于解决反向传播中的梯度消失与爆炸问题,可以加速模型的收敛,同时起到一定的正则化作用。BN层的具体位置是在每一个卷积之后,激活函数LeakyReLU之前。
  • 用连续3×3卷积替代了v1版本中的7×7卷积,这样既减少了计算量,又增加了网络深度。此外,DarkNet去掉了全连接层与Dropout层。
  • Passthrough层:DarkNet还进行了深浅层特征的融合,具体方法是将浅层26×26×512的特征变换为13×13×2048,这样就可以直接与深层13×13×1024的特征进行通道拼接。这种特征融合有利于小物体的检测,也为模型带来了1%的性能提升。
  • 由于YOLO v2在每一个区域预测5个边框每个边框有25个预测值,因此最后输出的特征图通道数为125。其中,一个边框的25个预测值分别是20个类别预测、4个位置预测及1个置信度预测值。这里与v1有很大区别,v1是一个区域内的边框共享类别预测,而这里则是相互独立的类别预测值。

6.2.2 先验框的设计

YOLO v2吸收了Faster RCNN的优点,设置了一定数量的预选框,使得模型不需要直接预测物体尺度与坐标,只需要预测先验框到真实物体的偏移,降低了预测难度。

关于先验框,YOLO v2首先使用了聚类的算法来确定先验框的尺度,并且优化了后续的偏移计算方法,下面详细介绍这两部分。

先验框的设计为YOLO v2带来了7%的召回率提升。

1.聚类提取先验框尺度

Faster RCNN中预选框(即Anchor)的大小与宽高是由人手工设计的,因此很难确定设计出的一组预选框是最贴合数据集的,也就有可能为模型性能带来负面影响。

针对此问题,YOLO v2通过在训练集上聚类来获得预选框,只需要设定预选框的数量k,就可以利用聚类算法得到最适合的k个框。在聚类时,两个边框之间的距离使用式(6-2)的计算方法,即IoU越大,边框距离越近。

在衡量一组预选框的好坏时,使用真实物体与这一组预选框的平均IoU作为标准。值得一提的是,这一判断标准在手工设计预选框的方法中也可以使用。

至于预选框的数量选取,显然数量k越多,平均IoU会越大,效果会更好,但相应的也会带来计算量的提升,YOLO v2在速度与精度的权衡中选择了预选框数量为5。

2.优化偏移公式

有了先验框后,YOLO v2不再直接预测边框的位置坐标,而是预测先验框与真实物体的偏移量。在Faster RCNN中,中心坐标的偏移公式如式(6-3)所示。

公式中wa、ha、xa及ya代表Anchor的宽高与中心坐标,tx与ty是模型预测的Anchor相对于真实物体的偏移量,经过计算后得到预测的物体中心坐标x和y。

YOLO v2认为这种预测方式没有对预测偏移进行限制,导致预测的边框中心可以出现在图像的任何位置,尤其是在训练初始阶段,模型参数还相对不稳定。例如tx是1与-1时,预测的物体中心点会有两个宽度的差距。

因此,YOLO v2提出了式(6-4)所示的预测公式:

公式中参数的意义可以与图6.5结合进行理解,图中实线框代表预测框,虚线框代表先验框:

  • cx与cy代表中心点所处区域左上角的坐标,pw与ph代表了当前先验框的宽高,如图6.5中的虚线框所示。
  • σ(tx)与σ(ty)代表预测框中心点与中心点所处区域左上角坐标的距离,加上cx与cy即得到预测框的中心坐标。
  • tw与th为预测的宽高偏移量。先验框的宽高乘上指数化后的宽高偏移量,即得到预测框的宽高。
  • 公式中的σ代表Sigmoid函数,作用是将坐标偏移量化到(0,1)区间,这样得到的预测边框的中心坐标bx、by会限制在当前区域内,保证一个区域只预测中心点在该区域内的物体,有利于模型收敛。
  • YOLO v1将预测值t0作为边框的置信度,而YOLO v2则是将做Sigmoid变换后的σ(t0)作为真正的置信度预测值。
图6.5 YOLO v2预测值的含义图6.5 YOLO v2预测值的含义

6.2.3 正、负样本与损失函数

关于正、负样本的选取,YOLO v2基本保持了之前的方法,其基本流程如下:

  1. 首先利用式(6-4),将预测的位置偏移量作用到先验框上,得到预测框的真实位置。
  2. 如果一个预测框与所有真实物体的最大IoU小于一定阈值(默认为0.6)时,该预测框视为负样本。
  3. 每一个真实物体的中心点落在了某个区域内,该区域就负责检测该物体。具体做法是将与该物体有最大IoU的预测框视为正样本。
  4. 确定了正样本与负样本后,最后是网络损失的计算。

由于利用了先验框,YOLO v2的损失函数也相应的进行了改变,公式如式(6-5)所示。

损失一共有5项组成,意义分别如下:

  • 第一项为负样本的置信度损失,公式中1max IoU<Thresh表示最大IoU小于阈值,即负样本的边框,λnoobj是负样本损失的权重,boijk为置信度σ(to)。
  • 第二项为先验框与预测框的损失,只存在于前12800次迭代中,目的是使预测框先收敛于先验框,模型更稳定。
  • 第三项为正样本的位置损失,表示筛选出的正样本,为权重。
  • 后两项分别为正样本的置信度损失与类别损失,为置信度的真值。

在计算正、负样本的过程中,虽然有些预测框的最大IoU可能小于0.6,即被赋予了负样本,但如果后续是某一个真实物体对应的最大IoU的框时,该预测框会被最终赋予成正样本,以保证recall。

有些预测框的最大IoU大于0.6,但是在一个区域内又不是与真实物体有最大IoU,这种预测框会被舍弃掉不参与损失计算,既不是正样本也不是负样本

6.2.5 工程技巧

除了模型上的改进,YOLO v2也是一个充满工程技巧的检测模型,下面从两个方面介绍其工程上的特点。

1.多尺度训练

由于移除了全连接层,因此YOLO v2可以接受任意尺寸的输入图片。在训练阶段,为了使模型对于不同尺度的物体鲁棒,YOLO v2采取了多种尺度的图片作为训练的输入。

由于下采样率为32,为了满足整除的需求,YOLO v2选取的输入尺度集合为{320,352,384,...,608},这样训练出的模型可以预测多个尺度的物体。并且,输入图片的尺度越大则精度越高,尺度越低则速度越快,因此YOLO v2多尺度训练出的模型可以适应多种不同的场景要求。

2.多阶段训练

由于物体检测数据标注成本较高,因此大多数物体检测模型都是先利用分类数据集来训练卷积层,然后再在物体检测数据集上训练。例如,YOLO v1先利用ImageNet上224×224大小的图像预训练,然后在448×448的尺度上进行物体检测的训练。这种转变使得模型要适应突变的图像尺度,增加了训练难度。

YOLO v2针对以上问题,优化了训练过程,采用如图6.6所示的训练方式,具体过程如下:

(1)利用DarkNet网络在ImageNet上预训练分类任务,图像尺度为224×224。

(2)将ImageNet图片放大到448×448,继续训练分类任务,让模型首先适应变化的尺度。

(3)去掉分类卷积层,在DarkNet上增加Passthrough层及3个卷积层,利用尺度为448×448的输入图像完成物体检测的训练。

总体上来看,YOLO v2相较于之前的版本有了质的飞跃,主要体现在吸收了其他算法的优点,使用了先验框、特征融合等方法,同时利用了多种训练技巧,使得模型在保持极快速度的同时大幅度提升了检测的精度。YOLO v2已经达到了较高的检测水平,但如果要分析其不足的话,大体有以下3点:

  • 单层特征图:虽然采用了Passthrough层来融合浅层的特征,增强多尺度检测性能,但仅仅采用一层特征图做预测,细粒度仍然不够,对小物体等检测提升有限,并且没有使用残差这种较为简单、有效的结构。
  • 受限于其整体结构,依然没有很好地解决小物体的检测问题。
  • 太工程化:YOLO v2的整体实现有较多工程化调参的过程,尤其是后续损失计算有些复杂,不是特别“优雅”,导致后续改进与扩展空间不足。

YOLO V3 论文翻译:

https://zhuanlan.zhihu.com/p/37201615

YOLO3网络结构YOLO3网络结构
输入 - 输出输入 - 输出

YOLO V3代码

ModuleList(

(0): Sequential( (conv_0): Conv2d(3, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) )

(1): Sequential( (conv_1): Conv2d(32, 64, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False) ) # 第一次下采样

代码语言:python代码运行次数:0复制
(2): Sequential((conv_2): Conv2d(64, 32, kernel_size=(1, 1), stride=(1, 1), bias=False))
(3): Sequential((conv_3): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False))
(4): Sequential((shortcut_4): EmptyLayer())

(5): Sequential( (conv_5): Conv2d(64, 128, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False) ) # 第二次下采样

代码语言:python代码运行次数:0复制
  (6): Sequential( (conv_6): Conv2d(128, 64, kernel_size=(1, 1), stride=(1, 1), bias=False) )
  (7): Sequential((conv_7): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)  )
  (8): Sequential( (shortcut_8): EmptyLayer() )
代码语言:python代码运行次数:0复制
  (9): Sequential( (conv_9): Conv2d(128, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)  )
  (10): Sequential(  (conv_10): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)  )
  (11): Sequential(  (shortcut_11): EmptyLayer())

(12): Sequential( (conv_12): Conv2d(128, 256, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False) ) # 第三次下采样

代码语言:python代码运行次数:0复制
  (13): Sequential( (conv_13): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1), bias=False))
  (14): Sequential( (conv_14): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) )
  (15): Sequential( (shortcut_15): EmptyLayer( )
代码语言:javascript复制
  (16): Sequential( (conv_16): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1), bias=False) )
  (17): Sequential( (conv_17): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False))
  (18): Sequential((shortcut_18): EmptyLayer())
代码语言:javascript复制
  (19): Sequential( (conv_19): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1), bias=False))
  (20): Sequential( (conv_20): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False))
  (21): Sequential(  (shortcut_21): EmptyLayer())
代码语言:javascript复制
  (22): Sequential(   (conv_22): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1), bias=False) )
  (23): Sequential( (conv_23): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) )
  (24): Sequential( (shortcut_24): EmptyLayer() )
代码语言:javascript复制
  (25): Sequential( (conv_25): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1), bias=False) )
  (26): Sequential( (conv_26): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) )
  (27): Sequential(  (shortcut_27): EmptyLayer() )
代码语言:javascript复制
  (28): Sequential(  (conv_28): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)  )
  (29): Sequential(   (conv_29): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)     )
  (30): Sequential( (shortcut_30): EmptyLayer())
代码语言:javascript复制
  (31): Sequential  (conv_31): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1), bias=False))
  (32): Sequential(    (conv_32): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)  )
  (33): Sequential(    (shortcut_33): EmptyLayer()  )
代码语言:javascript复制
  (34): Sequential(    (conv_34): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)  )
  (35): Sequential(    (conv_35): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)  )
  (36): Sequential(    (shortcut_36): EmptyLayer()  )

(37): Sequential( (conv_37): Conv2d(256, 512, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False) ) # 第四次下采样

代码语言:javascript复制
  (38): Sequential(    (conv_38): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)  )
  (39): Sequential(    (conv_39): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)  )
  (40): Sequential(    (shortcut_40): EmptyLayer()  )
代码语言:javascript复制
  (41): Sequential(    (conv_41): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)  )
  (42): Sequential(    (conv_42): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)  )
  (43): Sequential(    (shortcut_43): EmptyLayer()  )
代码语言:javascript复制
  (44): Sequential(    (conv_44): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)  )
  (45): Sequential(    (conv_45): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)     )
  (46): Sequential(    (shortcut_46): EmptyLayer()  )
代码语言:javascript复制
  (47): Sequential(    (conv_47): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)     )
  (48): Sequential(    (conv_48): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)  )
  (49): Sequential(    (shortcut_49): EmptyLayer()  )
代码语言:javascript复制
  (50): Sequential(    (conv_50): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)  )
  (51): Sequential(    (conv_51): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)   )
  (52): Sequential(    (shortcut_52): EmptyLayer()  )
代码语言:javascript复制
  (53): Sequential(    (conv_53): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)  )
  (54): Sequential(    (conv_54): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)  )
  (55): Sequential(    (shortcut_55): EmptyLayer(  )
代码语言:javascript复制
  (56): Sequential(    (conv_56): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)     )
  (57): Sequential    (conv_57): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)  )
  (58): Sequential(    (shortcut_58): EmptyLayer()  )
代码语言:javascript复制
  (59): Sequential(    (conv_59): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)    )
  (60): Sequential(    (conv_60): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)    )
  (61): Sequential(    (shortcut_61): EmptyLayer()  )

(62): Sequential( (conv_62): Conv2d(512, 1024, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False) ) # 第五次下采样

代码语言:javascript复制
  (63): Sequential(    (conv_63): Conv2d(1024, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)   )
  (64): Sequential(    (conv_64): Conv2d(512, 1024, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)  )
  (65): Sequential(    (shortcut_65): EmptyLayer()  )
代码语言:javascript复制
  (66): Sequential(    (conv_66): Conv2d(1024, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)   )
  (67): Sequential(  (conv_67): Conv2d(512, 1024, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)    )
  (68): Sequential(    (shortcut_68): EmptyLayer()  )
代码语言:javascript复制
  (69): Sequential(    (conv_69): Conv2d(1024, 512, kernel_size=(1, 1), stride=(1, 1), bias=False) )
  (70): Sequential(    (conv_70): Conv2d(512, 1024, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)  )
  (71): Sequential(    (shortcut_71): EmptyLayer()  )
代码语言:javascript复制
  (72): Sequential(    (conv_72): Conv2d(1024, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)  )
  (73): Sequential(    (conv_73): Conv2d(512, 1024, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)    )
  (74): Sequential(    (shortcut_74): EmptyLayer()  )

代码语言:javascript复制
 (75): Sequential(    (conv_75): Conv2d(1024, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)  )
  (76): Sequential(    (conv_76): Conv2d(512, 1024, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)    )
  (77): Sequential(    (conv_77): Conv2d(1024, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)  )
  (78): Sequential(   (conv_78): Conv2d(512, 1024, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)   )
  (79): Sequential(    (conv_79): Conv2d(1024, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)  )
  (80): Sequential(    (conv_80): Conv2d(512, 1024, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)  )
  (81): Sequential(    (conv_81): Conv2d(1024, 255, kernel_size=(1, 1), stride=(1, 1))  )

(82): Sequential( (Detection_82): DetectionLayer() ) # 32倍降采样

(83): Sequential( (route_83): EmptyLayer() ) # 此处route层直接把79层处特征取过来

(84): Sequential( (conv_84): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False) )

(85): Sequential( (upsample_85): Upsample(scale_factor=2.0, mode=nearest) )

(86): Sequential( (route_86): EmptyLayer() ) # 此处route将85和61层的特征拼接在一起

代码语言:javascript复制
  (87): Sequential(    (conv_87): Conv2d(768, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)  )
  (88): Sequential(    (conv_88): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)     )
  (89): Sequential(    (conv_89): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)    )
  (90): Sequential(    (conv_90): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)   )
  (91): Sequential(    (conv_91): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)  )
  (92): Sequential(    (conv_92): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)   )
  (93): Sequential(    (conv_93): Conv2d(512, 255, kernel_size=(1, 1), stride=(1, 1))  )

(94): Sequential( (Detection_94): DetectionLayer() ) # 16倍降采样

(95): Sequential( (route_95): EmptyLayer() ) # 此处route层直接把91层处特征取过来

(96): Sequential( (conv_96): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1), bias=False) )

(97): Sequential( (upsample_97): Upsample(scale_factor=2.0, mode=nearest) )

(98): Sequential( (route_98): EmptyLayer() ) # 此处route将97层和36层特征拼接在一起

代码语言:javascript复制
  (99): Sequential(    (conv_99): Conv2d(384, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)  )
  (100): Sequential(    (conv_100): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)  )
  (101): Sequential(    (conv_101): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)   )
  (102): Sequential(    (conv_102): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)  )
  (103): Sequential(    (conv_103): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1), bias=False) ) 
 (104): Sequential(   (conv_104): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)  )
  (105): Sequential(    (conv_105): Conv2d(256, 255, kernel_size=(1, 1), stride=(1, 1))  )

(106): Sequential( (Detection_106): DetectionLayer() )) # 8倍降采样

0 人点赞