Darknet53網絡結構圖及代碼實現
本文鏈接: https://blog.csdn.net/leiduifan6944/article/details/104857968
Darknet是最經典的一個深層網絡,結合Resnet的特點在保證對特徵進行超強表達的同時又避免了網絡過深帶來的梯度問題,主要有Darknet19和Darknet53,當然,如果你覺得這還不夠深,在你條件允許的情況下你也可以延伸到99,199,999,…。
1、結構圖大致如下
(這張圖是從網上扒來的,湊合着,懶得自己畫了)
2、清楚結構之後,那麼,這麼深的網絡,一層一層的寫出來的話,每層還有卷積、歸一化、激活…那得寫多少行才能寫完呀?
莫捉急,清楚了結構,再把他狠狠封裝成塊,一波帶走
1、先把卷積裝一塊
class Conv(nn.Module):
def __init__(self, c_in, c_out, k, s, p, bias=True):
"""
自定義一個卷積塊,一次性完成卷積+歸一化+激活,這在類似於像DarkNet53這樣的深層網絡編碼上可以節省很多代碼
:param c_in: in_channels
:param c_out: out_channels
:param k: kernel_size
:param s: stride
:param p: padding
:param bias: …
"""
super(Conv, self).__init__()
self.conv = nn.Sequential(
nn.Conv2d(c_in, c_out, k, s, p),
nn.BatchNorm2d(c_out),
nn.LeakyReLU(0.1),
)
def forward(self, entry):
return self.conv(entry)
2、再把殘差單元裝一塊
class ConvResidual(nn.Module):
def __init__(self, c_in): # converlution * 2 + residual
"""
自定義殘差單元,只需給出通道數,該單元完成兩次卷積,並進行加殘差後返回相同維度的特徵圖
:param c_in: 通道數
"""
c = c_in // 2
super(ConvResidual, self).__init__()
self.conv = nn.Sequential(
Conv(c_in, c, 1, 1, 0), # kernel_size = 1進行降通道
Conv(c, c_in, 3, 1, 1), # 再用kernel_size = 3把通道升回去
)
def forward(self, entry):
return entry + self.conv(entry) # 加殘差,既保留原始信息,又融入了提取到的特徵
# 採用 1*1 + 3*3 的形式加深網絡深度,加強特徵抽象
3、最後按照結構圖把殘差和卷積裝一塊
class Darknet53(nn.Module):
def __init__(self):
super(Darknet53, self).__init__()
self.conv1 = Conv(3, 32, 3, 1, 1) # 一個卷積塊 = 1層卷積
self.conv2 = Conv(32, 64, 3, 2, 1)
self.conv3_4 = ConvResidual(64) # 一個殘差塊 = 2層卷積
self.conv5 = Conv(64, 128, 3, 2, 1)
self.conv6_9 = nn.Sequential( # = 4層卷積
ConvResidual(128),
ConvResidual(128),
)
self.conv10 = Conv(128, 256, 3, 2, 1)
self.conv11_26 = nn.Sequential( # = 16層卷積
ConvResidual(256),
ConvResidual(256),
ConvResidual(256),
ConvResidual(256),
ConvResidual(256),
ConvResidual(256),
ConvResidual(256),
ConvResidual(256),
)
self.conv27 = Conv(256, 512, 3, 2, 1)
self.conv28_43 = nn.Sequential( # = 16層卷積
ConvResidual(512),
ConvResidual(512),
ConvResidual(512),
ConvResidual(512),
ConvResidual(512),
ConvResidual(512),
ConvResidual(512),
ConvResidual(512),
)
self.conv44 = Conv(512, 1024, 3, 2, 1)
self.conv45_52 = nn.Sequential( # = 8層卷積
ConvResidual(1024),
ConvResidual(1024),
ConvResidual(1024),
ConvResidual(1024),
)
def forward(self, entry):
conv1 = self.conv1(entry)
conv2 = self.conv2(conv1)
conv3_4 = self.conv3_4(conv2)
conv5 = self.conv5(conv3_4)
conv6_9 = self.conv6_9(conv5)
conv10 = self.conv10(conv6_9)
conv11_26 = self.conv11_26(conv10)
conv27 = self.conv27(conv11_26)
conv28_43 = self.conv28_43(conv27)
conv44 = self.conv44(conv28_43)
conv45_52 = self.conv45_52(conv44)
return conv45_52, conv28_43, conv11_26 # YOLOv3用,所以輸出了3次特徵
What?名字不是叫53嗎? 這裏只有52層?原因是原本的Darknet53還包括一層輸出層,前52層用於特徵提取,最後一層進行最終輸出。這裏就根據自己實際需求再定義一層或多層對前52層提取到的特徵進行融合和輸出。是不是感覺一下子就豁然開朗了,就53行代碼盡然就把經典的深度網絡模型Darknet53寫出來了