zoukankan      html  css  js  c++  java
  • 【源码解读】YOLO v3 训练

      首先,看一下YOLO v3 中的网络结构。

    YOLO v3 的整体流程

      番外步骤: 对训练集图片标记后产生的数据进行K-Means处理,筛选9个anchor-box

      详见:https://www.cnblogs.com/monologuesmw/p/12761653.html

      进入YOLO v3的结构中:

      1. 图像缩放。将训练集的图片缩放至416*416中,包括两种缩放的方式:等比缩放和非等比缩放(非等比缩放一般只在训练中)图像缩放至416*416以后,便可以作为YOLO v3 结构的输入。

      2. Darknet-53下采样过程,其内部的结构可以看成一个3*3,步长(1*1)(fitters:32)的same卷积 和 若干个(一个3*3,步长(2*2),fitters总是比前面的fitters多一倍的窄卷积 和 fitters减半的1*1卷积、fitters恢复的3*3同卷积 )组成的残差组合块(组合的意思是,一个3*3步长(2*2)其实不是卷积块的内容,但二者有依存关系)。 这样就会将416*416*3 的图像转换为13*13*1024的feature_map。过程中会包含26*26*512的feature_map 和 52*52*256 的feature_map。其内部是这样定义的,步长(1,1)的为同卷积,步长为(2*2)的为窄卷积(使用步长为2的卷积代替pool操作)。

       3. 多尺度预测与特征融合上采样,YOLO v3与v2结构上的不同点就在于v3增添了多尺度的预测特征融合的上采样(不同大小的特征适应于不同大小物体的检测)。并且卷积的结构没有池化层,在卷积层后都会搭载一个Leaky-ReLU的激活函数,并且在激活函数的输入中不使用偏执bias

      在生成13*13*1024后,会再经历两对1*1和3*3卷积,使得三种尺度的feature_map变为13*13*512, 26*26*256。(因为最大尺度的是52*52*256),然后通过上采样的方式,13*13*512变为26*26*512 ,26*26*256变为52*52*256, 这样便可以与高尺度的拼接融合。生成最终的13*13*255、26*26*255 、52*52*255的feature_map输出。

      P.S. 上采样的内部机制实际上是最近邻域插值的方式使维度翻倍。

      先一睹YOLOv3具体的网络结构:

    代码解析

    1. Darknet53部分

    1 darknet = Model(inputs, darknet_body(inputs)) # 此处返回了第一个下采样13*13 (?,416*416*32)->(?,13*13*1024)

      darknet_body(x)内部

     1 def darknet_body(x):
     2     '''Darknent body having 52 Convolution2D layers
     3     有52个卷积层(2D)
     4     '''
     5     x = DarknetConv2D_BN_Leaky(32, (3, 3))(x)      # (内置same卷积)输出的x-》416*416*32
     6     x = resblock_body(x, 64, 1)   # num_filters = 64 , num_blocks = 1(重复次数)  返回结果208*208*64
     7     x = resblock_body(x, 128, 2)    # 返回结果 104*104*128
     8     x = resblock_body(x, 256, 8)    # 返回结果 52*52*256
     9     x = resblock_body(x, 512, 8)    # 返回结果 26*26*512
    10     x = resblock_body(x, 1024, 4)    # 返回结果 13*13*1024     5组重复的resblock_body()单元
    11     return x         # 第一个下采样特征生成

      上述代码是Darknet53的主体部分。

     a. 其中,第5行是 一个3*3,步长(1*1)(fitters:32)的same卷积。 其中包含:

    •  1个Darknet的2维卷积Conv2D层,即DarknetConv2D();
    •  1个批归一化(BN)层,即BatchNormalization();
    •  1个LeakyReLU层,斜率是0.1,LeakyReLU是ReLU的变换;
    1 def DarknetConv2D_BN_Leaky(*args, **kwargs):
    2     """Darknet Convolution2D followed by BatchNormalization and LeakyReLU."""
    3     no_bias_kwargs = {'use_bias': False}
    4     no_bias_kwargs.update(kwargs)
    5     return compose(
    6         DarknetConv2D(*args, **no_bias_kwargs),
    7         BatchNormalization(),
    8         LeakyReLU(alpha=0.1))  

     b. 第6-9行,为若干个残差块组。(1+2+8+8=19个重复次数)fitters的通道数会逐渐倍增(深度逐渐加深)。最终返回的x为13*13*1024

    • ZeroPadding2D():填充x的边界为0,由(?, 416, 416, 32)转换为(?, 417, 417, 32)。

      因为下一步卷积操作的步长为2,所以图的边长需要是奇数;--- 需要padding

    • DarknetConv2D_BN_Leaky()是DarkNet的2维卷积操作,核是(3,3),步长是(2,2),

      注意,这会导致特征尺寸变小,由(?, 417, 417, 32)转换为(?, 208, 208, 64)。由于num_filters是64,所以产生64个通道。

    • compose():输出预测图y,功能是组合函数,先执行1x1的卷积操作,再执行3x3的卷积操作,filter先降低2倍后恢复,最后与输入相同,都是64; --- 残差块的另一分支
    • x = Add()([x, y])是残差(Residual)操作,将x的值与y的值相加。残差操作可以避免,在网络较深时所产生的梯度弥散问题(Vanishing Gradient Problem)。
     1 def resblock_body(x, num_filters, num_blocks):
     2     '''A series of resblocks starting with a downsampling Convolution2D
     3     '''
     4     # Darknet uses left and top padding instead of 'same' mode
     5     x = ZeroPadding2D(((1, 0), (1, 0)))(x)   # 增加一圈0 x有(?,416,416,32)-> (?,417,417,32)  Keras中的方法
     6     x = DarknetConv2D_BN_Leaky(num_filters, (3, 3), strides=(2, 2))(x)   # 步长上下2,则是窄卷积, 因此上述需padding(才会没有边缘遗失)
     7     for i in range(num_blocks):               # 此时,x的输出为208*208*64   (重复次数的循环)
     8         y = compose(
     9                 DarknetConv2D_BN_Leaky(num_filters//2, (1, 1)),   # 此时的输出为208*208*32  降一半
    10                 DarknetConv2D_BN_Leaky(num_filters, (3, 3)))(x)      # 此时的输出为208 * 208 *64  恢复
    11         x = Add()([x, y])   # 将残差块的结果加到源网络的结构上  (不影响运算) 208*208*64
    12     return x

    2. Darknet输出至y1,y2,y3部分(即13*13*1024至13*13*255. etc)

      A. 生成y1 ----> 13*13*255

    1 x, y1 = make_last_layers(darknet.output, 512, num_anchors*(num_classes+5))

      make_last_layers()的内部

      该函数已经是 特征图输出的计算部分,结合结构图来观察,在13*13*1024的地方至13*13*255的y1处的运算:

    • 第1步,x执行多组1x1的卷积操作和3x3的卷积操作,filter先缩小再恢复,最后与输入的filter保持不变,仍为512(num_filters),则x由(?, 13, 13, 1024)转变为(?, 13, 13, 512);
    • 第2步,x先执行3x3的卷积操作,再执行不含BN和Leaky的1x1的卷积操作,作用类似于全连接操作,生成预测矩阵y;
    •  从结构上可以看出,x此时有两个任务,一个是继续y的运算,作为y1特征的输出;另一个是需要上采样到26*26的y2中。,所以分为了x和y两个部分,同时也会返回这两个部分。
     1 def make_last_layers(x, num_filters, out_filters):
     2     '''6 Conv2D_BN_Leaky layers followed by a Conv2D_linear layer
     3     '''
     4     x = compose(
     5             DarknetConv2D_BN_Leaky(num_filters, (1, 1)),
     6             DarknetConv2D_BN_Leaky(num_filters*2, (3, 3)),
     7             DarknetConv2D_BN_Leaky(num_filters, (1, 1)),
     8             DarknetConv2D_BN_Leaky(num_filters*2, (3, 3)),
     9             DarknetConv2D_BN_Leaky(num_filters, (1, 1)))(x)   # 此时13*13*512
    10     y = compose(
    11             DarknetConv2D_BN_Leaky(num_filters*2, (3, 3)),
    12             DarknetConv2D(out_filters, (1,1)))(x)
    13     return x, y  

      B. 生成y2 ---》26*26*255 此部分会涉及到尺度特征融合的部分,即13*13*512的上采样过程。

    • 13*13*512--》13*13*256
    • 13*13*256 上采样 26*26*256
    • 融合后的结果 26*26*768 (原来的是26*26*512,融合后即512+256 = 768)
    1 # 第二部分 26*26*256 的y2
    2 x = compose(
    3         DarknetConv2D_BN_Leaky(256, (1,1)),          # 此时x的为13*13*256
    4         UpSampling2D(2))(x)   # x需要参加的上采样   此时的x为26*26*256
    5 x = Concatenate()([x, darknet.layers[152].output])  # 把之前结果的第152层的输出提取,即26*26*512的输出 即13*13*1024的前一层输出
    6 x, y2 = make_last_layers(x, 256, num_anchors*(num_classes+5))    

      C. 生成y3 ----》52*52*255 此部分包含26*26*256的上采样过程

    • 26*26*256 --》 26*26*128
    • 26*26*128上采样到52*52*128
    • 融合后的结果52*52*384 (128+256=384)
    1 # 第三部分 52*52*128*18(18=3*(1+5)1个类别,3个锚框) 的y3
    2 x = compose(
    3         DarknetConv2D_BN_Leaky(128, (1,1)),
    4         UpSampling2D(2))(x)      # 同样需要26的x 的上采样
    5 x = Concatenate()([x,darknet.layers[92].output])
    6 x, y3 = make_last_layers(x, 128, num_anchors*(num_classes+5))

      当然,此时返回的这个x就没有什么任务了。

      模型结构创建完毕。

      可以看出,Darknet的框架是由多组Conv+Conv+Residual的结构叠加而成的。

      即1+1+1*2+1+2*2+1+8*2+1+8*2+1+4*2 = 52

      配置文件中 ############# 的分割 就是结构中darknet的结束。

      

      

      

      

  • 相关阅读:
    Spring Cloud Hystrix Dashboard的使用 5.1.3
    Spring Cloud Hystrix 服务容错保护 5.1
    Spring Cloud Ribbon 客户端负载均衡 4.3
    Spring Cloud 如何实现服务间的调用 4.2.3
    hadoop3.1集成yarn ha
    hadoop3.1 hdfs的api使用
    hadoop3.1 ha高可用部署
    hadoop3.1 分布式集群部署
    hadoop3.1伪分布式部署
    KVM(八)使用 libvirt 迁移 QEMU/KVM 虚机和 Nova 虚机
  • 原文地址:https://www.cnblogs.com/monologuesmw/p/12793758.html
Copyright © 2011-2022 走看看