【yolo目标检测】(1) yolov3,网络结构Darknet53,特征提取
各位同學好,今天和大家分享一下使用Tensorflow2.0進行yolov3目標檢測,如何構建Darknet53整體網絡結構,如何使用特征金字塔強化特征提取。
1. 網絡簡介
yolov3借鑒了resnet的殘差單元,在加深網絡層數提高精度的同時大大降低了計算量。在yolov3中沒有池化層和全連接層。張量的尺度變換是通過改變卷積核的步長來實現的(也就是通過卷積實現下采樣)。例如stride=(2,2),相當于將圖像邊長縮小一般,整個特征圖縮小2^2倍。詳細見:目標檢測之YOLO v3(附代碼詳細解析) - 知乎,下圖為yolov3的網絡結構。
2.?卷積模塊
卷積模塊一般是指卷積層+批歸一化+激活函數,在卷積層函數中,為了方便處理參數,設置*args用于接收卷積核個數和卷積核大小,**kwargs存放用于設置的權重正則化方法,在這里使用 keras.regularizers.l2正則化方法;當卷積層后面接批標準化BN層,就不需要使用偏置,會導致占用內存。
# 卷積
def Conv(x, *args, **kwargs):# 列表一個*號: *args代表卷積核數量filter和尺寸kernel_size,# 字典兩個**號: **kwargs代表卷積部分其他參數,new_kwargs = {'kernel_regularizer': l2(5e-4), 'use_bias':False} #l2正則化,有BN層不需要偏置# new_kwargs是原來的字典參數,傳進來新的參數,比如步長,需要更新字典參數# 如果步長為2,填充方式需要變成'valid',步長為1填充方式為'same'new_kwargs['padding'] = 'valid' if kwargs.get('strides') == (2,2) else 'same' #字典添加元素# .get()返回指定key的值# .update()把字典kwargs的鍵和值更新到new_kwargs中去new_kwargs.update(kwargs) # 把步長更新進去,確定填充方式# 卷積,使用更新后的參數x = layers.Conv2D(*args, **new_kwargs)(x)return x# 卷積+BN+激活
def CBL(x, *args, **kwargs):# 卷積x = Conv(x, *args, **kwargs)# 批歸一化x = layers.BatchNormalization()(x)# 激活函數x = LeakyReLU(alpha=0.1)(x)return x
3. 殘差模塊
殘差模塊最顯著的特點是使用了 short cut 機制(有點類似于電路中的短路機制)來緩解在神經網絡中增加深度帶來的梯度消失問題,從而使得神經網絡變得更容易優化。它通過恒等映射(identity mapping)的方法使得輸入和輸出之間建立了一條直接的關聯通道,從而使得網絡集中學習輸入和輸出之間的殘差。
使用layers.ZeroPadding2D()方法用0填充圖像,size為偶數的圖像在使用步長為(2,2)的卷積可以得到偶數size的圖像。例如,[416,416,3]的圖像0填充后變成[417,417,3],經過卷積核size=(3,3),步長strides=(2,2)的卷積,圖像size變化為(417-3+1)/2 = 208
# 0填充+卷積+BN+激活
def PCBL(x, num_filter):# 補0,將圖片size補成奇數再降維得到偶數sizex = layers.ZeroPadding2D(((1,0),(1,0)))(x)# 卷積層,下采樣,步長為2# 傳入的*args有卷積核個數和大小,*kwargs有步長x = CBL(x, num_filter, (3,3), strides=(2,2))return x# 2次(卷積+BN+激活) + 殘差結構,需要把最后的輸出與輸入相加
def CBLR(x, num_filter):# 卷積層y = CBL(x, num_filter, (1,1)) # 步長默認為1# 卷積層y = CBL(y, num_filter*2, (3,3))# 殘差,add()相應元素相加,h,w,c都不變x = layers.Add()([x,y]) #殘差塊,將輸入和輸出相加return x
4. 網絡組件
CBL5用于處理各尺度輸出后的特征圖,如最后一層大尺度目標檢測的輸出結果為[13,13,1024],經過五次卷積層進一步提取特征,從[13,13,1024]==>[13,13,512]==>[13,13,1024]==>[13,13,512]==>[13,13,1024]==>[13,13,512]
特征提取后的結果,一是用于大尺度目標檢測;二是用于上采樣函數CBLU,從[13,13,512]==>[26,26,256],長寬都變為原來的2倍,和中尺度輸出的特征圖像[26,26,512]疊加,即特征融合layers.Concatenate(),圖像size不變,只增加通道數,得到疊加后的特征圖的shape為[26,26,768],也是分別用于中尺度目標檢測,和上采樣后與小尺度圖像疊加。
# 處理輸出結果的各尺度圖像
def CBL5(x, num_filter):# 卷積+BN+激活x = CBL(x, num_filter, (1,1))x = CBL(x, num_filter*2, (3,3))x = CBL(x, num_filter, (1,1))x = CBL(x, num_filter*2, (3,3))x = CBL(x, num_filter, (1,1))return x# 規整通道數得輸出
def CBLC(x, num_filter, out_filter):# 上升通道數,提特征x = CBL(x, num_filter*2, (3,3))# 卷積降維,規整通道數,得輸出結果的通道數,根據需求設置x = Conv(x, out_filter, (1,1))return x# 上采樣用于下一個尺度--圖像的size變大
def CBLU(x, num_filter):# 1*1卷積傳遞圖像信息x = CBL(x, num_filter, (1,1))# 上采樣,寬高size各變成原來的2倍x = layers.UpSampling2D(2)(x)return x
5. 網絡主體
darknet53網絡結構如下:
使用特征金字塔FPN的思想,采用多尺度來對不同size的目標進行檢測,這也對應了網絡結構圖的三個預測分支(輸出)y1,y2,y3(每一個特征圖會分配3個先驗框,一共9個先驗框)。y1,y2和y3的深度都是255,邊長的規律是13:26:52,越小的特征圖代表下采樣的倍數高,而感受野也大,所以13×13的y1特征圖對應檢測大物體,26×26的y2特征圖對應檢測中物體,52×52的y3特征圖對應檢測小物體。
def body(inputs, num_anchors, num_classes): # 預選框數目、類別數目# 存放最后三尺度的輸出結果out = []# CBL層,卷積+BN+激活,給定卷積核32個,size(3,3)x = CBL(inputs, 32, (3,3)) # CBLR在兩兩循環下的自身的循環次數n = [1, 2, 8, 8, 4]#(1)網絡構建# PCBL+CBLR出現連著出現五次for i in range(5):# PCBL==> zeropadding + conv + BN + 激活x = PCBL(x, 2**(6+i)) #卷積核個數為2^(6+i)# CBLR自身也有一個循環for _ in range(n[i]):# 殘差結構x = CBLR(x, 2**(5+i))# darknet53構建完成#(2)輸出最后三尺度的圖像if i in [2,3,4]:out.append(x)#(3)處理每個尺度的圖像# ==1== 大尺度13*13*1024x1 = CBL5(out[2], 512) #[13,13,512]# 普通卷積得輸出結果,將通道數變成輸出結果的分類個數y1 = CBLC(x1, 512, num_anchors*(num_classes + 5)) # 輸出結果,和預選框的數目和識別物體的種類有關# 5代表box的坐標(x,y), 寬w, 高h, 置信度c# 上采樣用于中尺度目標檢測x = CBLU(x1, 256) #[26,26,256]# 大尺度上采樣的返回結果與中尺度圖像拼接# Concatenate()拼接,h,w不變,通道數增加x = layers.Concatenate()([x, out[1]]) #out[1]代表中尺度的圖像# [26,26,256] + [26,26,512] = [26,26,768]# ==2== 中尺度26*26*768x2 = CBL5(x, 256) #[26,26,256]# 卷積得輸出結果y2 = CBLC(x2, 256, num_anchors*(num_classes + 5)) #[26,26,N]# 中尺度上采樣x = CBLU(x2, 128) #[52,52,128]# 與小尺度拼接x = layers.Concatenate()([x, out[0]])# [52,52,128] + [52,52,256] = [52,52,384]# ==3== 小尺度52*52*384x3 = CBL5(x, 128) # [52,52,128]y3 = CBLC(x3, 128, num_anchors*(num_classes + 5)) #[52,52,N]# 返回三個尺度信息return [y1, y2, y3]
6. 查看網絡結構
num_anchors = 3 # 預選框數目
num_classes = 5 # 識別種類,可改# 輸入層
input_tensor = keras.Input(shape=(416,416,3))
# 輸出層,預選框數目和,分類數目
output_tensor = body(input_tensor, num_anchors, num_classes)
# 構建網絡
model = Model(input_tensor, output_tensor)
# input_tensor是416*416*3的圖片,output_tensor是大中小三個尺度的數組# 查看網絡結構
model.summary()
---------------------------------------------------------------
省略 N 層
--------------------------------------------------------------- Normalization) batch_normalization_143 (Batch (None, 52, 52, 256) 1024 ['conv2d_148[0][0]'] Normalization) leaky_re_lu_129 (LeakyReLU) (None, 13, 13, 1024 0 ['batch_normalization_129[0][0]']) leaky_re_lu_136 (LeakyReLU) (None, 26, 26, 512) 0 ['batch_normalization_136[0][0]']leaky_re_lu_143 (LeakyReLU) (None, 52, 52, 256) 0 ['batch_normalization_143[0][0]']conv2d_133 (Conv2D) (None, 13, 13, 30) 30720 ['leaky_re_lu_129[0][0]'] conv2d_141 (Conv2D) (None, 26, 26, 30) 15360 ['leaky_re_lu_136[0][0]'] conv2d_149 (Conv2D) (None, 52, 52, 30) 7680 ['leaky_re_lu_143[0][0]'] ==================================================================================================
Total params: 61,597,792
Trainable params: 61,545,184
Non-trainable params: 52,608
__________________________________________________________________________________________________
總結
以上是生活随笔為你收集整理的【yolo目标检测】(1) yolov3,网络结构Darknet53,特征提取的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【神经网络】(8) 卷积神经网络(Mob
- 下一篇: 【MediaPipe】(1) AI视觉,