深度学习目标检测系列:一文弄懂YOLO算法|附Python源码
在之前的文章中,介紹了計(jì)算機(jī)視覺(jué)領(lǐng)域中目標(biāo)檢測(cè)的相關(guān)方法——RCNN系列算法原理,以及Faster RCNN的實(shí)現(xiàn)。這些算法面臨的一個(gè)問(wèn)題,不是端到端的模型,幾個(gè)構(gòu)件拼湊在一起組成整個(gè)檢測(cè)系統(tǒng),操作起來(lái)比較復(fù)雜,本文將介紹另外一個(gè)端到端的方法——YOLO算法,該方法操作簡(jiǎn)便且仿真速度快,效果也不差。
YOLO算法是什么?
? ? ? ?YOLO框架(You Only Look Once)與RCNN系列算法不一樣,是以不同的方式處理對(duì)象檢測(cè)。它將整個(gè)圖像放在一個(gè)實(shí)例中,并預(yù)測(cè)這些框的邊界框坐標(biāo)和及所屬類(lèi)別概率。使用YOLO算法最大優(yōu)的點(diǎn)是速度極快,每秒可處理45幀,也能夠理解一般的對(duì)象表示。
YOLO框架如何運(yùn)作?
? ? ? ?在本節(jié)中,將介紹YOLO用于檢測(cè)給定圖像中的對(duì)象的處理步驟。
- 首先,輸入圖像:
?
- 然后,YOLO將輸入圖像劃分為網(wǎng)格形式(例如3 X 3):
?
- 最后,對(duì)每個(gè)網(wǎng)格應(yīng)用圖像分類(lèi)和定位處理,獲得預(yù)測(cè)對(duì)象的邊界框及其對(duì)應(yīng)的類(lèi)概率。
? ? ? ?整個(gè)過(guò)程是不是很清晰,下面逐一詳細(xì)介紹。首先需要將標(biāo)記數(shù)據(jù)傳遞給模型以進(jìn)行訓(xùn)練。假設(shè)已將圖像劃分為大小為3 X 3的網(wǎng)格,且總共只有3個(gè)類(lèi)別,分別是行人(c1)、汽車(chē)(c2)和摩托車(chē)(c3)。因此,對(duì)于每個(gè)單元格,標(biāo)簽y將是一個(gè)八維向量:
其中:
- pc定義對(duì)象是否存在于網(wǎng)格中(存在的概率);
- bx、by、bh、bw指定邊界框;
- c1、c2、c3代表類(lèi)別。如果檢測(cè)對(duì)象是汽車(chē),則c2位置處的值將為1,c1和c3處的值將為0;
? ? ? ?假設(shè)從上面的例子中選擇第一個(gè)網(wǎng)格:
? ? ? ?由于此網(wǎng)格中沒(méi)有對(duì)象,因此pc將為零,此網(wǎng)格的y標(biāo)簽將為:
? ? ? ???意味著其它值是什么并不重要,因?yàn)榫W(wǎng)格中沒(méi)有對(duì)象。下面舉例另一個(gè)有車(chē)的網(wǎng)格(c2=1):
? ? ? ?在為此網(wǎng)格編寫(xiě)y標(biāo)簽之前,首先要了解YOLO如何確定網(wǎng)格中是否存在實(shí)際對(duì)象。大圖中有兩個(gè)物體(兩輛車(chē)),因此YOLO將取這兩個(gè)物體的中心點(diǎn),物體將被分配到包含這些物體中心的網(wǎng)格中。中心點(diǎn)左側(cè)網(wǎng)格的y標(biāo)簽會(huì)是這樣的:
? ? ? ?由于此網(wǎng)格中存在對(duì)象,因此pc將等于1,bx、by、bh、bw將相對(duì)于正在處理的特定網(wǎng)格單元計(jì)算。由于檢測(cè)出的對(duì)象是汽車(chē),所以c2=1,c1和c3均為0。對(duì)于9個(gè)網(wǎng)格中的每一個(gè)單元格,都具有八維輸出向量。最終的輸出形狀為3X3X8。
? ? ? ?使用上面的例子(輸入圖像:100X100X3,輸出:3X3X8),模型將按如下方式進(jìn)行訓(xùn)練:
? ? ? ?使用經(jīng)典的CNN網(wǎng)絡(luò)構(gòu)建模型,并進(jìn)行模型訓(xùn)練。在測(cè)試階段,將圖像傳遞給模型,經(jīng)過(guò)一次前向傳播就得到輸出y。為了簡(jiǎn)單起見(jiàn),使用3X3網(wǎng)格解釋這一點(diǎn),但通常在實(shí)際場(chǎng)景中會(huì)采用更大的網(wǎng)格(比如19X19)。
? ? ? ?即使一個(gè)對(duì)象跨越多個(gè)網(wǎng)格,它也只會(huì)被分配到其中點(diǎn)所在的單個(gè)網(wǎng)格。可以通過(guò)增加更多網(wǎng)格來(lái)減少多個(gè)對(duì)象出現(xiàn)在同一網(wǎng)格單元中的幾率。
如何編碼邊界框?
? ? ? ?如前所述,bx、by、bh和bw是相對(duì)于正在處理的網(wǎng)格單元計(jì)算而言的。下面通過(guò)一個(gè)例子來(lái)說(shuō)明這一點(diǎn)。以包含汽車(chē)的右邊網(wǎng)格為例:
? ? ? ?由于bx、by、bh和bw將僅相對(duì)于該網(wǎng)格計(jì)算。此網(wǎng)格的y標(biāo)簽將為:
? ? ? ?由于這個(gè)網(wǎng)格中有一個(gè)對(duì)象汽車(chē),所以pc=1、c2=1。現(xiàn)在,看看如何決定bx、by、bh和bw的取值。在YOLO中,分配給所有網(wǎng)格的坐標(biāo)都如下圖所示:
? ? ? ?bx、by是對(duì)象相對(duì)于該網(wǎng)格的中心點(diǎn)的x和y坐標(biāo)。在例子中,近似bx=0.4和by=0.3:
? ? ? ?bh是邊界框的高度與相應(yīng)單元網(wǎng)格的高度之比,在例子中約為0.9:bh=0.9,bw是邊界框的寬度與網(wǎng)格單元的寬度之比,bw=0.5。此網(wǎng)格的y標(biāo)簽將為:
? ? ? ?請(qǐng)注意,bx和by將始終介于0和1之間,因?yàn)橹行狞c(diǎn)始終位于網(wǎng)格內(nèi),而在邊界框的尺寸大于網(wǎng)格尺寸的情況下,bh和bw可以大于1。
非極大值抑制|Non-Max Suppression
? ? ? ?這里有一些思考的問(wèn)題——如何判斷預(yù)測(cè)的邊界框是否是一個(gè)好結(jié)果(或一個(gè)壞結(jié)果)?單元格之間的交叉點(diǎn),計(jì)算實(shí)際邊界框和預(yù)測(cè)的邊界框的并集交集。假設(shè)汽車(chē)的實(shí)際和預(yù)測(cè)邊界框如下所示:
? ? ? ?其中,紅色框是實(shí)際的邊界框,藍(lán)色框是預(yù)測(cè)的邊界框。如何判斷它是否是一個(gè)好的預(yù)測(cè)呢?IoU將計(jì)算這兩個(gè)框的并集交叉區(qū)域:
- IoU =交叉面積/聯(lián)合的面積;
-
在本例中:
- IoU =黃色面積/綠色面積;
? ? ? ?如果IoU大于0.5,就可以說(shuō)預(yù)測(cè)足夠好。0.5是在這里采取的任意閾值,也可以根據(jù)具體問(wèn)題進(jìn)行更改。閾值越大,預(yù)測(cè)就越準(zhǔn)確。
? ? ? ?還有一種技術(shù)可以顯著提高YOLO的效果——非極大值抑制。
? ? ? ?對(duì)象檢測(cè)算法最常見(jiàn)的問(wèn)題之一是,它不是一次僅檢測(cè)出一次對(duì)象,而可能獲得多次檢測(cè)結(jié)果。假設(shè):
? ? ? ?上圖中,汽車(chē)不止一次被識(shí)別,那么如何判定邊界框呢。非極大值抑可以解決這個(gè)問(wèn)題,使得每個(gè)對(duì)象只能進(jìn)行一次檢測(cè)。下面了解該方法的工作原理。
- 1.它首先查看與每次檢測(cè)相關(guān)的概率并取最大的概率。在上圖中,0.9是最高概率,因此首先選擇概率為0.9的方框:
?
- 2.現(xiàn)在,它會(huì)查看圖像中的所有其他框。與當(dāng)前邊界框較高的IoU的邊界框?qū)⒈灰种啤R虼?#xff0c;在示例中,0.6和0.7概率的邊界框?qū)⒈灰种?#xff1a;
?
- 3.在部分邊界框被抑制后,它會(huì)從概率最高的所有邊界框中選擇下一個(gè),在例子中為0.8的邊界框:
?
- 4.再次計(jì)算與該邊界框相連邊界框的IoU,去掉較高IoU值的邊界框:
?
- 5.重復(fù)這些步驟,得到最后的邊界框:
?
? ? ? ?以上就是非極大值抑制的全部?jī)?nèi)容,總結(jié)一下關(guān)于非極大值抑制算法的要點(diǎn):
- 丟棄概率小于或等于預(yù)定閾值(例如0.5)的所有方框;
- 對(duì)于剩余的邊界框:
- 選擇具有最高概率的邊界框并將其作為輸出預(yù)測(cè);
- 計(jì)算相關(guān)聯(lián)的邊界框的IoU值,舍去IoU大于閾值的邊界框;
- 重復(fù)步驟2,直到所有邊界框都被視為輸出預(yù)測(cè)或被舍棄;
Anchor Boxes
? ? ? ?在上述內(nèi)容中,每個(gè)網(wǎng)格只能識(shí)別一個(gè)對(duì)象。但是如果單個(gè)網(wǎng)格中有多個(gè)對(duì)象呢?這就行需要了解 Anchor Boxes的概念。假設(shè)將下圖按照3X3網(wǎng)格劃分:
? ? ? ?獲取對(duì)象的中心點(diǎn),并根據(jù)其位置將對(duì)象分配給相應(yīng)的網(wǎng)格。在上面的示例中,兩個(gè)對(duì)象的中心點(diǎn)位于同一網(wǎng)格中:
? ? ? ?上述方法只會(huì)獲得兩個(gè)邊界框其中的一個(gè),但是如果使用Anchor Boxes,可能會(huì)輸出兩個(gè)邊界框!我們?cè)撛趺醋瞿?#xff1f;首先,預(yù)先定義兩種不同的形狀,稱(chēng)為Anchor Boxes。對(duì)于每個(gè)網(wǎng)格將有兩個(gè)輸出。這里為了易于理解,這里選取兩個(gè)Anchor Boxes,也可以根據(jù)實(shí)際情況增加Anchor Boxes的數(shù)量:
- 沒(méi)有Anchor Boxes的YOLO輸出標(biāo)簽如下所示:
- 有Anchor Boxes的YOLO輸出標(biāo)簽如下所示:
?
? ? ? ?前8行屬于Anchor Boxes1,其余8行屬于Anchor Boxes2。基于邊界框和框形狀的相似性將對(duì)象分配給Anchor Boxes。由于Anchor Boxes1的形狀類(lèi)似于人的邊界框,后者將被分配給Anchor Boxes1,并且車(chē)將被分配給Anchor Boxes2.在這種情況下的輸出,將是3X3X16大小。
? ? ? ?因此,對(duì)于每個(gè)網(wǎng)格,可以根據(jù)Anchor Boxes的數(shù)量檢測(cè)兩個(gè)或更多個(gè)對(duì)象。
結(jié)合思想
? ? ? ?在本節(jié)中,首先介紹如何訓(xùn)練YOLO模型,然后是新的圖像進(jìn)行預(yù)測(cè)。
訓(xùn)練
? ? ? ?訓(xùn)練模型時(shí),輸入數(shù)據(jù)是由圖像及其相應(yīng)的y標(biāo)簽構(gòu)成。樣例如下:
? ? ? ?假設(shè)每個(gè)網(wǎng)格有兩個(gè)Anchor Boxes,并劃分為3X3網(wǎng)格,并且有3個(gè)不同的類(lèi)別。因此,相應(yīng)的y標(biāo)簽具有3X3X16的形狀。訓(xùn)練過(guò)程的完成方式就是將特定形狀的圖像映射到對(duì)應(yīng)3X3X16大小的目標(biāo)。
測(cè)試
? ? ? ?對(duì)于每個(gè)網(wǎng)格,模型將預(yù)測(cè)·3X3X16·大小的輸出。該預(yù)測(cè)中的16個(gè)值將與訓(xùn)練標(biāo)簽的格式相同。前8個(gè)值將對(duì)應(yīng)于Anchor Boxes1,其中第一個(gè)值將是該網(wǎng)絡(luò)中對(duì)象的概率,2-5的值將是該對(duì)象的邊界框坐標(biāo),最后三個(gè)值表明對(duì)象屬于哪個(gè)類(lèi)。以此類(lèi)推。
? ? ? ?最后,非極大值抑制方法將應(yīng)用于預(yù)測(cè)框以獲得每個(gè)對(duì)象的單個(gè)預(yù)測(cè)結(jié)果。
? ? ? ?以下是YOLO算法遵循的確切維度和步驟:
- 準(zhǔn)備對(duì)應(yīng)的圖像(608,608,3);
- 將圖像傳遞給卷積神經(jīng)網(wǎng)絡(luò)(CNN),該網(wǎng)絡(luò)返回(19,19,5,85)維輸出;
-
輸出的最后兩個(gè)維度被展平以獲得(19,19,425)的輸出量:
- 19×19網(wǎng)格的每個(gè)單元返回425個(gè)數(shù)字;
- 425=5 * 85,其中5是每個(gè)網(wǎng)格的Anchor Boxes數(shù)量;
- 85= 5+80,其中5表示(pc、bx、by、bh、bw),80是檢測(cè)的類(lèi)別數(shù);
- 最后,使用IoU和非極大值抑制去除重疊框;
YOLO算法實(shí)現(xiàn)
? ? ? ?本節(jié)中用于實(shí)現(xiàn)YOLO的代碼來(lái)自Andrew NG的GitHub存儲(chǔ)庫(kù),需要下載此zip文件,其中包含運(yùn)行此代碼所需的預(yù)訓(xùn)練權(quán)重。
? ? ? ?首先定義一些函數(shù),這些函數(shù)將用來(lái)選擇高于某個(gè)閾值的邊界框,并對(duì)其應(yīng)用非極大值抑制。首先,導(dǎo)入所需的庫(kù):
然后,實(shí)現(xiàn)基于概率和閾值過(guò)濾邊界框的函數(shù):
def yolo_filter_boxes(box_confidence, boxes, box_class_probs, threshold = .6):box_scores = box_confidence*box_class_probsbox_classes = K.argmax(box_scores,-1)box_class_scores = K.max(box_scores,-1)filtering_mask = box_class_scores>thresholdscores = tf.boolean_mask(box_class_scores,filtering_mask)boxes = tf.boolean_mask(boxes,filtering_mask)classes = tf.boolean_mask(box_classes,filtering_mask)return scores, boxes, classes之后,實(shí)現(xiàn)計(jì)算IoU的函數(shù):
def iou(box1, box2):xi1 = max(box1[0],box2[0])yi1 = max(box1[1],box2[1])xi2 = min(box1[2],box2[2])yi2 = min(box1[3],box2[3])inter_area = (yi2-yi1)*(xi2-xi1)box1_area = (box1[3]-box1[1])*(box1[2]-box1[0])box2_area = (box2[3]-box2[1])*(box2[2]-box2[0])union_area = box1_area+box2_area-inter_areaiou = inter_area/union_areareturn iou然后,實(shí)現(xiàn)非極大值抑制的函數(shù):
def yolo_non_max_suppression(scores, boxes, classes, max_boxes = 10, iou_threshold = 0.5):max_boxes_tensor = K.variable(max_boxes, dtype='int32')K.get_session().run(tf.variables_initializer([max_boxes_tensor]))nms_indices = tf.image.non_max_suppression(boxes,scores,max_boxes,iou_threshold)scores = K.gather(scores,nms_indices)boxes = K.gather(boxes,nms_indices)classes = K.gather(classes,nms_indices)return scores, boxes, classes隨機(jī)初始化下大小為(19,19,5,85)的輸出向量:
yolo_outputs = (tf.random_normal([19, 19, 5, 1], mean=1, stddev=4, seed = 1),tf.random_normal([19, 19, 5, 2], mean=1, stddev=4, seed = 1),tf.random_normal([19, 19, 5, 2], mean=1, stddev=4, seed = 1),tf.random_normal([19, 19, 5, 80], mean=1, stddev=4, seed = 1))最后,實(shí)現(xiàn)一個(gè)將CNN的輸出作為輸入并返回被抑制的邊界框的函數(shù):
def yolo_eval(yolo_outputs, image_shape = (720., 1280.), max_boxes=10, score_threshold=.6, iou_threshold=.5):box_confidence, box_xy, box_wh, box_class_probs = yolo_outputsboxes = yolo_boxes_to_corners(box_xy, box_wh)scores, boxes, classes = yolo_filter_boxes(box_confidence, boxes, box_class_probs, threshold = score_threshold)boxes = scale_boxes(boxes, image_shape)scores, boxes, classes = yolo_non_max_suppression(scores, boxes, classes, max_boxes, iou_threshold)return scores, boxes, classes使用yolo_eval函數(shù)對(duì)之前創(chuàng)建的隨機(jī)輸出向量進(jìn)行預(yù)測(cè):
scores, boxes, classes = yolo_eval(yolo_outputs) with tf.Session() as test_b:print("scores[2] = " + str(scores[2].eval()))print("boxes[2] = " + str(boxes[2].eval()))print("classes[2] = " + str(classes[2].eval()))?
score表示對(duì)象在圖像中的可能性,boxes返回檢測(cè)到的對(duì)象的(x1,y1,x2,y2)坐標(biāo),classes表示識(shí)別對(duì)象所屬的類(lèi)。
現(xiàn)在,在新的圖像上使用預(yù)訓(xùn)練的YOLO算法,看看其工作效果:
在加載類(lèi)別信息和預(yù)訓(xùn)練模型之后,使用上面定義的函數(shù)來(lái)獲取·yolo_outputs·。
yolo_outputs = yolo_head(yolo_model.output, anchors, len(class_names))之后,定義一個(gè)函數(shù)來(lái)預(yù)測(cè)邊界框并在圖像上標(biāo)記邊界框:
def predict(sess, image_file):image, image_data = preprocess_image("images/" + image_file, model_image_size = (608, 608))out_scores, out_boxes, out_classes = sess.run([scores, boxes, classes], feed_dict={yolo_model.input: image_data, K.learning_phase(): 0})print('Found {} boxes for {}'.format(len(out_boxes), image_file))# Generate colors for drawing bounding boxes.colors = generate_colors(class_names)# Draw bounding boxes on the image filedraw_boxes(image, out_scores, out_boxes, out_classes, class_names, colors)# Save the predicted bounding box on the imageimage.save(os.path.join("out", image_file), quality=90)# Display the results in the notebookoutput_image = scipy.misc.imread(os.path.join("out", image_file))plt.figure(figsize=(12,12))imshow(output_image)return out_scores, out_boxes, out_classes接下來(lái),將使用預(yù)測(cè)函數(shù)讀取圖像并進(jìn)行預(yù)測(cè):
img = plt.imread('images/img.jpg') image_shape = float(img.shape[0]), float(img.shape[1]) scores, boxes, classes = yolo_eval(yolo_outputs, image_shape)最后,輸出預(yù)測(cè)結(jié)果:
out_scores, out_boxes, out_classes = predict(sess, "img.jpg")?
以上就是YOLO算法的全部?jī)?nèi)容
?
原文鏈接
本文為云棲社區(qū)原創(chuàng)內(nèi)容,未經(jīng)允許不得轉(zhuǎn)載。
總結(jié)
以上是生活随笔為你收集整理的深度学习目标检测系列:一文弄懂YOLO算法|附Python源码的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 安全看得见,阿里云性能监控 ARMS 全
- 下一篇: (Python)零起步数学+神经网络入门