Darknet-53 模型结构:
好了 下面我们开始讲这个模型结构,yolov3的主体模型结构叫做Darknet-53。这里之所以叫53的意思是这个网络结构有53个卷积层。我们可以数一下,每个Convolutional代表一个卷积层,旁边的数代表这个卷积层有几个类似的。且最下面的Connected的全连接层也代表一个卷积层,因此(2+1*2+1+2*2+1+2*8+1+2*8+1+4*2+1)= 53
另外还有一点需要强调的是网络结构图中每个卷积层Convolutional的完整构成是:
(卷积层 + BN层 + LeakyRelu层) = Convolutional
这里我们简单介绍一下LeakyReLU激活函数:
与Relu激活将负数部分前部置为0不一样的是,LeakyReLU激活函数则是保留负数部分对模型带来的一点影响,给负数部分一个很小的非零的倾斜率,让其对模型保留相应的影响效果,不会完全失效。
从网络结构图可以看出这些卷积核都是有3*3和1*1的卷积核构成。yolov3的很大的一个改进之处是借鉴了Resnet的残差结构,残差结构的好处是可以增加相当部分的网络深度而不会出现梯度爆炸等问题,因此可以加深网络进行提高网络的性能。
在论文中作者关于这个网络结构的描述很模糊,我还是看了其他大佬的讲解和源代码才弄清楚Darknet-53 的结构。
下面是Darknet-53 的pytorch源代码
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) 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) out += residual return out class DarkNet(nn.Module): def __init__(self, layers): super(DarkNet, self).__init__() self.inplanes = 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) # 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_() #---------------------------------------------------------------------# # 在每一个layer里面,首先利用一个步长为2的3x3卷积进行下采样 # 然后进行残差结构的堆叠 #---------------------------------------------------------------------# 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", 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)) 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
如果光看论文的结构图感觉看不懂网络结构的可以读一下源代码就会清晰明了。(源码中是以416*416作为输入进行写的代码)
目标边界框的预测回归:
这就涉及到yolo的另一个定位的功能了,因为跟传统的目标检测不同的是,我们不仅需要识别目标的种类,还要对目标的位置进行定位。
σ(x)函数是sigmoid函数其目的是将预测的结果缩放到0到1之间。这里虚线框为anchor先验框的模板,pw,ph是模板的宽和高,tx,ty,tw,th分别为我们网络预测的偏移之后的目标中心坐标和需要对应到长款bw,bh的关键参数tw,th,这里可以理解为长的关键参数tw(即为影响长的缩放参数),宽的关键参数tw(即为影响宽的缩放参数)。
最终边界框的中心坐标为(bx,by),预测的目标框的长宽为bw,bh。
具体的计算方式如下:
简单总结一下分三步:
第一步:我们会在网络最开始的设置的时候得到pw,ph(先验框的长宽是最开始设置好的),也就是所谓anchor的长宽,Cx,Cy为Grid Cell(单元格)左上角的坐标
第二步:我们经过模型的预测获取tx,ty,tw,th四个参数
第三步:将参数进行如下图的运算,最终边界框的中心坐标为(bx,by),预测的目标框的长宽为bw,bh。
sigmoid函数其目的是将预测的结果缩放到0到1之间(这样能够将每个Grid Cell(单元格)中预测的边界框的中心坐标保持在在当前Grid Cell(单元格)当中)。因为如果不这样做,可能tx或者ty太大会导致偏移过大超出当前单元格,这就不是我们想看到的结果了。
损失函数和其计算方法:
注明:参考b站霹雳大神的ppt
yolov3的损失像他的名字一样,分为三种
all——loss = x1*目标定位偏移损失 + x2*目标分类损失 + x3*置信度confidence损失
x1 , x2 , x3为平衡系数
分类损失(划重点——只有正样本才有目标定位损失):
yolov3采用的是二值交叉熵损失(按照我的理解是,这不就是跟经典CNN模型一样嘛,这样yolov3不仅检测效果好,识别效果也好)
置信度损失:
这里这个IOU是交并比的意思,即为真实目标与预测目标交并的部分比上两者全部的部分,
定位损失(划重点——只有正样本才有目标定位损失):
真实目标矩形框是事先标注好的GTBox,默认矩形框为Anchor,预测的目标矩形框就是网络最终预测的结果(最终的结果 = 网络预测的回归参数在默认矩形框上调整的结果)
gx , gy , gw , gh 分别代表GT Boxes中心点的坐标x, y以及宽度和高度(对应到grid cell单元格中)。