0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

一文徹底搞懂YOLOv8(網(wǎng)絡(luò)結(jié)構(gòu)+代碼+實(shí)操)

jt_rfid5 ? 來源:新機(jī)器視覺 ? 2023-06-19 16:55 ? 次閱讀


	
								

本文概述了YOLOv8算法的核心特性及改進(jìn)點(diǎn),詳細(xì)介紹了網(wǎng)絡(luò)結(jié)構(gòu)、Loss計(jì)算、數(shù)據(jù)增強(qiáng)手段、訓(xùn)練策略、模型推理,對網(wǎng)絡(luò)結(jié)構(gòu)進(jìn)行了詳盡的分析,最后給出實(shí)操步驟。

0.引言

Section Name

Yolo系列對比:

e0bada28-0cf6-11ee-962d-dac502259ad0.png

1.概述

Section Name

YOLOv8 算法的核心特性和改動可以歸結(jié)為如下:

提供了一個(gè)全新的 SOTA 模型,包括 P5 640 和 P6 1280 分辨率的目標(biāo)檢測網(wǎng)絡(luò)和基于 YOLACT 的實(shí)例分割模型。和 YOLOv5 一樣,基于縮放系數(shù)也提供了 N/S/M/L/X 尺度的不同大小模型,用于滿足不同場景需求

01

Backbone

骨干網(wǎng)絡(luò)和 Neck 部分可能參考了 YOLOv7 ELAN 設(shè)計(jì)思想,將 YOLOv5 的 C3 結(jié)構(gòu)換成了梯度流更豐富的 C2f 結(jié)構(gòu),并對不同尺度模型調(diào)整了不同的通道數(shù)。

屬于對模型結(jié)構(gòu)精心微調(diào),不再是無腦一套參數(shù)應(yīng)用所有模型,大幅提升了模型性能。不過這個(gè) C2f 模塊中存在 Split 等操作對特定硬件部署沒有之前那么友好了

02

Head

Head部分較yolov5而言有兩大改進(jìn):1)換成了目前主流的解耦頭結(jié)構(gòu)(Decoupled-Head),將分類和檢測頭分離 2)同時(shí)也從 Anchor-Based 換成了 Anchor-Free

03

Loss

1) YOLOv8拋棄了以往的IOU匹配或者單邊比例的分配方式,而是使用了Task-Aligned Assigner正負(fù)樣本匹配方式。2)并引入了 Distribution Focal Loss(DFL)

04

Train

訓(xùn)練的數(shù)據(jù)增強(qiáng)部分引入了 YOLOX 中的最后 10 epoch 關(guān)閉 Mosiac 增強(qiáng)的操作,可以有效地提升精度

從上面可以看出,YOLOv8 主要參考了最近提出的諸如 YOLOX、YOLOv6、YOLOv7 和 PPYOLOE 等算法的相關(guān)設(shè)計(jì),本身的創(chuàng)新點(diǎn)不多,偏向工程實(shí)踐,主推的還是 ultralytics 這個(gè)框架本身。

下面將按照模型結(jié)構(gòu)設(shè)計(jì)、Loss 計(jì)算、訓(xùn)練數(shù)據(jù)增強(qiáng)、訓(xùn)練策略和模型推理過程共 5 個(gè)部分詳細(xì)介紹 YOLOv8 目標(biāo)檢測的各種改進(jìn),實(shí)例分割部分暫時(shí)不進(jìn)行描述。

2.模型結(jié)構(gòu)

Section Name

如下圖, 左側(cè)為 YOLOv5-s,右側(cè)為 YOLOv8-s。
在暫時(shí)不考慮 Head 情況下,對比 YOLOv5 和 YOLOv8 的 yaml 配置文件可以發(fā)現(xiàn)改動較小。

e0cfc8de-0cf6-11ee-962d-dac502259ad0.png

e0ddf7f6-0cf6-11ee-962d-dac502259ad0.jpg

01

Backbone和Neck的具體變化

a)第一個(gè)卷積層的 kernel 從 6x6 變成了 3x3
b)所有的 C3 模塊換成 C2f,結(jié)構(gòu)如下所示,可以發(fā)現(xiàn)多了更多的跳層連接和額外的 Split 操作

e0ed3fb8-0cf6-11ee-962d-dac502259ad0.png

c)去掉了 Neck 模塊中的 2 個(gè)卷積連接層

d) Backbone 中 C2f 的 block 數(shù)從 3-6-9-3 改成了 3-6-6-3

e) 查看 N/S/M/L/X 等不同大小模型,可以發(fā)現(xiàn) N/S 和 L/X 兩組模型只是改了縮放系數(shù),但是 S/M/L 等骨干網(wǎng)絡(luò)的通道數(shù)設(shè)置不一樣,沒有遵循同一套縮放系數(shù)。如此設(shè)計(jì)的原因應(yīng)該是同一套縮放系數(shù)下的通道設(shè)置不是最優(yōu)設(shè)計(jì),YOLOv7 網(wǎng)絡(luò)設(shè)計(jì)時(shí)也沒有遵循一套縮放系數(shù)作用于所有模型

02

Head的具體變化

從原先的耦合頭變成了解耦頭,并且從 YOLOv5 的 Anchor-Based 變成了 Anchor-Free。

e0ff914a-0cf6-11ee-962d-dac502259ad0.png

從上圖可以看出,不再有之前的 objectness 分支,只有解耦的分類和回歸分支,并且其回歸分支使用了 Distribution Focal Loss 中提出的積分形式表示法。

3.Loss 計(jì)算

Section Name

Loss 計(jì)算過程包括 2 個(gè)部分:正負(fù)樣本分配策略和 Loss 計(jì)算。

01

正負(fù)樣本分配策略

現(xiàn)代目標(biāo)檢測器大部分都會在正負(fù)樣本分配策略上面做文章,典型的如 YOLOX 的 simOTA、TOOD 的 TaskAlignedAssigner 和 RTMDet 的 DynamicSoftLabelAssigner,這類 Assigner 大都是動態(tài)分配策略,而 YOLOv5 采用的依然是靜態(tài)分配策略??紤]到動態(tài)分配策略的優(yōu)異性,YOLOv8 算法中則直接引用了 TOOD 的 TaskAlignedAssigner。

TaskAlignedAssigner 的匹配策略簡單總結(jié)為:根據(jù)分類與回歸的分?jǐn)?shù)加權(quán)的分?jǐn)?shù)選擇正樣本。

02

Loss計(jì)算

Loss 計(jì)算包括 2 個(gè)分支:分類和回歸分支,沒有了之前的 objectness 分支。

分類分支依然采用 BCE Loss?;貧w分支需要和 Distribution Focal Loss 中提出的積分形式表示法綁定,因此使用了 Distribution Focal Loss, 同時(shí)還使用了 CIoU Loss。3 個(gè) Loss 采用一定權(quán)重比例加權(quán)即可。

4.訓(xùn)練數(shù)據(jù)增強(qiáng)

Section Name

數(shù)據(jù)增強(qiáng)方面和 YOLOv5 差距不大,只不過引入了 YOLOX 中提出的最后 10 個(gè) epoch 關(guān)閉 Mosaic 的操作。假設(shè)訓(xùn)練 epoch 是 500,其示意圖如下所示:

e10ddebc-0cf6-11ee-962d-dac502259ad0.png

考慮到不同模型應(yīng)該采用的數(shù)據(jù)增強(qiáng)強(qiáng)度不一樣,因此對于不同大小模型,有部分超參會進(jìn)行修改,典型的如大模型會開啟 MixUp 和 CopyPaste。數(shù)據(jù)增強(qiáng)后典型效果如下所示:

e1173c0a-0cf6-11ee-962d-dac502259ad0.png

5.訓(xùn)練策略

Section Name

YOLOv8 的訓(xùn)練策略和 YOLOv5 沒有啥區(qū)別,最大區(qū)別就是模型的訓(xùn)練總 epoch 數(shù)從 300 提升到了 500,這也導(dǎo)致訓(xùn)練時(shí)間急劇增加。以 YOLOv8-S 為例,其訓(xùn)練策略匯總?cè)缦拢?/span>

e12dd5aa-0cf6-11ee-962d-dac502259ad0.png

6.模型推理過程

Section Name

YOLOv8 的推理過程和 YOLOv5 幾乎一樣,唯一差別在于前面需要對 Distribution Focal Loss 中的積分表示 bbox 形式進(jìn)行解碼,變成常規(guī)的 4 維度 bbox,后續(xù)計(jì)算過程就和 YOLOv5 一樣了。

e144a032-0cf6-11ee-962d-dac502259ad0.png

其推理和后處理過程為:

(1) bbox 積分形式轉(zhuǎn)換為 4d bbox 格式

對 Head 輸出的 bbox 分支進(jìn)行轉(zhuǎn)換,利用 Softmax 和 Conv 計(jì)算將積分形式轉(zhuǎn)換為 4 維 bbox 格式

(2) 維度變換

YOLOv8 輸出特征圖尺度為 80x80、40x40 和 20x20 的三個(gè)特征圖。Head 部分輸出分類和回歸共 6 個(gè)尺度的特征圖。將 3 個(gè)不同尺度的類別預(yù)測分支、bbox 預(yù)測分支進(jìn)行拼接,并進(jìn)行維度變換。為了后續(xù)方便處理,會將原先的通道維度置換到最后,類別預(yù)測分支 和 bbox 預(yù)測分支 shape 分別為 (b, 80x80+40x40+20x20, 80)=(b,8400,80),(b,8400,4)。

(3) 解碼還原到原圖尺度

分類預(yù)測分支進(jìn)行 Sigmoid 計(jì)算,而 bbox 預(yù)測分支需要進(jìn)行解碼,還原為真實(shí)的原圖解碼后 xyxy 格式。

(4) 閾值過濾

遍歷 batch 中的每張圖,采用 score_thr 進(jìn)行閾值過濾。在這過程中還需要考慮 multi_label 和 nms_pre,確保過濾后的檢測框數(shù)目不會多于 nms_pre。

(5) 還原到原圖尺度和 nms

基于前處理過程,將剩下的檢測框還原到網(wǎng)絡(luò)輸出前的原圖尺度,然后進(jìn)行 nms 即可。最終輸出的檢測框不能多于 max_per_img。

有一個(gè)特別注意的點(diǎn):YOLOv5 中采用的 Batch shape 推理策略,在 YOLOv8 推理中暫時(shí)沒有開啟,不清楚后面是否會開啟,在 MMYOLO 中快速測試了下,如果開啟 Batch shape 會漲大概 0.1~0.2。

7.網(wǎng)絡(luò)模型解析

Section Name

01

卷積神經(jīng)單元(model.py)

ultralytics/nn/modules.py文件中定義了yolov8網(wǎng)絡(luò)中的卷積神經(jīng)單元。

01

autopad

功能:返回pad的大小,使得padding后輸出張量的大小不變。

參數(shù):

k: 卷積核(kernel)的大小。類型可能是一個(gè)int也可能是一個(gè)序列。

p: 填充(padding)的大小。默認(rèn)為None。

d: 擴(kuò)張率(dilation rate)的大小, 默認(rèn)為1 。普通卷積的擴(kuò)張率為1,空洞卷積的擴(kuò)張率大于1。

假設(shè)k為原始卷積核大小,d為卷積擴(kuò)張率(dilation rate),加入空洞之后的實(shí)際卷積核尺寸與原始卷積核尺寸之間的關(guān)系:k =d(k-1)+1

e1518cd4-0cf6-11ee-962d-dac502259ad0.png

def autopad(k, p=None, d=1):  # kernel(卷積核), padding(填充), dilation(擴(kuò)張)
    # 返回pad的大小,使得padding后輸出張量的shape不變
    if d > 1: # 如果采用擴(kuò)張卷積,則計(jì)算擴(kuò)張后實(shí)際的kernel大小
        k = d * (k - 1) + 1 if isinstance(k, int) else [d * (x - 1) + 1 for x in k]  # 
    if p is None:
        p = k // 2 if isinstance(k, int) else [x // 2 for x in k]  # 自動pad
returnp

02

Conv

  • 功能:標(biāo)準(zhǔn)的卷積

  • 參數(shù):輸入通道數(shù)(c1), 輸出通道數(shù)(c2), 卷積核大?。?code style="padding:2px 4px;font-family:'Source Code Pro', 'DejaVu Sans Mono', 'Ubuntu Mono', 'Anonymous Pro', 'Droid Sans Mono', Menlo, Monaco, Consolas, Inconsolata, Courier, monospace, 'PingFang SC', 'Microsoft YaHei', sans-serif;font-size:14px;line-height:22px;color:rgb(199,37,78);background-color:rgb(249,242,244);">k,默認(rèn)是1), 步長(s,默認(rèn)是1), 填充(p,默認(rèn)為None), 組(g, 默認(rèn)為1), 擴(kuò)張率(d,默認(rèn)為1), 是否采用激活函數(shù)(act,默認(rèn)為True, 且采用SiLU為激活函數(shù))

e1627288-0cf6-11ee-962d-dac502259ad0.png

激活函數(shù)采用的是SiLU。

e16ff6c4-0cf6-11ee-962d-dac502259ad0.png

class Conv(nn.Module):
    # 標(biāo)準(zhǔn)的卷積 參數(shù)(輸入通道數(shù), 輸出通道數(shù), 卷積核大小, 步長, 填充, 組, 擴(kuò)張, 激活函數(shù))
    default_act = nn.SiLU()  # 默認(rèn)的激活函數(shù)


    def __init__(self, c1, c2, k=1, s=1, p=None, g=1, d=1, act=True):
        super().__init__()
        self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p, d), groups=g, dilation=d, bias=False) # 2維卷積,其中采用了自動填充函數(shù)。
        self.bn = nn.BatchNorm2d(c2) # 使得每一個(gè)batch的特征圖均滿足均值為0,方差為1的分布規(guī)律
        # 如果act=True 則采用默認(rèn)的激活函數(shù)SiLU;如果act的類型是nn.Module,則采用傳入的act; 否則不采取任何動作 (nn.Identity函數(shù)相當(dāng)于f(x)=x,只用做占位,返回原始的輸入)。
        self.act = self.default_act if act is True else act if isinstance(act, nn.Module) else nn.Identity() 


    def forward(self, x):  # 前向傳播
        return self.act(self.bn(self.conv(x))) # 采用BatchNorm
    def forward_fuse(self, x): #  用于Model類的fuse函數(shù)融合 Conv + BN 加速推理,一般用于測試/驗(yàn)證階段
returnself.act(self.conv(x))#不采用BatchNorm

03

DWConv

深度可分離卷積,繼承自Conv。g=math.gcd(c1, c2)分組數(shù)是輸入通道(c1)和輸出通道(c2)的最大公約數(shù)。(因?yàn)榉纸M卷積時(shí),分組數(shù)需要能夠整除輸入通道和輸出通道)

class DWConv(Conv):
    # 深度可分離卷積
    def __init__(self, c1, c2, k=1, s=1, d=1, act=True):  # ch_in, ch_out, kernel, stride, dilation, activation
super().__init__(c1,c2,k,s,g=math.gcd(c1,c2),d=d,act=act)

04

DWConvTranspose2d

帶有深度分離的轉(zhuǎn)置卷積,繼承自nn.ConvTranspose2d
groups=math.gcd(c1, c2)分組數(shù)是輸入通道(c1)和輸出通道(c2)的最大公約數(shù)。(因?yàn)榉纸M卷積時(shí),分組數(shù)需要能夠整除輸入通道和輸出通道)

class DWConvTranspose2d(nn.ConvTranspose2d):
    # Depth-wise transpose convolution
    def __init__(self, c1, c2, k=1, s=1, p1=0, p2=0):  # 輸入通道, 輸出通道, 卷積核大小, 步長, padding, padding_out
super().__init__(c1,c2,k,s,p1,p2,groups=math.gcd(c1,c2))

05

ConvTranspose

和Conv類似,只是把Conv2d換成了ConvTranspose2d

class ConvTranspose(nn.Module):
    # Convolution transpose 2d layer
    default_act = nn.SiLU()  # default activation


    def __init__(self, c1, c2, k=2, s=2, p=0, bn=True, act=True):
        super().__init__()
        self.conv_transpose = nn.ConvTranspose2d(c1, c2, k, s, p, bias=not bn)
        self.bn = nn.BatchNorm2d(c2) if bn else nn.Identity()
        self.act = self.default_act if act is True else act if isinstance(act, nn.Module) else nn.Identity()


    def forward(self, x):
returnself.act(self.bn(self.conv_transpose(x)))

06

DFL(Distribution Focal Loss)

本篇文章(https://ieeexplore.ieee.org/document/9792391)提出了GFL(了Generalized Focal Loss)。GFL具體又包括Quality Focal Loss(QFL)和Distribution Focal Loss(DFL),其中QFL用于優(yōu)化分類和質(zhì)量估計(jì)聯(lián)合分支,DFL用于優(yōu)化邊框分支。

class DFL(nn.Module):
    # Integral module of Distribution Focal Loss (DFL) proposed in Generalized Focal Loss 
    def __init__(self, c1=16):
        super().__init__()
        self.conv = nn.Conv2d(c1, 1, 1, bias=False).requires_grad_(False)
        x = torch.arange(c1, dtype=torch.float)
        self.conv.weight.data[:] = nn.Parameter(x.view(1, c1, 1, 1))
        self.c1 = c1


    def forward(self, x):
        b, c, a = x.shape  # batch, channels, anchors
        return self.conv(x.view(b, 4, self.c1, a).transpose(2, 1).softmax(1)).view(b, 4, a)
        # return self.conv(x.view(b, self.c1, 4, a).softmax(1)).view(b, 4, a)

07

TransformerLayer

e17e7ce4-0cf6-11ee-962d-dac502259ad0.png

e1942c9c-0cf6-11ee-962d-dac502259ad0.png

我們可以發(fā)現(xiàn)它和yolo中的TransformerLayer部分只是少了層規(guī)范化(LayerNorm),以及在Feed-Forward Networks 中只采用了兩個(gè)不帶偏置線性層,且沒有采用激活函數(shù)。

e1a4da9c-0cf6-11ee-962d-dac502259ad0.png

TransformerLayer代碼實(shí)現(xiàn)如下:

class TransformerLayer(nn.Module):
    # Transformer layer  (LayerNorm layers removed for better performance)
    def __init__(self, c, num_heads): # c: 詞特征向量的大小  num_heads 檢測頭的個(gè)數(shù)。
        super().__init__()
        self.q = nn.Linear(c, c, bias=False)# 計(jì)算query, in_features=out_features=c
        self.k = nn.Linear(c, c, bias=False)# 計(jì)算key
        self.v = nn.Linear(c, c, bias=False)# 計(jì)算value
        self.ma = nn.MultiheadAttention(embed_dim=c, num_heads=num_heads) # 多頭注意力機(jī)制
        self.fc1 = nn.Linear(c, c, bias=False)
        self.fc2 = nn.Linear(c, c, bias=False)


    def forward(self, x):
        x = self.ma(self.q(x), self.k(x), self.v(x))[0] + x  # 多頭注意力機(jī)制+殘差連接
        x = self.fc2(self.fc1(x)) + x  # 兩個(gè)全連接層+ 殘差連接
returnx

如果輸入是x,x的大小是(s,n,c) 。其中n是batch size, s是源序列長度,c是詞特征向量的大小(embed_dim)。

然后x分別通過3個(gè)Linear層 (線性層的結(jié)構(gòu)相同,但是可學(xué)習(xí)參數(shù)不同)計(jì)算得到鍵k、查詢q、值v。因?yàn)榫€性層的輸入特征數(shù)和輸出特征數(shù)均等于c, 所以k,q,v的大小也是(s,n,c)。

接著,把k、q、v作為參數(shù)輸入到多頭注意力ma中,返回兩個(gè)結(jié)果attn_output(注意力機(jī)制的輸出)和attn_output_weights(注意力機(jī)制的權(quán)重)。在這里,我們只需要注意力機(jī)制的輸出就可以,因此,我們?nèi)?a target="_blank">索引0 self.ma(self.q(x), self.k(x), self.v(x))[0],它的大小是(s,n,c)。+x 表示殘差連接,不改變x的形狀。

self.fc2(self.fc1(x)) 表示經(jīng)過兩個(gè)全連接層,輸出大小是(s,n,c)。+x 表示殘差連接,不改變x的形狀。因此最終輸出的形狀大小和輸入的形狀一樣。

08

Transformer Block

TransformerBlock是把若干個(gè)TransformerLayer串聯(lián)起來。

對于圖像數(shù)據(jù)而言,輸入數(shù)據(jù)形狀是 [batch, channel, height, width],變換成 [height × width, batch, channel]。height × width把圖像中各個(gè)像素點(diǎn)看作一個(gè)單詞,其對應(yīng)通道的信息連在一起就是詞向量。channel就是詞向量的長度。

e1af09a4-0cf6-11ee-962d-dac502259ad0.png

TransformerBlock的實(shí)現(xiàn)代碼如下:

class TransformerBlock(nn.Module):
    def __init__(self, c1, c2, num_heads, num_layers):
        super().__init__()
        self.conv = None
        if c1 != c2:
            self.conv = Conv(c1, c2)
        self.linear = nn.Linear(c2, c2)  # learnable position embedding
        self.tr = nn.Sequential(*(TransformerLayer(c2, num_heads) for _ in range(num_layers)))
        self.c2 = c2


    def forward(self, x):  # x:(b,c1,w0,h0)
        if self.conv is not None:
            x = self.conv(x) # x:(b,c2,w,h)
        b, _, w, h = x.shape
        p = x.flatten(2).permute(2, 0, 1) # flatten后:(b,c2,w*h)  p: (w*h,b,c2)  
        # linear后: (w*h,b,c2)   tr后: (w*h,b,c2)   permute后: (b,c2,w*h)  reshape后:(b,c2,w,h)
returnself.tr(p+self.linear(p)).permute(1,2,0).reshape(b,self.c2,w,h)

1)輸入的x大小為(b,c1,w,h)。其中b為batch size, c1 是輸入通道數(shù)大小, w 和h 分別表示圖像的寬和高。

2)經(jīng)過Conv層:Conv層中的2d卷積,卷積核大小是1x1, 步長為1,無填充,擴(kuò)張率為1。因此不改變w和h, 只改變輸出通道數(shù),形狀變?yōu)?b,c2,w,h)。Conv層中的BN和SiLU不改變形狀大小。輸出的x大小為(b,c2,w,h)

3)對x進(jìn)行變換得到p: x.flatten(2)后,大小變?yōu)?(b,c2,w*h) permute(2, 0, 1)后,p的大小為(w*h,b,c2)

4) 將p輸入到線性層后,因?yàn)榫€性層的輸入特征數(shù)和輸出特征數(shù)相等,因此輸出的大小為(w*h,b,c2)。

+p 進(jìn)行殘差連接后,大小不變,仍為(w*h,b,c2)

5) 然后將上一步的結(jié)果輸入到num_layers個(gè)TransformerLayer中。w*h 相當(dāng)于序列長度,b是批量的大小,c2相當(dāng)于詞嵌入特征長度。每個(gè)TransformerLayer的輸入和輸出的大小不變。經(jīng)過若干個(gè)TransformerLayer后,大小是(w*h,b,c2)。

6)permute(1, 2, 0)后: 形狀變?yōu)?b,c2,w*h) reshape(b, self.c2, w, h)后:(b,c2,w,h)

09

Bottleneck

先使用 3x3 卷積降維,剔除冗余信息;再使用 3×3 卷積升維。

e1bb8aa8-0cf6-11ee-962d-dac502259ad0.png

class Bottleneck(nn.Module):
    # Standard bottleneck
    def __init__(self, c1, c2, shortcut=True, g=1, k=(3, 3), e=0.5):  # ch_in, ch_out, shortcut, groups, kernels, expand
        super().__init__()
        c_ = int(c2 * e)  # hidden channels
        self.cv1 = Conv(c1, c_, k[0], 1)  # 輸入通道: c1, 輸出通道:c_ , 卷積核:3x3, 步長1
        self.cv2 = Conv(c_, c2, k[1], 1, g=g) # 輸入通道:c_ , 輸出通道c2, 卷積核:3x3, 步長1
        self.add = shortcut and c1 == c2  # 當(dāng)傳入的shortcut參數(shù)為true,且c1和c2相等時(shí),則使用殘差連接。


    def forward(self, x):
returnx+self.cv2(self.cv1(x))ifself.addelseself.cv2(self.cv1(x))

第一層卷積,輸入通道: c1, 輸出通道:c_ , 卷積核:3x3, 步長1

第一層卷積,輸入通道: c_, 輸出通道:c2 , 卷積核:3x3, 步長1

其中c _ = c2/2。當(dāng)c1和c2相等時(shí),采用殘差連接。

10

BottleneckCSP

詳細(xì)請參考CSPNet的論文和源碼。論文《CSPNet: A New Backbone that can Enhance Learning Capability of CNN》

源碼https://github.com/WongKinYiu/CrossStagePartialNetworks

e1cbdbb0-0cf6-11ee-962d-dac502259ad0.png

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().__init__()  
        c_ = int(c2 * e)  # hidden channels
        # 輸出x的大小是(b,c1,w,h)
        self.cv1 = Conv(c1, c_, 1, 1) # cv1的大小為(b,c_,w,h)
        self.cv2 = nn.Conv2d(c1, c_, 1, 1, bias=False) # cv2的大小為(b,c_,w,h)
        self.cv3 = nn.Conv2d(c_, c_, 1, 1, bias=False) # m通過Conv2d,變成cv3,大小是(b,c_,w,h)
        self.cv4 = Conv(2 * c_, c2, 1, 1)
        self.bn = nn.BatchNorm2d(2 * c_)  # applied to cat(cv2, cv3)
        self.act = nn.SiLU()
        self.m = nn.Sequential(*(Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)))  
        # cv1通過n個(gè)串聯(lián)的bottleneck,變成m,大小為(b,c_,w,h)


    def forward(self, x):
        y1 = self.cv3(self.m(self.cv1(x))) # (b,c_,w,h)
        y2 = self.cv2(x) # (b,c_,w,h)
        return self.cv4(self.act(self.bn(torch.cat((y1, y2), 1))))
        # cat后:(b,2*c_,w,h) 返回cv4: (b,c2,w,h)

1)輸出x的大小是(b,c1,w,h), 然后有兩條計(jì)算路徑分別計(jì)算得到y(tǒng)1和y2。

y1的計(jì)算路徑:先x通過cv1,大小變成(b,c_,w,h) 。cv1通過n個(gè)串聯(lián)的bottleneck,變成m,大小為(b,c_,w,h)。m通過cv3, 得到y(tǒng)1, 大小是(b,c_,w,h)

y2的計(jì)算路徑:x通過cv2得到y(tǒng)2,大小是(b,c_,w,h)

2)y1和y2在dim=1處連接, 大小是(b,2*c_,w,h), 然后再通過BN和SiLU,大小不變。

3)最終,通過cv4, 返回結(jié)果的大小是(b,c2,w,h)

11

C3

與 BottleneckCSP 類似,但少了 1 個(gè) Conv、1 個(gè) BN、1 個(gè) Act,運(yùn)算量更少??偣仓挥?次卷積(cv1,cv2,cv3)。

e1d82cda-0cf6-11ee-962d-dac502259ad0.png

class C3(nn.Module):
    # CSP Bottleneck with 3 convolutions
    def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):  # ch_in, ch_out, number, shortcut, groups, expansion
        super().__init__()
        c_ = int(c2 * e)  # hidden channels
        self.cv1 = Conv(c1, c_, 1, 1)
        self.cv2 = Conv(c1, c_, 1, 1)
        self.cv3 = Conv(2 * c_, c2, 1)  # optional act=FReLU(c2)
        self.m = nn.Sequential(*(Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)))


    def forward(self, x):
returnself.cv3(torch.cat((self.m(self.cv1(x)),self.cv2(x)),1))

12

C2

C2只有兩個(gè)卷積(cv1,cv2)的CSP Bottleneck。

e1e45dca-0cf6-11ee-962d-dac502259ad0.png

class C2(nn.Module):
    # CSP Bottleneck with 2 convolutions
    def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):  # ch_in, ch_out, number, shortcut, groups, expansion
        super().__init__()
     # 假設(shè)輸入的x大小是(b,c1,w,h)
        self.c = int(c2 * e)  # hidden channels e=0.5,對輸出通道進(jìn)行平分。
        self.cv1 = Conv(c1, 2 * self.c, 1, 1) # cv1的大小是(b,c2,w,h)
        self.cv2 = Conv(2 * self.c, c2, 1)  # optional act=FReLU(c2)
        # self.attention = ChannelAttention(2 * self.c)  # or SpatialAttention()   #此處可以使用空間注意力或者跨通道的注意力機(jī)制。
        self.m = nn.Sequential(*(Bottleneck(self.c, self.c, shortcut, g, k=((3, 3), (3, 3)), e=1.0) for _ in range(n)))  # a通過n個(gè)串聯(lián)的Bottleneck后的到m,m的大小是(b,c,w,h)


    def forward(self, x):
        a, b = self.cv1(x).split((self.c, self.c), 1)# 對cv進(jìn)行在維度1進(jìn)行平分,a和b的大小都是(b,c,w,h)
returnself.cv2(torch.cat((self.m(a),b),1))#把m和b在維度1進(jìn)行cat后,大小是(b,c2,w,h)。最終通過cv2,大小是(b,c2,w,h)

1)輸出x的大小是(b,c1,w,h), 通過Conv層,得到cv1, cv1的大小是(b,c2,w,h)

2) 然后再dim=1的維度上對cv1進(jìn)行分割,a和b的大小都是(b,c2/2,w,h)。

3) a通過n個(gè)串聯(lián)的Bottleneck后的到m,m的大小是(b,c,w,h)

4) 把m和b在維度1進(jìn)行cat后,大小是(b,c2,w,h)。最終m通過cv2,輸出的大小是(b,c2,w,h)

13

C2f

C2f與C2相比,每個(gè)Bottleneck的輸出都會被Concat到一起。

e1f13784-0cf6-11ee-962d-dac502259ad0.png

class C2f(nn.Module):
    # CSP Bottleneck with 2 convolutions
    def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5):  # ch_in, ch_out, number, shortcut, groups, expansion
        super().__init__()
        # 假設(shè)輸入的x大小是(b,c1,w,h)
        self.c = int(c2 * e)  # hidden channels
        self.cv1 = Conv(c1, 2 * self.c, 1, 1) # cv1的大小是(b,c2,w,h)
        self.cv2 = Conv((2 + n) * self.c, c2, 1)  # optional act=FReLU(c2)
        self.m = nn.ModuleList(Bottleneck(self.c, self.c, shortcut, g, k=((3, 3), (3, 3)), e=1.0) for _ in range(n)) # n個(gè)Bottleneck組成的ModuleList,可以把m看做是一個(gè)可迭代對象


    def forward(self, x):
        y = list(self.cv1(x).split((self.c, self.c), 1))
        # cv1的大小是(b,c2,w,h),對cv1在維度1等分成兩份(假設(shè)分別是a和b),a和b的大小均是(b,c2/2,w,h)。此時(shí)y=[a,b]。
        y.extend(m(y[-1]) for m in self.m)
        # 然后對列表y中的最后一個(gè)張量b輸入到ModuleList中的第1個(gè)bottleneck里,得到c,c的大小是(b,c2/2,w,h)。然后把c也加入y中。此時(shí)y=[a,b,c]
        # 重復(fù)上述操作n次(因?yàn)槭莕個(gè)bottleneck),最終得到的y列表中一共有n+2個(gè)元素。
        return self.cv2(torch.cat(y, 1)) 
        # 對列表y中的張量在維度1進(jìn)行連接,得到的張量大小是(b,(n+2)*c2/2,w,h)。
#最終通過cv2,輸出張量的大小是(b,c2,w,h)

1)cv1的大小是(b,c2,w,h),對cv1在維度1等分成兩份(假設(shè)分別是a和b),a和b的大小均是(b,c2/2,w,h)。此時(shí)y=[a,b]。

2)然后對列表y中的最后一個(gè)張量b輸入到ModuleList中的第1個(gè)bottleneck里,得到c,c的大小是(b,c2/2,w,h)。然后把c也加入y中。此時(shí)y=[a,b,c]。

3)上述步驟重復(fù)上述操作n次(因?yàn)槭莕個(gè)bottleneck),最終得到的y列表中一共有n+2個(gè)元素。

4)對列表y中的張量在維度1進(jìn)行連接,得到的張量大小是(b,(n+2)*c2/2,w,h)。

5)最終通過cv2,輸出張量的大小是(b,c2,w,h)

14

ChannelAttention

通道注意力模型: 通道維度不變,壓縮空間維度。該模塊關(guān)注輸入圖片中有意義的信息。

e1fc8080-0cf6-11ee-962d-dac502259ad0.png

class ChannelAttention(nn.Module):
    # Channel-attention module https://github.com/open-mmlab/mmdetection/tree/v3.0.0rc1/configs/rtmdet
    def __init__(self, channels: int) -> None:
        super().__init__()
        self.pool = nn.AdaptiveAvgPool2d(1) # 自適應(yīng)平均池化后,大小為(b,c,1,1)
        self.fc = nn.Conv2d(channels, channels, 1, 1, 0, bias=True)
        self.act = nn.Sigmoid()


    def forward(self, x: torch.Tensor) -> torch.Tensor:
returnx*self.act(self.fc(self.pool(x)))

1)假設(shè)輸入的數(shù)據(jù)大小是(b,c,w,h)
2)通過自適應(yīng)平均池化使得輸出的大小變?yōu)?/span>(b,c,1,1)
3)通過2d卷積和sigmod激活函數(shù)后,大小是(b,c,1,1)
4)將上一步輸出的結(jié)果和輸入的數(shù)據(jù)相乘,輸出數(shù)據(jù)大小是(b,c,w,h)。

15

SpatialAttention

空間注意力模塊:空間維度不變,壓縮通道維度。該模塊關(guān)注的是目標(biāo)的位置信息。

e20ef274-0cf6-11ee-962d-dac502259ad0.png

class SpatialAttention(nn.Module):
    # Spatial-attention module
    def __init__(self, kernel_size=7):
        super().__init__()
        assert kernel_size in (3, 7), 'kernel size must be 3 or 7'  # kernel size 的大小必須是3或者7
        padding = 3 if kernel_size == 7 else 1  # 當(dāng)kernel_size是7時(shí),padding=3; 當(dāng)kernel_size是3時(shí),padding=1
        self.cv1 = nn.Conv2d(2, 1, kernel_size, padding=padding, bias=False)
        self.act = nn.Sigmoid()


    def forward(self, x):
returnx*self.act(self.cv1(torch.cat([torch.mean(x,1,keepdim=True),torch.max(x,1,keepdim=True)[0]],1)))

1) 假設(shè)輸入的數(shù)據(jù)x是(b,c,w,h),并進(jìn)行兩路處理。

2)其中一路在通道維度上進(jìn)行求平均值,得到的大小是(b,1,w,h);另外一路也在通道維度上進(jìn)行求最大值,得到的大小是(b,1,w,h)。

3) 然后對上述步驟的兩路輸出進(jìn)行連接,輸出的大小是(b,2,w,h)

4)經(jīng)過一個(gè)二維卷積網(wǎng)絡(luò),把輸出通道變?yōu)?,輸出大小是(b,1,w,h)

4)將上一步輸出的結(jié)果和輸入的數(shù)據(jù)x相乘,最終輸出數(shù)據(jù)大小是(b,c,w,h)。

16

CBAM

CBAM就是把ChannelAttention和SpatialAttention串聯(lián)在一起。

e21a05a6-0cf6-11ee-962d-dac502259ad0.png

class CBAM(nn.Module):
    # Convolutional Block Attention Module
    def __init__(self, c1, kernel_size=7):  # ch_in, kernels
        super().__init__()
        self.channel_attention = ChannelAttention(c1)
        self.spatial_attention = SpatialAttention(kernel_size)


    def forward(self, x):
        return self.spatial_attention(self.channel_attention(x))

17

C1

總共只有3次卷積(cv1,cv2,cv3)的Bottleneck。

e223a80e-0cf6-11ee-962d-dac502259ad0.png

class C1(nn.Module):
    # CSP Bottleneck with 1 convolution
    def __init__(self, c1, c2, n=1):  # ch_in, ch_out, number
        super().__init__()
        self.cv1 = Conv(c1, c2, 1, 1)
        self.m = nn.Sequential(*(Conv(c2, c2, 3) for _ in range(n)))


    def forward(self, x):
        y = self.cv1(x)
returnself.m(y)+y

1)假設(shè)輸入的數(shù)據(jù)是(b,c1,w,h)
2) 首先通過一個(gè)Conv塊,得到y, 大小為(b,c2,w,h)
3) 然后讓y通過n個(gè)3x3的Conv塊,得到m
4) 最后讓m和y相加。

18

C3x

C3x 繼承自C3, 變換是Bottleneck中的卷積核大小變?yōu)?1,3)和(3,3)

class C3x(C3):
    # C3 module with cross-convolutions
    def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):
        super().__init__(c1, c2, n, shortcut, g, e)
        self.c_ = int(c2 * e)
self.m=nn.Sequential(*(Bottleneck(self.c_,self.c_,shortcut,g,k=((1,3),(3,1)),e=1)for_inrange(n)))

19

C3TR

C3TR繼承自C3, n 個(gè) Bottleneck 更換為 1 個(gè) TransformerBlock。

e2300464-0cf6-11ee-962d-dac502259ad0.png

class C3TR(C3):
    # C3 module with TransformerBlock()
    def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):
        super().__init__(c1, c2, n, shortcut, g, e)
        c_ = int(c2 * e)
        self.m = TransformerBlock(c_, c_, 4, n)# num_heads=4, num_layers=n

20

SPP

《Spatial Pyramid Pooling in Deep Convolutional Networks for Visual Recognition》

e23d2d10-0cf6-11ee-962d-dac502259ad0.png

class SPP(nn.Module):
    # Spatial Pyramid Pooling (SPP) layer https://arxiv.org/abs/1406.4729
    def __init__(self, c1, c2, k=(5, 9, 13)):
        super().__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)
        with warnings.catch_warnings():
            warnings.simplefilter('ignore')  # suppress torch 1.9.0 max_pool2d() warning
returnself.cv2(torch.cat([x]+[m(x)forminself.m],1))

21

SPPF

這個(gè)是YOLOv5作者Glenn Jocher基于SPP提出的,速度較SPP快很多,所以叫SPP-Fast。

三個(gè)MaxPool 串行連接,kerner size都是5*5。效果等價(jià)于SPP,但是運(yùn)算量從原來的5^2 + 9^2 + 13^2 = 275減少到3* 5^2 =75

e24af576-0cf6-11ee-962d-dac502259ad0.png

池化尺寸等價(jià)于SPP中kernel size分別為5 * 5,9 * 913 * 13的池化層并行連接。

class SPPF(nn.Module):
    # Spatial Pyramid Pooling - Fast (SPPF) layer for YOLOv5 by Glenn Jocher
    def __init__(self, c1, c2, k=5):  # equivalent to SPP(k=(5, 9, 13))
        super().__init__()
        c_ = c1 // 2  # hidden channels
        self.cv1 = Conv(c1, c_, 1, 1)
        self.cv2 = Conv(c_ * 4, c2, 1, 1)
        self.m = nn.MaxPool2d(kernel_size=k, stride=1, padding=k // 2)


    def forward(self, x):
        x = self.cv1(x)
        with warnings.catch_warnings():
            warnings.simplefilter('ignore')  # suppress torch 1.9.0 max_pool2d() warning
            y1 = self.m(x)
            y2 = self.m(y1)
returnself.cv2(torch.cat((x,y1,y2,self.m(y2)),1))

22

Focus

Focus模塊在v5中是圖片進(jìn)入backbone前,對圖片進(jìn)行切片操作,具體操作是在一張圖片中每隔一個(gè)像素拿到一個(gè)值,類似于鄰近下采樣,這樣就拿到了四張圖片,四張圖片互補(bǔ),長的差不多,但是沒有信息丟失,這樣一來,將W、H信息就集中到了通道空間,輸入通道擴(kuò)充了4倍,即拼接起來的圖片相對于原先的RGB三通道模式變成了12個(gè)通道,最后將得到的新圖片再經(jīng)過卷積操作,最終得到了沒有信息丟失情況下的二倍下采樣特征圖。

例如:原始的640 × 640 × 3的圖像輸入Focus結(jié)構(gòu),采用切片操作,先變成320 × 320 × 12的特征圖,再經(jīng)過一次卷積操作,最終變成320 × 320 × 32的特征圖。切片操作如下

e260c450-0cf6-11ee-962d-dac502259ad0.png

e26dce16-0cf6-11ee-962d-dac502259ad0.png

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().__init__()
        self.conv = Conv(c1 * 4, c2, k, s, p, g, act=act)
        # self.contract = Contract(gain=2)


    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))
#returnself.conv(self.contract(x))

23

GhostConv

Ghost卷積來自華為諾亞方舟實(shí)驗(yàn)室,《GhostNet: More Features from Cheap Operations》發(fā)表于2020年的CVPR上。提供了一個(gè)全新的Ghost模塊,旨在通過廉價(jià)操作生成更多的特征圖。

原理如下圖所示:

e277df6e-0cf6-11ee-962d-dac502259ad0.png

Ghost Module分為兩步操作來獲得與普通卷積一樣數(shù)量的特征圖:

Step1:少量卷積(比如正常用128個(gè)卷積核,這里就用64個(gè),從而減少一半的計(jì)算量);

Step2:cheap operations,用圖中的Φ表示,Φ是諸如33、55的卷積,并且是逐個(gè)特征圖的進(jìn)行卷積(Depth-wise convolutional,深度卷積)。

e294c7d2-0cf6-11ee-962d-dac502259ad0.png

class GhostConv(nn.Module):
    # Ghost Convolution https://github.com/huawei-noah/ghostnet
    def __init__(self, c1, c2, k=1, s=1, g=1, act=True):  # ch_in, ch_out, kernel, stride, groups
        super().__init__()
        c_ = c2 // 2  # hidden channels
        self.cv1 = Conv(c1, c_, k, s, None, g, act=act)
        self.cv2 = Conv(c_, c_, 5, 1, None, c_, act=act)  # 分組數(shù)=c_=通道數(shù),進(jìn)行point-wise的深度分離卷積


    def forward(self, x):
        y = self.cv1(x)
returntorch.cat((y,self.cv2(y)),1)

24

GhostBottleneck

e2a2c116-0cf6-11ee-962d-dac502259ad0.png

class GhostBottleneck(nn.Module):
    # Ghost Bottleneck https://github.com/huawei-noah/ghostnet
    def __init__(self, c1, c2, k=3, s=1):  # ch_in, ch_out, kernel, stride
        super().__init__()
        c_ = c2 // 2
        self.conv = nn.Sequential(
            GhostConv(c1, c_, 1, 1),  # 卷積核的大小是1*1,屬于point-wise的深度可分離卷積
            DWConv(c_, c_, k, s, act=False) if s == 2 else nn.Identity(),  # 輸入通道數(shù)和輸出通道數(shù)相等,屬于depth-wise的深度可分離卷積
            GhostConv(c_, c2, 1, 1, act=False))  #point-wise的深度可分離卷積,且不采用偏置項(xiàng)。
        self.shortcut = nn.Sequential(DWConv(c1, c1, k, s, act=False), Conv(c1, c2, 1, 1,
                                                                            act=False)) if s == 2 else nn.Identity()


    def forward(self, x):
returnself.conv(x)+self.shortcut(x)

25

C3Ghost

C3Ghost繼承自C3, Bottleneck更換為GhostBottleneck

e2af0886-0cf6-11ee-962d-dac502259ad0.png

class C3Ghost(C3):
    # C3 module with GhostBottleneck()
    def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):
        super().__init__(c1, c2, n, shortcut, g, e)
        c_ = int(c2 * e)  # hidden channels
self.m=nn.Sequential(*(GhostBottleneck(c_,c_)for_inrange(n)))

26

Concat

當(dāng)dimension=1時(shí),將多張相同尺寸的圖像在通道維度維度上進(jìn)行拼接。

class Concat(nn.Module):
    # Concatenate a list of tensors along dimension
    def __init__(self, dimension=1):
        super().__init__()
        self.d = dimension


    def forward(self, x):
returntorch.cat(x,self.d)

8.Yolov8實(shí)操

Section Name

01

下載工程并安裝ultralytics

git clone https://github.com/ultralytics/ultralytics
cd ultralytics
pipinstall-e.

02

數(shù)據(jù)集準(zhǔn)備

數(shù)據(jù)集制作參考:YOLO格式數(shù)據(jù)集制作

# 訓(xùn)練/驗(yàn)證/測試 數(shù)據(jù)
train: /data/zyw/project/dataset/finalTrafficLightDataset/train/images
val: /data/zyw/project/dataset/finalTrafficLightDataset/val/images
test: /data/zyw/project/dataset/finalTrafficLightDataset/WPIDataset/images
# 類別個(gè)數(shù)
nc: 12


# 類別名稱
names:["greenCircle","yellowCircle","redCircle","greenLeft","yellowLeft","redLeft","greenRight","yellowRight","redRight","greenForward","yellowForward","redForward"]

03

模型的訓(xùn)練/驗(yàn)證/預(yù)測/驗(yàn)證

01

使用CLI

如果你想對模型進(jìn)行訓(xùn)練、驗(yàn)證或運(yùn)行推斷,并且不需要對代碼進(jìn)行任何修改,那么使用YOLO命令行接口是最簡單的入門方法。

YOLO命令行界面(command line interface, CLI) 方便在各種任務(wù)和版本上訓(xùn)練、驗(yàn)證或推斷模型。CLI不需要定制或代碼,可以使用yolo命令從終端運(yùn)行所有任務(wù)。

(1)語法

yolo task=detect    mode=train    model=yolov8n.yaml      args...
          classify       predict        yolov8n-cls.yaml  args...
          segment        val            yolov8n-seg.yaml  args...
exportyolov8n.ptformat=onnxargs...

(2)訓(xùn)練示例

yolotask=detectmode=trainmodel=yolov8n.ptdata=coco128.yamldevice=0

(3)多GPU訓(xùn)練示例

yolotask=detectmode=trainmodel=yolov8n.ptdata=coco128.yamldevice='0,1,2,3'

(4)重寫默認(rèn)的配置參數(shù)

# 語法
yolo task= ... mode= ...  arg=val
# 例子:進(jìn)行10個(gè)epoch的檢測訓(xùn)練,learning_rate為0.01
yolotask=detectmode=trainepochs=10lr0=0.01

(5)重寫默認(rèn)配置文件

# 可以在當(dāng)前工作目錄下創(chuàng)建一個(gè)默認(rèn)配置文件的副本
yolo task=init
# 然后可以使用cfg=name.yaml命令來傳遞新的配置文件
yolocfg=default.yaml

02

使用python

允許用戶在Python項(xiàng)目中輕松使用YOLOv8。它提供了加載和運(yùn)行模型以及處理模型輸出的函數(shù)。該界面設(shè)計(jì)易于使用,以便用戶可以在他們的項(xiàng)目中快速實(shí)現(xiàn)目標(biāo)檢測。

(1)訓(xùn)練

方式1:從預(yù)訓(xùn)練模型開始訓(xùn)練

from ultralytics import YOLO


model = YOLO("yolov8n.pt") # pass any model type
model.train(epochs=5)

方式2:從頭開始訓(xùn)練

from ultralytics import YOLO


model = YOLO("yolov8n.yaml")
model.train(data="coco128.yaml",epochs=5)

(2)驗(yàn)證

訓(xùn)練后驗(yàn)證:

from ultralytics import YOLO


  model = YOLO("yolov8n.yaml")
  model.train(data="coco128.yaml", epochs=5)
model.val()#It'llautomaticallyevaluatethedatayoutrained.

單獨(dú)驗(yàn)證:

from ultralytics import YOLO


  model = YOLO("model.pt")
  # 如果不設(shè)置數(shù)據(jù)的話,就使用model.pt中的data yaml文件
  model.val()
  # 或者直接設(shè)置需要驗(yàn)證的數(shù)據(jù)。
model.val(data="coco128.yaml")

(3)預(yù)測

從源文件預(yù)測:

from ultralytics import YOLO


model = YOLO("model.pt")
model.predict(source="0") # accepts all formats - img/folder/vid.*(mp4/format). 0 for webcam
model.predict(source="folder",show=True)#Displaypreds.Acceptsallyolopredictarguments

返回結(jié)果:

from ultralytics import YOLO


model = YOLO("model.pt")
outputs = model.predict(source="0", return_outputs=True) # treat predict as a Python generator
for output in outputs:
  # each output here is a dict.
  # for detection
  print(output["det"])  # np.ndarray, (N, 6), xyxy, score, cls
  # for segmentation
  print(output["det"])  # np.ndarray, (N, 6), xyxy, score, cls
  print(output["segment"])  # List[np.ndarray] * N, bounding coordinates of masks
  # for classify
print(output["prob"])#np.ndarray,(num_class,),clsprob

04

數(shù)據(jù)擴(kuò)充

YOLO模型的增強(qiáng)設(shè)置是指應(yīng)用于訓(xùn)練數(shù)據(jù)的各種變換和修改,以增加數(shù)據(jù)集的多樣性和大小。這些設(shè)置會影響模型的性能、速度和精度。一些常見的YOLO增強(qiáng)設(shè)置包括應(yīng)用的轉(zhuǎn)換類型和強(qiáng)度(例如隨機(jī)翻轉(zhuǎn)、旋轉(zhuǎn)、裁剪、顏色變化),應(yīng)用每個(gè)轉(zhuǎn)換的概率,以及是否存在其他功能,如掩碼或每個(gè)框多個(gè)標(biāo)簽。其他可能影響數(shù)據(jù)擴(kuò)充過程的因素包括原始數(shù)據(jù)集的大小和組成,以及模型正在用于的特定任務(wù)。重要的是要仔細(xì)調(diào)整和實(shí)驗(yàn)這些設(shè)置,以確保增強(qiáng)后的數(shù)據(jù)集具有足夠的多樣性和代表性,以訓(xùn)練高性能的模型。

e2bcf018-0cf6-11ee-962d-dac502259ad0.png

05

日志、檢查點(diǎn)、繪圖與文件管理

在訓(xùn)練YOLO模型時(shí),日志記錄、檢查點(diǎn)、繪圖和文件管理是重要的考慮因素。

日志記錄:在訓(xùn)練期間記錄各種指標(biāo)和統(tǒng)計(jì)數(shù)據(jù)通常有助于跟蹤模型的進(jìn)展和診斷任何可能出現(xiàn)的問題。這可以通過使用日志庫(如TensorBoard)或?qū)⑷罩鞠懭胛募韺?shí)現(xiàn)。

檢查點(diǎn):在訓(xùn)練期間,定期保存模型的檢查點(diǎn)是一個(gè)很好的做法。如果訓(xùn)練過程被中斷,或者你想嘗試不同的訓(xùn)練配置,這允許你從之前的點(diǎn)恢復(fù)訓(xùn)練。繪圖:可視化模型的性能和訓(xùn)練過程,有助于理解模型的行為方式和識別潛在問題。這可以使用matplotlib等繪圖庫完成,也可以使用TensorBoard等日志庫來繪圖。

文件管理:管理訓(xùn)練過程中生成的各種文件,例如模型檢查點(diǎn)、日志文件和繪圖,可能具有挑戰(zhàn)性。有一個(gè)清晰和有組織的文件結(jié)構(gòu)是很重要的,以便跟蹤這些文件,并使其易于根據(jù)需要訪問和分析它們。

有效的日志記錄、檢查點(diǎn)、繪圖和文件管理可以幫助您跟蹤模型的進(jìn)度,并使其更容易調(diào)試和優(yōu)化訓(xùn)練過程。

e2c675ac-0cf6-11ee-962d-dac502259ad0.png


			

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • 模型
    +關(guān)注

    關(guān)注

    1

    文章

    3114

    瀏覽量

    48660
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4722

    瀏覽量

    68236
  • 網(wǎng)絡(luò)結(jié)構(gòu)

    關(guān)注

    0

    文章

    48

    瀏覽量

    11059

原文標(biāo)題:【光電智造】一文徹底搞懂YOLOv8(網(wǎng)絡(luò)結(jié)構(gòu)+代碼+實(shí)操)

文章出處:【微信號:今日光電,微信公眾號:今日光電】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    YOLOv5網(wǎng)絡(luò)結(jié)構(gòu)解析

    1、YOLOv5 網(wǎng)絡(luò)結(jié)構(gòu)解析  YOLOv5針對不同大?。╪, s, m, l, x)的網(wǎng)絡(luò)整體架構(gòu)都是樣的,只不過會在每個(gè)子模塊中采用
    發(fā)表于 10-31 16:30

    使用YOLOv8做目標(biāo)檢測和實(shí)例分割的演示

    YOLOv8是來自Ultralytics的最新的基于YOLO的對象檢測模型系列,提供最先進(jìn)的性能。
    的頭像 發(fā)表于 02-06 10:11 ?7266次閱讀

    YOLOv8自定義數(shù)據(jù)集訓(xùn)練到模型部署推理簡析

    如果你只是想使用而不是開發(fā),強(qiáng)烈推薦通過pip安裝方式獲取YOLOv8包!YOLOv8安裝命令行
    的頭像 發(fā)表于 03-24 09:27 ?4558次閱讀

    TensorRT 8.6 C++開發(fā)環(huán)境配置與YOLOv8實(shí)例分割推理演示

    YOLOv8實(shí)例分割TensorRT 推理代碼已經(jīng)完成C++類封裝,三行代碼即可實(shí)現(xiàn)YOLOv8對象檢測與實(shí)例分割模型推理,不需要改任何代碼
    的頭像 發(fā)表于 04-25 10:49 ?5529次閱讀
    TensorRT 8.6 C++開發(fā)環(huán)境配置與<b class='flag-5'>YOLOv8</b>實(shí)例分割推理演示

    在AI愛克斯開發(fā)板上用OpenVINO?加速YOLOv8目標(biāo)檢測模型

    《在 AI 愛克斯開發(fā)板上用 OpenVINO 加速 YOLOv8 分類模型》介紹了在 AI 愛克斯開發(fā)板上使用 OpenVINO 開發(fā)套件部署并測評 YOLOv8 的分類模型,本文將介紹在 AI 愛克斯開發(fā)板上使用 OpenVINO 加速
    的頭像 發(fā)表于 05-12 09:08 ?1240次閱讀
    在AI愛克斯開發(fā)板上用OpenVINO?加速<b class='flag-5'>YOLOv8</b>目標(biāo)檢測模型

    YOLOv8版本升級支持小目標(biāo)檢測與高分辨率圖像輸入

    YOLOv8版本最近版本又更新了,除了支持姿態(tài)評估以外,通過模型結(jié)構(gòu)的修改還支持了小目標(biāo)檢測與高分辨率圖像檢測。原始的YOLOv8模型結(jié)構(gòu)如下。
    的頭像 發(fā)表于 05-16 11:14 ?1.2w次閱讀
    <b class='flag-5'>YOLOv8</b>版本升級支持小目標(biāo)檢測與高分辨率圖像輸入

    AI愛克斯開發(fā)板上使用OpenVINO加速YOLOv8目標(biāo)檢測模型

    《在AI愛克斯開發(fā)板上用OpenVINO加速YOLOv8分類模型》介紹了在AI愛克斯開發(fā)板上使用OpenVINO 開發(fā)套件部署并測評YOLOv8的分類模型,本文將介紹在AI愛克斯開發(fā)板上使用OpenVINO加速YOLOv8目標(biāo)檢
    的頭像 發(fā)表于 05-26 11:03 ?1161次閱讀
    AI愛克斯開發(fā)板上使用OpenVINO加速<b class='flag-5'>YOLOv8</b>目標(biāo)檢測模型

    徹底搞懂YOLOv8網(wǎng)絡(luò)結(jié)構(gòu)+代碼+實(shí)

    從上面可以看出,YOLOv8 主要參考了最近提出的諸如 YOLOX、YOLOv6、YOLOv7 和 PPYOLOE 等算法的相關(guān)設(shè)計(jì),本身的創(chuàng)新點(diǎn)不多,偏向工程實(shí)踐,主推的還是 ultralytics 這個(gè)框架本身。
    的頭像 發(fā)表于 06-15 17:15 ?1.2w次閱讀
    <b class='flag-5'>一</b><b class='flag-5'>文</b><b class='flag-5'>徹底</b><b class='flag-5'>搞懂</b><b class='flag-5'>YOLOv8</b>【<b class='flag-5'>網(wǎng)絡(luò)結(jié)構(gòu)</b>+<b class='flag-5'>代碼</b>+<b class='flag-5'>實(shí)</b><b class='flag-5'>操</b>】

    教你如何用兩行代碼搞定YOLOv8各種模型推理

    大家好,YOLOv8 框架本身提供的API函數(shù)是可以兩行代碼實(shí)現(xiàn) YOLOv8 模型推理,這次我把這段代碼封裝成了個(gè)類,只有40行
    的頭像 發(fā)表于 06-18 11:50 ?2934次閱讀
    教你如何用兩行<b class='flag-5'>代碼</b>搞定<b class='flag-5'>YOLOv8</b>各種模型推理

    目標(biāo)檢測算法再升級!YOLOv8保姆級教程鍵體驗(yàn)

    YOLO作為種基于圖像全局信息進(jìn)行預(yù)測的目標(biāo)檢測系統(tǒng),始終保持著極高的迭代更新率,從YOLOv5到YOLOv8,本次升級主要包括結(jié)構(gòu)算法、命令行界面、PythonAPI等。具體到
    的頭像 發(fā)表于 02-28 11:16 ?2504次閱讀
    目標(biāo)檢測算法再升級!<b class='flag-5'>YOLOv8</b>保姆級教程<b class='flag-5'>一</b>鍵體驗(yàn)

    解鎖YOLOv8修改+注意力模塊訓(xùn)練與部署流程

    很多人也想跟修改YOLOv5源碼樣的方式去修改YOLOv8的源碼,但是在github上面卻發(fā)現(xiàn)找到的YOLOv8項(xiàng)目下面TAG分支是空的
    的頭像 發(fā)表于 08-11 14:14 ?4065次閱讀
    解鎖<b class='flag-5'>YOLOv8</b>修改+注意力模塊訓(xùn)練與部署流程

    如何修改YOLOv8的源碼

    很多人也想跟修改YOLOv5源碼樣的方式去修改YOLOv8的源碼,但是在github上面卻發(fā)現(xiàn)找到的YOLOv8項(xiàng)目下面TAG分支是空的,然后就直接從master/main下面把源碼
    的頭像 發(fā)表于 09-04 10:02 ?1873次閱讀
    如何修改<b class='flag-5'>YOLOv8</b>的源碼

    YOLOv8實(shí)現(xiàn)任意目錄下命令行訓(xùn)練

    當(dāng)你使用YOLOv8命令行訓(xùn)練模型的時(shí)候,如果當(dāng)前執(zhí)行的目錄下沒有相關(guān)的預(yù)訓(xùn)練模型文件,YOLOv8就會自動下載模型權(quán)重文件。這個(gè)是個(gè)正常操作,但是你還會發(fā)現(xiàn),當(dāng)你在參數(shù)model中指定已有
    的頭像 發(fā)表于 09-04 10:50 ?1057次閱讀
    <b class='flag-5'>YOLOv8</b>實(shí)現(xiàn)任意目錄下命令行訓(xùn)練

    基于YOLOv8的自定義醫(yī)學(xué)圖像分割

    YOLOv8種令人驚嘆的分割模型;它易于訓(xùn)練、測試和部署。在本教程中,我們將學(xué)習(xí)如何在自定義數(shù)據(jù)集上使用YOLOv8。但在此之前,我想告訴你為什么在存在其他優(yōu)秀的分割模型時(shí)應(yīng)該使用YOLO
    的頭像 發(fā)表于 12-20 10:51 ?708次閱讀
    基于<b class='flag-5'>YOLOv8</b>的自定義醫(yī)學(xué)圖像分割

    基于OpenCV DNN實(shí)現(xiàn)YOLOv8的模型部署與推理演示

    基于OpenCV DNN實(shí)現(xiàn)YOLOv8推理的好處就是代碼就可以部署在Windows10系統(tǒng)、烏班圖系統(tǒng)、Jetson的Jetpack系統(tǒng)
    的頭像 發(fā)表于 03-01 15:52 ?1334次閱讀
    基于OpenCV DNN實(shí)現(xiàn)<b class='flag-5'>YOLOv8</b>的模型部署與推理演示