1. 程式人生 > >YOLO演算法的原理與實現

YOLO演算法的原理與實現

目錄

前言

當我們談起計算機視覺時,首先想到的就是影象分類,沒錯,影象分類是計算機視覺最基本的任務之一,但是在影象分類的基礎上,還有更復雜和有意思的任務,如目標檢測,物體定位,影象分割等,見圖1所示。其中目標檢測是一件比較實際的且具有挑戰性的計算機視覺任務,其可以看成影象分類與定位的結合,給定一張圖片,目標檢測系統要能夠識別出圖片的目標並給出其位置,由於圖片中目標數是不定的,且要給出目標的精確位置,目標檢測相比分類任務更復雜。目標檢測的一個實際應用場景就是無人駕駛,如果能夠在無人車上裝載一個有效的目標檢測系統,那麼無人車將和人一樣有了眼睛,可以快速地檢測出前面的行人與車輛,從而作出實時決策。

計算機視覺任務
圖1 計算機視覺任務(來源: cs231n)

近幾年來,目標檢測演算法取得了很大的突破。比較流行的演算法可以分為兩類,一類是基於Region Proposal的R-CNN系演算法(R-CNN,Fast R-CNN, Faster R-CNN),它們是two-stage的,需要先使用啟發式方法(selective search)或者CNN網路(RPN)產生Region Proposal,然後再在Region Proposal上做分類與迴歸。而另一類是Yolo,SSD這類one-stage演算法,其僅僅使用一個CNN網路直接預測不同目標的類別與位置。第一類方法是準確度高一些,但是速度慢,但是第二類演算法是速度快,但是準確性要低一些。這可以在圖2中看到。本文介紹的是Yolo演算法,其全稱是You Only Look Once: Unified, Real-Time Object Detection,其實個人覺得這個題目取得非常好,基本上把Yolo演算法的特點概括全了:You Only Look Once說的是隻需要一次CNN運算,Unified指的是這是一個統一的框架,提供end-to-end的預測,而Real-Time體現是Yolo演算法速度快。這裡我們談的是Yolo-v1版本演算法,其效能是差於後來的SSD演算法的,但是Yolo後來也繼續進行改進,產生了Yolo9000演算法。本文主要講述Yolo-v1演算法的原理,特別是演算法的訓練與預測中詳細細節,最後將給出如何使用TensorFlow實現Yolo演算法。

這裡寫圖片描述
圖2 目標檢測演算法進展與對比

滑動視窗與CNN

在介紹Yolo演算法之前,首先先介紹一下滑動視窗技術,這對我們理解Yolo演算法是有幫助的。採用滑動視窗的目標檢測演算法思路非常簡單,它將檢測問題轉化為了影象分類問題。其基本原理就是採用不同大小和比例(寬高比)的視窗在整張圖片上以一定的步長進行滑動,然後對這些視窗對應的區域做影象分類,這樣就可以實現對整張圖片的檢測了,如下圖3所示,如DPM就是採用這種思路。但是這個方法有致命的缺點,就是你並不知道要檢測的目標大小是什麼規模,所以你要設定不同大小和比例的視窗去滑動,而且還要選取合適的步長。但是這樣會產生很多的子區域,並且都要經過分類器去做預測,這需要很大的計算量,所以你的分類器不能太複雜,因為要保證速度。解決思路之一就是減少要分類的子區域,這就是R-CNN的一個改進策略,其採用了selective search方法來找到最有可能包含目標的子區域(Region Proposal),其實可以看成採用啟發式方法過濾掉很多子區域,這會提升效率。

這裡寫圖片描述 
圖3 採用滑動視窗進行目標檢測(來源:deeplearning.ai)

如果你使用的是CNN分類器,那麼滑動視窗是非常耗時的。但是結合卷積運算的特點,我們可以使用CNN實現更高效的滑動視窗方法。這裡要介紹的是一種全卷積的方法,簡單來說就是網路中用卷積層代替了全連線層,如圖4所示。輸入圖片大小是16x16,經過一系列卷積操作,提取了2x2的特徵圖,但是這個2x2的圖上每個元素都是和原圖是一一對應的,如圖上藍色的格子對應藍色的區域,這不就是相當於在原圖上做大小為14x14的視窗滑動,且步長為2,共產生4個字區域。最終輸出的通道數為4,可以看成4個類別的預測概率值,這樣一次CNN計算就可以實現視窗滑動的所有子區域的分類預測。這其實是overfeat演算法的思路。之所可以CNN可以實現這樣的效果是因為卷積操作的特性,就是圖片的空間位置資訊的不變性,儘管卷積過程中圖片大小減少,但是位置對應關係還是儲存的。說點題外話,這個思路也被R-CNN借鑑,從而誕生了Fast R-cNN演算法。

這裡寫圖片描述
圖4 滑動視窗的CNN實現(來源:deeplearning.ai)

上面儘管可以減少滑動視窗的計算量,但是隻是針對一個固定大小與步長的視窗,這是遠遠不夠的。Yolo演算法很好的解決了這個問題,它不再是視窗滑動了,而是直接將原始圖片分割成互不重合的小方塊,然後通過卷積最後生產這樣大小的特徵圖,基於上面的分析,可以認為特徵圖的每個元素也是對應原始圖片的一個小方塊,然後用每個元素來可以預測那些中心點在該小方格內的目標,這就是Yolo演算法的樸素思想。下面將詳細介紹Yolo演算法的設計理念。

設計理念

整體來看,Yolo演算法採用一個單獨的CNN模型實現end-to-end的目標檢測,整個系統如圖5所示:首先將輸入圖片resize到448x448,然後送入CNN網路,最後處理網路預測結果得到檢測的目標。相比R-CNN演算法,其是一個統一的框架,其速度更快,而且Yolo的訓練過程也是end-to-end的。

這裡寫圖片描述
圖5 Yolo檢測系統

具體來說,Yolo的CNN網路將輸入的圖片分割成S×SS×S網格,然後每個單元格負責去檢測那些中心點落在該格子內的目標,如圖6所示,可以看到狗這個目標的中心落在左下角一個單元格內,那麼該單元格負責預測這個狗。每個單元格會預測BB個邊界框(bounding box)以及邊界框的置信度(confidence score)。所謂置信度其實包含兩個方面,一是這個邊界框含有目標的可能性大小,二是這個邊界框的準確度。前者記為Pr(object)Pr(object),當該邊界框是背景時(即不包含目標),此時Pr(object)=0Pr(object)=0。而當該邊界框包含目標時,Pr(object)=1Pr(object)=1。邊界框的準確度可以用預測框與實際框(ground truth)的IOU(intersection over union,交併比)來表徵,記為IOUtruthpredIOUpredtruth。因此置信度可以定義為Pr(object)∗IOUtruthpredPr(object)∗IOUpredtruth。很多人可能將Yolo的置信度看成邊界框是否含有目標的概率,但是其實它是兩個因子的乘積,預測框的準確度也反映在裡面。邊界框的大小與位置可以用4個值來表徵:(x,y,w,h)(x,y,w,h),其中(x,y)(x,y)是邊界框的中心座標,而ww和hh是邊界框的寬與高。還有一點要注意,中心座標的預測值(x,y)(x,y)是相對於每個單元格左上角座標點的偏移值,並且單位是相對於單元格大小的,單元格的座標定義如圖6所示。而邊界框的ww和hh預測值是相對於整個圖片的寬與高的比例,這樣理論上4個元素的大小應該在[0,1][0,1]範圍。這樣,每個邊界框的預測值實際上包含5個元素:(x,y,w,h,c)(x,y,w,h,c),其中前4個表徵邊界框的大小與位置,而最後一個值是置信度。

這裡寫圖片描述 
圖6 網格劃分

還有分類問題,對於每一個單元格其還要給出預測出CC個類別概率值,其表徵的是由該單元格負責預測的邊界框其目標屬於各個類別的概率。但是這些概率值其實是在各個邊界框置信度下的條件概率,即Pr(classi|object)Pr(classi|object)。值得注意的是,不管一個單元格預測多少個邊界框,其只預測一組類別概率值,這是Yolo演算法的一個缺點,在後來的改進版本中,Yolo9000是把類別概率預測值與邊界框是繫結在一起的。同時,我們可以計算出各個邊界框類別置信度(class-specific confidence scores):Pr(classi|object)∗Pr(object)∗IOUtruthpred=Pr(classi)∗IOUtruthpredPr(classi|object)∗Pr(object)∗IOUpredtruth=Pr(classi)∗IOUpredtruth。邊界框類別置信度表徵的是該邊界框中目標屬於各個類別的可能性大小以及邊界框匹配目標的好壞。後面會說,一般會根據類別置信度來過濾網路的預測框。

總結一下,每個單元格需要預測(B∗5+C)(B∗5+C)個值。如果將輸入圖片劃分為S×SS×S網格,那麼最終預測值為S×S×(B∗5+C)S×S×(B∗5+C)大小的張量。整個模型的預測值結構如下圖所示。對於PASCAL VOC資料,其共有20個類別,如果使用S=7,B=2S=7,B=2,那麼最終的預測結果就是7×7×307×7×30大小的張量。在下面的網路結構中我們會詳細講述每個單元格的預測值的分佈位置。

這裡寫圖片描述
圖7 模型預測值結構

網路設計

Yolo採用卷積網路來提取特徵,然後使用全連線層來得到預測值。網路結構參考GooLeNet模型,包含24個卷積層和2個全連線層,如圖8所示。對於卷積層,主要使用1x1卷積來做channle reduction,然後緊跟3x3卷積。對於卷積層和全連線層,採用Leaky ReLU啟用函式:max(x,0.1x)max(x,0.1x)。但是最後一層卻採用線性啟用函式。除了上面這個結構,文章還提出了一個輕量級版本Fast Yolo,其僅使用9個卷積層,並且卷積層中使用更少的卷積核。

這裡寫圖片描述
圖8 網路結構

可以看到網路的最後輸出為7×7×307×7×30大小的張量。這和前面的討論是一致的。這個張量所代表的具體含義如圖9所示。對於每一個單元格,前20個元素是類別概率值,然後2個元素是邊界框置信度,兩者相乘可以得到類別置信度,最後8個元素是邊界框的(x,y,w,h)(x,y,w,h)。大家可能會感到奇怪,對於邊界框為什麼把置信度cc和(x,y,w,h)(x,y,w,h)都分開排列,而不是按照(x,y,w,h,c)(x,y,w,h,c)這樣排列,其實純粹是為了計算方便,因為實際上這30個元素都是對應一個單元格,其排列是可以任意的。但是分離排布,可以方便地提取每一個部分。這裡來解釋一下,首先網路的預測值是一個二維張量PP,其shape為[batch,7×7×30][batch,7×7×30]。採用切片,那麼P[:,0:7∗7∗20]P[:,0:7∗7∗20]就是類別概率部分,而P[:,7∗7∗20:7∗7∗(20+2)]P[:,7∗7∗20:7∗7∗(20+2)]是置信度部分,最後剩餘部分P[:,7∗7∗(20+2):]P[:,7∗7∗(20+2):]是邊界框的預測結果。這樣,提取每個部分是非常方便的,這會方面後面的訓練及預測時的計算。

這裡寫圖片描述
圖9 預測張量的解析

網路訓練

在訓練之前,先在ImageNet上進行了預訓練,其預訓練的分類模型採用圖8中前20個卷積層,然後新增一個average-pool層和全連線層。預訓練之後,在預訓練得到的20層卷積層之上加上隨機初始化的4個卷積層和2個全連線層。由於檢測任務一般需要更高清的圖片,所以將網路的輸入從224x224增加到了448x448。整個網路的流程如下圖所示:

這裡寫圖片描述
圖10 Yolo網路流程

下面是訓練損失函式的分析,Yolo演算法將目標檢測看成迴歸問題,所以採用的是均方差損失函式。但是對不同的部分採用了不同的權重值。首先區分定位誤差和分類誤差。對於定位誤差,即邊界框座標預測誤差,採用較大的權重λcoord=5λcoord=5。然後其區分不包含目標的邊界框與含有目標的邊界框的置信度,對於前者,採用較小的權重值λnoobj=0.5λnoobj=0.5。其它權重值均設為1。然後採用均方誤差,其同等對待大小不同的邊界框,但是實際上較小的邊界框的座標誤差應該要比較大的邊界框要更敏感。為了保證這一點,將網路的邊界框的寬與高預測改為對其平方根的預測,即預測值變為(x,y,w−−√,h−−√)(x,y,w,h)。 
另外一點時,由於每個單元格預測多個邊界框。但是其對應類別只有一個。那麼在訓練時,如果該單元格內確實存在目標,那麼只選擇與ground truth的IOU最大的那個邊界框來負責預測該目標,而其它邊界框認為不存在目標。這樣設定的一個結果將會使一個單元格對應的邊界框更加專業化,其可以分別適用不同大小,不同高寬比的目標,從而提升模型效能。大家可能會想如果一個單元格記憶體在多個目標怎麼辦,其實這時候Yolo演算法就只能選擇其中一個來訓練,這也是Yolo演算法的缺點之一。要注意的一點時,對於不存在對應目標的邊界框,其誤差項就是隻有置信度,左標項誤差是沒法計算的。而只有當一個單元格內確實存在目標時,才計算分類誤差項,否則該項也是無法計算的。 
綜上討論,最終的損失函式計算如下:

這裡寫圖片描述

其中第一項是邊界框中心座標的誤差項,1objij1ijobj指的是第ii個單元格存在目標,且該單元格中的第jj個邊界框負責預測該目標。第二項是邊界框的高與寬的誤差項。第三項是包含目標的邊界框的置信度誤差項。第四項是不包含目標的邊界框的置信度誤差項。而最後一項是包含目標的單元格的分類誤差項,1obji1iobj指的是第ii個單元格存在目標。

網路預測

在說明Yolo演算法的預測過程之前,這裡先介紹一下非極大值抑制演算法(non maximum suppression, NMS),這個演算法不單單是針對Yolo演算法的,而是所有的檢測演算法中都會用到。NMS演算法主要解決的是一個目標被多次檢測的問題,如圖11中人臉檢測,可以看到人臉被多次檢測,但是其實我們希望最後僅僅輸出其中一個最好的預測框,比如對於美女,只想要紅色那個檢測結果。那麼可以採用NMS演算法來實現這樣的效果:首先從所有的檢測框中找到置信度最大的那個框,然後挨個計算其與剩餘框的IOU,如果其值大於一定閾值(重合度過高),那麼就將該框剔除;然後對剩餘的檢測框重複上述過程,直到處理完所有的檢測框。Yolo預測過程也需要用到NMS演算法。

這裡寫圖片描述 
圖11 NMS應用在人臉檢測

下面就來分析Yolo的預測過程,這裡我們不考慮batch,認為只是預測一張輸入圖片。根據前面的分析,最終的網路輸出是7×7×307×7×30,但是我們可以將其分割成三個部分:類別概率部分為[7,7,20][7,7,20],置信度部分為[7,7,2][7,7,2],而邊界框部分為[7,7,2,4][7,7,2,4](對於這部分不要忘記根據原始圖片計算出其真實值)。然後將前兩項相乘(矩陣[7,7,20][7,7,20]乘以[7,7,2][7,7,2]可以各補一個維度來完成7,7,1,20]×[7,7,2,1]7,7,1,20]×[7,7,2,1])可以得到類別置信度值為[7,7,2,20][7,7,2,20],這裡總共預測了7∗7∗2=987∗7∗2=98個邊界框。

所有的準備資料已經得到了,那麼我們先說第一種策略來得到檢測框的結果,我認為這是最正常與自然的處理。首先,對於每個預測框根據類別置信度選取置信度最大的那個類別作為其預測標籤,經過這層處理我們得到各個預測框的預測類別及對應的置信度值,其大小都是[7,7,2][7,7,2]。一般情況下,會設定置信度閾值,就是將置信度小於該閾值的box過濾掉,所以經過這層處理,剩餘的是置信度比較高的預測框。最後再對這些預測框使用NMS演算法,最後留下來的就是檢測結果。一個值得注意的點是NMS是對所有預測框一視同仁,還是區分每個類別,分別使用NMS。Ng在deeplearning.ai中講應該區分每個類別分別使用NMS,但是看了很多實現,其實還是同等對待所有的框,我覺得可能是不同類別的目標出現在相同位置這種概率很低吧。

上面的預測方法應該非常簡單明瞭,但是對於Yolo演算法,其卻採用了另外一個不同的處理思路(至少從C原始碼看是這樣的),其區別就是先使用NMS,然後再確定各個box的類別。其基本過程如圖12所示。對於98個boxes,首先將小於置信度閾值的值歸0,然後分類別地對置信度值採用NMS,這裡NMS處理結果不是剔除,而是將其置信度值歸為0。最後才是確定各個box的類別,當其置信度值不為0時才做出檢測結果輸出。這個策略不是很直接,但是貌似Yolo原始碼就是這樣做的。Yolo論文裡面說NMS演算法對Yolo的效能是影響很大的,所以可能這種策略對Yolo更好。但是我測試了普通的圖片檢測,兩種策略結果是一樣的。

這裡寫圖片描述
圖12 Yolo的預測處理流程

演算法效能分析

這裡看一下Yolo演算法在PASCAL VOC 2007資料集上的效能,這裡Yolo與其它檢測演算法做了對比,包括DPM,R-CNN,Fast R-CNN以及Faster R-CNN。其對比結果如表1所示。與實時性檢測方法DPM對比,可以看到Yolo演算法可以在較高的mAP上達到較快的檢測速度,其中Fast Yolo演算法比快速DPM還快,而且mAP是遠高於DPM。但是相比Faster R-CNN,Yolo的mAP稍低,但是速度更快。所以。Yolo演算法算是在速度與準確度上做了折中。

表1 Yolo在PASCAL VOC 2007上與其他演算法的對比 
這裡寫圖片描述

為了進一步分析Yolo演算法,文章還做了誤差分析,將預測結果按照分類與定位準確性分成以下5類:

  • Correct:類別正確,IOU>0.5;(準確度)
  • Localization:類別正確,0.1 < IOU<0.5(定位不準);
  • Similar:類別相似,IOU>0.1;
  • Other:類別錯誤,IOU>0.1;
  • Background:對任何目標其IOU<0.1。(誤把背景當物體)

    Yolo與Fast R-CNN的誤差對比分析如下圖所示:

這裡寫圖片描述 
圖13 Yolo與Fast R-CNN的誤差對比分析

可以看到,Yolo的Correct的是低於Fast R-CNN。另外Yolo的Localization誤差偏高,即定位不是很準確。但是Yolo的Background誤差很低,說明其對背景的誤判率較低。Yolo的那篇文章中還有更多效能對比,感興趣可以看看。

現在來總結一下Yolo的優缺點。首先是優點,Yolo採用一個CNN網路來實現檢測,是單管道策略,其訓練與預測都是end-to-end,所以Yolo演算法比較簡潔且速度快。第二點由於Yolo是對整張圖片做卷積,所以其在檢測目標有更大的視野,它不容易對背景誤判。其實我覺得全連線層也是對這個有貢獻的,因為全連線起到了attention的作用。另外,Yolo的泛化能力強,在做遷移時,模型魯棒性高。

最後不得不談一下Yolo的缺點,首先Yolo各個單元格僅僅預測兩個邊界框,而且屬於一個類別。對於小物體,Yolo的表現會不如人意。這方面的改進可以看SSD,其採用多尺度單元格。也可以看Faster R-CNN,其採用了anchor boxes。Yolo對於在物體的寬高比方面泛化率低,就是無法定位不尋常比例的物體。當然Yolo的定位不準確也是很大的問題。

演算法的TF實現

Yolo的原始碼是用C實現的,但是好在Github上有很多開源的TF復現。這裡我們參考gliese581gg的實現來分析Yolo的Inference實現細節。我們的程式碼將構建一個end-to-end的Yolo的預測模型,利用的已經訓練好的權重檔案,你將可以用自然的圖片去測試檢測效果。 
首先,我們定義Yolo的模型引數:

    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

然後是我們模型的主體網路部分,這個網路將輸出[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

接下來,我們要去解析網路的預測結果,這裡採用了第一種預測策略,即判斷預測框類別,再NMS,多虧了TF提供了NMS的函式tf.image.non_max_suppression,其實實現起來很簡單,所有的細節前面已經交代了:

    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)

其他的就比較容易了,詳細程式碼附在Github上了,歡迎給點個贊,權重檔案在這裡下載。 
最後就是愉快地測試你自己的圖片了:

這裡寫圖片描述

當然,如果你對訓練過程感興趣,你可以參考這裡的實現,如果你看懂了預測過程的程式碼,這裡也會很容易閱讀。

小結

這篇長文詳細介紹了Yolo演算法的原理及實現,當然Yolo-v1還是有很多問題的,所以後續可以讀讀Yolo9000演算法,看看其如何改進的。Ng說Yolo的paper是比較難讀的,其實是很多實現細節,如果不看程式碼是很難理解的。所以,文章中如果有錯誤也可能是難免的,歡迎交流指正。

參考文獻

相關推薦

希爾排序演算法原理實現

1.問題描述 輸入:n個數的序列<a1,a2,a3,...,an>。 輸出:原序列的一個重排<a1*,a2*,a3*,...,an*>;,使得a1*<=a2*<=a3*<=...<=an*。 2. 問題分析 例如,假設有

SM2演算法第二十五篇:ECDSA數字簽名演算法原理實現

---------------------------------------------轉載原因------------------------------------------------- 這邊部落格中有關 EC_KEY_set_private_key和EC_KEY_set_public_key

knn演算法原理實現(1)

一、演算法原理與模型 knn演算法即最近鄰演算法,其原理非常簡單即根據給定的資料集,計算資料集中點的特徵到待分類資料的歐氏距離,然後選擇距離最近的k個作為判斷依據,這k個數據中出現類別最多的作為新輸入資料的label。模型用公式表示如下: 二、python程式碼實現

non-local Means(非區域性均值)降噪演算法及快速演算法原理實現

Non-Local Means演算法原理: Non-Local Means顧名思義,這是一種非區域性平均演算法。何為區域性平均濾波演算法呢?那是在一個目標畫素周圍區域平滑取均值的方法,所以非區域性均值濾波就意味著它使用影象中的所有畫素,這些畫素根據某種相似度進行加權平均。濾

MD5演算法原理實現

由於各種原因,可能存在諸多不足,歡迎斧正! 一、MD5概念 MD5,全名Message Digest Algorithm 5 ,中文名為訊息摘要演算法第五版,為電腦保安領域廣泛使用的一種雜湊函式,用以提供訊息的完整性保護。上面這段話話引用自百度百科,我的理解MD5是

PageRank演算法原理實現

正文共835個字,8張圖,預計閱讀時間6分鐘。1、PageRank1.1.簡介PageRank,

《李航:統計學習方法》--- 感知機演算法原理實現

感知機模型 感知機是一個二類分類的線性分類模型。所謂二類分類就是它只能將例項分為正類和負類兩個類別。那麼為什麼是線性分類模型呢,我的理解是感知機學習旨在求出可以將資料進行劃分的分離超平面,而分離超平面的方程 w⋅x+b=0 為線性方程,所以感知機為線性分類模型

NandFlash ECC 校驗演算法原理實現

ECC的全稱是Error Checking and Correction,是一種用於Nand的差錯檢測和修正演算法。如果操作時序和電路穩定性不存在問題的話,NAND Flash出錯的時候一般不會造成整個Block或是Page不能讀取或是全部出錯,而是整個Page(例如5

Elo評分演算法原理實現

《社交網路》裡的Mark Zackburg被女朋友甩後,在舍友的啟發下,充分發揮了技術宅男自娛自樂的惡搞天分,做出了Facemash網站,對學校女生的相貌進行排名打分,結果網站訪問流量過大,直接把學校網路搞癱瘓了。Facemask大受歡迎的關鍵就在於Zackburg基友E

二分圖匹配,匈牙利演算法原理實現

>文章首先於微信公眾號:幾何思維,關注第一時間獲取更新資訊 >以下場景太過真實,但都是虛構,為了講清楚理論的過程。如有雷同,純屬我瞎編,還望勿對號入座。 ### 1 婚戀市場,明碼實價 中國如今男女比例嚴重失衡,2021年預計將有9200萬單身貴族。為了幫助解決這個社會性問題,提升整體人民的幸福感,小K

YOLO演算法原理實現

目錄 前言 當我們談起計算機視覺時,首先想到的就是影象分類,沒錯,影象分類是計算機視覺最基本的任務之一,但是在影象分類的基礎上,還有更復雜和有意思的任務,如目標檢測,物體定位,影象分割等,見圖1所示。其中目標檢測是一件比較實際的且具有挑戰性的計算機視覺任務,其

目標檢測|YOLO原理實現

轉載自知乎目標檢測|YOLO原理與實現 目標檢測|YOLO原理與實現 最新的YOLOv2和YOLOv3: 小白將:目標檢測|YOLOv2原理與實現(附YOLOv3)​ zhuanlan.zhihu.com 前言 當我們談起計算機視覺時,首先想到的就是影象分類,沒錯

演算法快學筆記(四):快速排序的原理實現

1. 原理介紹 快速排序是一種排序演算法,速度比選擇排序快得多,其主要基於“分而治之”的思想對集合進行排序,本文將對該演算法進行分析。 2. 分而治之(D&C)的思想 D&C主要指利用遞迴的方式來不斷的縮小需要處理問題的規模,最終使問題容易解決。使用D&C

演算法快學筆記(三):選擇排序的原理實現

1. 原理介紹 選擇排序是個簡單的排序,思路主要通過多次遍歷待排序的集合,每次彈出最大/小值並放入新的集合,直到原始集合為空。舉個例子: 假設要對A=[1,2,5,9,3]按照升序的方式進行排序,步驟與結果如下 從A中找出最大值,將其pop,並放入B中,執行後的結果如下:

《機器學習實戰》AdaBoost方法的演算法原理程式實現

一、引言提升(boosting)方法是一種常用的統計學習方法,應用廣泛且有效,在分類問題中,它通過改變訓練樣本的權重,學習多個分類器,並將這些分類器進行線性組合,提高分類的效能。對於分類問題,給定一個訓練樣本集,比較粗糙的分類規則(弱分類器),要比精確分類規則(強分類器)容易

決策樹演算法原理 Python實現

轉自: https://blog.csdn.net/huahuazhu/article/details/73167610?locationNum=2&fps=1    ###########################################

K-means均值聚類演算法原理實現

轉自:http://blog.csdn.net/xiaolewennofollow/article/details/45541159 K-均值聚類演算法的原理與實現 聚類是一種無監督的學習,它將相似的物件歸到同一個簇中,聚類方法幾乎可以應用於所有物件,簇內的物件越相似,聚類的效果越好,本文主要介紹K-均值聚

眼底影象血管增強分割--(2)Gabor濾波演算法原理實現

Gabor濾波演算法 維基裡給出的解釋,“In image processing, aGabor filter is a linear filter used fortextureanalysis,

SM2演算法第五篇:socket的基本原理實現

一、為什麼要用到socket? 我的畢設題目是要實現客戶端與伺服器之間的祕鑰協商,也就是說,我需要用到客戶端與伺服器之間通訊的相關知識,而客戶端與伺服器通訊用到的就是網路程式設計——socket。 二、socket簡要介紹 1、什麼是socket? socket是在應

資料探勘十大演算法(五):EM(Expectation Maximum)演算法原理Python實現

參考: 一、一個簡單的概率問題 實驗:現在有A和B兩個硬幣,我們從這兩個硬幣中,隨機選取5次,做5組實驗,每組實驗內容是:丟擲所選的硬幣,記錄正反面。 實驗資料如下: 目標:根據所得到的實驗資料,分別求出硬幣A和B丟擲後正面向上的概率。 根據古典概率的原