目標(biāo)檢測(cè)簡(jiǎn)介
圖片理解的三個(gè)層次
理解一張圖片拼窥?根據(jù)后續(xù)任務(wù)的需要戏蔑,有三個(gè)主要的層次。
一是分類(Classification)鲁纠,即是將圖像結(jié)構(gòu)化為某一類別的信息总棵,用事先確定好的類別(string)或?qū)嵗齀D來描述圖片。這一任務(wù)是最簡(jiǎn)單改含、最基礎(chǔ)的圖像理解任務(wù)情龄,也是深度學(xué)習(xí)模型最先取得突破和實(shí)現(xiàn)大規(guī)模應(yīng)用的任務(wù)。其中捍壤,ImageNet是最權(quán)威的評(píng)測(cè)集骤视,每年的ILSVRC催生了大量的優(yōu)秀深度網(wǎng)絡(luò)結(jié)構(gòu),為其他任務(wù)提供了基礎(chǔ)鹃觉。在應(yīng)用領(lǐng)域专酗,人臉、場(chǎng)景的識(shí)別等都可以歸為分類任務(wù)盗扇。
二是檢測(cè)(Detection)祷肯。分類任務(wù)關(guān)心整體沉填,給出的是整張圖片的內(nèi)容描述,而檢測(cè)則關(guān)注特定的物體目標(biāo)佑笋,要求同時(shí)獲得這一目標(biāo)的類別信息和位置信息拜轨。相比分類,檢測(cè)給出的是對(duì)圖片前景和背景的理解允青,我們需要從背景中分離出感興趣的目標(biāo),并確定這一目標(biāo)的描述(類別和位置)卵沉,因而颠锉,檢測(cè)模型的輸出是一個(gè)列表,列表的每一項(xiàng)使用一個(gè)數(shù)據(jù)組給出檢出目標(biāo)的類別和位置(常用矩形檢測(cè)框的坐標(biāo)表示)史汗。
三是分割(Segmentation)琼掠。分割包括語義分割(semantic segmentation)和實(shí)例分割(instance segmentation),前者是對(duì)前背景分離的拓展停撞,要求分離開具有不同語義的圖像部分瓷蛙,而后者是檢測(cè)任務(wù)的拓展,要求描述出目標(biāo)的輪廓(相比檢測(cè)框更為精細(xì))戈毒。分割是對(duì)圖像的像素級(jí)描述艰猬,它賦予每個(gè)像素類別(實(shí)例)意義,適用于理解要求較高的場(chǎng)景埋市,如無人駕駛中對(duì)道路和非道路的分割冠桃。
目標(biāo)檢測(cè)
本系列文章關(guān)注的領(lǐng)域是目標(biāo)檢測(cè),即圖像理解的中層次道宅。常見的深度學(xué)習(xí)目標(biāo)檢測(cè)算法分為兩類:以yolo系列網(wǎng)絡(luò)為代表的One-stage網(wǎng)絡(luò)和以faster-rcnn為代表的Two-stage網(wǎng)絡(luò)食听。
對(duì)于上述網(wǎng)絡(luò)的常見理解是,one-stage網(wǎng)絡(luò)速度要快很多污茵,one-stage網(wǎng)絡(luò)的準(zhǔn)確性低于two-stage網(wǎng)絡(luò)樱报。
- 為什么?
Two-stage檢測(cè)分兩個(gè)階段進(jìn)行:(1)首先泞当,模型通過選擇搜索或區(qū)域提議網(wǎng)絡(luò)生成一組候選區(qū)域迹蛤。由于潛在的邊界框候選可以是無限的,因此所提出的區(qū)域是稀疏的襟士。(2)然后分類器僅處理候選區(qū)域笤受。
One-stage會(huì)跳過區(qū)域提議階段,并直接在可能位置的密集采樣上運(yùn)行檢測(cè)敌蜂。這更快更簡(jiǎn)單箩兽,但可能會(huì)降低性能。
one-stage 的精度不如 two-stage 的精度章喉,一個(gè)主要的原因是訓(xùn)練過程中樣本極度不均衡造成的汗贫。
目標(biāo)檢測(cè)常見概念
目標(biāo)檢測(cè)中的 正/負(fù)樣本:
樣本即預(yù)測(cè)出來的box身坐。Faster R-CNN中的anchor boxes以及SSD中的特征圖中的default boxes,這些框中的一部分被選為正樣本(正確識(shí)別目標(biāo))落包,一部分被選為負(fù)樣本(出現(xiàn)誤檢)部蛇,另外一部分被當(dāng)作背景或者不參與運(yùn)算。不同的框架有不同的策略咐蝇,大致篩選策略是根據(jù)IOU的值涯鲁,選取個(gè)閾值范圍進(jìn)行判定。two-stage算法會(huì)在生成RP階段保證正:負(fù)=1:3有序,包括YOLO在內(nèi)的大部分one-stage算法無此環(huán)節(jié)抹腿。比如人臉識(shí)別中的例子,正樣本很好理解旭寿,就是人臉的圖片警绩,負(fù)樣本的選取就與問題場(chǎng)景相關(guān),具體而言盅称,如果你要進(jìn)行教室中學(xué)生的人臉識(shí)別肩祥,那么負(fù)樣本就是教室的窗子、墻等等缩膝。目標(biāo)檢測(cè)中的(樣本)類別不平衡問題:
在訓(xùn)練的過程中要特別注意均衡正負(fù)樣本之間的比例混狠。 因?yàn)椋驗(yàn)樨?fù)樣本占比過大會(huì)使得模型的參數(shù)迭代方向偏離重心疾层,由“把正要樣本正確識(shí)別為正樣本”偏向?yàn)椤安话沿?fù)樣本誤認(rèn)為正樣本”檀蹋。one-stage目標(biāo)檢測(cè)算法在訓(xùn)練時(shí)會(huì)將所有框(正負(fù)樣本)投入訓(xùn)練,因此普遍存在類別不平衡的問題云芦。
yolov原理
逐步介紹YOLO v1~v3的設(shè)計(jì)歷程:
yolov1基本思想
* 時(shí)間:CVPR 2016(YOLO v1)
* 作者:Joseph Redmon, Santosh Divvala, Ross Girshick, Ali Farhadi
* 論文:[You Only Look Once: Unified, Real-Time Object Detection](https://arxiv.org/pdf/1506.02640.pdf)
* 代碼:[yolo_tensorflow](https://github.com/hizhangp/yolo_tensorflow)
* 源碼分析參考:[YOLO源碼解析](https://zhuanlan.zhihu.com/p/25053311)
* 博客參考:[圖解YOLO](https://zhuanlan.zhihu.com/p/24916786)
* 網(wǎng)絡(luò)設(shè)計(jì)
* 輸入shape:448×448×3
* 輸出shape:7×7×30
* 卷積層個(gè)數(shù):24
* 全連接層個(gè)數(shù):2
YOLO(You Only Look Once俯逾,YOLO)將輸入圖像分成SxS個(gè)格子,若某個(gè)物體 Ground truth 的中心位置的坐標(biāo)落入到某個(gè)格子舅逸,那么這個(gè)格子就負(fù)責(zé)檢測(cè)出這個(gè)物體桌肴。
每個(gè)格子預(yù)測(cè)B個(gè)bounding box及其置信度(confidence score),以及C個(gè)類別概率琉历。bbox信息(x,y,w,h)為物體的中心位置相對(duì)格子位置的偏移及寬度和高度,均被歸一化.置信度反映是否包含物體以及包含物體情況下位置的準(zhǔn)確性,定義為:
我們希望預(yù)測(cè)的置信度和ground truth的IOU相同坠七。每個(gè)格子(grid cell)預(yù)測(cè)條件概率值C()。在測(cè)試時(shí)旗笔,每個(gè)box通過類別概率和box置信度相乘來得到特定類別置信度:
網(wǎng)絡(luò)結(jié)構(gòu)
YOLOv1采用卷積神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)彪置。開始的卷積層提取圖像特征,全連接層預(yù)測(cè)輸出概率蝇恶。借鑒了GoogLeNet分類網(wǎng)絡(luò)結(jié)構(gòu)拳魁。24個(gè)卷積層,2個(gè)全鏈接層撮弧。(用1×1 reduction layers 緊跟 3×3 convolutional layers 取代Goolenet的 inception modules )潘懊。YOLOv1網(wǎng)絡(luò)在最后使用全連接層進(jìn)行類別輸出姚糊,因此全連接層的輸出維度是。
- 每個(gè)單元格雖然有多個(gè)Bounding Box授舟,但是只預(yù)測(cè)一個(gè)類別而不是針對(duì)每個(gè)Box預(yù)測(cè)一個(gè)類別
- 預(yù)測(cè)Box救恨,除了x,y,w,h外,還有一個(gè)score释树,如果grid負(fù)責(zé)預(yù)測(cè)某個(gè)object肠槽,score就是預(yù)測(cè)出來的Box和該object的IoU,如果沒有任何object就是0奢啥,所以scores其實(shí)表征了grid是否含有物體和與物體的重疊度兩個(gè)概念秸仙;在測(cè)試的時(shí)候,可以直接把score和class probabilities向量相乘作為class-specific confidence sores
訓(xùn)練
- 預(yù)訓(xùn)練:作者采用ImageNet 1000-class 數(shù)據(jù)集來預(yù)訓(xùn)練卷積層扫尺。預(yù)訓(xùn)練階段,采用圖2-2網(wǎng)絡(luò)中的前20卷積層炊汤,外加average-pooling 層和全連接層正驻。模型訓(xùn)練了一周,獲得了top-5 accuracy為0.88(ImageNet2012 validation set)抢腐,與GoogleNet模型準(zhǔn)確率相當(dāng)姑曙。訓(xùn)練圖像分辨率resize到224x224。
- 將模型轉(zhuǎn)換為檢測(cè)模型:用步驟1)得到的前20個(gè)卷積層網(wǎng)絡(luò)參數(shù)來初始化YOLO模型前20個(gè)卷積層的網(wǎng)絡(luò)參數(shù)迈倍,向預(yù)訓(xùn)練模型中加入了4個(gè)卷積層和兩層全連接層伤靠,提高了模型輸入分辨率(224×224->448×448)。頂層預(yù)測(cè)類別概率和bounding box協(xié)調(diào)值啼染。bounding box的寬和高通過輸入圖像寬和高歸一化到0-1區(qū)間宴合。頂層采用linear activation,其它層使用 leaky rectified linear迹鹅。作者采用sum-squared error為目標(biāo)函數(shù)來優(yōu)化卦洽,增加bounding box loss權(quán)重,減少置信度權(quán)重斜棚。
作者在PASCAL VOC2007和PASCAL VOC2012數(shù)據(jù)集上進(jìn)行了訓(xùn)練和測(cè)試阀蒂。訓(xùn)練135輪,batch size為64弟蚀,動(dòng)量為0.9蚤霞,學(xué)習(xí)速率延遲為0.0005. Learning schedule為:第一輪,學(xué)習(xí)速率從0.001緩慢增加到0.01(因?yàn)槿绻跏紴楦邔W(xué)習(xí)速率义钉,會(huì)導(dǎo)致模型發(fā)散)昧绣;保持0.01速率到75輪;然后在后30輪中捶闸,下降到0.001滞乙;最后30輪奏纪,學(xué)習(xí)速率為0.0001.作者還采用了dropout和 data augmentation來預(yù)防過擬合。dropout值為0.5斩启;data augmentation包括:random scaling序调,translation,adjust exposure和saturation兔簇。
損失函數(shù)
YOLO全部使用了均方和誤差作為loss函數(shù).由三部分組成:坐標(biāo)誤差发绢、IOU誤差和分類誤差。
YOLO在訓(xùn)練過程中Loss計(jì)算如下式所示:
其中垄琐,為預(yù)測(cè)值边酒,為訓(xùn)練標(biāo)記值,表示物體落入格子i的第j個(gè)bbox內(nèi).如果某個(gè)單元格中沒有目標(biāo),則不對(duì)分類誤差進(jìn)行反向傳播;B個(gè)bbox中與GT具有最高IoU的一個(gè)進(jìn)行坐標(biāo)誤差的反向傳播,其余不進(jìn)行.
損失函數(shù)的設(shè)計(jì)目標(biāo)就是讓坐標(biāo)(x,y,w,h)狸窘,confidence墩朦,classification 這個(gè)三個(gè)方面達(dá)到很好的平衡。簡(jiǎn)單的全部采用了平方誤差損失(sum-squared error loss)來做這件事會(huì)有以下不足:
更重視8維的坐標(biāo)預(yù)測(cè)翻擒,給這些損失前面賦予更大的loss weight, 記為 ,在pascal VOC訓(xùn)練中取5氓涣。
如果一個(gè)網(wǎng)格中沒有object(一幅圖中這種網(wǎng)格很多),那么就會(huì)將這些網(wǎng)格中的box的confidence push到0陋气,相比于較少的有object的網(wǎng)格劳吠,這種做法是overpowering的,這會(huì)導(dǎo)致網(wǎng)絡(luò)不穩(wěn)定甚至發(fā)散巩趁。對(duì)沒有object的bbox的confidence loss痒玩,賦予小的loss weight,記為议慰,在pascal VOC訓(xùn)練中取0.5蠢古。有object的bbox的confidence loss 和類別的loss 的loss weight正常取1
-
對(duì)不同大小的bbox預(yù)測(cè)中,相比于大bbox預(yù)測(cè)偏一點(diǎn)别凹,小box預(yù)測(cè)偏一點(diǎn)更不能忍受便瑟。而平方誤差損失(sum-squared error loss)中對(duì)同樣的偏移loss是一樣。作者用了一個(gè)比較取巧的辦法番川,就是將box的width和height取平方根代替原本的height和width到涂。 因?yàn)閟mall bbox的橫軸值較小,發(fā)生偏移時(shí)颁督,反應(yīng)到y(tǒng)軸上的loss 比 big box 要大
一個(gè)網(wǎng)格預(yù)測(cè)多個(gè)bounding box践啄,在訓(xùn)練時(shí)我們希望每個(gè)object(ground true box)只有一個(gè)bounding box專門負(fù)責(zé)(一個(gè)object 一個(gè)bbox)。具體做法是與ground true box(object)的IOU最大的bounding box 負(fù)責(zé)該ground true box(object)的預(yù)測(cè)沉御。
Non-Maximum Suppression就是需要根據(jù)score矩陣和region的坐標(biāo)信息屿讽,從中找到置信度比較高的bounding box。對(duì)于有重疊在一起的預(yù)測(cè)框,只保留得分最高的那個(gè)伐谈。
±猛辍(1)NMS計(jì)算出每一個(gè)bounding box的面積,然后根據(jù)score進(jìn)行排序诵棵,把score最大的bounding box作為隊(duì)列中首個(gè)要比較的對(duì)象抠蚣;
(2)計(jì)算其余bounding box與當(dāng)前最大score與box的IoU履澳,去除IoU大于設(shè)定的閾值的bounding box嘶窄,保留小的IoU得預(yù)測(cè)框;
【啻(3)然后重復(fù)上面的過程柄冲,直至候選bounding box為空。
最終忠蝗,檢測(cè)了bounding box的過程中有兩個(gè)閾值现横,一個(gè)就是IoU,另一個(gè)是在過程之后阁最,從候選的bounding box中剔除score小于閾值的bounding box戒祠。需要注意的是:Non-Maximum Suppression一次處理一個(gè)類別,如果有N個(gè)類別闽撤,Non-Maximum Suppression就需要執(zhí)行N次得哆。
預(yù)測(cè)
-- 設(shè)置閾值脯颜,濾掉分類置信度得分(class-specific confidence score)得分低的boxes
-- 對(duì)于PASCAL VOC數(shù)據(jù)集哟旗,模型需要對(duì)每張圖片預(yù)測(cè)98個(gè)bounding box和對(duì)應(yīng)的類別。對(duì)于大部分目標(biāo)只包含一個(gè)box栋操;其它有些面積大的目標(biāo)包含了多個(gè)boxes闸餐,采用了Non-maximal suppression(非最大值抑制)來提高準(zhǔn)確率。
缺餡:
一矾芙,YOLO的每一個(gè)網(wǎng)格只預(yù)測(cè)兩個(gè)boxes舍沙,一種類別。這導(dǎo)致模型對(duì)相鄰目標(biāo)預(yù)測(cè)準(zhǔn)確率下降剔宪。因此拂铡,YOLO對(duì)成隊(duì)列的目標(biāo)(如 一群鳥)識(shí)別準(zhǔn)確率較低。
二葱绒,YOLO是從數(shù)據(jù)中學(xué)習(xí)預(yù)測(cè)bounding boxes感帅,因此,對(duì)新的或者不常見角度的目標(biāo)無法識(shí)別地淀。
三失球,YOLO的loss函數(shù)對(duì)small bounding boxes和large bounding boxes的error平等對(duì)待,影響了模型識(shí)別準(zhǔn)確率帮毁。因?yàn)閷?duì)于小的bounding boxes实苞,small error影響更大豺撑。
對(duì)比
在準(zhǔn)確率保證的情況下,YOLO速度快于其它方法黔牵。
YOLO定位錯(cuò)誤率高于Fast R-CNN聪轿;Fast R-CNN背景預(yù)測(cè)錯(cuò)誤率高于YOLO∮梗基于此作者設(shè)計(jì)了 Fast-R-CNN + YOLO 檢測(cè)識(shí)別模式屹电,即先用R-CNN提取得到一組bounding box,然后用YOLO處理圖像也得到一組bounding box跃巡。對(duì)比這兩組bounding box是否基本一致危号,如果一致就用YOLO計(jì)算得到的概率對(duì)目標(biāo)分類,最終的bouding box的區(qū)域選取二者的相交區(qū)域素邪。這種組合方式將準(zhǔn)確率提高了3個(gè)百分點(diǎn)外莲。
yolov2基本思想
* [YOLO9000:Better,F(xiàn)aster兔朦,Stronger]偷线,YOLOv2是Joseph Redmon提出的針對(duì)YOLO算法不足的改進(jìn)版本,作者使用了一系列的方法對(duì)原來的YOLO多目標(biāo)檢測(cè)框架進(jìn)行了改進(jìn)沽甥,在保持原有速度的優(yōu)勢(shì)之下声邦,相比v1提高了訓(xùn)練圖像的分辨率;引入了faster rcnn中anchor box的思想摆舟,對(duì)網(wǎng)絡(luò)結(jié)構(gòu)的設(shè)計(jì)進(jìn)行了改進(jìn)亥曹,輸出層使用卷積層替代YOLO的全連接層,此外作者提出了一種目標(biāo)分類與檢測(cè)的聯(lián)合訓(xùn)練方法恨诱,通過這種方法YOLO9000可以同時(shí)在COCO和ImageNet數(shù)據(jù)集中進(jìn)行訓(xùn)練媳瞪,訓(xùn)練后的模型可以實(shí)現(xiàn)多達(dá)9000種物體的實(shí)時(shí)檢測(cè)。
* 時(shí)間:CVPR 2017(YOLO v2),Arxiv 2018(YOLO v3)
* 作者:Joseph Redmon, Ali Farhadi
* 論文:[YOLO9000: Better, Faster, Stronger](https://arxiv.org/pdf/1612.08242v1.pdf)
* 代碼:[yolo_tensorflow](https://github.com/hizhangp/yolo_tensorflow)
* 源碼分析參考:[YOLOv2源碼分析(c版)](https://blog.csdn.net/qq_17550379/column/info/18380)
* 博客參考
* [YOLO升級(jí)版:YOLOv2和YOLO9000解析](https://zhuanlan.zhihu.com/p/25052190)
* [在Python 3中使用YOLOv2](http://www.reibang.com/p/3e77cefeb49b)
YOLOv2 改進(jìn)之處
Batch Normalization:v1中也大量用了Batch Normalization照宝,同時(shí)在定位層后邊用了dropout蛇受,v2中取消了dropout,在卷積層全部使用Batch Normalization厕鹃。
高分辨率分類器:v1中使用224 × 224訓(xùn)練分類器網(wǎng)絡(luò)兢仰,擴(kuò)大到448用于檢測(cè)網(wǎng)絡(luò)。v2將ImageNet以448×448 的分辨率微調(diào)最初的分類網(wǎng)絡(luò)剂碴,迭代10 epochs把将。
網(wǎng)絡(luò)結(jié)構(gòu)與輸入的更改:去掉原來YOLO的一個(gè)池化層使得net的分辨率更高,調(diào)整了網(wǎng)絡(luò)的輸入(448->416)以使得位置坐標(biāo)是奇數(shù)只有一個(gè)中心點(diǎn)(yolo使用pooling來下采樣,有5個(gè)size=2,stride=2的max pooling,而卷積層沒有降低大小,因此最后的特征是416/(2^5)=13).v1中每張圖片預(yù)測(cè)7x7x2=98個(gè)box,而v2加上Anchor Boxes能預(yù)測(cè)超過1000個(gè).檢測(cè)結(jié)果從69.5mAP,81% recall變?yōu)?9.2 mAP,88% recall.
Anchor Boxes:v1中直接在卷積層之后使用全連接層預(yù)測(cè)bbox的坐標(biāo)汗茄。v2借鑒Faster R-CNN的思想預(yù)測(cè)bbox的偏移.移除了全連接層;不再是每個(gè)grid預(yù)測(cè)一個(gè)類別而是每個(gè)Box預(yù)測(cè)一個(gè)類別秸弛,實(shí)現(xiàn)解耦;
Dimension Clusters:對(duì)Faster R-CNN的手選先驗(yàn)框方法做了改進(jìn),采樣k-means在訓(xùn)練集bbox上進(jìn)行聚類產(chǎn)生合適的先驗(yàn)框.由于使用歐氏距離會(huì)使較大的bbox比小的bbox產(chǎn)生更大的誤差,而IOU與bbox尺寸無關(guān),因此使用IOU參與距離計(jì)算,使得通過這些anchor boxes獲得好的IOU分值。距離公式:
使用聚類進(jìn)行選擇的優(yōu)勢(shì)是達(dá)到相同的IOU結(jié)果時(shí)所需的anchor box數(shù)量更少,使得模型的表示能力更強(qiáng),任務(wù)更容易學(xué)習(xí).k-means算法代碼實(shí)現(xiàn)參考:k_means_yolo.py.算法過程是:將每個(gè)bbox的寬和高相對(duì)整張圖片的比例(wr,hr)進(jìn)行聚類,得到k個(gè)anchor box,由于darknet代碼需要配置文件中region層的anchors參數(shù)是絕對(duì)值大小,因此需要將這個(gè)比例值乘上卷積層的輸出特征的大小.如輸入是416x416,那么最后卷積層的特征是13x13.-
Direct location prediction:預(yù)測(cè)邊界框的寬度和高度看起來非常合理递览,但在實(shí)踐中叼屠,訓(xùn)練會(huì)帶來不穩(wěn)定的梯度宣鄙。所以怖糊,現(xiàn)在大部分目標(biāo)檢測(cè)器都是預(yù)測(cè)對(duì)數(shù)空間(log-space)變換贝次,或者預(yù)測(cè)與預(yù)訓(xùn)練默認(rèn)邊界框(即錨點(diǎn))之間的偏移堪滨,即預(yù)測(cè)tx ty tw th。
- (tx,ty):目標(biāo)中心點(diǎn)相對(duì)于該點(diǎn)所在網(wǎng)格左上角的偏移量,使用 sigmoid 函數(shù)進(jìn)行中心坐標(biāo)預(yù)測(cè)茬故。這使得輸出值在 0 和 1 之間戳鹅。如果預(yù)測(cè)到的 x,y 坐標(biāo)大于 1今妄,該中心在該單元右下側(cè)的單元中菲盾,為了解決這個(gè)問題颓影,我們對(duì)輸出執(zhí)行 sigmoid 函數(shù),將輸出壓縮到區(qū)間 0 到 1 之間懒鉴,有效確保中心處于執(zhí)行預(yù)測(cè)的網(wǎng)格單元中诡挂。
- (cx,cy):該點(diǎn)所在網(wǎng)格的左上角距離最左上角相差的格子數(shù)。
- (pw,ph):anchor box 的邊長(zhǎng)
- (tw,th):預(yù)測(cè)邊框的寬和高
細(xì)粒度特征(fine grain features):特征融合临谱,融合了低語義層的信息璃俗,來幫助檢測(cè)小目標(biāo)物體。在Faster R-CNN 和 SSD 均使用了不同的feature map以適應(yīng)不同尺度大小的目標(biāo).YOLOv2使用了一種不同的方法悉默,簡(jiǎn)單添加一個(gè) pass through layer城豁,把淺層特征圖(26x26)連接到深層特征圖(連接到新加入的三個(gè)卷積核尺寸為3 * 3的卷積層最后一層的輸入)。 通過疊加淺層特征圖相鄰特征到不同通道(而非空間位置)抄课,類似于Resnet中的identity mapping唱星。這個(gè)方法把26x26x512的特征圖疊加成13x13x2048的特征圖,與原生的深層特征圖相連接剖膳,使模型有了細(xì)粒度特征魏颓。此方法使得模型的性能獲得了1%的提升岭辣。
Multi-Scale Training:和YOLOv1訓(xùn)練時(shí)網(wǎng)絡(luò)輸入的圖像尺寸固定不變不同吱晒,YOLOv2網(wǎng)絡(luò)由于去掉了fc層,因此任意輸入維度都可以在整個(gè)網(wǎng)絡(luò)上運(yùn)行,(在cfg文件中random=1時(shí))每隔幾次迭代后就會(huì)微調(diào)網(wǎng)絡(luò)的輸入尺寸沦童。訓(xùn)練時(shí)每迭代10次仑濒,就會(huì)隨機(jī)選擇新的輸入圖像尺寸。因?yàn)閅OLOv2的網(wǎng)絡(luò)使用的downsamples倍率為32偷遗,所以使用32的倍數(shù)調(diào)整輸入圖像尺寸{320,352墩瞳,…,608}氏豌。訓(xùn)練使用的最小的圖像尺寸為320 x 320喉酌,最大的圖像尺寸為608 x 608。 這使得網(wǎng)絡(luò)可以適應(yīng)多種不同尺度的輸入.
YOLOv2網(wǎng)絡(luò)結(jié)構(gòu)
YOLOv2的神經(jīng)網(wǎng)絡(luò)部分使用了一個(gè)帶跳層的神經(jīng)網(wǎng)絡(luò),具體結(jié)構(gòu)如下所示:
- 輸入尺寸變?yōu)?16 × 416 × 3泪电,識(shí)別更高分辨率的圖片般妙。
- 每個(gè)卷積層后添加了批標(biāo)準(zhǔn)化層,加速了網(wǎng)絡(luò)的收斂相速。
- (跳層)在第16層開始分為兩條路徑碟渺,將低層的特征直接連接到高層,可提高模型性能突诬。
- 移除全連接層苫拍,使用卷積層預(yù)測(cè)框的偏移量,最終的輸出向量中保存了原來的位置信息旺隙。
訓(xùn)練
神經(jīng)網(wǎng)絡(luò)部分基于模型Darknet-19绒极,該模型的訓(xùn)練部分分為兩個(gè)部分:預(yù)訓(xùn)練和訓(xùn)練部分
a)預(yù)訓(xùn)練:預(yù)訓(xùn)練是在ImageNet上按分類的方式進(jìn)行預(yù)訓(xùn)練160輪,使用SGD優(yōu)化方法蔬捷,初始學(xué)習(xí)率0.1集峦,每次下降4倍,到0.0005時(shí)終止抠刺。除了訓(xùn)練224x224尺寸的圖像外塔淤,還是用448x448尺寸的圖片。
b)訓(xùn)練:去除Darknet的最后一個(gè)卷積層速妖,并將網(wǎng)絡(luò)結(jié)構(gòu)修改為YOLOv2的網(wǎng)絡(luò)高蜂,在VOC數(shù)據(jù)集上進(jìn)行訓(xùn)練。訓(xùn)練使用的代價(jià)函數(shù)是MSE代價(jià)函數(shù)罕容。
在訓(xùn)練過程中备恤,還引入了多尺寸訓(xùn)練,由于網(wǎng)絡(luò)刪除了全連接層锦秒,所以該網(wǎng)絡(luò)并不關(guān)心圖片的具體大小露泊,訓(xùn)練時(shí)使用320~608尺寸的圖像{320,352,….旅择,608}惭笑。使用Darknet框架在ImageNet 1000類上訓(xùn)練160 epochs,學(xué)習(xí)率初始為0.1,以4級(jí)多項(xiàng)式衰減.weight decay=0.0005 , momentum=0.9.使用標(biāo)準(zhǔn)的數(shù)據(jù)增廣方法:random crops, rotations, (hue, saturation), exposure shifts.
#Darknet訓(xùn)練VOC的參數(shù)如下:
learning_rate=0.0001
batch=64
max_batches = 45000 # 最大迭代batch數(shù)
policy=steps # 學(xué)習(xí)率衰減策略
steps=100,25000,35000 # 訓(xùn)練到這些batch次數(shù)時(shí)learning_rate按scale縮放
scales=10,.1,.1 # 與steps對(duì)應(yīng)
檢測(cè)規(guī)則
YOLOv2使用了Anchor Box的方法,神經(jīng)網(wǎng)絡(luò)輸出的向量尺寸是13 × 13 × 125生真,其中13 × 13是將圖片劃分為13行和13列共169個(gè)cell沉噩,每個(gè)cell預(yù)測(cè)125個(gè)數(shù)據(jù)。對(duì)于每個(gè)cell的125個(gè)數(shù)據(jù)柱蟀,分解為125 = 5 × (5+20)川蒙,即每個(gè)cell包括5個(gè)anchor box(預(yù)測(cè)框),每個(gè)anchor box(預(yù)測(cè)框)包括25個(gè)數(shù)據(jù)长已,分別為物體存在置信度畜眨,物體中心位置(x,y)昼牛,物體尺寸(w,h)和類別信息(20個(gè))。如下圖所示:
- 對(duì)于每個(gè)cell包括5個(gè)anchor box信息康聂,每個(gè)anchor box包括25個(gè)數(shù)據(jù)匾嘱,分別:
為是否有物品(1個(gè)):表示位于第i,j的cell中第K個(gè)anchor box中有物品的置信度(標(biāo)簽值為cell預(yù)測(cè)框與gtbox進(jìn)行IOU的值)
-
物品位置(4個(gè)):物品位置(x,y,w,h)與物品位置中心點(diǎn)和尺寸的關(guān)系:
其中,為網(wǎng)絡(luò)為邊界框anchor box預(yù)測(cè)4個(gè)坐標(biāo)早抠,為單元格從圖像的左上角偏移,為之前的邊界框anchor box具有寬度和高度
物體種類(20個(gè)):softmax計(jì)算每一種物體的概率值
每個(gè)cell預(yù)測(cè)5個(gè)anchor box霎烙,這5個(gè)anchor box有不同的預(yù)設(shè)尺寸,該預(yù)設(shè)尺寸可以手動(dòng)指定也可以在訓(xùn)練集上訓(xùn)練獲得蕊连。在YOLOv2中悬垃,預(yù)設(shè)尺寸是通過在測(cè)試集上進(jìn)行類聚獲得的。
添加跨層跳躍連接(借鑒ResNet等思想),融合粗細(xì)粒度的特征:將前面最后一個(gè)3x3x512卷積的特征圖,對(duì)于416x416的輸入,該層輸出26x26x512,直接連接到最后新加的三個(gè)3x3卷積層的最后一個(gè)的前邊.將26x26x512變形為13x13x1024與后邊的13x13x1024特征按channel堆起來得到13x13x3072.從yolo-voc.cfg文件可以看到甘苍,第25層為route層尝蠕,逆向9層拿到第16層26 * 26 * 512的輸出,并由第26層的reorg層把26 * 26 * 512 變形為13 * 13 * 2048载庭,再有第27層的route層連接24層和26層的輸出看彼,堆疊為13 * 13 * 3072,由最后一個(gè)卷積核為3 * 3的卷積層進(jìn)行跨通道的信息融合并把通道降維為1024囚聚。
yolov3網(wǎng)絡(luò)框架
* YOLOv3在YOLOv2的基礎(chǔ)進(jìn)行了一些改進(jìn)靖榕,這些更改使其效果變得更好。 在320×320的圖像上顽铸,YOLOv3運(yùn)行速度達(dá)到了22.2毫秒茁计,mAP為28.2。其與SSD一樣準(zhǔn)確谓松,但速度快了三倍
* 時(shí)間:Arxiv 2018(YOLO v3)
* 作者:Joseph Redmon, Ali Farhadi
* 論文:[YOLOv3: An Incremental Improvement](https://pjreddie.com/media/files/papers/YOLOv3.pdf)
* 代碼:
* [darknet](https://github.com/pjreddie/darknet)
* [keras-yolo3](https://github.com/qqwweee/keras-yolo3])
* [tensorflow-yolov3](https://github.com/YunYang1994/tensorflow-yolov3)
* 官網(wǎng):[darknet](https://pjreddie.com/darknet/yolo/)
* 源碼分析參考:[YOLO v3源碼解讀](https://luckmoonlight.github.io/2018/12/04/yolov3SourceCode/)
* 博客參考
* [YOLOv3論文中文解讀](https://zhuanlan.zhihu.com/p/34945787)
* [yolo系列之yolo v3【深度解析】](https://blog.csdn.net/leviopku/article/details/82660381)
* 相關(guān)博客
* [知乎-計(jì)算機(jī)視覺論文速遞](https://zhuanlan.zhihu.com/c_172507674)
* [YOLO-LITE](https://zhuanlan.zhihu.com/p/50170492)
YOLOv3 改進(jìn)之處
- 網(wǎng)絡(luò)結(jié)構(gòu)改變:網(wǎng)絡(luò)的結(jié)構(gòu)由Darknet-19變?yōu)镈arknet-53星压,跳層的現(xiàn)象越來越普遍。
-
多尺度預(yù)測(cè):輸出3層鬼譬,每層 S × S個(gè)網(wǎng)格娜膘,分別為 13×13 ,26 ×26 优质,52×52
- 小尺度:(13×13的feature map)網(wǎng)絡(luò)接收一張(416×416)的圖竣贪,經(jīng)過5個(gè)步長(zhǎng)為2的卷積來進(jìn)行降采樣(416 / 2?5 = 13),輸出(13×13×512),再經(jīng)過7個(gè)卷積得到第一個(gè)特征圖譜盆赤,在這個(gè)特征圖譜上做第一次預(yù)測(cè)贾富。
- 中尺度: (26×26的feature map)從小尺度中從后向前獲得倒數(shù)第3個(gè)卷積層的輸出歉眷,進(jìn)行一次卷積一次x2上采樣牺六,將上采樣特征(26×26×256)與第43個(gè)卷積特征(26×26×512)連接,輸出(26×26×728),經(jīng)過7個(gè)卷積得到第二個(gè)特征圖譜(26×26×255)汗捡,在這個(gè)特征圖譜上做第二次預(yù)測(cè)淑际。
- 大尺度:(52×52的feature map)操作同中尺度,從后向前獲得倒數(shù)第3個(gè)卷積層的輸出畏纲,進(jìn)行一次卷積一次x2上采樣,將上采樣特征與第26個(gè)卷積特征連接春缕,經(jīng)過7個(gè)卷積得到第三個(gè)特征圖譜盗胀,在這個(gè)特征圖譜上做第三次預(yù)測(cè)。輸出(52×52×255)
- 好處:讓網(wǎng)絡(luò)同時(shí)學(xué)習(xí)到深層和淺層的特征锄贼,通過疊加淺層特征圖特征到相鄰?fù)ǖ榔被遥愃朴贔PN中的umsample+concat。這個(gè)方法把26x26x512的特征圖疊加13x13x256的特征圖宅荤,使模型有了細(xì)粒度特征,增加對(duì)小目標(biāo)的識(shí)別能力
-
anchor box:yolov3 anchor box一共有9個(gè)屑迂,由k-means聚類得到。不同尺寸特征圖對(duì)應(yīng)不同大小的先驗(yàn)框
感受一下9種先驗(yàn)框的尺寸冯键,下圖中藍(lán)色框?yàn)榫垲惖玫降南闰?yàn)框惹盼。黃色框式ground truth,紅框是對(duì)象中心點(diǎn)所在的網(wǎng)格惫确。
* 13×13尺度的anchor box【(116×90)手报,(156×198),(373×326)】
* 26×26尺度的anchor box【(30×61)改化,(62×45)掩蛤,(59×119)】
* 52×52尺度的anchor box【(10×13),(16×30)陈肛,(33×23)】
* 原因:(越精細(xì)的grid cell就可以檢測(cè)出越精細(xì)的物體)尺度越大盏档,感受野越小,對(duì)小物體越敏感燥爷,所以選擇小的anchor box
-
輸入到輸出映射
不考慮神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)細(xì)節(jié)的話蜈亩,總的來說,對(duì)于一個(gè)輸入圖像前翎,YOLO3將其映射到3個(gè)尺度的輸出張量稚配,代表圖像各個(gè)位置存在各種對(duì)象的概率。
我們看一下YOLO3共進(jìn)行了多少個(gè)預(yù)測(cè)港华。對(duì)于一個(gè)416416的輸入圖像道川,在每個(gè)尺度的特征圖的每個(gè)網(wǎng)格設(shè)置3個(gè)先驗(yàn)框,總共有 13133 + 26263 + 5252*3 = 10647 個(gè)預(yù)測(cè)立宜。每一個(gè)預(yù)測(cè)是一個(gè)(4+1+80)=85維向量冒萄,這個(gè)85維向量包含邊框坐標(biāo)(4個(gè)數(shù)值),邊框置信度(1個(gè)數(shù)值)橙数,對(duì)象類別的概率(對(duì)于COCO數(shù)據(jù)集尊流,有80種對(duì)象)。
對(duì)比一下灯帮,YOLO2采用13135 = 845個(gè)預(yù)測(cè)崖技,YOLO3的嘗試預(yù)測(cè)邊框數(shù)量增加了10多倍逻住,而且是在不同分辨率上進(jìn)行,所以mAP以及對(duì)小物體的檢測(cè)效果有一定的提升迎献。
* PS:最終得到的邊框坐標(biāo)值是bx,by,bw,bh.而網(wǎng)絡(luò)學(xué)習(xí)目標(biāo)是tx,ty,tw,th
- 損失函數(shù)LOSS:YOLO V3把YOLOV2中的Softmax loss變成Logistic loss瞎访。這樣能夠支持多標(biāo)簽對(duì)象(比如一個(gè)人有Woman 和 Person兩個(gè)標(biāo)簽)。
網(wǎng)絡(luò)結(jié)構(gòu)
YOLO3采用了稱之為Darknet-53的網(wǎng)絡(luò)結(jié)構(gòu)(含有53個(gè)卷積層)吁恍。
上圖的Darknet-53網(wǎng)絡(luò)采用2562563作為輸入扒秸,最左側(cè)那一列的1、2冀瓦、8等數(shù)字表示多少個(gè)重復(fù)的殘差組件鸦采。每個(gè)殘差組件有兩個(gè)卷積層和一個(gè)快捷鏈路,示意圖如下:
目標(biāo)檢測(cè)算法比較
YOLO3借鑒了殘差網(wǎng)絡(luò)結(jié)構(gòu)咕幻,形成更深的網(wǎng)絡(luò)層次渔伯,以及多尺度檢測(cè),提升了mAP及小物體檢測(cè)效果肄程。如果采用COCO mAP50做評(píng)估指標(biāo)(不是太介意預(yù)測(cè)框的準(zhǔn)確性的話)锣吼,YOLO3的表現(xiàn)相當(dāng)驚人,如下圖所示蓝厌,在精確度相當(dāng)?shù)那闆r下玄叠,YOLOv3的速度是其它模型的3、4倍拓提。
不過如果要求更精準(zhǔn)的預(yù)測(cè)邊框读恃,采用COCO AP做評(píng)估標(biāo)準(zhǔn)的話,YOLO3在精確率上的表現(xiàn)就弱了一些代态。如下圖所示寺惫。
訓(xùn)練自己的數(shù)據(jù)集
本文介紹在darknet框架下訓(xùn)練yolov3網(wǎng)絡(luò), darknet官網(wǎng)地址:https://pjreddie.com/darknet/yolo/
構(gòu)造自己的數(shù)據(jù)集
數(shù)據(jù)集存儲(chǔ)目錄
#目錄格式
- VOCdevkit
- VOC×××
- Annotations #儲(chǔ)存xml
- ImageSets #儲(chǔ)存訓(xùn)練文本信息
- Main
- test.txt
- train.txt
- trainval.txt
- val.txt
- JPEGImages #儲(chǔ)存圖片,不支持png格式
- labels #儲(chǔ)存標(biāo)簽信息
將圖片保存在'JPEGImages'文件夾下,然后使用標(biāo)注軟件(這里使用labelImg軟件)標(biāo)注圖片蹦疑,并將標(biāo)注信息保存在'Annotations'文件夾下西雀。
labelImg標(biāo)注圖片
- 安裝labelImg
#系統(tǒng)為Ubuntu,Python版本不同歉摧,安裝方式也不同艇肴。
#Python 2 + Qt4
sudo apt-get install pyqt4-dev-tools
sudo pip install lxml
git clone https://github.com/tzutalin/labelImg.git
cd labelImg
make all
python labelImg.py #打開labelImg
python labelImg.py [IMAGE_PATH] [PRE-DEFINED CLASS FILE]
#Python 3 + Qt5
sudo apt-get install pyqt5-dev-tools
sudo pip3 install lxml
git clone https://github.com/tzutalin/labelImg.git
cd labelImg
make all
python3 labelImg.py #打開labelImg
python3 labelImg.py [IMAGE_PATH] [PRE-DEFINED CLASS FILE]
使用labelImg
Open:導(dǎo)入單張圖片。
Open Dir:打開文件夾目錄叁温,用Next Image(快捷鍵:A)和Prev Image(快捷鍵:D)查看所有圖片再悼。
Change Save Dir:更改xml文件保存的路徑。
Verify Image:更改xml文件的內(nèi)容膝但。
Save(快捷鍵:Ctrl+S):保存xml文件冲九。
Create\nRectBox(快捷鍵:W):新建標(biāo)注框-
xml解釋
分配訓(xùn)練數(shù)據(jù)
python test_eval.py
test_eval.py文件
import os
if __name__ == '__main__':
path = os.getcwd()
dataAnnotated = os.listdir(path + '/Annotations')
dataNum = len(dataAnnotated) # 數(shù)據(jù)集數(shù)量
ftest = open('ImageSets/Main/test.txt', 'w') # 測(cè)試集
ftrain = open('ImageSets/Main/train.txt', 'w') # 訓(xùn)練集
ftrainval = open('ImageSets/Main/trainval.txt', 'w') # 訓(xùn)練驗(yàn)證集
fval = open('ImageSets/Main/val.txt', 'w') # 驗(yàn)證集
testScale = 0.1 # 測(cè)試集占總數(shù)據(jù)集的比例
trainScale = 0.9 # 訓(xùn)練集占訓(xùn)練驗(yàn)證集的比例
i = 1
testNum = int(dataNum * testScale) # 測(cè)試集的數(shù)量
trainNum = int((dataNum - testNum) * trainScale) # 訓(xùn)練集的數(shù)量
for name in dataAnnotated:
if i <= testNum:
ftest.write(name[0:6]+'\n')
elif i <= testNum + trainNum:
ftrain.write(name[0:6]+'\n')
ftrainval.write(name[0:6]+'\n')
else:
fval.write(name[0:6]+'\n')
ftrainval.write(name[0:6]+'\n')
i += 1
ftrain.close()
ftrainval.close()
fval.close()
ftest.close()
- 生成label文件
python voc_label.py
voc_label.py文件
import os
import pickle
import xml.etree.ElementTree as ET
from os import listdir, getcwd
from os.path import join
from PIL import Image
# Imageset目錄
sets = [('×××', 'train'), ('×××', 'val'), ('×××', 'test')]
#類型名稱
classes = ["××1", "××2"]
def convert(size, box):
dw = 1. / size[0]
dh = 1. / size[1]
x = (box[0] + box[1]) / 2.0
y = (box[2] + box[3]) / 2.0
w = box[1] - box[0]
h = box[3] - box[2]
x = x * dw
w = w * dw
y = y * dh
h = h * dh
return (x, y, w, h)
def convert_annotation(year, image_id):
in_file = open('VOCdevkit/VOC%s/Annotations/%s.xml' % (year, image_id))
out_file = open('VOCdevkit/VOC%s/labels/%s.txt' % (year, image_id), 'w')
tree = ET.parse(in_file)
root = tree.getroot()
size = root.find('size')
w = int(size.find('width').text)
h = int(size.find('height').text)
if w == 0 or h == 0:
image = Image.open('VOCdevkit/VOC%s/JPEGImages/%s.jpg' % (year, image_id))
image_size = image.size
w = image_size[0]
h = image_size[1]
size_w = size.find('width')
size_h = size.find('height')
# a = ET.SubElement(size_w, 'width')
# b = ET.SubElement(size_h, 'height')
size_w.text = str(w)
size_h.text = str(h)
tree.write('VOCdevkit/VOC%s/Annotations/%s.xml' % (year, image_id))
for obj in root.iter('object'):
difficult = obj.find('difficult').text
cls = obj.find('name').text
if cls not in classes or int(difficult) == 1:
continue
cls_id = classes.index(cls)
xmlbox = obj.find('bndbox')
b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text),
float(xmlbox.find('ymax').text))
bb = convert((w, h), b)
out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')
wd = getcwd()
for year, image_set in sets:
if not os.path.exists('VOCdevkit/VOC%s/labels/' % (year)):
os.makedirs('VOCdevkit/VOC%s/labels/' % (year))
image_ids = open('VOCdevkit/VOC%s/ImageSets/Main/%s.txt' % (year, image_set)).read().strip().split()
list_file = open('%s_%s.txt' % (year, image_set), 'w')
for image_id in image_ids:
print(image_id)
list_file.write('%s/VOCdevkit/VOC%s/JPEGImages/%s.jpg\n' % (wd, year, image_id))
convert_annotation(year, image_id)
list_file.close()
開始訓(xùn)練
下載模型
git clone https://github.com/pjreddie/darknet
cd darknet
配置GPU
- 修改Makefile文件配置
vim Makefile
Darknet在GPU上運(yùn)行可以得到500倍的提速,編譯使用GPU要求顯卡是Nvidia卡并且正確安裝了CUDA锰镀。
#1.更改Makefile前兩行GPU和CUDNN的配置
GPU=1
CUDNN=1
#2.更改CUDA的路徑
#48~51行娘侍,在"ifeq ($(GPU), 1)"語句塊中修改為自己的CUDA安裝路徑
#23行咖刃,修改NVCC的路徑:
ifeq ($(GPU), 1)
COMMON+= -DGPU -I/usr/local/cuda-8.0/include/
CFLAGS+= -DGPU
LDFLAGS+= -L/usr/local/cuda-8.0/lib64 -lcuda -lcudart -lcublas -lcurand
NVCC=/usr/local/cuda-8.0/bin/nvcc
#3.修改ARCH配置
"""
如果經(jīng)過1和2的配置修改后編譯的darknet運(yùn)行可能會(huì)報(bào)以下錯(cuò)誤:
Loadingweights from yolo.weights...Done!
CUDA Error:invalid device function
darknet: ./src/cuda.c:21: check_error: Assertion `0' failed.
Aborted (core dumped)
這是因?yàn)榕渲梦募﨧akefile中配置的GPU架構(gòu)和本機(jī)GPU型號(hào)不一致導(dǎo)致的泳炉。
更改前默認(rèn)配置如下(不同版本可能有變):
ARCH= -gencode arch=compute_30,code=sm_30 \
-gencode arch=compute_35,code=sm_35 \
-gencode arch=compute_50,code=[sm_50,compute_50] \
-gencode arch=compute_52,code=[sm_52,compute_52]
# -gencode arch=compute_20,code=[sm_20,sm_21] \ This one is deprecated?
# This is what I use, uncomment if you know your arch and want to specify
# ARCH= -gencode arch=compute_52,code=compute_52
compute_30表示顯卡的計(jì)算能力是3.0憾筏,幾款主流GPU的compute capability列表:
GTX Titan x : 5.2
GTX 980 : 5.2
Tesla K80 : 3.7
Tesla K40 : 3.5
K4200 : 3.0
所以Tesla K80對(duì)應(yīng)compute_30,Tesla K40c對(duì)應(yīng)compute_35花鹅,Titan X對(duì)應(yīng)compute_52氧腰,根據(jù)自己的GPU型號(hào)的計(jì)算能力進(jìn)行配置,例如配置為:
"""
ARCH= -gencode arch=compute_35,code=compute_35 \
- 編譯
make
- 若前面的路徑錯(cuò)誤或GPU算力不匹配都會(huì)報(bào)錯(cuò)刨肃,在服務(wù)器上跑的話將Makefile中的opencv置0古拴,否則報(bào)錯(cuò)。
- 修改Makefile需要重新make真友,修改config.cfg不需要make
下載預(yù)訓(xùn)練模型
在darknet目錄下
wget https://pjreddie.com/media/files/yolov3.weights
./darknet detector test cfg/coco.data cfg/yolov3.cfg yolov3.weights data/dog.jpg
若demo運(yùn)行成功黄痪,則說明沒問題。
修改配置文件
- 修改cfg/yolov3.cfg文件
[net] ★ [xxx]開始的行表示網(wǎng)絡(luò)的一層盔然,其后的內(nèi)容為該層的參數(shù)配置桅打,[net]為特殊的層,配置整個(gè)網(wǎng)絡(luò)
# Testing ★ #號(hào)開頭的行為注釋行愈案,在解析cfg的文件時(shí)會(huì)忽略該行
# batch=1
# subdivisions=1
# Training
batch=64 ★ 這兒batch與機(jī)器學(xué)習(xí)中的batch有少許差別挺尾,僅表示網(wǎng)絡(luò)積累多少個(gè)樣本后進(jìn)行一次BP
subdivisions=16 ★ 這個(gè)參數(shù)表示將一個(gè)batch的圖片分sub次完成網(wǎng)絡(luò)的前向傳播
★★ 敲黑板:在Darknet中,batch和sub是結(jié)合使用的站绪,例如這兒的batch=64遭铺,sub=16表示訓(xùn)練的過
程中將一次性加載64張圖片進(jìn)內(nèi)存,然后分16次完成前向傳播恢准,意思是每次4張魂挂,前向傳播的循環(huán)過程中
累加loss求平均,待64張圖片都完成前向傳播后馁筐,再一次性后傳更新參數(shù)
★★★ 調(diào)參經(jīng)驗(yàn):sub一般設(shè)置16锰蓬,不能太大或太小,且為8的倍數(shù)眯漩,其實(shí)也沒啥硬性規(guī)定芹扭,看著舒服就好
batch的值可以根據(jù)顯存占用情況動(dòng)態(tài)調(diào)整,一次性加減sub大小即可赦抖,通常情況下batch越大越好舱卡,還需
注意一點(diǎn),在測(cè)試的時(shí)候batch和sub都設(shè)置為1队萤,避免發(fā)生神秘錯(cuò)誤轮锥!
width=608 ★ 網(wǎng)絡(luò)輸入的寬width
height=608 ★ 網(wǎng)絡(luò)輸入的高h(yuǎn)eight
channels=3 ★ 網(wǎng)絡(luò)輸入的通道數(shù)channels
★★★ width和height一定要為32的倍數(shù),否則不能加載網(wǎng)絡(luò)
★ 提示:width也可以設(shè)置為不等于height要尔,通常情況下舍杜,width和height的值越大新娜,對(duì)于小目標(biāo)的識(shí)別
效果越好,但受到了顯存的限制既绩,讀者可以自行嘗試不同組合
momentum=0.9 ★ 動(dòng)量 DeepLearning1中最優(yōu)化方法中的動(dòng)量參數(shù)概龄,這個(gè)值影響著梯度下降到最優(yōu)值得速度
decay=0.0005 ★ 權(quán)重衰減正則項(xiàng),防止過擬合
angle=0 ★ 數(shù)據(jù)增強(qiáng)參數(shù)饲握,通過旋轉(zhuǎn)角度來生成更多訓(xùn)練樣本
saturation = 1.5 ★ 數(shù)據(jù)增強(qiáng)參數(shù)私杜,通過調(diào)整飽和度來生成更多訓(xùn)練樣本
exposure = 1.5 ★ 數(shù)據(jù)增強(qiáng)參數(shù),通過調(diào)整曝光量來生成更多訓(xùn)練樣本
hue=.1 ★ 數(shù)據(jù)增強(qiáng)參數(shù)救欧,通過調(diào)整色調(diào)來生成更多訓(xùn)練樣本
learning_rate=0.001 ★ 學(xué)習(xí)率決定著權(quán)值更新的速度衰粹,設(shè)置得太大會(huì)使結(jié)果超過最優(yōu)值,太小會(huì)使下降速度過慢笆怠。
如果僅靠人為干預(yù)調(diào)整參數(shù)铝耻,需要不斷修改學(xué)習(xí)率。剛開始訓(xùn)練時(shí)可以將學(xué)習(xí)率設(shè)置的高一點(diǎn)蹬刷,
而一定輪數(shù)之后瓢捉,將其減小在訓(xùn)練過程中,一般根據(jù)訓(xùn)練輪數(shù)設(shè)置動(dòng)態(tài)變化的學(xué)習(xí)率箍铭。
剛開始訓(xùn)練時(shí):學(xué)習(xí)率以 0.01 ~ 0.001 為宜泊柬。一定輪數(shù)過后:逐漸減緩。
接近訓(xùn)練結(jié)束:學(xué)習(xí)速率的衰減應(yīng)該在100倍以上诈火。
學(xué)習(xí)率的調(diào)整參考https://blog.csdn.net/qq_33485434/article/details/80452941
★★★ 學(xué)習(xí)率調(diào)整一定不要太死兽赁,實(shí)際訓(xùn)練過程中根據(jù)loss的變化和其他指標(biāo)動(dòng)態(tài)調(diào)整,手動(dòng)ctrl+c結(jié)
束此次訓(xùn)練后冷守,修改學(xué)習(xí)率刀崖,再加載剛才保存的模型繼續(xù)訓(xùn)練即可完成手動(dòng)調(diào)參,調(diào)整的依據(jù)是根據(jù)訓(xùn)練
日志來拍摇,如果loss波動(dòng)太大亮钦,說明學(xué)習(xí)率過大,適當(dāng)減小充活,變?yōu)?/5蜂莉,1/10均可,如果loss幾乎不變混卵,
可能網(wǎng)絡(luò)已經(jīng)收斂或者陷入了局部極小映穗,此時(shí)可以適當(dāng)增大學(xué)習(xí)率,注意每次調(diào)整學(xué)習(xí)率后一定要訓(xùn)練久
一點(diǎn)幕随,充分觀察蚁滋,調(diào)參是個(gè)細(xì)活,慢慢琢磨
★★ 一點(diǎn)小說明:實(shí)際學(xué)習(xí)率與GPU的個(gè)數(shù)有關(guān),例如你的學(xué)習(xí)率設(shè)置為0.001辕录,如果你有4塊GPU睦霎,那
真實(shí)學(xué)習(xí)率為0.001/4
burn_in=1000 ★ 在迭代次數(shù)小于burn_in時(shí),其學(xué)習(xí)率的更新有一種方式走诞,大于burn_in時(shí)副女,才采用policy的更新方式
max_batches = 500200 ★ 訓(xùn)練次數(shù)達(dá)到max_batches后停止學(xué)習(xí),一次為跑完一個(gè)batch
policy=steps ★ 學(xué)習(xí)率調(diào)整的策略:constant, steps, exp, poly, step, sig, RANDOM速梗,constant等方式
參考https://nanfei.ink/2018/01/23/YOLOv2%E8%B0%83%E5%8F%82%E6%80%BB%E7%BB%93/#more
steps=400000,450000
scales=.1,.1 ★ steps和scale是設(shè)置學(xué)習(xí)率的變化肮塞,比如迭代到400000次時(shí)襟齿,學(xué)習(xí)率衰減十倍姻锁,45000次迭代時(shí),學(xué)
習(xí)率又會(huì)在前一個(gè)學(xué)習(xí)率的基礎(chǔ)上衰減十倍
[convolutional] ★ 一層卷積層的配置說明
batch_normalize=1 ★ 是否進(jìn)行BN處理猜欺,什么是BN此處不贅述位隶,1為是,0為不是
filters=32 ★ 卷積核個(gè)數(shù)开皿,也是輸出通道數(shù)
size=3 ★ 卷積核尺寸
stride=1 ★ 卷積步長(zhǎng)
pad=1 ★ 卷積時(shí)是否進(jìn)行0 padding,padding的個(gè)數(shù)與卷積核尺寸有關(guān)涧黄,為size/2向下取整,如3/2=1
activation=leaky ★ 網(wǎng)絡(luò)層激活函數(shù)
★★ 卷積核尺寸3*3配合padding且步長(zhǎng)為1時(shí)赋荆,不改變feature map的大小
# Downsample
[convolutional] ★ 下采樣層的配置說明
batch_normalize=1
filters=64
size=3
stride=2
pad=1
activation=leaky ★★ 卷積核尺寸為3*3笋妥,配合padding且步長(zhǎng)為2時(shí),feature map變?yōu)樵瓉淼囊话氪笮?
[shortcut] ★ shotcut層配置說明
from=-3 ★ 與前面的多少次進(jìn)行融合窄潭,-3表示前面第三層
activation=linear ★ 層次激活函數(shù)
......
......
[convolutional] ★ YOLO層前面一層卷積層配置說明
size=1
stride=1
pad=1
filters=255 ★ filters=num(預(yù)測(cè)框個(gè)數(shù))*(classes+5)春宣,5的意義是4個(gè)坐標(biāo)加一個(gè)置信率,論文中的tx,ty,tw,th,
c嫉你,classes為類別數(shù)月帝,COCO為80,num表示YOLO中每個(gè)cell預(yù)測(cè)的框的個(gè)數(shù),YOLOV3中為3
★★★ 自己使用時(shí)幽污,此處的值一定要根據(jù)自己的數(shù)據(jù)集進(jìn)行更改嚷辅,例如你識(shí)別4個(gè)類,則:
filters=3*(4+5)=27,三個(gè)fileters都需要修改距误,切記
activation=linear
[yolo] ★ YOLO層配置說明
mask = 0,1,2 ★ 使用anchor的索引簸搞,0,1准潭,2表示使用下面定義的anchors中的前三個(gè)anchor
anchors = 10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326
classes=80 ★ 類別數(shù)目
num=9 ★ 每個(gè)grid cell總共預(yù)測(cè)幾個(gè)box,和anchors的數(shù)量一致趁俊。當(dāng)想要使用更多anchors時(shí)需要調(diào)大num
jitter=.3 ★ 數(shù)據(jù)增強(qiáng)手段,此處jitter為隨機(jī)調(diào)整寬高比的范圍惋鹅,該參數(shù)不好理解则酝,在我的源代碼注釋中有詳細(xì)說明
ignore_thresh = .7
truth_thresh = 1 ★ 參與計(jì)算的IOU閾值大小.當(dāng)預(yù)測(cè)的檢測(cè)框與ground true的IOU大于ignore_thresh的時(shí)候,參與
loss的計(jì)算,否則沽讹,檢測(cè)框的不參與損失計(jì)算般卑。
★ 理解:目的是控制參與loss計(jì)算的檢測(cè)框的規(guī)模,當(dāng)ignore_thresh過于大爽雄,接近于1的時(shí)候蝠检,那么參與
檢測(cè)框回歸loss的個(gè)數(shù)就會(huì)比較少,同時(shí)也容易造成過擬合挚瘟;而如果ignore_thresh設(shè)置的過于小理卑,那么
參與計(jì)算的會(huì)數(shù)量規(guī)模就會(huì)很大。同時(shí)也容易在進(jìn)行檢測(cè)框回歸的時(shí)候造成欠擬合苛白。
★ 參數(shù)設(shè)置:一般選取0.5-0.7之間的一個(gè)值倾芝,之前的計(jì)算基礎(chǔ)都是小尺度(13*13)用的是0.7,
(26*26)用的是0.5订框。這次先將0.5更改為0.7析苫。參考:https://www.e-learn.cn/content/qita/804953
random=1 ★ 為1打開隨機(jī)多尺度訓(xùn)練,為0則關(guān)閉
★★ 提示:當(dāng)打開隨機(jī)多尺度訓(xùn)練時(shí)穿扳,前面設(shè)置的網(wǎng)絡(luò)輸入尺寸width和height其實(shí)就不起作用了衩侥,width
會(huì)在320到608之間隨機(jī)取值,且width=height矛物,沒10輪隨機(jī)改變一次茫死,一般建議可以根據(jù)自己需要修改
隨機(jī)尺度訓(xùn)練的范圍,這樣可以增大batch履羞,望讀者自行嘗試峦萎!
- 修改cfg/voc.data文件
classes= 2 #你的數(shù)據(jù)及類別
train = .../VOCdevkit/VOC×××/ImageSets/Main/train.txt #產(chǎn)生的train.txt文件路徑
valid = .../VOCdevkit/VOC×××/ImageSets/Main/val.txt #生成的val.txt文件路徑
names = data/voc.names
backup = backup
- 修改data/voc.names文件
man #自己的數(shù)據(jù)集標(biāo)簽
woman
darknet命令
#訓(xùn)練模型(將log信息打印到nohuo.out中,并后臺(tái)運(yùn)行)
nohup ./darknet detector train cfg/voc.data cfg/yolov3-voc.cfg darknet53.conv.74 &
#繼續(xù)訓(xùn)練
nohup ./darknet detector train cfg/voc.data cfg/yolov3-voc.cfg backup/yolov3.backup &
#單張圖片測(cè)試
./darknet detector test cfg/voc.data cfg/yolov3.cfg backup/yolov3.weights img_abs_path
訓(xùn)練log中各參數(shù)的意義
- log示例
- 關(guān)鍵參數(shù)為:IOU .5R .75R Loss Avg loss rate
訓(xùn)練結(jié)果
評(píng)價(jià)指標(biāo)
準(zhǔn)確率 (Accuracy)吧雹,混淆矩陣 (Confusion Matrix)骨杂,精確率(Precision),召回率(Recall)雄卷,平均正確率(AP)搓蚪,mean Average Precision(mAP),交除并(IoU)丁鹉,ROC + AUC
1妒潭、準(zhǔn)確率 (Accuracy)
分對(duì)的樣本數(shù)除以所有的樣本數(shù) ,即:準(zhǔn)確(分類)率 = 正確預(yù)測(cè)的正反例數(shù) / 總數(shù)揣钦。
準(zhǔn)確率一般用來評(píng)估模型的全局準(zhǔn)確程度雳灾,不能包含太多信息,無法全面評(píng)價(jià)一個(gè)模型性能冯凹。
2谎亩、混淆矩陣 (Confusion Matrix)
混淆矩陣中的橫軸是模型預(yù)測(cè)的類別數(shù)量統(tǒng)計(jì),縱軸是數(shù)據(jù)真實(shí)標(biāo)簽的數(shù)量統(tǒng)計(jì)。
對(duì)角線匈庭,表示模型預(yù)測(cè)和數(shù)據(jù)標(biāo)簽一致的數(shù)目夫凸,所以對(duì)角線之和除以測(cè)試集總數(shù)就是準(zhǔn)確率。對(duì)角線上數(shù)字越大越好阱持,在可視化結(jié)果中顏色越深夭拌,說明模型在該類的預(yù)測(cè)準(zhǔn)確率越高。如果按行來看衷咽,每行不在對(duì)角線位置的就是錯(cuò)誤預(yù)測(cè)的類別鸽扁。總的來說镶骗,我們希望對(duì)角線越高越好桶现,非對(duì)角線越低越好。
3卖词、精確率(Precision)與召回率(Recall)
一些相關(guān)的定義巩那。假設(shè)現(xiàn)在有這樣一個(gè)測(cè)試集吏夯,測(cè)試集中的圖片只由大雁和飛機(jī)兩種圖片組成此蜈,假設(shè)你的分類系統(tǒng)最終的目的是:能取出測(cè)試集中所有飛機(jī)的圖片,而不是大雁的圖片噪生。
- True positives : 正樣本被正確識(shí)別為正樣本裆赵,飛機(jī)的圖片被正確的識(shí)別成了飛機(jī)。
- True negatives: 負(fù)樣本被正確識(shí)別為負(fù)樣本跺嗽,大雁的圖片沒有被識(shí)別出來战授,系統(tǒng)正確地認(rèn)為它們是大雁。
- False positives: 假的正樣本桨嫁,即負(fù)樣本被錯(cuò)誤識(shí)別為正樣本植兰,大雁的圖片被錯(cuò)誤地識(shí)別成了飛機(jī)。
- False negatives: 假的負(fù)樣本璃吧,即正樣本被錯(cuò)誤識(shí)別為負(fù)樣本楣导,飛機(jī)的圖片沒有被識(shí)別出來,系統(tǒng)錯(cuò)誤地認(rèn)為它們是大雁畜挨。
Precision其實(shí)就是在識(shí)別出來的圖片中筒繁,True positives所占的比率。也就是本假設(shè)中巴元,所有被識(shí)別出來的飛機(jī)中毡咏,真正的飛機(jī)所占的比例。
Recall 是測(cè)試集中所有正樣本樣例中逮刨,被正確識(shí)別為正樣本的比例呕缭。也就是本假設(shè)中,被正確識(shí)別出來的飛機(jī)個(gè)數(shù)與測(cè)試集中所有真實(shí)飛機(jī)的個(gè)數(shù)的比值。
Precision-recall 曲線:改變識(shí)別閾值恢总,使得系統(tǒng)依次能夠識(shí)別前K張圖片落恼,閾值的變化同時(shí)會(huì)導(dǎo)致Precision與Recall值發(fā)生變化,從而得到曲線离熏。
如果一個(gè)分類器的性能比較好佳谦,那么它應(yīng)該有如下的表現(xiàn):在Recall值增長(zhǎng)的同時(shí),Precision的值保持在一個(gè)很高的水平滋戳。而性能比較差的分類器可能會(huì)損失很多Precision值才能換來Recall值的提高钻蔑。通常情況下,文章中都會(huì)使用Precision-recall曲線奸鸯,來顯示出分類器在Precision與Recall之間的權(quán)衡咪笑。
4、平均精度(Average-Precision娄涩,AP)與 ****mean Average Precision(mAP)
AP就是Precision-recall 曲線下面的面積窗怒,通常來說一個(gè)越好的分類器,AP值越高蓄拣。
mAP是多個(gè)類別AP的平均值扬虚。這個(gè)mean的意思是對(duì)每個(gè)類的AP再求平均,得到的就是mAP的值球恤,mAP的大小一定在[0,1]區(qū)間辜昵,越大越好。該指標(biāo)是目標(biāo)檢測(cè)算法中最重要的一個(gè)咽斧。
在正樣本非常少的情況下堪置,PR表現(xiàn)的效果會(huì)更好。
5张惹、IoU
IoU這一值舀锨,可以理解為系統(tǒng)預(yù)測(cè)出來的框與原來圖片中標(biāo)記的框的重合程度。 計(jì)算方法即檢測(cè)結(jié)果Detection Result與 Ground Truth 的交集比上它們的并集宛逗,即為檢測(cè)的準(zhǔn)確率坎匿。
IOU正是表達(dá)這種bounding box和groundtruth的差異的指標(biāo):
6、ROC(Receiver Operating Characteristic)曲線與AUC(Area Under Curve)
ROC曲線:
- 橫坐標(biāo):假正率(False positive rate拧额, FPR)碑诉,F(xiàn)PR = FP / [ FP + TN] ,代表所有負(fù)樣本中錯(cuò)誤預(yù)測(cè)為正樣本的概率侥锦,假警報(bào)率进栽;
- 縱坐標(biāo):真正率(True positive rate, TPR)恭垦,TPR = TP / [ TP + FN] 快毛,代表所有正樣本中預(yù)測(cè)正確的概率格嗅,命中率。
對(duì)角線對(duì)應(yīng)于隨機(jī)猜測(cè)模型唠帝,而(0,1)對(duì)應(yīng)于所有整理排在所有反例之前的理想模型屯掖。曲線越接近左上角,分類器的性能越好襟衰。
ROC曲線有個(gè)很好的特性:當(dāng)測(cè)試集中的正負(fù)樣本的分布變化的時(shí)候贴铜,ROC曲線能夠保持不變。在實(shí)際的數(shù)據(jù)集中經(jīng)常會(huì)出現(xiàn)類不平衡(class imbalance)現(xiàn)象瀑晒,即負(fù)樣本比正樣本多很多(或者相反)绍坝,而且測(cè)試數(shù)據(jù)中的正負(fù)樣本的分布也可能隨著時(shí)間變化。
- ROC曲線繪制:
(1)根據(jù)每個(gè)測(cè)試樣本屬于正樣本的概率值從大到小排序苔悦;
(2)從高到低轩褐,依次將“Score”值作為閾值threshold,當(dāng)測(cè)試樣本屬于正樣本的概率大于或等于這個(gè)threshold時(shí)玖详,我們認(rèn)為它為正樣本把介,否則為負(fù)樣本;
(3)每次選取一個(gè)不同的threshold蟋座,我們就可以得到一組FPR和TPR拗踢,即ROC曲線上的一點(diǎn)。
當(dāng)我們將threshold設(shè)置為1和0時(shí)蜈七,分別可以得到ROC曲線上的(0,0)和(1,1)兩個(gè)點(diǎn)秒拔。將這些(FPR,TPR)對(duì)連接起來,就得到了ROC曲線飒硅。當(dāng)threshold取值越多,ROC曲線越平滑作谚。
AUC(Area Under Curve)即為ROC曲線下的面積三娩。AUC越接近于1,分類器性能越好妹懒。
物理意義:首先AUC值是一個(gè)概率值雀监,當(dāng)你隨機(jī)挑選一個(gè)正樣本以及一個(gè)負(fù)樣本,當(dāng)前的分類算法根據(jù)計(jì)算得到的Score值將這個(gè)正樣本排在負(fù)樣本前面的概率就是AUC值眨唬。當(dāng)然会前,AUC值越大,當(dāng)前的分類算法越有可能將正樣本排在負(fù)樣本前面匾竿,即能夠更好的分類瓦宜。
計(jì)算公式:就是求曲線下矩形面積。
7岭妖、PR曲線和ROC曲線比較
- ROC曲線特點(diǎn):
×俦印(1)優(yōu)點(diǎn):當(dāng)測(cè)試集中的正負(fù)樣本的分布變化的時(shí)候反璃,ROC曲線能夠保持不變。因?yàn)門PR聚焦于正例假夺,F(xiàn)PR聚焦于與負(fù)例淮蜈,使其成為一個(gè)比較均衡的評(píng)估方法。在實(shí)際的數(shù)據(jù)集中經(jīng)常會(huì)出現(xiàn)類不平衡(class imbalance)現(xiàn)象已卷,即負(fù)樣本比正樣本多很多(或者相反)梧田,而且測(cè)試數(shù)據(jù)中的正負(fù)樣本的分布也可能隨著時(shí)間變化。
〔嗾骸(2)缺點(diǎn):上文提到ROC曲線的優(yōu)點(diǎn)是不會(huì)隨著類別分布的改變而改變柿扣,但這在某種程度上也是其缺點(diǎn)。因?yàn)樨?fù)例N增加了很多闺魏,而曲線卻沒變未状,這等于產(chǎn)生了大量FP。像信息檢索中如果主要關(guān)心正例的預(yù)測(cè)準(zhǔn)確性的話析桥,這就不可接受了司草。在類別不平衡的背景下,負(fù)例的數(shù)目眾多致使FPR的增長(zhǎng)不明顯泡仗,導(dǎo)致ROC曲線呈現(xiàn)一個(gè)過分樂觀的效果估計(jì)埋虹。ROC曲線的橫軸采用FPR,根據(jù)FPR 娩怎,當(dāng)負(fù)例N的數(shù)量遠(yuǎn)超正例P時(shí)搔课,F(xiàn)P的大幅增長(zhǎng)只能換來FPR的微小改變。結(jié)果是雖然大量負(fù)例被錯(cuò)判成正例截亦,在ROC曲線上卻無法直觀地看出來爬泥。(當(dāng)然也可以只分析ROC曲線左邊一小段) - PR曲線:
(1)PR曲線使用了Precision,因此PR曲線的兩個(gè)指標(biāo)都聚焦于正例崩瓤。類別不平衡問題中由于主要關(guān)心正例袍啡,所以在此情況下PR曲線被廣泛認(rèn)為優(yōu)于ROC曲線。
8却桶、使用場(chǎng)景
- ROC曲線由于兼顧正例與負(fù)例境输,所以適用于評(píng)估分類器的整體性能,相比而言PR曲線完全聚焦于正例颖系。
- 如果有多份數(shù)據(jù)且存在不同的類別分布嗅剖,比如信用卡欺詐問題中每個(gè)月正例和負(fù)例的比例可能都不相同,這時(shí)候如果只想單純地比較分類器的性能且剔除類別分布改變的影響嘁扼,則ROC曲線比較適合信粮,因?yàn)轭悇e分布改變可能使得PR曲線發(fā)生變化時(shí)好時(shí)壞,這種時(shí)候難以進(jìn)行模型比較偷拔;反之蒋院,如果想測(cè)試不同類別分布下對(duì)分類器的性能的影響亏钩,則PR曲線比較適合。
- 如果想要評(píng)估在相同的類別分布下正例的預(yù)測(cè)情況欺旧,則宜選PR曲線姑丑。
- 類別不平衡問題中,ROC曲線通常會(huì)給出一個(gè)樂觀的效果估計(jì)辞友,所以大部分時(shí)候還是PR曲線更好栅哀。
- 最后可以根據(jù)具體的應(yīng)用,在曲線上找到最優(yōu)的點(diǎn)称龙,得到相對(duì)應(yīng)的precision留拾,recall,f1 score等指標(biāo)鲫尊,去調(diào)整模型的閾值痴柔,從而得到一個(gè)符合具體應(yīng)用的模型。
批量化測(cè)試圖片
- step1:修改darknet/examples目錄下的detector.c文件
\\在文件開頭添加*GetFilename(char *p)函數(shù)如下:
#include "darknet.h"
static int coco_ids[] = {1,2,3,4,5,6,7,8,9,10,11,13,14,15,16,17,18,19,20,21,22,23,24,25,27,28,31,32,33,34,35,36,37,38,39,40,41,42,43,44,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,67,70,72,73,74,75,76,77,78,79,80,81,82,84,85,86,87,88,89,90};
char *GetFilename(char *p) //此函數(shù)為在原文件中新加的
{
static char name[20]={""};
char *q = strrchr(p,'/') + 1;
strncpy(name,q,6);//注意后面的6疫向,如果你的測(cè)試集的圖片的名字字符(不包括后綴)是其他長(zhǎng)度咳蔚,請(qǐng)改為你需要的長(zhǎng)度(官方的默認(rèn)的長(zhǎng)度是6)
return name;
}
- step2:修改detector.c的test_detector函數(shù)
void test_detector(char *datacfg, char *cfgfile, char *weightfile, char *filename, float thresh, float hier_thresh, char *outfile, int fullscreen)
{
list *options = read_data_cfg(datacfg);
char *name_list = option_find_str(options, "names", "data/names.list");
char **names = get_labels(name_list);
image **alphabet = load_alphabet();
network *net = load_network(cfgfile, weightfile, 0);
set_batch_network(net, 1);
srand(2222222);
double time;
char buff[256];
char *input = buff;
float nms=.45;
int i=0;
while(1){
if(filename){
strncpy(input, filename, 256);
image im = load_image_color(input,0,0);
image sized = letterbox_image(im, net->w, net->h);
//image sized = resize_image(im, net->w, net->h);
//image sized2 = resize_max(im, net->w);
//image sized = crop_image(sized2, -((net->w - sized2.w)/2), -((net->h - sized2.h)/2), net->w, net->h);
//resize_network(net, sized.w, sized.h);
layer l = net->layers[net->n-1];
float *X = sized.data;
time=what_time_is_it_now();
network_predict(net, X);
printf("%s: Predicted in %f seconds.\n", input, what_time_is_it_now()-time);
int nboxes = 0;
detection *dets = get_network_boxes(net, im.w, im.h, thresh, hier_thresh, 0, 1, &nboxes);
//printf("%d\n", nboxes);
//if (nms) do_nms_obj(boxes, probs, l.w*l.h*l.n, l.classes, nms);
if (nms) do_nms_sort(dets, nboxes, l.classes, nms);
draw_detections(im, dets, nboxes, thresh, names, alphabet, l.classes);
free_detections(dets, nboxes);
if(outfile)
{
save_image(im, outfile);
}
else{
save_image(im, "predictions");
#ifdef OPENCV
//cvNamedWindow("predictions", CV_WINDOW_NORMAL);
//if(fullscreen){
//cvSetWindowProperty("predictions", CV_WND_PROP_FULLSCREEN, CV_WINDOW_FULLSCREEN);
// }
// show_image(im, "predictions");
// cvWaitKey(0);
// cvDestroyAllWindows();
#endif
}
free_image(im);
free_image(sized);
if (filename) break;
}
else {
printf("Enter Image Path: ");
fflush(stdout);
input = fgets(input, 256, stdin);
if(!input) return;
strtok(input, "\n");
list *plist = get_paths(input);
char **paths = (char **)list_to_array(plist);
printf("Start Testing!\n");
int m = plist->size;
if(access("/home/pxt/darknet/data/out",0)==-1)//"/home/FENGsl/darknet/data"修改成自己的路徑
{
if (mkdir("/home/pxt/darknet/data/out",0777))//"/home/FENGsl/darknet/data"修改成自己的路徑
{
printf("creat file bag failed!!!");
}
}
for(i = 0; i < m; ++i){
char *path = paths[i];
image im = load_image_color(path,0,0);
image sized = letterbox_image(im, net->w, net->h);
//image sized = resize_image(im, net->w, net->h);
//image sized2 = resize_max(im, net->w);
//image sized = crop_image(sized2, -((net->w - sized2.w)/2), -((net->h - sized2.h)/2), net->w, net->h);
//resize_network(net, sized.w, sized.h);
layer l = net->layers[net->n-1];
float *X = sized.data;
time=what_time_is_it_now();
network_predict(net, X);
printf("Try Very Hard:");
printf("%s: Predicted in %f seconds.\n", path, what_time_is_it_now()-time);
int nboxes = 0;
detection *dets = get_network_boxes(net, im.w, im.h, thresh, hier_thresh, 0, 1, &nboxes);
//printf("%d\n", nboxes);
//if (nms) do_nms_obj(boxes, probs, l.w*l.h*l.n, l.classes, nms);
if (nms) do_nms_sort(dets, nboxes, l.classes, nms);
draw_detections(im, dets, nboxes, thresh, names, alphabet, l.classes);
free_detections(dets, nboxes);
if(outfile){
save_image(im, outfile);
}
else{
char b[2048];
sprintf(b,"/home/pxt/darknet/data/out/%s",GetFilename(path));//"/home/FENGsl/darknet/data"修改成自己的路徑
save_image(im, b);
printf("save %s successfully!\n",GetFilename(path));
#ifdef OPENCV
//cvNamedWindow("predictions", CV_WINDOW_NORMAL);
//if(fullscreen){
// cvSetWindowProperty("predictions", CV_WND_PROP_FULLSCREEN, CV_WINDOW_FULLSCREEN);
// }
// show_image(im, "predictions");
//cvWaitKey(0);
//cvDestroyAllWindows();
#endif
}
free_image(im);
free_image(sized);
if (filename) break;
}
}
}
}
- step3:打開中端進(jìn)入/darknet目錄下,重新編譯(編譯中可能有warning,不影響最終結(jié)果)
make
- step4:將想要測(cè)試的圖片路徑搔驼,放到一個(gè).txt文檔中,然后運(yùn)行測(cè)試
vim test.txt
./darknet detector test cfg/voc.data cfg/yolov3-voc.cfg yolov3-voc_final.weights
- step5: 運(yùn)行加載模型成功后提示你在中斷輸入圖片路徑谈火,將剛才新建的test.txt文件路徑輸入按回車即可,或者也可以將訓(xùn)練時(shí)生成的2007_test.txt文件路徑輸入舌涨。按照前面修改detector.c文件時(shí)設(shè)置的檢測(cè)后圖片保存路徑糯耍,我將圖片保存在data/out路徑下,測(cè)試結(jié)束后即可在該文件夾下看到保存的圖片囊嘉。
Loading weights from yolov3-voc_final.weights...Done!
Enter Image Path: 2007_test.txt
log可視化
- 處理log文件
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#this code is to extract the yolov3 train log
'''
import inspect
import os
import random
import sys
'''
def extract_log(log_file,new_log_file,key_word):
f=open(log_file,'r')
train_log=open(new_log_file,'w')
for line in f:
if 'Syncing' in line: #多gpu同步信息温技,我就一個(gè)GPU,這里是可以不要的。
continue
if 'nan' in line: #包含nan的不要
continue
if key_word in line: #包含關(guān)鍵字
train_log.write(line)
f.close()
train_log.close()
extract_log('train_yolov3.log','DJI_yolov3_train_loss.txt','images')
extract_log('train_yolov3.log','DJI_yolov3_train_iou.txt','IOU')
- loss可視化
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
#%matplotlib inline
lines =16000 #rows to be draw
result = pd.read_csv('DJI_yolov3_train_loss.txt', skiprows=[x for x in range(lines) if ((x%10!=9) |(x<1000))] ,error_bad_lines=False, names=['loss', 'avg', 'rate', 'seconds', 'images'])
result.head()
#print(result)
result['loss']=result['loss'].str.split(' ').str.get(1)
result['avg']=result['avg'].str.split(' ').str.get(1)
result['rate']=result['rate'].str.split(' ').str.get(1)
result['seconds']=result['seconds'].str.split(' ').str.get(1)
result['images']=result['images'].str.split(' ').str.get(1)
result.head()
result.tail()
#print(result.head())
# print(result.tail())
# print(result.dtypes)
'''
print(result['loss'])
print(result['avg'])
print(result['rate'])
print(result['seconds'])
print(result['images'])
'''
result['loss']=pd.to_numeric(result['loss'])
result['avg']=pd.to_numeric(result['avg'])
result['rate']=pd.to_numeric(result['rate'])
result['seconds']=pd.to_numeric(result['seconds'])
result['images']=pd.to_numeric(result['images'])
result.dtypes
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.plot(result['avg'].values,label='avg_loss')
#ax.plot(result['loss'].values,label='loss')
ax.legend(loc='best')
ax.set_title('The loss curves')
ax.set_xlabel('batches*10')
fig.savefig('avg_loss',dpi=600)
#fig.savefig('loss')
- IOU可視化
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
#%matplotlib inline
lines = 16000 #根據(jù)train_log_iou.txt的行數(shù)修改
result = pd.read_csv('DJI_yolov3_train_iou.txt', skiprows=[x for x in range(lines) if (x%10==0 or x%10==9) ] ,error_bad_lines=False, names=['Region Avg IOU', 'Class', 'Obj', 'No Obj', 'Avg Recall','count'])
result.head()
result['Region Avg IOU']=result['Region Avg IOU'].str.split(': ').str.get(1)
result['Class']=result['Class'].str.split(': ').str.get(1)
result['Obj']=result['Obj'].str.split(': ').str.get(1)
result['No Obj']=result['No Obj'].str.split(': ').str.get(1)
result['Avg Recall']=result['Avg Recall'].str.split(': ').str.get(1)
result['count']=result['count'].str.split(': ').str.get(1)
result.head()
result.tail()
# print(result.head())
# print(result.tail())
# print(result.dtypes)
print(result['Region Avg IOU'])
result['Region Avg IOU']=pd.to_numeric(result['Region Avg IOU'])
result['Class']=pd.to_numeric(result['Class'])
result['Obj']=pd.to_numeric(result['Obj'])
result['No Obj']=pd.to_numeric(result['No Obj'])
result['Avg Recall']=pd.to_numeric(result['Avg Recall'])
result['count']=pd.to_numeric(result['count'])
result.dtypes
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.plot(result['Region Avg IOU'].values,label='Region Avg IOU')
# ax.plot(result['Class'].values,label='Class')
# ax.plot(result['Obj'].values,label='Obj')
# ax.plot(result['No Obj'].values,label='No Obj')
# ax.plot(result['Avg Recall'].values,label='Avg Recall')
# ax.plot(result['count'].values,label='count')
ax.legend(loc='best')
# ax.set_title('The Region Avg IOU curves')
ax.set_title('The Region Avg IOU curves')
ax.set_xlabel('batches')
# fig.savefig('Avg IOU')
fig.savefig('Region Avg IOU')
常見問題
- final其實(shí)就是max_batch訓(xùn)練完之后的那一個(gè)
- CUDA: out of memory 以及 resizing 問題:顯存不夠哗伯,調(diào)小batch荒揣,增大subservision,關(guān)閉多尺度訓(xùn)練
- fix error in validate_detector_recall
++total;
box t = {truth[j].x, truth[j].y, truth[j].w, truth[j].h};
float best_iou = 0;
//for(k = 0; k < l.w*l.h*l.n; ++k){
for(k = 0; k < nboxes; ++k){
float iou = box_iou(dets[k].bbox, t);
if(dets[k].objectness > thresh && iou > best_iou){
best_iou = iou;
cited:
[1]YOLOv3: An Incremental Improvement
[2]Deep Residual Learning for Image Recognition
[3]http://www.reibang.com/p/d13ae1055302
[4]https://zhuanlan.zhihu.com/p/34142321
[5]https://www.twblogs.net/a/5bfd51eabd9eee7aed3316d6/zh-cn
[6]https://www.cnblogs.com/makefile/p/YOLOv3.html
[7]https://luckmoonlight.github.io/2018/11/28/yoloV1yolov2yoloV3/
[8]https://blog.csdn.net/qq_34806812/article/details/82355614
[9]https://www.cnblogs.com/yumoye/p/10548714.html
[10]https://blog.csdn.net/phinoo/article/details/83022101
[11]https://www.cnblogs.com/eilearn/p/9071440.html
[12]http://www.reibang.com/p/9a4d5db3cf0a