网络整体结构

YOLOv3原理代码赏析

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/19312022-cc26-4e80-8429-b237afcb9753/Untitled.png

YOLO网络部分可以分成特征提取backbone主体(darknet_body),Neck脖子(make_last_layer, upsamples),和头部(YOLO_head)

  1. backbone: 负责提取特征,输出feature map
  2. Neck: YOLO要把feature map中蕴含的信息转换为坐标,类别,这就需要把feature map的维度使用conv拉到指定的维度,结合anchor训练来输出关键性信息;同时为了提高回归位置精度,借鉴了fpn的思想,需要多个scale的feature map来提供最后的输出,因此还要用到一些upsample和concat
  3. head: 得到了网络的输出,要和真实数据标注对接上计算loss,需要对数据的格式进行reshape,同时对原始grid_cell中的(x,y,w,h)做相应的激活,yolo_head主要干这些 我们可以借鉴netron来分析网络层,整个yolo_v3_body包含252层
from  n    params  module                                  arguments
  0                -1  1       928  models.common.Conv                      [3, 32, 3, 1]
  1                -1  1     18560  models.common.Conv                      [32, 64, 3, 2]
  2                -1  1     20672  models.common.Bottleneck                [64, 64]
  3                -1  1     73984  models.common.Conv                      [64, 128, 3, 2]
  4                -1  2    164608  models.common.Bottleneck                [128, 128]
  5                -1  1    295424  models.common.Conv                      [128, 256, 3, 2]
  6                -1  8   2627584  models.common.Bottleneck                [256, 256]
  7                -1  1   1180672  models.common.Conv                      [256, 512, 3, 2]
  8                -1  8  10498048  models.common.Bottleneck                [512, 512]
  9                -1  1   4720640  models.common.Conv                      [512, 1024, 3, 2]
 10                -1  4  20983808  models.common.Bottleneck                [1024, 1024]
 11                -1  1   5245952  models.common.Bottleneck                [1024, 1024, False]
 12                -1  1    525312  models.common.Conv                      [1024, 512, [1, 1]]
 13                -1  1   4720640  models.common.Conv                      [512, 1024, 3, 1]
 14                -1  1    525312  models.common.Conv                      [1024, 512, 1, 1]
 15                -1  1   4720640  models.common.Conv                      [512, 1024, 3, 1]
 16                -2  1    131584  models.common.Conv                      [512, 256, 1, 1]
 17                -1  1         0  torch.nn.modules.upsampling.Upsample    [None, 2, 'nearest']
 18           [-1, 8]  1         0  models.common.Concat                    [1]
 19                -1  1   1377792  models.common.Bottleneck                [768, 512, False]
 20                -1  1   1312256  models.common.Bottleneck                [512, 512, False]
 21                -1  1    131584  models.common.Conv                      [512, 256, 1, 1]
 22                -1  1   1180672  models.common.Conv                      [256, 512, 3, 1]
 23                -2  1     33024  models.common.Conv                      [256, 128, 1, 1]
 24                -1  1         0  torch.nn.modules.upsampling.Upsample    [None, 2, 'nearest']
 25           [-1, 6]  1         0  models.common.Concat                    [1]
 26                -1  1    344832  models.common.Bottleneck                [384, 256, False]
 27                -1  2    656896  models.common.Bottleneck                [256, 256, False]
 28      [27, 22, 15]  1    457725  Detect                                  [80, [[10, 13, 16, 30, 33, 23], [30, 61, 62, 45, 59, 119], [116, 90, 156, 198, 373, 326]], [256, 512, 1024]]
Model Summary: 333 layers, 61949149 parameters, 61949149 gradients, 156.4 GFLOPS

部分层说明

YOLOv5网络结构学习_绿柳山庄赵公子的博客-CSDN博客

  1. Bottleneck层:

    一个1×1的卷积后接一个3×3的卷积,其中1×1的卷积将通道数减半,3×3的卷积将通道数加倍,然后加上输入(注意这里是add操作,不是concat操作)。所以经过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(Bottleneck, self).__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))
    

    注意此层的参数,参数shortcut表示是否使用残差。

  2. Concat层:

    把多个输入合成为一个输出。

    class Concat(nn.Module):
        # Concatenate a list of tensors along dimension
        def __init__(self, dimension=1):
            super(Concat, self).__init__()
            self.d = dimension
    
        def forward(self, x):
            return torch.cat(x, self.d)