最近在做課題用到了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的理解,有錯誤的地方請大佬多多指正,謝謝大家
更多細節請閱讀大佬的博客
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/376063.html
標籤:AI
