YOLOV3代码主干部分代码解析

       最近在做课题用到了YOLOv3,之前在B站听bulingbuling大神讲的课收益颇丰,所以用的代码依然选择了bulingbuling的,先说说总体的感受吧,代码结构清晰,阅读起来非常方便,而且代码中已经有了很多注释,整体的逻辑就更加明朗,本人为了更加深刻理解代码,同时也为了备份,在大神注释的基础上进行添砖加瓦,解释错误的地方请各路大神多多指正。

本篇文章主要讲解yolov3中的darknet.py文件!

import math
from collections import OrderedDict

import torch.nn as nn


#---------------------------------------------------------------------#
#   残差结构
#   利用一个1x1卷积下降通道数,然后利用一个3x3卷积提取特征并且上升通道数
#   最后接上一个残差边
    首先对残差边进行了类定义,残差边中包含包含了两个卷积层,其中一个大小1*1,另外一个大小为3*3, 
    两个卷积层步长均为1,采用的是LeakyRelu函数
#---------------------------------------------------------------------#
class BasicBlock(nn.Module):
    def __init__(self, inplanes, planes):
        super(BasicBlock, self).__init__()
        self.conv1  = nn.Conv2d(inplanes, planes[0], kernel_size=1, stride=1, padding=0, bias=False)
        self.bn1    = nn.BatchNorm2d(planes[0])
        self.relu1  = nn.LeakyReLU(0.1)
        
        self.conv2  = nn.Conv2d(planes[0], planes[1], kernel_size=3, stride=1, padding=1, bias=False)
        self.bn2    = nn.BatchNorm2d(planes[1])
        self.relu2  = nn.LeakyReLU(0.1)
#---------------------------------------------------------------------#
#   这里本人简单的认为是feature map在上述残差边的传递顺序
#---------------------------------------------------------------------#
    def forward(self, x):
        residual = x

        out = self.conv1(x)
        out = self.bn1(out)
        out = self.relu1(out)

        out = self.conv2(out)
        out = self.bn2(out)
        out = self.relu2(out)
#---------------------------------------------------------------------#
#   将上述经过残差边的feature map(out)与输入进来的x进行加和,输出为out
#---------------------------------------------------------------------#
        out += residual
        return out

class DarkNet(nn.Module):
    def __init__(self, layers):
        super(DarkNet, self).__init__()
        self.inplanes = 32
#---------------------------------------------------------------------#
#   输出进来的RGB图片首先进行第一次卷积,大小不变,通道数由3变为32
#---------------------------------------------------------------------#
        # 416,416,3 -> 416,416,32
        self.conv1  = nn.Conv2d(3, self.inplanes, kernel_size=3, stride=1, padding=1, bias=False)
        self.bn1    = nn.BatchNorm2d(self.inplanes)
        self.relu1  = nn.LeakyReLU(0.1)
#---------------------------------------------------------------------#
#   如果查看过YOLOv3的网络结构图可以知道在主干网络(不包括残差边)上,一共包含6次卷积计算(上面的 
#   通道3变32算一次)下面就是剩余下的五次(在接下来的卷积层中均说的是这五个)
#---------------------------------------------------------------------#
        # 416,416,32 -> 208,208,64
        self.layer1 = self._make_layer([32, 64], layers[0])
        # 208,208,64 -> 104,104,128
        self.layer2 = self._make_layer([64, 128], layers[1])
        # 104,104,128 -> 52,52,256
        self.layer3 = self._make_layer([128, 256], layers[2])
        # 52,52,256 -> 26,26,512
        self.layer4 = self._make_layer([256, 512], layers[3])
        # 26,26,512 -> 13,13,1024
        self.layer5 = self._make_layer([512, 1024], layers[4])

        self.layers_out_filters = [64, 128, 256, 512, 1024]

        # 进行权值初始化
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
                m.weight.data.normal_(0, math.sqrt(2. / n))
            elif isinstance(m, nn.BatchNorm2d):
                m.weight.data.fill_(1)
                m.bias.data.zero_()

    #---------------------------------------------------------------------#
    #   在每一个卷积层中(即上述的五个卷积层)里面,首先利用一个步长为2的3x3卷积进行下采样(ds)
    #   然后进行残差结构的堆叠
    #   第一眼看到下面的代码时候我有点晕,首先是在self.inplanes处,通过上边的定义知道是32,那么            
    #    在ds_conv里只有一次输入是32,有时候也是64、128、256、512啊,原来在下面的代码中将输出的 
    #   通道数重新赋值给了self.inplanes实现了输入通道数64、128、256、512
    #---------------------------------------------------------------------#
    def _make_layer(self, planes, blocks):
        layers = []
        # 下采样,步长为2,卷积核大小为3
        layers.append(("ds_conv", nn.Conv2d(self.inplanes, planes[1], kernel_size=3, stride=2, padding=1, bias=False)))
        layers.append(("ds_bn", nn.BatchNorm2d(planes[1])))
        layers.append(("ds_relu",ds_conv nn.LeakyReLU(0.1)))
        # 加入残差结构
        self.inplanes = planes[1]
        for i in range(0, blocks):
            layers.append(("residual_{}".format(i), BasicBlock(self.inplanes, planes)))
        return nn.Sequential(OrderedDict(layers))
#---------------------------------------------------------------------#
#   仍然采用forward方法进行feature map的传递顺序,或与有人想要采用增加第四尺度104*104的预测,仅 
    需将x = self.layer2(x)变为out2 = self.layer2(x), x = self.layer2(out2)即可,return增加 
    一个out2,这种方法慎用,可能反而起到反作用,
#---------------------------------------------------------------------#
    def forward(self, x):
        x = self.conv1(x)
        x = self.bn1(x)
        x = self.relu1(x)

        x = self.layer1(x)
        x = self.layer2(x)
        out3 = self.layer3(x)
        out4 = self.layer4(out3)
        out5 = self.layer5(out4)

        return out3, out4, out5
#---------------------------------------------------------------------#
#   定义残差结构每阶段的个数
#---------------------------------------------------------------------#
def darknet53():
    model = DarkNet([1, 2, 8, 8, 4])
    return model

上述就是我对darknet.py的理解,有错误的地方请大佬多多指正,谢谢大家

更多细节请阅读大佬的博客

你可能感兴趣的:(python,cnn,pytorch)