https://blog.csdn.net/leviopku/article/details/82660381
版權(quán)申明:轉(zhuǎn)載和引用圖片缘回,都必須經(jīng)過書面同意帆离。獲得留言同意即可
本文使用圖片多為本人所畫尔邓,需要高清圖片可以留言聯(lián)系我,先點(diǎn)贊后取圖
這篇博文比較推薦的yolo v3代碼是qwe的keras版本豪治,復(fù)現(xiàn)比較容易叼丑,代碼相對(duì)來說比較容易理解。同學(xué)們可以結(jié)合代碼和博文共同理解v3的精髓陆赋。
github地址:https://github.com/qqwweee/keras-yolo3
基于tensorflow的實(shí)現(xiàn)代碼可以參考:
https://github.com/wizyoung/YOLOv3_TensorFlow
前言
前言就是嘮嘮嗑边篮,想直接看干貨可以跳過前言,直接看Yolo v3奏甫。
yolo_v3是我最近一段時(shí)間主攻的算法,寫下博客凌受,以作分享交流阵子。
看過yolov3論文的應(yīng)該都知道,這篇論文寫得很隨意胜蛉,很多亮點(diǎn)都被作者都是草草描述挠进。很多騷年入手yolo算法都是從v3才開始色乾,這是不可能掌握yolo精髓的,因?yàn)関3很多東西是保留v2甚至v1的東西领突,而且v3的論文寫得很隨心暖璧。想深入了解yolo_v3算法,是有必要先了解v1和v2的君旦。以下是我關(guān)于v1和v2算法解析所寫的文章:
v1算法解析:《yolo系列之yolo v1》
v2算法解析:《yolo系列之yolo v2》
yolo_v3作為yolo系列目前最新的算法澎办,對(duì)之前的算法既有保留又有改進(jìn)。先分析一下yolo_v3上保留的東西:
“分而治之”金砍,從yolo_v1開始局蚀,yolo算法就是通過劃分單元格來做檢測(cè),只是劃分的數(shù)量不一樣恕稠。
采用"leaky ReLU"作為激活函數(shù)琅绅。
端到端進(jìn)行訓(xùn)練。一個(gè)loss function搞定訓(xùn)練鹅巍,只需關(guān)注輸入端和輸出端千扶。
從yolo_v2開始,yolo就用batch normalization作為正則化骆捧、加速收斂和避免過擬合的方法澎羞,把BN層和leaky relu層接到每一層卷積層之后。
多尺度訓(xùn)練凑懂。在速度和準(zhǔn)確率之間tradeoff煤痕。想速度快點(diǎn),可以犧牲準(zhǔn)確率接谨;想準(zhǔn)確率高點(diǎn)兒摆碉,可以犧牲一點(diǎn)速度。
yolo每一代的提升很大一部分決定于backbone網(wǎng)絡(luò)的提升脓豪,從v2的darknet-19到v3的darknet-53巷帝。yolo_v3還提供替換backbone——tiny darknet。要想性能牛叉扫夜,backbone可以用Darknet-53楞泼,要想輕量高速,可以用tiny-darknet笤闯《槔總之,yolo就是天生“靈活”颗味,所以特別適合作為工程算法超陆。
當(dāng)然,yolo_v3在之前的算法上保留的點(diǎn)不可能只有上述幾點(diǎn)浦马。由于本文章主要針對(duì)yolo_v3進(jìn)行剖析时呀,不便跑題张漂,下面切入正題。
YOLO v3
網(wǎng)上關(guān)于yolo v3算法分析的文章一大堆谨娜,但大部分看著都不爽航攒,為什么呢?因?yàn)樗麄儧]有這個(gè)玩意兒:
圖1. yolo_v3結(jié)構(gòu)圖
yolo系列里面趴梢,作者只在v1的論文里給出了結(jié)構(gòu)圖漠畜,而v2和v3的論文里都沒有結(jié)構(gòu)圖,這使得讀者對(duì)后兩代yolo結(jié)構(gòu)的理解變得比較難垢油。but盆驹,對(duì)于yolo學(xué)習(xí)者來說,腦子里沒有一個(gè)清晰的結(jié)構(gòu)圖滩愁,就別說自己懂yolo了躯喇。上圖是我根據(jù)官方代碼和官方論文以及模型結(jié)構(gòu)可視化工具等經(jīng)過好幾個(gè)小時(shí)畫出來的,修訂過幾個(gè)版本硝枉。所以廉丽,上圖的準(zhǔn)確性是可以保證的。
這里推薦的模型結(jié)構(gòu)可視化工具是:Netron
netron方便好用妻味,可以直觀看到y(tǒng)olo_v3的實(shí)際計(jì)算結(jié)構(gòu)正压,精細(xì)到卷積層。But责球,要進(jìn)一步在人性化的角度分析v3的結(jié)構(gòu)圖焦履,還需要結(jié)合論文和代碼。對(duì)此雏逾,我是下了不少功夫嘉裤。
上圖表示了yolo_v3整個(gè)yolo_body的結(jié)構(gòu),沒有包括把輸出解析整理成咱要的[box, class, score]栖博。對(duì)于把輸出張量包裝成[box, class, score]那種形式屑宠,還需要寫一些腳本,但這已經(jīng)在神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)之外了(我后面會(huì)詳細(xì)解釋這波操作)仇让。
為了讓yolo_v3結(jié)構(gòu)圖更好理解典奉,我對(duì)圖1做一些補(bǔ)充解釋:
DBL: 如圖1左下角所示,也就是代碼中的Darknetconv2d_BN_Leaky丧叽,是yolo_v3的基本組件卫玖。就是卷積+BN+Leaky relu。對(duì)于v3來說踊淳,BN和leaky relu已經(jīng)是和卷積層不可分離的部分了(最后一層卷積除外)假瞬,共同構(gòu)成了最小組件。
resn:n代表數(shù)字,有res1笨触,res2, … ,res8等等,表示這個(gè)res_block里含有多少個(gè)res_unit雹舀。這是yolo_v3的大組件芦劣,yolo_v3開始借鑒了ResNet的殘差結(jié)構(gòu),使用這種結(jié)構(gòu)可以讓網(wǎng)絡(luò)結(jié)構(gòu)更深(從v2的darknet-19上升到v3的darknet-53说榆,前者沒有殘差結(jié)構(gòu))虚吟。對(duì)于res_block的解釋,可以在圖1的右下角直觀看到签财,其基本組件也是DBL串慰。
concat:張量拼接。將darknet中間層和后面的某一層的上采樣進(jìn)行拼接唱蒸。拼接的操作和殘差層add的操作是不一樣的邦鲫,拼接會(huì)擴(kuò)充張量的維度,而add只是直接相加不會(huì)導(dǎo)致張量維度的改變神汹。
我們可以借鑒netron來分析網(wǎng)絡(luò)層庆捺,整個(gè)yolo_v3_body包含252層,組成如下:
表0. yolo_v3_layers
根據(jù)表0可以得出屁魏,對(duì)于代碼層面的layers數(shù)量一共有252層滔以,包括add層23層(主要用于res_block的構(gòu)成,每個(gè)res_unit需要一個(gè)add層氓拼,一共有1+2+8+8+4=23層)你画。除此之外,BN層和LeakyReLU層數(shù)量完全一樣(72層)桃漾,在網(wǎng)絡(luò)結(jié)構(gòu)中的表現(xiàn)為:每一層BN后面都會(huì)接一層LeakyReLU坏匪。卷積層一共有75層,其中有72層后面都會(huì)接BN+LeakyReLU的組合構(gòu)成基本組件DBL呈队“保看結(jié)構(gòu)圖,可以發(fā)現(xiàn)上采樣和concat都有2次宪摧,和表格分析中對(duì)應(yīng)上粒竖。每個(gè)res_block都會(huì)用上一個(gè)零填充,一共有5個(gè)res_block几于。
1. backbone
整個(gè)v3結(jié)構(gòu)里面蕊苗,是沒有池化層和全連接層的。前向傳播過程中沿彭,張量的尺寸變換是通過改變卷積核的步長(zhǎng)來實(shí)現(xiàn)的朽砰,比如stride=(2, 2),這就等于將圖像邊長(zhǎng)縮小了一半(即面積縮小到原來的1/4)。在yolo_v2中瞧柔,要經(jīng)歷5次縮小漆弄,會(huì)將特征圖縮小到原輸入尺寸的,即1/32造锅。輸入為416x416撼唾,則輸出為13x13(416/32=13)。
yolo_v3也和v2一樣哥蔚,backbone都會(huì)將輸出特征圖縮小到輸入的1/32倒谷。所以,通常都要求輸入圖片是32的倍數(shù)糙箍〔吵睿可以對(duì)比v2和v3的backbone看看:(DarkNet-19 與 DarkNet-53)
圖2. darknet-19 vs darknet-53
yolo_v2中對(duì)于前向過程中張量尺寸變換,都是通過最大池化來進(jìn)行深夯,一共有5次抖格。而v3是通過卷積核增大步長(zhǎng)來進(jìn)行,也是5次塌西。(darknet-53最后面有一個(gè)全局平均池化他挎,在yolo-v3里面沒有這一層,所以張量維度變化只考慮前面那5次)捡需。
這也是416x416輸入得到13x13輸出的原因办桨。從圖2可以看出,darknet-19是不存在殘差結(jié)構(gòu)(resblock站辉,從resnet上借鑒過來)的呢撞,和VGG是同類型的backbone(屬于上一代CNN結(jié)構(gòu)),而darknet-53是可以和resnet-152正面剛的backbone饰剥,看下表:
表1. backbone對(duì)比圖
從上表也可以看出殊霞,darknet-19在速度上仍然占據(jù)很大的優(yōu)勢(shì)。其實(shí)在其他細(xì)節(jié)也可以看出(比如bounding box prior采用k=9)汰蓉,yolo_v3并沒有那么追求速度绷蹲,而是在保證實(shí)時(shí)性(fps>60)的基礎(chǔ)上追求performance。不過前面也說了顾孽,你要想更快祝钢,還有一個(gè)tiny-darknet作為backbone可以替代darknet-53,在官方代碼里用一行代碼就可以實(shí)現(xiàn)切換backbone若厚。搭用tiny-darknet的yolo拦英,也就是tiny-yolo在輕量和高速兩個(gè)特點(diǎn)上,顯然是state of the art級(jí)別测秸,tiny-darknet是和squeezeNet正面剛的網(wǎng)絡(luò)疤估,詳情可以看下表:
表2. 輕量級(jí)對(duì)比圖
所以灾常,有了yolo v3,就真的用不著yolo v2了铃拇,更用不著yolo v1了钞瀑。這也是[yolo官方網(wǎng)站](https://pjreddie.com/darknet/),在v3出來以后慷荔,就沒提供v1和v2代碼下載鏈接的原因了仔戈。
2. Output
對(duì)于圖1而言,更值得關(guān)注的是輸出張量:
yolo v3輸出了3個(gè)不同尺度的feature map拧廊,如上圖所示的y1, y2, y3。這也是v3論文中提到的為數(shù)不多的改進(jìn)點(diǎn):predictions across scales
這個(gè)借鑒了FPN(feature pyramid networks)晋修,采用多尺度來對(duì)不同size的目標(biāo)進(jìn)行檢測(cè)吧碾,越精細(xì)的grid cell就可以檢測(cè)出越精細(xì)的物體。
y1,y2和y3的深度都是255墓卦,邊長(zhǎng)的規(guī)律是13:26:52
對(duì)于COCO類別而言倦春,有80個(gè)種類,所以每個(gè)box應(yīng)該對(duì)每個(gè)種類都輸出一個(gè)概率落剪。
yolo v3設(shè)定的是每個(gè)網(wǎng)格單元預(yù)測(cè)3個(gè)box睁本,所以每個(gè)box需要有(x, y, w, h, confidence)五個(gè)基本參數(shù),然后還要有80個(gè)類別的概率忠怖。所以3*(5 + 80) = 255呢堰。這個(gè)255就是這么來的。(還記得yolo v1的輸出張量嗎凡泣? 7x7x30枉疼,只能識(shí)別20類物體,而且每個(gè)cell只能預(yù)測(cè)2個(gè)box鞋拟,和v3比起來就像老人機(jī)和iphoneX一樣)
v3用上采樣的方法來實(shí)現(xiàn)這種多尺度的feature map骂维,可以結(jié)合圖1和圖2右邊來看,圖1中concat連接的兩個(gè)張量是具有一樣尺度的(兩處拼接分別是26x26尺度拼接和52x52尺度拼接贺纲,通過(2, 2)上采樣來保證concat拼接的張量尺度相同)航闺。作者并沒有像SSD那樣直接采用backbone中間層的處理結(jié)果作為feature map的輸出,而是和后面網(wǎng)絡(luò)層的上采樣結(jié)果進(jìn)行一個(gè)拼接之后的處理結(jié)果作為feature map猴誊。為什么這么做呢潦刃? 我感覺是有點(diǎn)玄學(xué)在里面,一方面避免和其他算法做法重合稠肘,另一方面這也許是試驗(yàn)之后并且結(jié)果證明更好的選擇福铅,再者有可能就是因?yàn)檫@么做比較節(jié)省模型size的。這點(diǎn)的數(shù)學(xué)原理不用去管项阴,知道作者是這么做的就對(duì)了滑黔。
3. some tricks
上文把yolo_v3的結(jié)構(gòu)討論了一下笆包,下文將對(duì)yolo v3的若干細(xì)節(jié)進(jìn)行剖析。
Bounding Box Prediction
b-box預(yù)測(cè)手段是v3論文中提到的又一個(gè)亮點(diǎn)略荡。先回憶一下v2的b-box預(yù)測(cè):想借鑒faster R-CNN RPN中的anchor機(jī)制庵佣,但不屑于手動(dòng)設(shè)定anchor prior(模板框),于是用維度聚類的方法來確定anchor box prior(模板框)汛兜,最后發(fā)現(xiàn)聚類之后確定的prior在k=5也能夠又不錯(cuò)的表現(xiàn)巴粪,于是就選用k=5。后來呢粥谬,v2又嫌棄anchor機(jī)制線性回歸的不穩(wěn)定性(因?yàn)榛貧w的offset可以使box偏移到圖片的任何地方)肛根,所以v2最后選用了自己的方法:直接預(yù)測(cè)相對(duì)位置。預(yù)測(cè)出b-box中心點(diǎn)相對(duì)于網(wǎng)格單元左上角的相對(duì)坐標(biāo)漏策。
公式1
yolo v2直接predict出()派哲,并不像RPN中anchor機(jī)制那樣去遍歷每一個(gè)pixel〔粲鳎可以從上面的公式看出芭届,b-box的位置大小和confidence都可以通過(
)計(jì)算得來,v2相當(dāng)直接predict出了b-box的位置大小和confidence感耙。box寬和高的預(yù)測(cè)是受prior影響的褂乍,對(duì)于v2而言,b-box prior數(shù)為5即硼,在論文中并沒有說明拋棄anchor機(jī)制之后是否拋棄了聚類得到的prior(沒看代碼逃片,所以我不能確定),如果prior數(shù)繼續(xù)為5只酥,那么v2需要對(duì)不同prior預(yù)測(cè)出tw?
對(duì)于v3而言题诵,在prior這里的處理有明確解釋:選用的b-box priors 的k=9,對(duì)于tiny-yolo的話层皱,k=6性锭。priors都是在數(shù)據(jù)集上聚類得來的,有確定的數(shù)值叫胖,如下:
10,13,? 16,30,? 33,23,? 30,61,? 62,45,? 59,119,? 116,90,? 156,198,? 373,326
每個(gè)anchor prior(名字叫anchor prior草冈,但并不是用anchor機(jī)制)就是兩個(gè)數(shù)字組成的,一個(gè)代表高度另一個(gè)代表寬度瓮增。
v3對(duì)b-box進(jìn)行預(yù)測(cè)的時(shí)候怎棱,采用了logistic regression。這一波操作sao得就像RPN中的線性回歸調(diào)整b-box绷跑。v3每次對(duì)b-box進(jìn)行predict時(shí)拳恋,輸出和v2一樣都是(),然后通過公式1計(jì)算出絕對(duì)的(x, y, w, h, c)砸捏。
logistic回歸用于對(duì)anchor包圍的部分進(jìn)行一個(gè)目標(biāo)性評(píng)分(objectness score)谬运,即這塊位置是目標(biāo)的可能性有多大隙赁。這一步是在predict之前進(jìn)行的,可以去掉不必要anchor梆暖,可以減少計(jì)算量伞访。作者在論文種的描述如下:
If the bounding box prior is not the best but does overlap a ground truth object by more than some threshold we ignore the prediction, following[17]. We use the threshold of 0.5. Unlike [17] our system only assigns one bounding box prior for each ground truth object.
如果模板框不是最佳的即使它超過我們?cè)O(shè)定的閾值,我們還是不會(huì)對(duì)它進(jìn)行predict轰驳。
不同于faster R-CNN的是厚掷,yolo_v3只會(huì)對(duì)1個(gè)prior進(jìn)行操作,也就是那個(gè)最佳prior级解。而logistic回歸就是用來從9個(gè)anchor priors中找到objectness score(目標(biāo)存在可能性得分)最高的那一個(gè)冒黑。logistic回歸就是用曲線對(duì)prior相對(duì)于 objectness score映射關(guān)系的線性建模。
疑問解答和說明:
在評(píng)論里有同學(xué)問我關(guān)于輸出的問題勤哗,看來我在這里沒有說的很清楚薛闪。了解v3輸出的輸出是至關(guān)重要的。
第一點(diǎn)俺陋, 9個(gè)anchor會(huì)被三個(gè)輸出張量平分的。根據(jù)大中小三種size各自取自己的anchor昙篙。
第二點(diǎn)腊状,每個(gè)輸出y在每個(gè)自己的網(wǎng)格都會(huì)輸出3個(gè)預(yù)測(cè)框,這3個(gè)框是9除以3得到的苔可,這是作者設(shè)置
的缴挖,我們可以從輸出張量的維度來看,13x13x255焚辅。255是怎么來的呢映屋,3*(5+80)。80表示80個(gè)種類同蜻,5表
示位置信息和置信度棚点,3表示要輸出3個(gè)prediction。在代碼上來看湾蔓,3*(5+80)中的3是直接由
num_anchors//3得到的瘫析。
第三點(diǎn),作者使用了logistic回歸來對(duì)每個(gè)anchor包圍的內(nèi)容進(jìn)行了一個(gè)目標(biāo)性評(píng)分(objectness score)默责。
根據(jù)目標(biāo)性評(píng)分來選擇anchor prior進(jìn)行predict贬循,而不是所有anchor prior都會(huì)有輸出。
loss function
對(duì)掌握Yolo來講桃序,loss function不可謂不重要杖虾。在v3的論文里沒有明確提所用的損失函數(shù),確切地說媒熊,yolo系列論文里面只有yolo v1明確提了損失函數(shù)的公式奇适。對(duì)于yolo這樣一種討喜的目標(biāo)檢測(cè)算法坟比,就連損失函數(shù)都非常討喜。在v1中使用了一種叫sum-square error的損失計(jì)算方法滤愕,就是簡(jiǎn)單的差方相加而已温算。想詳細(xì)了解的可以看我關(guān)于v1解釋的博文。我們知道间影,在目標(biāo)檢測(cè)任務(wù)里注竿,有幾個(gè)關(guān)鍵信息是需要確定的:
(x,y),(w,h),class,confidence (x, y), (w, h), class, confidence
(x,y),(w,h),class,confidence
根據(jù)關(guān)鍵信息的特點(diǎn)可以分為上述四類,損失函數(shù)應(yīng)該由各自特點(diǎn)確定魂贬。最后加到一起就可以組成最終的loss_function了巩割,也就是一個(gè)loss_function搞定端到端的訓(xùn)練「对铮可以從代碼分析出v3的損失函數(shù)宣谈,同樣也是對(duì)以上四類,不過相比于v1中簡(jiǎn)單的總方誤差键科,還是有一些調(diào)整的:
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代碼闻丑。忽略恒定系數(shù)不看,可以從上述代碼看出:除了w, h的損失函數(shù)依然采用總方誤差之外勋颖,其他部分的損失函數(shù)用的是二值交叉熵嗦嗡。最后加到一起。那么這個(gè)binary_crossentropy又是個(gè)什么玩意兒呢饭玲?就是一個(gè)最簡(jiǎn)單的交叉熵而已侥祭,一般用于二分類,這里的兩種二分類類別可以理解為"對(duì)和不對(duì)"這兩種茄厘。關(guān)于binary_crossentropy的公式詳情可參考博文《常見的損失函數(shù)》矮冬。
總結(jié)
v3毫無疑問現(xiàn)在成為了工程界首選的檢測(cè)算法之一了,結(jié)構(gòu)清晰次哈,實(shí)時(shí)性好胎署。這是我十分安利的目標(biāo)檢測(cè)算法,更值得贊揚(yáng)的是窑滞,yolo_v3給了近乎白癡的復(fù)現(xiàn)教程硝拧,這種氣量在頂層算法研究者中并不常見。你可以很快的用上v3葛假,但你不能很快地懂v3障陶,我花了近一個(gè)月的時(shí)間才對(duì)v3有一個(gè)清晰的了解(可能是我悟性不夠哈哈哈)。在算法學(xué)習(xí)的過程中聊训,去一些浮躁抱究,好好理解算法比只會(huì)用算法難得很多。
---------------------
作者:木盞
來源:CSDN
原文:https://blog.csdn.net/leviopku/article/details/82660381
版權(quán)聲明:本文為博主原創(chuàng)文章带斑,轉(zhuǎn)載請(qǐng)附上博文鏈接鼓寺!