摘要:近幾年來(lái),目標(biāo)檢測(cè)算法取得了很大的突破。本文主要講述算法的原理,特別是算法的訓(xùn)練與預(yù)測(cè)中詳細(xì)細(xì)節(jié),最后將給出如何使用實(shí)現(xiàn)算法。但是結(jié)合卷積運(yùn)算的特點(diǎn),我們可以使用實(shí)現(xiàn)更高效的滑動(dòng)窗口方法。這其實(shí)是算法的思路。下面將詳細(xì)介紹算法的設(shè)計(jì)理念。
1、前言
當(dāng)我們談起計(jì)算機(jī)視覺時(shí),首先想到的就是圖像分類,沒錯(cuò),圖像分類是計(jì)算機(jī)視覺最基本的任務(wù)之一,但是在圖像分類的基礎(chǔ)上,還有更復(fù)雜和有意思的任務(wù),如目標(biāo)檢測(cè),物體定位,圖像分割等,見圖1所示。其中目標(biāo)檢測(cè)是一件比較實(shí)際的且具有挑戰(zhàn)性的計(jì)算機(jī)視覺任務(wù),其可以看成圖像分類與定位的結(jié)合,給定一張圖片,目標(biāo)檢測(cè)系統(tǒng)要能夠識(shí)別出圖片的目標(biāo)并給出其位置,由于圖片中目標(biāo)數(shù)是不定的,且要給出目標(biāo)的較精確位置,目標(biāo)檢測(cè)相比分類任務(wù)更復(fù)雜。目標(biāo)檢測(cè)的一個(gè)實(shí)際應(yīng)用場(chǎng)景就是無(wú)人駕駛,如果能夠在無(wú)人車上裝載一個(gè)有效的目標(biāo)檢測(cè)系統(tǒng),那么無(wú)人車將和人一樣有了眼睛,可以快速地檢測(cè)出前面的行人與車輛,從而作出實(shí)時(shí)決策。
圖1 計(jì)算機(jī)視覺任務(wù)(來(lái)源: cs231n)
在介紹Yolo算法之前,首先先介紹一下滑動(dòng)窗口技術(shù),這對(duì)我們理解Yolo算法是有幫助的。采用滑動(dòng)窗口的目標(biāo)檢測(cè)算法思路非常簡(jiǎn)單,它將檢測(cè)問題轉(zhuǎn)化為了圖像分類問題。其基本原理就是采用不同大小和窗口在整張圖片上以一定的步長(zhǎng)進(jìn)行滑動(dòng),然后對(duì)這些窗口對(duì)應(yīng)的區(qū)域做圖像分類,這樣就可以實(shí)現(xiàn)對(duì)整張圖片的檢測(cè)了,如下圖3所示,如DPM就是采用這種思路。但是這個(gè)方法有致命的缺點(diǎn),就是你并不知道要檢測(cè)的目標(biāo)大小是什么規(guī)模,所以你要設(shè)置不同大小的窗口去滑動(dòng),而且還要選取合適的步長(zhǎng)。但是這樣會(huì)產(chǎn)生很多的子區(qū)域,并且都要經(jīng)過分類器去做預(yù)測(cè),這需要很大的計(jì)算量,所以你的分類器不能太復(fù)雜,因?yàn)橐WC速度。解決思路之一就是減少要分類的子區(qū)域,這就是R-CNN的一個(gè)改進(jìn)策略,其采用了selective search方法來(lái)找到最有可能包含目標(biāo)的子區(qū)域(Region Proposal),其實(shí)可以看成采用啟發(fā)式方法過濾掉很多子區(qū)域,這會(huì)提升效率。
近幾年來(lái),目標(biāo)檢測(cè)算法取得了很大的突破。比較流行的算法可以分為兩類,一類是基于Region Proposal的R-CNN系算法(R-CNN,F(xiàn)ast R-CNN, Faster R-CNN),它們是two-stage的,需要先使用啟發(fā)式方法(selective search)或者CNN網(wǎng)絡(luò)(RPN)產(chǎn)生Region Proposal,然后再在Region Proposal上做分類與回歸。而另一類是Yolo,SSD這類one-stage算法,其僅僅使用一個(gè)CNN網(wǎng)絡(luò)直接預(yù)測(cè)不同目標(biāo)的類別與位置。第一類方法是準(zhǔn)確度高一些,但是速度慢,但是第二類算法是速度快,但是準(zhǔn)確性要低一些。這可以在圖2中看到。本文介紹的是Yolo算法,其全稱是You Only Look Once: Unified, Real-Time Object Detection,其實(shí)個(gè)人覺得這個(gè)題目取得非常好,基本上把Yolo算法的特點(diǎn)概括全了:You Only Look Once說(shuō)的是只需要一次CNN運(yùn)算,Unified指的是這是一個(gè)統(tǒng)一的框架,提供end-to-end的預(yù)測(cè),而Real-Time體現(xiàn)是Yolo算法速度快。這里我們談的是Yolo-v1版本算法,其性能是差于后來(lái)的SSD算法的,但是Yolo后來(lái)也繼續(xù)進(jìn)行改進(jìn),產(chǎn)生了Yolo9000算法。本文主要講述Yolo-v1算法的原理,特別是算法的訓(xùn)練與預(yù)測(cè)中詳細(xì)細(xì)節(jié),最后將給出如何使用TensorFlow實(shí)現(xiàn)Yolo算法。
圖2 目標(biāo)檢測(cè)算法進(jìn)展與對(duì)比
2、滑動(dòng)窗口與CNN
在介紹Yolo算法之前,首先先介紹一下滑動(dòng)窗口技術(shù),這對(duì)我們理解Yolo算法是有幫助的。采用滑動(dòng)窗口的目標(biāo)檢測(cè)算法思路非常簡(jiǎn)單,它將檢測(cè)問題轉(zhuǎn)化為了圖像分類問題。其基本原理就是采用不同大小和窗口在整張圖片上以一定的步長(zhǎng)進(jìn)行滑動(dòng),然后對(duì)這些窗口對(duì)應(yīng)的區(qū)域做圖像分類,這樣就可以實(shí)現(xiàn)對(duì)整張圖片的檢測(cè)了,如下圖3所示,如DPM就是采用這種思路。但是這個(gè)方法有致命的缺點(diǎn),就是你并不知道要檢測(cè)的目標(biāo)大小是什么規(guī)模,所以你要設(shè)置不同大小的窗口去滑動(dòng),而且還要選取合適的步長(zhǎng)。但是這樣會(huì)產(chǎn)生很多的子區(qū)域,并且都要經(jīng)過分類器去做預(yù)測(cè),這需要很大的計(jì)算量,所以你的分類器不能太復(fù)雜,因?yàn)橐WC速度。解決思路之一就是減少要分類的子區(qū)域,這就是R-CNN的一個(gè)改進(jìn)策略,其采用了selective search方法來(lái)找到最有可能包含目標(biāo)的子區(qū)域(Region Proposal),其實(shí)可以看成采用啟發(fā)式方法過濾掉很多子區(qū)域,這會(huì)提升效率。
圖3 采用滑動(dòng)窗口進(jìn)行目標(biāo)檢測(cè)(來(lái)源:deeplearning.ai)
如果你使用的是CNN分類器,那么滑動(dòng)窗口是非常耗時(shí)的。但是結(jié)合卷積運(yùn)算的特點(diǎn),我們可以使用CNN實(shí)現(xiàn)更高效的滑動(dòng)窗口方法。這里要介紹的是一種全卷積的方法,簡(jiǎn)單來(lái)說(shuō)就是網(wǎng)絡(luò)中用卷積層代替了全連接層,如圖4所示。輸入圖片大小是16x16,經(jīng)過一系列卷積操作,提取了2x2的特征圖,但是這個(gè)2x2的圖上每個(gè)元素都是和原圖是一一對(duì)應(yīng)的,如圖上藍(lán)色的格子對(duì)應(yīng)藍(lán)色的區(qū)域,這不就是相當(dāng)于在原圖上做大小為14x14的窗口滑動(dòng),且步長(zhǎng)為2,共產(chǎn)生4個(gè)字區(qū)域。最終輸出的通道數(shù)為4,可以看成4個(gè)類別的預(yù)測(cè)概率值,這樣一次CNN計(jì)算就可以實(shí)現(xiàn)窗口滑動(dòng)的所有子區(qū)域的分類預(yù)測(cè)。這其實(shí)是overfeat算法的思路。之所可以CNN可以實(shí)現(xiàn)這樣的效果是因?yàn)榫矸e操作的特性,就是圖片的空間位置信息的不變性,盡管卷積過程中圖片大小減少,但是位置對(duì)應(yīng)關(guān)系還是保存的。說(shuō)點(diǎn)題外話,這個(gè)思路也被R-CNN借鑒,從而誕生了Fast R-CNN算法。
圖4 滑動(dòng)窗口的CNN實(shí)現(xiàn)(來(lái)源:deeplearning.ai)
上面盡管可以減少滑動(dòng)窗口的計(jì)算量,但是只是針對(duì)一個(gè)固定大小與步長(zhǎng)的窗口,這是遠(yuǎn)遠(yuǎn)不夠的。Yolo算法很好的解決了這個(gè)問題,它不再是窗口滑動(dòng)了,而是直接將原始圖片分割成互不重合的小方塊,然后通過卷積最后生產(chǎn)這樣大小的特征圖,基于上面的分析,可以認(rèn)為特征圖的每個(gè)元素也是對(duì)應(yīng)原始圖片的一個(gè)小方塊,然后用每個(gè)元素來(lái)可以預(yù)測(cè)那些中心點(diǎn)在該小方格內(nèi)的目標(biāo),這就是Yolo算法的樸素思想。下面將詳細(xì)介紹Yolo算法的設(shè)計(jì)理念。
這就是Yolo算法的樸素思想。下面將詳細(xì)介紹Yolo算法的設(shè)計(jì)理念。
3、設(shè)計(jì)理念
整體來(lái)看,Yolo算法采用一個(gè)多帶帶的CNN模型實(shí)現(xiàn)end-to-end的目標(biāo)檢測(cè),整個(gè)系統(tǒng)如圖5所示:首先將輸入圖片resize到448x448,然后送入CNN網(wǎng)絡(luò),最后處理網(wǎng)絡(luò)預(yù)測(cè)結(jié)果得到檢測(cè)的目標(biāo)。相比R-CNN算法,其是一個(gè)統(tǒng)一的框架,其速度更快,而且Yolo的訓(xùn)練過程也是end-to-end的。
圖5 Yolo檢測(cè)系統(tǒng)
具體來(lái)說(shuō),Yolo的CNN網(wǎng)絡(luò)將輸入的圖片分割成S*S網(wǎng)格,然后每個(gè)單元格負(fù)責(zé)去檢測(cè)那些中心點(diǎn)落在該格子內(nèi)的目標(biāo),如圖6所示,可以看到狗這個(gè)目標(biāo)的中心落在左下角一個(gè)單元格內(nèi),那么該單元格負(fù)責(zé)預(yù)測(cè)這個(gè)狗。每個(gè)單元格會(huì)預(yù)測(cè)B個(gè)邊界框(bounding box)以及邊界框的置信度(confidence score)。所謂置信度其實(shí)包含兩個(gè)方面,一是這個(gè)邊界框含有目標(biāo)的可能性大小,二是這個(gè)邊界框的準(zhǔn)確度。前者記為Pr(object),當(dāng)該邊界框是背景時(shí)(即不包含目標(biāo)),此時(shí)Pr(object)=0。而當(dāng)該邊界框包含目標(biāo)時(shí),Pr(object)=1。邊界框的準(zhǔn)確度可以用預(yù)測(cè)框與實(shí)際框(ground truth)的IOU(intersection over union,交并比)來(lái)表征,記為IOU。因此置信度可以定義為Pr(object)*IOU。很多人可能將Yolo的置信度看成邊界框是否含有目標(biāo)的概率,但是其實(shí)它是兩個(gè)因子的乘積,預(yù)測(cè)框的準(zhǔn)確度也反映在里面。邊界框的大小與位置可以用4個(gè)值來(lái)表征:(x,y,h,w),其中(x,y)是邊界框的中心坐標(biāo),而和是邊界框的寬與高。還有一點(diǎn)要注意,中心坐標(biāo)的預(yù)測(cè)值(x,y)是相對(duì)于每個(gè)單元格左上角坐標(biāo)點(diǎn)的偏移值,并且單位是相對(duì)于單元格大小的,單元格的坐標(biāo)定義如圖6所示。而邊界框的w和h預(yù)測(cè)值是相對(duì)于整個(gè)圖片的寬與高的比例,這樣理論上4個(gè)元素的大小應(yīng)該在[0,1]范圍。這樣,每個(gè)邊界框的預(yù)測(cè)值實(shí)際上包含5個(gè)元素:(x,y,w,h,c),其中前4個(gè)表征邊界框的大小與位置,而最后一個(gè)值是置信度。
圖6 網(wǎng)格劃分
總結(jié)一下,每個(gè)單元格需要預(yù)測(cè)(B*5+C)個(gè)值。如果將輸入圖片劃分為S*S網(wǎng)格,那么最終預(yù)測(cè)值為S*S*(B*5+C)大小的張量。整個(gè)模型的預(yù)測(cè)值結(jié)構(gòu)如下圖所示。對(duì)于PASCALVOC數(shù)據(jù),其共有20個(gè)類別,如果使用S=7,B=2,那么最終的預(yù)測(cè)結(jié)果就是7*7*30大小的張量。在下面的網(wǎng)絡(luò)結(jié)構(gòu)中我們會(huì)詳細(xì)講述每個(gè)單元格的預(yù)測(cè)值的分布位置。
圖7 模型預(yù)測(cè)值結(jié)構(gòu)
4、網(wǎng)絡(luò)設(shè)計(jì)
Yolo采用卷積網(wǎng)絡(luò)來(lái)提取特征,然后使用全連接層來(lái)得到預(yù)測(cè)值。網(wǎng)絡(luò)結(jié)構(gòu)參考GooLeNet模型,包含24個(gè)卷積層和2個(gè)全連接層,如圖8所示。對(duì)于卷積層,主要使用1x1卷積來(lái)做channle reduction,然后緊跟3x3卷積。對(duì)于卷積層和全連接層,采用Leaky ReLU激活函數(shù):max(x,0)。但是最后一層卻采用線性激活函數(shù)。除了上面這個(gè)結(jié)構(gòu),文章還提出了一個(gè)輕量級(jí)版本Fast Yolo,其僅使用9個(gè)卷積層,并且卷積層中使用更少的卷積核。
圖8 網(wǎng)絡(luò)結(jié)構(gòu)
圖9 預(yù)測(cè)張量的解析
5、網(wǎng)絡(luò)訓(xùn)練
在訓(xùn)練之前,先在ImageNet上進(jìn)行了預(yù)訓(xùn)練,其預(yù)訓(xùn)練的分類模型采用圖8中前20個(gè)卷積層,然后添加一個(gè)average-pool層和全連接層。預(yù)訓(xùn)練之后,在預(yù)訓(xùn)練得到的20層卷積層之上加上隨機(jī)初始化的4個(gè)卷積層和2個(gè)全連接層。由于檢測(cè)任務(wù)一般需要更高清的圖片,所以將網(wǎng)絡(luò)的輸入從224x224增加到了448x448。整個(gè)網(wǎng)絡(luò)的流程如下圖所示:
圖10 Yolo網(wǎng)絡(luò)流程
另外一點(diǎn)時(shí),由于每個(gè)單元格預(yù)測(cè)多個(gè)邊界框。但是其對(duì)應(yīng)類別只有一個(gè)。那么在訓(xùn)練時(shí),如果該單元格內(nèi)確實(shí)存在目標(biāo),那么只選擇與ground truth的IOU較大的那個(gè)邊界框來(lái)負(fù)責(zé)預(yù)測(cè)該目標(biāo),而其它邊界框認(rèn)為不存在目標(biāo)。這樣設(shè)置的一個(gè)結(jié)果將會(huì)使一個(gè)單元格對(duì)應(yīng)的邊界框更加專業(yè)化,其可以分別適用不同大小,不同高寬比的目標(biāo),從而提升模型性能。大家可能會(huì)想如果一個(gè)單元格內(nèi)存在多個(gè)目標(biāo)怎么辦,其實(shí)這時(shí)候Yolo算法就只能選擇其中一個(gè)來(lái)訓(xùn)練,這也是Yolo算法的缺點(diǎn)之一。要注意的一點(diǎn)時(shí),對(duì)于不存在對(duì)應(yīng)目標(biāo)的邊界框,其誤差項(xiàng)就是只有置信度,左標(biāo)項(xiàng)誤差是沒法計(jì)算的。而只有當(dāng)一個(gè)單元格內(nèi)確實(shí)存在目標(biāo)時(shí),才計(jì)算分類誤差項(xiàng),否則該項(xiàng)也是無(wú)法計(jì)算的。?
綜上討論,最終的損失函數(shù)計(jì)算如下:?
6、網(wǎng)絡(luò)預(yù)測(cè)
在說(shuō)明Yolo算法的預(yù)測(cè)過程之前,這里先介紹一下非極大值抑制算法(non maximum suppression, NMS),這個(gè)算法不單單是針對(duì)Yolo算法的,而是所有的檢測(cè)算法中都會(huì)用到。NMS算法主要解決的是一個(gè)目標(biāo)被多次檢測(cè)的問題,如圖11中人臉檢測(cè),可以看到人臉被多次檢測(cè),但是其實(shí)我們希望最后僅僅輸出其中一個(gè)較好的預(yù)測(cè)框,比如對(duì)于美女,只想要紅色那個(gè)檢測(cè)結(jié)果。那么可以采用NMS算法來(lái)實(shí)現(xiàn)這樣的效果:首先從所有的檢測(cè)框中找到置信度較大的那個(gè)框,然后挨個(gè)計(jì)算其與剩余框的IOU,如果其值大于一定閾值(重合度過高),那么就將該框剔除;然后對(duì)剩余的檢測(cè)框重復(fù)上述過程,直到處理完所有的檢測(cè)框。Yolo預(yù)測(cè)過程也需要用到NMS算法。
圖11 NMS應(yīng)用在人臉檢測(cè)
下面就來(lái)分析Yolo的預(yù)測(cè)過程,這里我們不考慮batch,認(rèn)為只是預(yù)測(cè)一張輸入圖片。根據(jù)前面的分析,最終的網(wǎng)絡(luò)輸出是7*7*30,但是我們可以將其分割成三個(gè)部分:類別概率部分為[7,7,20],置信度部分為[7,7,2,2],而邊界框部分為[7,7,2,4](對(duì)于這部分不要忘記根據(jù)原始圖片計(jì)算出其真實(shí)值)。然后將前兩項(xiàng)相乘可以得到類別置信度值為[7,7,2,20],這里總共預(yù)測(cè)了7*7*2=98邊界框。
所有的準(zhǔn)備數(shù)據(jù)已經(jīng)得到了,那么我們先說(shuō)第一種策略來(lái)得到檢測(cè)框的結(jié)果,我認(rèn)為這是最正常與自然的處理。首先,對(duì)于每個(gè)預(yù)測(cè)框根據(jù)類別置信度選取置信度較大的那個(gè)類別作為其預(yù)測(cè)標(biāo)簽,經(jīng)過這層處理我們得到各個(gè)預(yù)測(cè)框的預(yù)測(cè)類別及對(duì)應(yīng)的置信度值,其大小都是[7,7,2]。一般情況下,會(huì)設(shè)置置信度閾值,就是將置信度小于該閾值的box過濾掉,所以經(jīng)過這層處理,剩余的是置信度比較高的預(yù)測(cè)框。最后再對(duì)這些預(yù)測(cè)框使用NMS算法,最后留下來(lái)的就是檢測(cè)結(jié)果。一個(gè)值得注意的點(diǎn)是NMS是對(duì)所有預(yù)測(cè)框一視同仁,還是區(qū)分每個(gè)類別,分別使用NMS。Ng在deeplearning.ai中講應(yīng)該區(qū)分每個(gè)類別分別使用NMS,但是看了很多實(shí)現(xiàn),其實(shí)還是同等對(duì)待所有的框,我覺得可能是不同類別的目標(biāo)出現(xiàn)在相同位置這種概率很低吧。
上面的預(yù)測(cè)方法應(yīng)該非常簡(jiǎn)單明了,但是對(duì)于Yolo算法,其卻采用了另外一個(gè)不同的處理思路(至少?gòu)腃源碼看是這樣的),其區(qū)別就是先使用NMS,然后再確定各個(gè)box的類別。其基本過程如圖12所示。對(duì)于98個(gè)boxes,首先將小于置信度閾值的值歸0,然后分類別地對(duì)置信度值采用NMS,這里NMS處理結(jié)果不是剔除,而是將其置信度值歸為0。最后才是確定各個(gè)box的類別,當(dāng)其置信度值不為0時(shí)才做出檢測(cè)結(jié)果輸出。這個(gè)策略不是很直接,但是貌似Yolo源碼就是這樣做的。Yolo論文里面說(shuō)NMS算法對(duì)Yolo的性能是影響很大的,所以可能這種策略對(duì)Yolo更好。但是我測(cè)試了普通的圖片檢測(cè),兩種策略結(jié)果是一樣的。
圖12 Yolo的預(yù)測(cè)處理流程
7、算法性能分析
這里看一下Yolo算法在PASCAL VOC 2007數(shù)據(jù)集上的性能,這里Yolo與其它檢測(cè)算法做了對(duì)比,包括DPM,R-CNN,F(xiàn)ast R-CNN以及Faster R-CNN。其對(duì)比結(jié)果如表1所示。與實(shí)時(shí)性檢測(cè)方法DPM對(duì)比,可以看到Y(jié)olo算法可以在較高的mAP上達(dá)到較快的檢測(cè)速度,其中Fast Yolo算法比快速DPM還快,而且mAP是遠(yuǎn)高于DPM。但是相比Faster R-CNN,Yolo的mAP稍低,但是速度更快。所以。Yolo算法算是在速度與準(zhǔn)確度上做了折中。
表1 Yolo在PASCAL VOC 2007上與其他算法的對(duì)比?
為了進(jìn)一步分析Yolo算法,文章還做了誤差分析,將預(yù)測(cè)結(jié)果按照分類與定位準(zhǔn)確性分成以下5類:
Correct:類別正確,IOU>0.5;(準(zhǔn)確度)
Localization:類別正確,0.1 < IOU<0.5(定位不準(zhǔn));
Similar:類別相似,IOU>0.1;
Other:類別錯(cuò)誤,IOU>0.1;
Background:對(duì)任何目標(biāo)其IOU<0.1。(誤把背景當(dāng)物體)
Yolo與Fast R-CNN的誤差對(duì)比分析如下圖所示:
圖13 Yolo與Fast R-CNN的誤差對(duì)比分析?
可以看到,Yolo的Correct的是低于Fast R-CNN。另外Yolo的Localization誤差偏高,即定位不是很準(zhǔn)確。但是Yolo的Background誤差很低,說(shuō)明其對(duì)背景的誤判率較低。Yolo的那篇文章中還有更多性能對(duì)比,感興趣可以看看。
現(xiàn)在來(lái)總結(jié)一下Yolo的優(yōu)缺點(diǎn)。首先是優(yōu)點(diǎn),Yolo采用一個(gè)CNN網(wǎng)絡(luò)來(lái)實(shí)現(xiàn)檢測(cè),是單管道策略,其訓(xùn)練與預(yù)測(cè)都是end-to-end,所以Yolo算法比較簡(jiǎn)潔且速度快。第二點(diǎn)由于Yolo是對(duì)整張圖片做卷積,所以其在檢測(cè)目標(biāo)有更大的視野,它不容易對(duì)背景誤判。其實(shí)我覺得全連接層也是對(duì)這個(gè)有貢獻(xiàn)的,因?yàn)槿B接起到了attention的作用。另外,Yolo的泛化能力強(qiáng),在做遷移時(shí),模型魯棒性高。
最后不得不談一下Yolo的缺點(diǎn),首先Yolo各個(gè)單元格僅僅預(yù)測(cè)兩個(gè)邊界框,而且屬于一個(gè)類別。對(duì)于小物體,Yolo的表現(xiàn)會(huì)不如人意。這方面的改進(jìn)可以看SSD,其采用多尺度單元格。也可以看Faster R-CNN,其采用了anchor boxes。Yolo對(duì)于在物體的寬高比方面泛化率低,就是無(wú)法定位不尋常比例的物體。當(dāng)然Yolo的定位不準(zhǔn)確也是很大的問題。
8、算法的TF實(shí)現(xiàn)
Yolo的源碼是用C實(shí)現(xiàn)的,但是好在Github上有很多開源的TF復(fù)現(xiàn)。這里我們參考gliese581gg的實(shí)現(xiàn)來(lái)分析Yolo的Inference實(shí)現(xiàn)細(xì)節(jié)。我們的代碼將構(gòu)建一個(gè)end-to-end的Yolo的預(yù)測(cè)模型,利用的已經(jīng)訓(xùn)練好的權(quán)重文件,你將可以用自然的圖片去測(cè)試檢測(cè)效果。?
首先,我們定義Yolo的模型參數(shù):
class Yolo(object):
? ? def __init__(self, weights_file, verbose=True):
? ? ? ? self.verbose = verbose
? ? ? ? # detection params
? ? ? ? self.S = 7 ?# cell size
? ? ? ? self.B = 2 ?# boxes_per_cell
? ? ? ? self.classes = ["aeroplane", "bicycle", "bird", "boat", "bottle",
? ? ? ? ? ? ? ? ? ? ? ? "bus", "car", "cat", "chair", "cow", "diningtable",
? ? ? ? ? ? ? ? ? ? ? ? "dog", "horse", "motorbike", "person", "pottedplant",
? ? ? ? ? ? ? ? ? ? ? ? "sheep", "sofa", "train","tvmonitor"]
? ? ? ? self.C = len(self.classes) # number of classes
? ? ? ? # offset for box center (top left point of each cell)
? ? ? ? self.x_offset = np.transpose(np.reshape(np.array([np.arange(self.S)]*self.S*self.B),
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? [self.B, self.S, self.S]), [1, 2, 0])
? ? ? ? self.y_offset = np.transpose(self.x_offset, [1, 0, 2])
? ? ? ? self.threshold = 0.2 ?# confidence scores threhold
? ? ? ? self.iou_threshold = 0.4
? ? ? ? # ?the maximum number of boxes to be selected by non max suppression
? ? ? ? self.max_output_size = 10
然后是我們模型的主體網(wǎng)絡(luò)部分,這個(gè)網(wǎng)絡(luò)將輸出[batch,7*7*30]的張量:
def _build_net(self):
? ? """build the network"""
? ? if self.verbose:
? ? ? ? print("Start to build the network ...")
? ? self.images = tf.placeholder(tf.float32, [None, 448, 448, 3])
? ? net = self._conv_layer(self.images, 1, 64, 7, 2)
? ? net = self._maxpool_layer(net, 1, 2, 2)
? ? net = self._conv_layer(net, 2, 192, 3, 1)
? ? net = self._maxpool_layer(net, 2, 2, 2)
? ? net = self._conv_layer(net, 3, 128, 1, 1)
? ? net = self._conv_layer(net, 4, 256, 3, 1)
? ? net = self._conv_layer(net, 5, 256, 1, 1)
? ? net = self._conv_layer(net, 6, 512, 3, 1)
? ? net = self._maxpool_layer(net, 6, 2, 2)
? ? net = self._conv_layer(net, 7, 256, 1, 1)
? ? net = self._conv_layer(net, 8, 512, 3, 1)
? ? net = self._conv_layer(net, 9, 256, 1, 1)
? ? net = self._conv_layer(net, 10, 512, 3, 1)
? ? net = self._conv_layer(net, 11, 256, 1, 1)
? ? net = self._conv_layer(net, 12, 512, 3, 1)
? ? net = self._conv_layer(net, 13, 256, 1, 1)
? ? net = self._conv_layer(net, 14, 512, 3, 1)
? ? net = self._conv_layer(net, 15, 512, 1, 1)
? ? net = self._conv_layer(net, 16, 1024, 3, 1)
? ? net = self._maxpool_layer(net, 16, 2, 2)
? ? net = self._conv_layer(net, 17, 512, 1, 1)
? ? net = self._conv_layer(net, 18, 1024, 3, 1)
? ? net = self._conv_layer(net, 19, 512, 1, 1)
? ? net = self._conv_layer(net, 20, 1024, 3, 1)
? ? net = self._conv_layer(net, 21, 1024, 3, 1)
? ? net = self._conv_layer(net, 22, 1024, 3, 2)
? ? net = self._conv_layer(net, 23, 1024, 3, 1)
? ? net = self._conv_layer(net, 24, 1024, 3, 1)
? ? net = self._flatten(net)
? ? net = self._fc_layer(net, 25, 512, activation=leak_relu)
? ? net = self._fc_layer(net, 26, 4096, activation=leak_relu)
? ? net = self._fc_layer(net, 27, self.S*self.S*(self.C+5*self.B))
? ? self.predicts = net
接下來(lái),我們要去解析網(wǎng)絡(luò)的預(yù)測(cè)結(jié)果,這里采用了第一種預(yù)測(cè)策略,即判斷預(yù)測(cè)框類別,再NMS,多虧了TF提供了NMS的函數(shù)tf.image.non_max_suppression,其實(shí)實(shí)現(xiàn)起來(lái)很簡(jiǎn)單,所有的細(xì)節(jié)前面已經(jīng)交代了:
def _build_detector(self):
? ? """Interpret the net output and get the predicted boxes"""
? ? # the width and height of orignal image
? ? self.width = tf.placeholder(tf.float32, name="img_w")
? ? self.height = tf.placeholder(tf.float32, name="img_h")
? ? # get class prob, confidence, boxes from net output
? ? idx1 = self.S * self.S * self.C
? ? idx2 = idx1 + self.S * self.S * self.B
? ? # class prediction
? ? class_probs = tf.reshape(self.predicts[0, :idx1], [self.S, self.S, self.C])
? ? # confidence
? ? confs = tf.reshape(self.predicts[0, idx1:idx2], [self.S, self.S, self.B])
? ? # boxes -> (x, y, w, h)
? ? boxes = tf.reshape(self.predicts[0, idx2:], [self.S, self.S, self.B, 4])
? ? # convert the x, y to the coordinates relative to the top left point of the image
? ? # the predictions of w, h are the square root
? ? # multiply the width and height of image
? ? boxes = tf.stack([(boxes[:, :, :, 0] + tf.constant(self.x_offset, dtype=tf.float32)) / self.S * self.width,
? ? ? ? ? ? ? ? ? ? ? (boxes[:, :, :, 1] + tf.constant(self.y_offset, dtype=tf.float32)) / self.S * self.height,
? ? ? ? ? ? ? ? ? ? ? tf.square(boxes[:, :, :, 2]) * self.width,
? ? ? ? ? ? ? ? ? ? ? tf.square(boxes[:, :, :, 3]) * self.height], axis=3)
? ? # class-specific confidence scores [S, S, B, C]
? ? scores = tf.expand_dims(confs, -1) * tf.expand_dims(class_probs, 2)
? ? scores = tf.reshape(scores, [-1, self.C]) ?# [S*S*B, C]
? ? boxes = tf.reshape(boxes, [-1, 4]) ?# [S*S*B, 4]
? ? # find each box class, only select the max score
? ? box_classes = tf.argmax(scores, axis=1)
? ? box_class_scores = tf.reduce_max(scores, axis=1)
? ? # filter the boxes by the score threshold
? ? filter_mask = box_class_scores >= self.threshold
? ? scores = tf.boolean_mask(box_class_scores, filter_mask)
? ? boxes = tf.boolean_mask(boxes, filter_mask)
? ? box_classes = tf.boolean_mask(box_classes, filter_mask)
? ? # non max suppression (do not distinguish different classes)
? ? # ref: https://tensorflow.google.cn/api_docs/python/tf/image/non_max_suppression
? ? # box (x, y, w, h) -> box (x1, y1, x2, y2)
? ? _boxes = tf.stack([boxes[:, 0] - 0.5 * boxes[:, 2], boxes[:, 1] - 0.5 * boxes[:, 3],
? ? ? ? ? ? ? ? ? ? ? ?boxes[:, 0] + 0.5 * boxes[:, 2], boxes[:, 1] + 0.5 * boxes[:, 3]], axis=1)
? ? nms_indices = tf.image.non_max_suppression(_boxes, scores,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?self.max_output_size, self.iou_threshold)
? ? self.scores = tf.gather(scores, nms_indices)
? ? self.boxes = tf.gather(boxes, nms_indices)
? ? self.box_classes = tf.gather(box_classes, nms_indices)
其他的就比較容易了,詳細(xì)代碼附在Github上了,歡迎給點(diǎn)個(gè)贊,權(quán)重文件在這里下載。?
最后就是愉快地測(cè)試你自己的圖片了:
當(dāng)然,如果你對(duì)訓(xùn)練過程感興趣,你可以參考這里的實(shí)現(xiàn),如果你看懂了預(yù)測(cè)過程的代碼,這里也會(huì)很容易閱讀。
9、小結(jié)
這篇長(zhǎng)文詳細(xì)介紹了Yolo算法的原理及實(shí)現(xiàn),當(dāng)然Yolo-v1還是有很多問題的,所以后續(xù)可以讀讀Yolo9000算法,看看其如何改進(jìn)的。Ng說(shuō)Yolo的paper是比較難讀的,其實(shí)是很多實(shí)現(xiàn)細(xì)節(jié),如果不看代碼是很難理解的。所以,文章中如果有錯(cuò)誤也可能是難免的,歡迎交流指正。
10、參考文獻(xiàn)
You Only Look Once: Unified, Real-Time Object Detection.
Yolo官網(wǎng).
Yolo的TF實(shí)現(xiàn).
YOLO: You only look once (How it works).(注:很多實(shí)現(xiàn)細(xì)節(jié),需要墻)
Ng的deeplearning.ai課程.
歡迎加入本站公開興趣群商業(yè)智能與數(shù)據(jù)分析群
興趣范圍包括各種讓數(shù)據(jù)產(chǎn)生價(jià)值的辦法,實(shí)際應(yīng)用案例分享與討論,分析工具,ETL工具,數(shù)據(jù)倉(cāng)庫(kù),數(shù)據(jù)挖掘工具,報(bào)表系統(tǒng)等全方位知識(shí)
QQ群:81035754
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/4716.html
摘要:將圖像到作為輸入,輸出,即將圖片劃分為,每個(gè)單元格獨(dú)立檢測(cè)。類別損失當(dāng)有物體的中心點(diǎn)落在單元格中,此單元格就負(fù)責(zé)預(yù)測(cè)該物體。 YOLO-v1介紹 YOLO是一個(gè)端到端的目標(biāo)檢測(cè)算法,不需要預(yù)先提取region proposal(RCNN目標(biāo)檢測(cè)系列),通過一個(gè)網(wǎng)絡(luò)就可以輸出:類別,置信度,坐標(biāo)位置,檢測(cè)速度很快,不過,定位精度相對(duì)低些,特別是密集型小目標(biāo)。 showImg(https:...
閱讀 2736·2021-11-22 13:54
閱讀 1075·2021-10-14 09:48
閱讀 2300·2021-09-08 09:35
閱讀 1565·2019-08-30 15:53
閱讀 1177·2019-08-30 13:14
閱讀 614·2019-08-30 13:09
閱讀 2531·2019-08-30 10:57
閱讀 3343·2019-08-29 13:18