實(shí)踐教程|使用YOLO V5訓(xùn)練自動(dòng)駕駛目標(biāo)檢測(cè)網(wǎng)絡(luò)

極市導(dǎo)讀
?本文詳細(xì)介紹YOLO V5的網(wǎng)絡(luò)結(jié)構(gòu)及組成模塊,并使用YOLO V5s在BDD100K自動(dòng)駕駛數(shù)據(jù)集上進(jìn)行遷移學(xué)習(xí),搭建屬于自己的自動(dòng)駕駛交通物體對(duì)象識(shí)別網(wǎng)絡(luò)。?>>加入極市CV技術(shù)交流群,走在計(jì)算機(jī)視覺的最前沿
YOLO 是一種快速緊湊的開源對(duì)象檢測(cè)模型,與其它網(wǎng)絡(luò)相比,同等尺寸下性能更強(qiáng),并且具有很不錯(cuò)的穩(wěn)定性,是第一個(gè)可以預(yù)測(cè)對(duì)象的類別和邊界框的端對(duì)端神經(jīng)網(wǎng)絡(luò)。YOLO 家族一直有著旺盛的生命力,從YOLO V1一直到”V5“,如今已經(jīng)延續(xù)五代,憑借著不斷的創(chuàng)新和完善,一直被計(jì)算機(jī)視覺工程師作為對(duì)象檢測(cè)的首選框架之一。
Ultralytics于5月27日發(fā)布了YOLOv5 的第一個(gè)正式版本,其性能與YOLO V4不相伯仲,是現(xiàn)今最先進(jìn)的對(duì)象檢測(cè)技術(shù)之一,并在推理速度上是目前最強(qiáng)。
Ultralytics:https://github.com/ultralytics/yolov5
我在前一篇文章:一文讀懂YOLO V5 與 YOLO V4介紹了YOLO V5和YOLO V4的原理,相似點(diǎn)及區(qū)別。
在本文章中,我會(huì)詳細(xì)介紹YOLO V5的網(wǎng)絡(luò)結(jié)構(gòu)及組成模塊,并使用YOLO V5s對(duì)BDD100K自動(dòng)駕駛數(shù)據(jù)集進(jìn)行遷移學(xué)習(xí),使得訓(xùn)練出的模型能夠識(shí)別包括交通燈顏色在內(nèi)的所有交通對(duì)象。
Github: https://github.com/williamhyin/yolov5s_bdd100k
Email: [email protected]
知乎專欄: 自動(dòng)駕駛?cè)珬9こ處?https://www.zhihu.com/people/william.hyin
本文分成兩塊:模型結(jié)構(gòu)及遷移學(xué)習(xí)。
Model architecture Overview Focus BottleneckCSP SPP PANET Transfer learning Data prepration Setup enviorment Configuration Modify model architecture Transfer learning theory Inference
Model architecture
YOLO網(wǎng)絡(luò)由三個(gè)主要組件組成:
1)Backbone -在不同圖像細(xì)粒度上聚合并形成圖像特征的卷積神經(jīng)網(wǎng)絡(luò)。
2)Neck:一系列混合和組合圖像特征的網(wǎng)絡(luò)層,并將圖像特征傳遞到預(yù)測(cè)層。
3)Head:對(duì)圖像特征進(jìn)行預(yù)測(cè),生成邊界框和并預(yù)測(cè)類別。
本文主要采用YOLO V5 1.0結(jié)構(gòu),7月23日作者更新了2.0版本代碼,對(duì)于模型定義做了些改變,我會(huì)后續(xù)進(jìn)行更新。
YOLO V5 1.0中用到的重要的模塊包括Focus,BottleneckCSP,SPP,PANET。模型的上采樣Upsample是采用nearst兩倍上采樣插值。值得注意的是YOLO V5 1.0最初為COCO數(shù)據(jù)集訓(xùn)練的Pretrained_model 使用的是FPN作為Neck,在6月22日后,Ultralytics已經(jīng)更新模型的Neck為PANET。網(wǎng)上很多的YOLO V5網(wǎng)絡(luò)結(jié)構(gòu)介紹都是基于FPN-NECK,本文的模型訓(xùn)練是基于PANET-NECK,下文中只介紹PANET-NECK。
對(duì)于YOLO V5,無論是V5s,V5m,V5l還是V5x其Backbone,Neck和Head一致。唯一的區(qū)別在與模型的深度和寬度設(shè)置,只需要修改這兩個(gè)參數(shù)就可以調(diào)整模型的網(wǎng)絡(luò)結(jié)構(gòu)。V5l 的參數(shù)是默認(rèn)參數(shù)。
depth multiple是用來控制模型的深度,例如V5s的深度是0.33,而V5l的深度是1,也就是說V5l的Bottleneck個(gè)數(shù)是V5s的3倍。 width_multiple是用來控制卷積核的個(gè)數(shù),V5s的寬度是0.5,而V5l的寬度是1,表示V5s的卷積核數(shù)量是默認(rèn)設(shè)置的一半,當(dāng)然你也可以設(shè)置到1.25倍,即V5x。例如下面YOLO V5的yaml文件中的backbone的第一層是 [[-1, 1, Focus, [64, 3]],而V5s的寬度是0.5,因此這一層實(shí)際上是[[-1, 1, Focus, [32, 3]]。from列參數(shù):-1 代表是從上一層獲得的輸入,-2表示從上兩層獲得的輸入(head同理)。 number列參數(shù):1表示只有一個(gè),3表示有三個(gè)相同的模塊。
下圖為YOLO V5 1.0的網(wǎng)絡(luò)結(jié)構(gòu)圖(默認(rèn)對(duì)應(yīng)YOLO V5l),引用自Laughing-q(https://blog.csdn.net/Q1u1NG)。
下圖中存在三種括號(hào),其中 In_channel:輸入通道,out_channel:輸出通道,Kernel_size:卷積核大小,Stride:步長(zhǎng),x N代表此模塊的疊加次數(shù),方框外數(shù)字:depth x weight x height,默認(rèn)輸入為寬高為640x640的三通道圖像。

下文我將詳細(xì)講述Focus,BottleneckCSP,SPP,PANET這幾個(gè)重要模塊,由于本項(xiàng)目使用YOLO V5s網(wǎng)絡(luò)結(jié)構(gòu)訓(xùn)練模型,因此下文中的網(wǎng)絡(luò)圖及實(shí)例都基于YOLO V5s,并且輸入圖像為3x640x640。YOLO V5默認(rèn)depth_multiple=0.33, width_multiple=0.50。即BottleneckCSP中Bottleneck的數(shù)量為默認(rèn)的1/3,而所有卷積操作的卷積核個(gè)數(shù)均為默認(rèn)的1/2。
Focus
下圖為YOLO V5s的Focus 隔行采樣拼接結(jié)構(gòu)。

YOLO V5默認(rèn)3x640x640的輸入,復(fù)制四份,然后通過切片操作將這個(gè)四個(gè)圖片切成了四個(gè)3x320x320的切片,接下來使用concat從深度上連接這四個(gè)切片,輸出為12x320x320,之后再通過卷積核數(shù)為32的卷積層,生成32x320x320的輸出,最后經(jīng)過batch_borm 和leaky_relu將結(jié)果輸入到下一個(gè)卷積層。
Focus的代碼分析如下
class?Focus(nn.Module):
????#?Focus?wh?information?into?c-space
????def?__init__(self,?c1,?c2,?k=1,?s=1,?p=None,?g=1,?act=True):??#?ch_in,?ch_out,?kernel,?stride,?padding,?groups
????????super(Focus,?self).__init__()
????????self.conv?=?Conv(c1?*?4,?c2,?k,?s,?p,?g,?act)
????def?forward(self,?x):??#?x(b,c,w,h)?->?y(b,4c,w/2,h/2)
????????return?self.conv(torch.cat([x[...,?::2,?::2],?x[...,?1::2,?::2],?x[...,?::2,?1::2],?x[...,?1::2,?1::2]],?1))
核心為這段代碼self.conv(torch.cat([x[..., ::2, ::2], x[..., 1::2, ::2], x[..., ::2, 1::2], x[..., 1::2, 1::2]], 1)) 。x[..., ::2, ::2]是黃色部分,x[..., 1::2, ::2],是紅色部分,以此類推。對(duì)于x[..., ::2, ::2]其中第一個(gè)參數(shù)“..."代表深度,也就是說三個(gè)通道都要切,第二個(gè)和第三個(gè)代表不論是寬和高都是每隔一個(gè)采樣。對(duì)于x[..., 1::2, ::2],1::2代表從列位置1開始,也就是每序號(hào)奇數(shù)列采樣。藍(lán)色,綠色的生成方式以此類推。最后用cat連接這些隔行采樣圖,生成通道數(shù)為12的特征圖。
BottlenneckCSP
下圖為YOLO V5s的第一個(gè)BottlenneckCSP結(jié)構(gòu)。

BottlenneckCSP分為兩部分,Bottlenneck以及CSP。
Bottlenneck
Bottlenneck其實(shí)就是經(jīng)典的殘差結(jié)構(gòu),先是1x1的卷積層(conv+batch_norm+leaky relu),然后再是3x3的卷積層,最后通過殘差結(jié)構(gòu)與初始輸入相加。
值得注意的是YOLO V5通過depth multiple控制模型的深度,例如V5s的深度是0.33,而V5l的深度是1,也就是說V5x的BottlenneckCSP中Bottleneck個(gè)數(shù)是V5s的3倍,模型中第一個(gè)BottlenneckCSP默認(rèn)Bottleneck個(gè)數(shù)x3,對(duì)于V5s只有上圖中的一個(gè)Bottleneck。
作者的代碼如下,值得注意的是e就是width_multiple,表示當(dāng)前操作卷積核個(gè)數(shù)占默認(rèn)個(gè)數(shù)的比例:
class?Bottleneck(nn.Module):
????#?Standard?bottleneck
????def?__init__(self,?c1,?c2,?shortcut=True,?g=1,?e=0.5):??#?ch_in,?ch_out,?shortcut,?groups,?expansion
????????super(Bottleneck,?self).__init__()
????????c_?=?int(c2?*?e)??#?hidden?channels
????????self.cv1?=?Conv(c1,?c_,?1,?1)
????????self.cv2?=?Conv(c_,?c2,?3,?1,?g=g)
????????self.add?=?shortcut?and?c1?==?c2
????def?forward(self,?x):
????????return?x?+?self.cv2(self.cv1(x))?if?self.add?else?self.cv2(self.cv1(x))
class?BottleneckCSP(nn.Module):
????#?CSP?Bottleneck?https://github.com/WongKinYiu/CrossStagePartialNetworks
????def?__init__(self,?c1,?c2,?n=1,?shortcut=True,?g=1,?e=0.5):??#?ch_in,?ch_out,?number,?shortcut,?groups,?expansion
????????super(BottleneckCSP,?self).__init__()
????????c_?=?int(c2?*?e)??#?hidden?channels
????????self.cv1?=?Conv(c1,?c_,?1,?1)
????????self.cv2?=?nn.Conv2d(c1,?c_,?1,?1,?bias=False)
????????self.cv3?=?nn.Conv2d(c_,?c_,?1,?1,?bias=False)
????????self.cv4?=?Conv(2?*?c_,?c2,?1,?1)
????????self.bn?=?nn.BatchNorm2d(2?*?c_)??#?applied?to?cat(cv2,?cv3)
????????self.act?=?nn.LeakyReLU(0.1,?inplace=True)
????????self.m?=?nn.Sequential(*[Bottleneck(c_,?c_,?shortcut,?g,?e=1.0)?for?_?in?range(n)])
????def?forward(self,?x):
????????y1?=?self.cv3(self.m(self.cv1(x)))
????????y2?=?self.cv2(x)
????????return?self.cv4(self.act(self.bn(torch.cat((y1,?y2),?dim=1))))
CSP
下圖為YOLO V5s的CSP結(jié)構(gòu),也就是說將原輸入分成兩個(gè)分支,分別進(jìn)行卷積操作使得通道數(shù)減半,然后分支一進(jìn)行Bottlenneck x N操作,隨后concat分支一和分支二,從而使得BottlenneckCSP的輸入與輸出是一樣的大小,目的是為了讓模型學(xué)習(xí)到更多的特征。

很多人都對(duì)yaml文件中[[-1, 3, BottleneckCSP, [1024, False]]False的作用不太理解,其實(shí)這就是關(guān)閉了shortcut的選項(xiàng)。
def?__init__(self,?c1,?c2,?n=1,?shortcut=True,?g=1,?e=0.5)
下圖是YOLO V5s 中BottlenneckCSP有無False選項(xiàng)的結(jié)構(gòu)對(duì)比:

SPP
下圖為YOLO V5s的SPP結(jié)構(gòu)。

SPP的輸入是512x20x20,經(jīng)過1x1的卷積層后輸出256x20x20,然后經(jīng)過并列的三個(gè)Maxpool進(jìn)行下采樣,將結(jié)果與其初始特征相加,輸出1024x20x20,最后用512的卷積核將其恢復(fù)到512x20x20。
作者代碼如下,重點(diǎn)是Maxpool操作:
class?SPP(nn.Module):
????#?Spatial?pyramid?pooling?layer?used?in?YOLOv3-SPP
????def?__init__(self,?c1,?c2,?k=(5,?9,?13)):
????????super(SPP,?self).__init__()
????????c_?=?c1?//?2??#?hidden?channels
????????self.cv1?=?Conv(c1,?c_,?1,?1)
????????self.cv2?=?Conv(c_?*?(len(k)?+?1),?c2,?1,?1)
????????self.m?=?nn.ModuleList([nn.MaxPool2d(kernel_size=x,?stride=1,?padding=x?//?2)?for?x?in?k])
????def?forward(self,?x):
????????x?=?self.cv1(x)
????????return?self.cv2(torch.cat([x]?+?[m(x)?for?m?in?self.m],?1))
PANET
YOLO V5 1.0最初一版模型使用FPN作為NECK,后續(xù)在6月22號(hào)已經(jīng)全面更新為PANET。PANET基于 Mask R-CNN 和 FPN 框架,加強(qiáng)了信息傳播,具有準(zhǔn)確保留空間信息的能力,這有助于對(duì)像素進(jìn)行適當(dāng)?shù)亩ㄎ灰孕纬裳谀!?/p>
下圖中pi 代表 CSP 主干網(wǎng)絡(luò)中的一個(gè)特征層

該網(wǎng)絡(luò)的特征提取器采用了一種新的增強(qiáng)自下向上路徑的 FPN 結(jié)構(gòu),改善了低層特征的傳播(a部分)。第三條通路的每個(gè)階段都將前一階段的特征映射作為輸入,并用3x3卷積層處理它們。輸出通過橫向連接被添加到自上而下通路的同一階段特征圖中,這些特征圖為下一階段提供信息(b部分)。橫向連接,有助于縮短路徑,被稱為shortcut連接。同時(shí)使用自適應(yīng)特征池化(Adaptive feature pooling)恢復(fù)每個(gè)候選區(qū)域和所有特征層次之間被破壞的信息路徑,聚合每個(gè)特征層次上的每個(gè)候選區(qū)域,避免被任意分配(c部分)。對(duì)于 Mask-RCNN(e部分),FCN可以保留空間信息并減少網(wǎng)絡(luò)中的參數(shù)數(shù)量,但是由于參數(shù)是為所有空間位置共享的,因此該網(wǎng)路實(shí)際上并未學(xué)習(xí)如何使用像素位置進(jìn)行預(yù)測(cè)。而FC對(duì)位置敏感,可以適應(yīng)不同的空間位置。因此PANet使用來自Fully Convolutional Network (FCN)和Fully-connected layers(FC)的信息提供更準(zhǔn)確的掩碼預(yù)測(cè)。
YOLO V5借鑒了YOLO V4的修改版PANET結(jié)構(gòu)。
PANET通常使用自適應(yīng)特征池將相鄰層加在一起,以進(jìn)行掩模預(yù)測(cè)。但是,當(dāng)在YOLOv4中使用PANET時(shí),此方法略麻煩,因此,YOLO V4的作者沒有使用自適應(yīng)特征池添加相鄰層,而是對(duì)其進(jìn)行Concat操作,從而提高了預(yù)測(cè)的準(zhǔn)確性。

YOLO V5同樣采用了級(jí)聯(lián)操作。詳情可以參看模型大圖及Netron網(wǎng)絡(luò)圖中對(duì)應(yīng)的Concat操作。
Transfer learning
在自定義數(shù)據(jù)集上訓(xùn)練YOLO V5,包括以下幾個(gè)步驟:
準(zhǔn)備數(shù)據(jù)集 環(huán)境設(shè)定 配置/修改文件和目錄結(jié)構(gòu) 訓(xùn)練 推理 結(jié)果
Data Prepration
在準(zhǔn)備數(shù)據(jù)集方面,最重要的是明白YOLO家族獨(dú)特的標(biāo)簽數(shù)據(jù)集格式。
每個(gè)圖片文件.jpg,都有同一命名的標(biāo)簽文件.txt。
標(biāo)簽文件中每個(gè)對(duì)象獨(dú)占一行,格式為。
其中:
-表示對(duì)象的類別序號(hào):從0 到 (classes-1)-參照?qǐng)D片寬度和高度的相對(duì)比例(浮點(diǎn)數(shù)值),從0.0到1.0例如: 或= / = / 注意: 是矩形的中心,而不是左上角位置。
如下圖所示:

接下來我們要清楚YOLO V5的訓(xùn)練文件結(jié)構(gòu)是什么。
YOLO V5的標(biāo)簽文件夾和圖像文件夾應(yīng)位于同一目錄下。
其次自定義數(shù)據(jù)集應(yīng)該分成Train,Valid, Test三個(gè)部分,比例可以按照7:2:1分配。由于BDD100k數(shù)據(jù)集已經(jīng)為我們分好了Train,Valid, Test三部分,因此我們不需要自己分割數(shù)據(jù)集。
下圖為YOLO V5的訓(xùn)練文件結(jié)構(gòu):

讓我們來看看BDD100K數(shù)據(jù)集的概覽。
BDD100K是最大的開放式駕駛視頻數(shù)據(jù)集之一,其中包含10萬個(gè)視頻和10個(gè)任務(wù),目的是方便評(píng)估自動(dòng)駕駛圖像識(shí)別算法的的進(jìn)展。每個(gè)高分辨率視頻一共40秒。該數(shù)據(jù)集包括超過1000個(gè)小時(shí)的駕駛數(shù)據(jù),總共超過1億幀。這些視頻帶有GPU / IMU數(shù)據(jù)以獲取軌跡信息。該數(shù)據(jù)集具有地理,環(huán)境和天氣多樣性,從而能讓模型能夠識(shí)別多種場(chǎng)景,具備更多的泛化能力。這些豐富的戶外場(chǎng)景和復(fù)雜的車輛運(yùn)動(dòng)使感知任務(wù)更具挑戰(zhàn)性。該數(shù)據(jù)集上的任務(wù)包括圖像標(biāo)記,車道檢測(cè),可駕駛區(qū)域分割,道路對(duì)象檢測(cè),語義分割,實(shí)例分割,多對(duì)象檢測(cè)跟蹤,多對(duì)象分割跟蹤,領(lǐng)域自適應(yīng)和模仿學(xué)習(xí)。
我們可以在BDD100K數(shù)據(jù)網(wǎng)站上下載數(shù)據(jù):https://bdd-data.berkeley.edu/

Bdd100k的標(biāo)簽是由Scalabel(https://www.scalabel.ai/)生成的JSON格式。
-?labels?[?]:
????-?id:?int32
????-?category:?string?(classification)
????-?manualShape:?boolean?(whether?the?shape?of?the?label?is?created?or?modified?manually)
????-?manualAttributes:?boolean?(whether?the?attribute?of?the?label?is?created?or?modified?manually)
????-?score:?float?(the?confidence?or?some?other?ways?of?measuring?the?quality?of?the?label.)
????-?attributes:
????????-?occluded:?boolean
????????-?truncated:?boolean
????????-?trafficLightColor:?"red|green|yellow|none"
????????-?areaType:?"direct?|?alternative"?(for?driving?area)
????????-?laneDirection:?"parallel|vertical"?(for?lanes)
????????-?laneStyle:?"solid?|?dashed"?(for?lanes)
????????-?laneTypes:?(for?lanes)
????-?box2d:
???????-?x1:?float
???????-?y1:?float
???????-?x2:?float
???????-?y2:?float
道路對(duì)象類別包括以下幾類:
[
????"bike",
????"bus",
????"car",
????"motor",
????"person",
????"rider",
????"traffic?light",
????"traffic?sign",
????"train",
????"truck"
]
我們實(shí)際關(guān)注的只有- labels [ ]欄目下的內(nèi)容。
現(xiàn)在我們可以開始轉(zhuǎn)換Bdd100k的標(biāo)簽為YOLO 格式了。
Berkerley 提供了Bdd100k數(shù)據(jù)集的標(biāo)簽查看及標(biāo)簽格式轉(zhuǎn)化工具。由于沒有直接從bdd100k轉(zhuǎn)換成YOLO的工具,因此我們首先得使用將bdd100k的標(biāo)簽轉(zhuǎn)換為coco格式,然后再將coco格式轉(zhuǎn)換為yolo格式。
bdd to coco
我的目的是識(shí)別包括不同顏色交通燈在內(nèi)的所有交通對(duì)象,因此我們需要對(duì)原版的bdd2coco.py進(jìn)行一些修改,以獲取交通燈顏色并產(chǎn)生新的類別。
這是修改完的核心代碼:
for?label?in?i['labels']:
????????????annotation?=?dict()
????????????category=label['category']
????????????if?(category?==?"traffic?light"):
????????????????color?=?label['attributes']['trafficLightColor']
????????????????category?=?"tl_"?+?color
????????????if?category?in?id_dict.keys():
????????????????empty_image?=?False
????????????????annotation["iscrowd"]?=?0
????????????????annotation["image_id"]?=?image['id']
????????????????x1?=?label['box2d']['x1']
????????????????y1?=?label['box2d']['y1']
????????????????x2?=?label['box2d']['x2']
????????????????y2?=?label['box2d']['y2']
????????????????annotation['bbox']?=?[x1,?y1,?x2-x1,?y2-y1]
????????????????annotation['area']?=?float((x2?-?x1)?*?(y2?-?y1))
????????????????annotation['category_id']?=?id_dict[category]
????????????????annotation['ignore']?=?0
????????????????annotation['id']?=?label['id']
????????????????annotation['segmentation']?=?[[x1,?y1,?x1,?y2,?x2,?y2,?x2,?y1]]
????????????????annotations.append(annotation)
在完成bdd100k格式到y(tǒng)olo格式的轉(zhuǎn)換后,會(huì)獲得bdd100k_labels_images_det_coco_train.json和bdd100k_labels_images_det_coco_val.json兩個(gè)文件。
Coco to yolo
在完成先前的轉(zhuǎn)換之后,我們需要將訓(xùn)練集和驗(yàn)證集的coco格式標(biāo)簽轉(zhuǎn)換為yolo格式。注意需要分別指定訓(xùn)練集和驗(yàn)證集圖片位置,對(duì)應(yīng)的coco標(biāo)簽文件位置,及生成yolo標(biāo)簽的目標(biāo)位置。
config_train?={
????????"datasets":?"COCO",
????????"img_path":?"bdd100k_images/bdd100k/images/100k/train",
????????"label":?"labels/bdd100k_labels_images_det_coco_train.json",
????????"img_type":?".jpg",
????????"manipast_path":?"./",
????????"output_path":?"labels/trains/",
????????"cls_list":?"bdd100k.names",
????}
????config_valid?={
????????"datasets":?"COCO",
????????"img_path":?"bdd100k_images/bdd100k/images/100k/val",
????????"label":?"labels/bdd100k_labels_images_det_coco_val.json",
????????"img_type":?".jpg",
????????"manipast_path":?"./",
????????"output_path":?"labels/valids/",
????????"cls_list":?"bdd100k.names",
????}
除此之外,我們還得將所有的類別寫入bdd100k.names文件。
person
rider
car
bus
truck
bike
motor
tl_green
tl_red
tl_yellow
tl_none
traffic?sign
train
tl_green
運(yùn)行Bdd_preprocessing中的完整代碼可以完成Bdd100k格式標(biāo)簽到Y(jié)OLO標(biāo)簽格式的轉(zhuǎn)換。
Bdd2coco以及coco2yolo的詳細(xì)說明可以參看bdd100k代碼庫和convert2Yolo代碼庫。
bdd100k代碼庫:https://github.com/ucbdrive/bdd100k
convert2Yolo代碼庫:https://github.com/ssaru/convert2Yolo
為了方便將重心放在YOLO V5模型訓(xùn)練上,我為大家提供了預(yù)處理過后的Bdd100k數(shù)據(jù)集(https://1drv.ms/u/s!An7G4eYRvZzthI5HCnVaEGvrdiDWAw?e=v6C4US),該預(yù)處理過后的數(shù)據(jù)集可以直接用來訓(xùn)練YOLO V5對(duì)象檢測(cè)網(wǎng)絡(luò)。
Setup environment
運(yùn)行YOLO V5的第一步是克隆YOLO V5的官方代碼庫。
YOLO V5 需要的Pytorch版本>=1.5, Python版本3.7, CUDA版本10.2。
Ultralytics提供了requirement.txt文件來方便新環(huán)境配置。
通過在shell中運(yùn)行pip install \-r requirement.txt 命令,可以自動(dòng)安裝所有依賴項(xiàng)。
numpy==1.17
scipy==1.4.1
cudatoolkit==10.2.89
opencv-python
torch==1.5
torchvision==0.6.0
matplotlib
pycocotools
tqdm
pillow
tensorboard
pyyaml
Configuration
YOLO V5的默認(rèn)YAML文件coco.yaml 中是coco數(shù)據(jù)集所有的類對(duì)象名稱和類數(shù)量(80)。由于我們的目的是基于bdd100k數(shù)據(jù)集來訓(xùn)練檢測(cè)少量特定交通物體的模型,我們不需要訓(xùn)練檢測(cè)80類網(wǎng)絡(luò)的模型,所有我們得重新創(chuàng)建一個(gè)uc_data.yaml文件來描述bdd100k數(shù)據(jù)集的數(shù)據(jù)特性。由于我們模型的輸出不是coco數(shù)據(jù)集的80個(gè)類,而是13類,因此我們得修改此處的輸出類別數(shù)量為13。
#?here?you?need?to?specify?the?files?train,?test?and?validation?txt?
train:?bdd100k/images/train
val:?bdd100k/images/valid
test:?bdd100k/images/test
nc:?13
names:?['person','rider','car','bus','truck','bike','motor','tl_green','tl_red','tl_yellow','tl_none','t_sign','train']
之后我們會(huì)用到上述YAML文件來訓(xùn)練模型。
Modify Model arichtecture
YOLO V5通過models文件家中的cfg文件*.yaml來調(diào)整訓(xùn)練模型的結(jié)構(gòu)。
由于我們模型的輸出不是coco數(shù)據(jù)集的80個(gè)類,而是13類,因此我們需要修改模型的對(duì)象預(yù)測(cè)層輸出類別數(shù)量為13。
#?parameters
nc:?13??#?number?of?classes
我們可以直接修改YAML文件下各個(gè)組件的細(xì)節(jié)(如數(shù)字),來重新定義自己的模型架構(gòu)。
#?YOLO?V5s
#?parameters
nc:?13??#?number?of?classes
depth_multiple:?0.33??#?model?depth?multiple
width_multiple:?0.50??#?layer?channel?multiple
#?anchors
anchors:
??-?[116,90,?156,198,?373,326]??#?P5/32
??-?[30,61,?62,45,?59,119]??#?P4/16
??-?[10,13,?16,30,?33,23]??#?P3/8
#?YOLOv5?backbone
backbone:
??#?[from,?number,?module,?args]
??[[-1,?1,?Focus,?[64,?3]],??#?0-P1/2
???[-1,?1,?Conv,?[128,?3,?2]],??#?1-P2/4
???[-1,?3,?BottleneckCSP,?[128]],
???[-1,?1,?Conv,?[256,?3,?2]],??#?3-P3/8
???[-1,?9,?BottleneckCSP,?[256]],
???[-1,?1,?Conv,?[512,?3,?2]],??#?5-P4/16
???[-1,?9,?BottleneckCSP,?[512]],
???[-1,?1,?Conv,?[1024,?3,?2]],?#?7-P5/32
???[-1,?1,?SPP,?[1024,?[5,?9,?13]]],
??]
#?YOLOv5?head
head:
??[[-1,?3,?BottleneckCSP,?[1024,?False]],??#?9
???[-1,?1,?Conv,?[512,?1,?1]],
???[-1,?1,?nn.Upsample,?[None,?2,?'nearest']],
???[[-1,?6],?1,?Concat,?[1]],??#?cat?backbone?P4
???[-1,?3,?BottleneckCSP,?[512,?False]],??#?13
???[-1,?1,?Conv,?[256,?1,?1]],
???[-1,?1,?nn.Upsample,?[None,?2,?'nearest']],
???[[-1,?4],?1,?Concat,?[1]],??#?cat?backbone?P3
???[-1,?3,?BottleneckCSP,?[256,?False]],
???[-1,?1,?nn.Conv2d,?[na?*?(nc?+?5),?1,?1]],??#?18?(P3/8-small)
???[-2,?1,?Conv,?[256,?3,?2]],
???[[-1,?14],?1,?Concat,?[1]],??#?cat?head?P4
???[-1,?3,?BottleneckCSP,?[512,?False]],
???[-1,?1,?nn.Conv2d,?[na?*?(nc?+?5),?1,?1]],??#?22?(P4/16-medium)
???[-2,?1,?Conv,?[512,?3,?2]],
???[[-1,?10],?1,?Concat,?[1]],??#?cat?head?P5
???[-1,?3,?BottleneckCSP,?[1024,?False]],
???[-1,?1,?nn.Conv2d,?[na?*?(nc?+?5),?1,?1]],??#?26?(P5/32-large)
???[[],?1,?Detect,?[nc,?anchors]],??#?Detect(P5,?P4,?P3)
??]
為了更清楚的了解YOLO V5的模型結(jié)構(gòu),我們使用netron(https://github.com/lutzroeder/netron)來實(shí)現(xiàn)模型可視化,值得注意的是,如果想獲得清晰的網(wǎng)絡(luò)圖,需要將pt文件轉(zhuǎn)化為torchscipt格式。

以下鏈接為YOLO V5s的網(wǎng)絡(luò)圖:
由于Bdd100k數(shù)據(jù)集與COCO數(shù)據(jù)的數(shù)據(jù)量級(jí),場(chǎng)景及部分對(duì)象類別相近,因此我并沒有修改模型結(jié)構(gòu)。如果將YOLO V5運(yùn)用在一些小數(shù)據(jù)場(chǎng)景或者對(duì)象類別相差較大的場(chǎng)景如醫(yī)學(xué)視覺,則可以根據(jù)實(shí)際情況增減模型。
Transfer learning theory
現(xiàn)在讓我們來了解下本文的重點(diǎn)遷移學(xué)習(xí)。
什么是遷移學(xué)習(xí)?遷移學(xué)習(xí)(Transfer learning) 顧名思義就是就是把已學(xué)訓(xùn)練好的模型參數(shù)遷移到新的模型來幫助新模型訓(xùn)練??紤]到大部分?jǐn)?shù)據(jù)或任務(wù)是存在相關(guān)性的,所以通過遷移學(xué)習(xí)我們可以將已經(jīng)學(xué)到的模型參數(shù)(也可理解為模型學(xué)到的知識(shí))通過某種方式來分享給新模型從而加快并優(yōu)化模型的學(xué)習(xí)效率不用像大多數(shù)網(wǎng)絡(luò)那樣從零學(xué)習(xí)(starting from scratch,tabula rasa)。
https://www.zhihu.com/question/41979241/answer/123545914
再來看看我們面臨的問題,我們已經(jīng)有了YOLO V5模型框架,有了針對(duì)COCO數(shù)據(jù)集預(yù)訓(xùn)練的權(quán)重文件*.pt,Bdd100k的訓(xùn)練數(shù)據(jù)很龐大,而我們需要額外提取紅綠燈的顏色作為新的類別,那怎么樣才能把YOLO V5已經(jīng)學(xué)習(xí)的模型參數(shù)通過某種方式分享給新模型從而加快并優(yōu)化模型的學(xué)習(xí)效率?
下圖為針對(duì)不同場(chǎng)景的遷移學(xué)習(xí)指南。

如果訓(xùn)練集小,訓(xùn)練數(shù)據(jù)與預(yù)訓(xùn)練數(shù)據(jù)相似,那么我們可以凍住卷積層,直接訓(xùn)練全連接層。 如果訓(xùn)練集小,訓(xùn)練數(shù)據(jù)與預(yù)訓(xùn)練數(shù)據(jù)不相似,那么必須從頭訓(xùn)練卷積層及全連接層。 如果訓(xùn)練集大,訓(xùn)練數(shù)據(jù)與預(yù)訓(xùn)練數(shù)據(jù)相似,那么我們可以使用預(yù)訓(xùn)練的權(quán)重參數(shù)初始化網(wǎng)絡(luò),然后從頭開始訓(xùn)練。 如果訓(xùn)練集大,訓(xùn)練數(shù)據(jù)與預(yù)訓(xùn)練數(shù)據(jù)不相似,那么我們可以使用預(yù)訓(xùn)練的權(quán)重參數(shù)初始化網(wǎng)絡(luò),然后從頭開始訓(xùn)練或者完全不使用預(yù)訓(xùn)練權(quán)重,重新開始從頭訓(xùn)練。 值得注意的是,對(duì)于大數(shù)據(jù)集,不推薦凍住卷積層,直接訓(xùn)練全連接層的方式,這可能會(huì)對(duì)性能造成很大影響。
我們的情況,符合上述第三種,通常只需要使用預(yù)訓(xùn)練的權(quán)重初始化網(wǎng)絡(luò),然后直接從頭開始訓(xùn)練,從而更快的使模型有效收斂。但是由于之前沒有人公開過對(duì)于Bdd100k數(shù)據(jù)集使用YOLO V5預(yù)訓(xùn)練權(quán)重和不使用其訓(xùn)練權(quán)重的對(duì)比,甚至你也可以說COCO數(shù)據(jù)集80類,而Bdd100k數(shù)據(jù)集13類,兩者大部分類是不相似的。我并不能百分百確定哪個(gè)方案更適合本項(xiàng)目。于是我分別使用YOLO V5s預(yù)訓(xùn)練權(quán)重和不使用其訓(xùn)練權(quán)重來訓(xùn)練基于Bdd100k數(shù)據(jù)集的對(duì)象識(shí)別網(wǎng)絡(luò),并對(duì)比它們的效果。
Ultralytics(https://github.com/ultralytics/yolov5)一共提供了四個(gè)版本的YOLO V5模型。
下圖是它們的比較:

YOLO V5x是非常巨型的網(wǎng)絡(luò),同樣也是訓(xùn)練精度最好的網(wǎng)絡(luò),關(guān)于YOLO V5x與YOLO V4的性能對(duì)比尚未有百分百定論,根據(jù)WongKinYiu的6月22日的Benchmarks結(jié)論,YOLO V4仍然稍微優(yōu)于YOLO V5x,但是根據(jù)最近很多kaggle比賽的同學(xué)反映,YOLO V5的比賽結(jié)果普遍由于YOLO V4,當(dāng)然不排除這是tensorflow和pytorch等版本的YOLO V4優(yōu)化不夠。我覺得YOLO V5最驚艷的是它的速度和尺寸。因此我在本文中只使用YOLO V5s來訓(xùn)練基于Bdd100k自動(dòng)駕駛數(shù)據(jù)集的對(duì)象檢測(cè)深度網(wǎng)絡(luò)。另外一個(gè)影響因素是,Bdd100k的數(shù)據(jù)集龐大,YOLO V5s在 Intel Xeon W-2145 ,64 GB RAM,NVIDIA RTX 2080Ti,batch_size 32, Use RAM cache的情況下訓(xùn)練300 epochs 需要66小時(shí),YOLO V5x是它的三倍。還是等我有時(shí)間再訓(xùn)練下YOLO V5x吧~
Traininig
在我們完成所有的準(zhǔn)備工作之后,我們可以開始訓(xùn)練了!
準(zhǔn)備文件:
YOLO v5代碼庫 預(yù)處理后的bdd100k數(shù)據(jù)集:將JSON標(biāo)簽轉(zhuǎn)換為YOLO格式,并按照YOLO V5的訓(xùn)練文件結(jié)構(gòu)要求布置 custom_yolov5s.yaml:修改后的模型文件 uc_data.yaml: 包含訓(xùn)練,驗(yàn)證集的位置,類別數(shù)目及名稱
訓(xùn)練配置:
Intel Xeon W-2145 ,64 GB RAM,NVIDIA RTX 2080Ti。
訓(xùn)練參數(shù)(基于bdd100k數(shù)據(jù)集進(jìn)行分析):
— img: 輸入圖像的大小,建議使用640,因?yàn)閷?duì)于交通場(chǎng)景,輸入圖片尺寸過小時(shí),會(huì)導(dǎo)致部分對(duì)象寬高小于3像素,可能會(huì)影響訓(xùn)練精度 — batch-size: 批次大小,對(duì)于2080Ti-11GB 或者P100-16GB,輸入img-size 640,batch-size 32為上限 — epochs: 訓(xùn)練迭代數(shù),作者建議訓(xùn)練300個(gè)epochs起 — data: 創(chuàng)建的 YAML 文件uc_data.yaml — cfg: 模型文件Custom_yolov5s.yaml,需要自己至少修改類別數(shù)量及類別種類 — weights: 對(duì)于本項(xiàng)目不使用預(yù)訓(xùn)練權(quán)重,如果需要預(yù)訓(xùn)練權(quán)重,可以訪問此地址 — cache-images: 將預(yù)處理后的訓(xùn)練數(shù)據(jù)全部存儲(chǔ)在RAM中,能夠加快訓(xùn)練速度 — hyp: 這個(gè)參數(shù)是自定義的hyp.yaml地址,對(duì)于小尺寸數(shù)據(jù),可以更改hyp中optimizer為Adam,并修改配套參數(shù)為作者預(yù)設(shè)的Adam參數(shù) — rect:輸入這個(gè)參數(shù),會(huì)關(guān)閉Mosaic數(shù)據(jù)增強(qiáng) — resume:從上次訓(xùn)練的結(jié)束last.pt繼續(xù)訓(xùn)練 — nosave:輸入這個(gè)參數(shù)將存儲(chǔ)最后的checkpoint,可以加快整體訓(xùn)練速度,但是建議關(guān)閉這個(gè)參數(shù),這樣能保留best.pt — notest:只測(cè)試最后一個(gè)epoch,能加快整體訓(xùn)練速度 — noautoanchor:關(guān)閉自適應(yīng)自適應(yīng)錨定框,YOLO V5會(huì)自動(dòng)分析當(dāng)前錨定框的 Best Possible Recall (BPR) ,對(duì)于img-size 640,最佳BPR為0.9900,隨著img-size降低,BPR也隨之變差 — multi-scale:輸入圖像多尺度訓(xùn)練,在訓(xùn)練過程中,輸入圖像會(huì)自動(dòng)resize至 img-size +/- 50%,能一定程度上防止模型過擬合,但是對(duì)于GPU顯存要求很高,對(duì)于640的img-size至少使用16GB顯存,才能保證運(yùn)行不出錯(cuò) — single-cls:模型的輸出為單一類別,比如我只需要識(shí)別Trunk — device: 選擇使用CUDA或者CPU
YOLO V5的作者建議至少訓(xùn)練300個(gè)回合,每次訓(xùn)練完成后所有的結(jié)果及權(quán)重會(huì)儲(chǔ)存在runs文件夾下。
訓(xùn)練過程:
Train from pre-weight(橘黃色)
!python?train.py?--img?640?--batch?32?--epochs?300?--data?'./models/uc_data.yaml'?--cfg?./models/custom_yolov5s.yaml?--weights?"./weights/yolov5s.pt"?--name?yolov5s_bdd_prew??--cache
Train from scatch(藍(lán)色)
!python?train.py?--img?640?--batch?32?--epochs?300?--data?'./models/uc_data.yaml'?--cfg?./models/custom_yolov5s.yaml?--weights?""?--name?yolov5s_bdd??--cache
訓(xùn)練結(jié)果:
Metrics

Train loss

Valid loss

結(jié)果分析:
Train from pre-weight和Train from scatch的最高mAP_0.5均能達(dá)到46.5%。 Train from pre-weight比Train from scatch能更快收斂,但是在250epochs左右兩者已經(jīng)達(dá)到一致。 Train from pre-weight和Train from scatch的模型大小均為14.8M,值得注意的是YOLO V5在訓(xùn)練結(jié)束后會(huì)自動(dòng)給模型剪枝,訓(xùn)練過程中的last.pt有58.6M,作者考慮的非常周到。 總的來說Train from pre-weight比Train from scatch能更快收斂,能一定程度上減少訓(xùn)練時(shí)間開銷,對(duì)于和COCO數(shù)據(jù)集相近的數(shù)據(jù)集,可以采用Train from pre-weight,如果時(shí)間充裕,Train from scatch更為妥當(dāng)。
Inference
現(xiàn)在我們已經(jīng)完成了模型訓(xùn)練了,讓我們?cè)谝恍﹫D像上測(cè)試它的性能吧。
檢測(cè)參數(shù):
— weights: 訓(xùn)練權(quán)重的路徑 — source:推理目標(biāo)的路徑,可以是圖片,視頻,網(wǎng)絡(luò)攝像頭等 — source:推理結(jié)果的輸出路徑 — img-size:推理圖片的大小 — conf-thres:對(duì)象置信閾值,默認(rèn)0.4 — iou-thres:NMS的IOU閾值,可以根據(jù)實(shí)際對(duì)象的重疊度調(diào)節(jié),默認(rèn)0.5 — device: 選擇使用CUDA或者CPU — view-img:顯示所有推理結(jié)果 — save-txt:將每一幀的推理結(jié)果及邊界框的位置,存入*.txt文件 — classes:類別過濾,意思是只推理目標(biāo)類別 — agnostic-nms:使用agnostic-nms NMS(https://www.quora.com/What-does-)
!python?detect.py?--weights?runs/exp0_yolov5s_bdd_prew/weights/best_yolov5s_bdd_prew.pt??--source?bdd100k/images/test?--save-txt




為了測(cè)試YOLO V5s的實(shí)時(shí)視頻處理性能,我測(cè)試了一個(gè)4K 道路場(chǎng)景錄制視頻,推理速度高達(dá)7ms/幀。
點(diǎn)擊下方鏈接可以直接訪問bilibili上的完整視頻:
https://www.bilibili.com/video/BV1sz4y1Q7wi/
Summary
至此我們已經(jīng)了解了YOLO V5的網(wǎng)絡(luò)結(jié)構(gòu),并且基于Bdd100k數(shù)據(jù)集訓(xùn)練了屬于自己的自動(dòng)駕駛對(duì)象檢測(cè)模型。YOLO V5是個(gè)非常棒的開源對(duì)象檢測(cè)網(wǎng)絡(luò),代碼庫的更新速度非???,不管它現(xiàn)階段配不配的上V5的名稱,它都是一個(gè)快速而且強(qiáng)大的對(duì)象檢測(cè)器。YOLO V5值得你去嘗試!
如果覺得有用,就請(qǐng)分享到朋友圈吧!
公眾號(hào)后臺(tái)回復(fù)“CVPR21檢測(cè)”獲取CVPR2021目標(biāo)檢測(cè)論文下載~

#?CV技術(shù)社群邀請(qǐng)函?#

備注:姓名-學(xué)校/公司-研究方向-城市(如:小極-北大-目標(biāo)檢測(cè)-深圳)
即可申請(qǐng)加入極市目標(biāo)檢測(cè)/圖像分割/工業(yè)檢測(cè)/人臉/醫(yī)學(xué)影像/3D/SLAM/自動(dòng)駕駛/超分辨率/姿態(tài)估計(jì)/ReID/GAN/圖像增強(qiáng)/OCR/視頻理解等技術(shù)交流群
每月大咖直播分享、真實(shí)項(xiàng)目需求對(duì)接、求職內(nèi)推、算法競(jìng)賽、干貨資訊匯總、與?10000+來自港科大、北大、清華、中科院、CMU、騰訊、百度等名校名企視覺開發(fā)者互動(dòng)交流~

