參考地址:https://blog.csdn.net/leviopku/article/details/82660381
YOLO v3結構圖
DBL:卷積+BN+leaky relu,是v3的最小組件
resn:n代表數字,有res1,res2,...,res8等,表示這個res_block里含有多少個res_unit。這是YOLO-v3的大組件,YOLO-v3借鑒了ResNet的殘差結構,使用這個結構可以讓網絡更深(從v2的darknet-19上升到darknet-53,前者沒有殘差結構)。其實resn的基本組件也是DBL
concat:張量拼接。將darknet中間層和后面的某一層的上采樣進行拼接。拼接的操作和殘差層add的操作是不一樣的,拼接會擴充張量的維度,而add只是直接相加不會導致張量維度的改變。
可以使用netron來分析網絡層,整個YOLO-v3包含252層
從圖中可以看出,一共有252層。包括add層23層(主要用於res_block的構成,每個res_unit需要一個add層,一共有1+2+8+8+4=23層)。除此之外,BN層和LeakyReLU層數量完全一樣(72層),在網絡結構中表現為:每一層BN后面都會接一層LeakyReLU。卷積層一共有75層,其中72層后面都會接BN+LeakyReLU的組合構成基本組件DBL,還有三個卷積用於最后的輸出。一共有5個零填充層,表現為每一個res_block都會用上一個零填充,一共有5個零填充
1. backbone(骨架)
整個v3結構里面,是沒有池化層和全連接層的,前向傳播中,張量的尺寸變換是通過改變卷積核的步長來實現的,比如stride=(2, 2),這就等於將圖像邊長縮小了一半(即面積縮小到原來的1/4)。在YOLO-v2中,要經歷5次縮小,將特征圖縮小到原輸入尺寸的1/32,即輸入為416x416,輸出為13x13(416/32=13)。
YOLO-v3也和v2一樣,backbone都會講輸出特征圖縮小到輸入的1/32。所以,通常要求輸入圖片是32的倍數。下圖是v2和v3的backbone對比(DarkNet-19與DarkNet-53)
YOLO-v2中對於前兩過程中張量尺寸變換,都是通過最大池化來進行,一共有5次,而v3是通過卷積核增大步長來進行,也是5次(darknet-53最后面有一個全局平均池化,在YOLO-v3里面沒有這一層,所以張量維度變化只考慮前面那5次)
這也是416x416輸入得到13x13輸出的原因。從圖中可以看出darknet-19不存在殘差結構
從上表中也可以看出,darknet-19在速度上任然占據很大優勢。其實在其他細節也可以看出(比如bounding box prior采用k=9,而v2中k=5,明顯計算復雜度超過v2,隨之而來的是速度比v2要慢),YOLO-v3並沒有那么追求速度,而是在保證實時性的同時(fps>60)的基礎上追求performance。當然,如果想速度更快,可以使用tiny-darknet作為backbone替代darknet-53,在官方里用一行代碼就可以切換backbone。使用tiny-darknet的YOLO,也就是tiny-YOLO在輕量和高速兩個特點上,顯然是最先進的。
所以,有了YOLO v3,就真的用不着YOLO v2,更用不着YOLO v1了,這也是YOLO官方網站(https://pjreddie.com/darknet/),在v3出來以后,就沒提供v1和v2代碼下載鏈接的原因
2. Output
YOLO v3輸出了3個不同尺度的feature map,如上圖所示的y1,y2,y3,這也就是跨尺度預測 (predictions across scales)。這個借鑒了FPN(feature pyramid networks,特征金字塔網絡),采用多尺度來對不同size的目標進行檢測,越精細的grid cell就可以檢測出越精細的物體
y1,y2,y3的深度都是255,邊長的規律是13:26:52
對於coco類別而言,有80個種類,所以每個box應該對每個種類都輸出一個概率。YOLO v3設定的是每個網格單元預測3個box,所以每個box需要有(x,y,w,h,confidence)五個基本參數,然后還要有80個類別的概率。所以3*(5+80)=255。255就是這么來的
3. some tricks
Bounding Box Prediction
先說明一下v2的b-box預測:它借鑒了faster R-CNN RPN中的anchor機制,但不屑於手動設定anchor prior(模板框),於是用維度聚類的方法大來確定anchor box prior,最后選用了k=5。后來,v2又嫌棄anchor機制線性回歸的不穩定性(因為回歸的offset可以使box偏移到圖片的任何地方),所以v2最后采用了自己的方法:直接預測相對位置。預測出b-box中心點相對於網格單元左上角的相對坐標
對於v3而言,選用的b-box priors的k=9,對於tiny-yolo的話,k=6。priors都是在數據集上聚類得來的。每個anchor prior(名字叫anchor prior,但不是用anchor機制)就是兩個數字組成的,一個代表高度另一個代表寬度。
v3對b-box進行預測的時候,采用了logistic regression。v3每次對b-box進行預測時,輸出和v2一樣都是,通過公式1計算出絕對的
(x,y,w,h,c)。
logistic回歸用於對anchor包圍的部分進行一個目標性評分(objectness score),即這塊位置是目標的可能性有多大。這一步是在predict之前進行的,可以去掉不必要的anchor,可以減少計算量。
如果模板框不是最佳的,即使它超過我們設定的閾值,我們還是不會對它進行預測,不同於faster R-CNN的是,YOLO v3只會對一個prior進行操作,也就是那個最佳的prior。而logistic回歸就是從9個anchor priors中找到目標性評分最高的那一個。logistic回歸就是用曲線對prior相對於 objectness score映射關系的線性建模
4. 損失函數
在YOLO系列中,v1使用了sum-square error的損失計算方法,就是簡單的差方相加而已。而在v3中沒有明確的提到損失函數。在目標檢測任務中,有幾個關鍵信息是需要確定的:
(x,y),(w,h),class,confidence
根據關鍵信息的特點可以分為上述四類,損失函數應該由各自特點確定。最后加到一起就可以組成最終的loss_function了,也就是一個loss_function就搞定了端到端的訓練。可以從代碼分析出v3的損失函數,同樣是對以上四類,不過相對於v1中簡單的總方誤差,還是有些調整的:
xy_loss = object_mask * box_loss_scale * K.binary_crossentropy(raw_true_xy, raw_pred[..., 0:2], from_logits=True) wh_loss = object_mask * box_loss_scale * 0.5 * K.square(raw_true_wh - raw_pred[..., 2:4]) confidence_loss = object_mask * K.binary_crossentropy(object_mask, raw_pred[..., 4:5], from_logits=True) + \ (1 - object_mask) * K.binary_crossentropy(object_mask, raw_pred[..., 4:5], from_logits=True) * ignore_mask class_loss = object_mask * K.binary_crossentropy(true_class_probs, raw_pred[..., 5:], from_logits=True) xy_loss = K.sum(xy_loss) / mf wh_loss = K.sum(wh_loss) / mf confidence_loss = K.sum(confidence_loss) / mf class_loss = K.sum(class_loss) / mf loss += xy_loss + wh_loss + confidence_loss + class_loss
以上是keras框架描述的YOLO v3的loss_function代碼。忽略恆定系數不看,可以從上述代碼看出:除了w,h的損失函數依然采用總方誤差之外,其他部分的損失函數用的是二元交叉熵(binary_crossentropy)。這個一般用作二分類的損失函數。