天天看點

YOLOv8來啦 | 詳細解讀YOLOv8的改進子產品!YOLOv5官方出品YOLOv8!1、YOLOv5回顧2、YOLOv8核心介紹參考文章

YOLOv8是Ultralytics開發的 YOLO(You Only Look Once)物體檢測和圖像分割模型的最新版本,詳細介紹可以參考Ultralytics釋出的網址,可以通過ultralytics python 包擷取代碼,暫時還沒有官方公布代碼

安裝ultralytics python包

pip install -i https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ ultralytics==0.0.59
pip install -e ultralytics
           

您可以在 /usr/local/lib/pythonx.x/dist-packages/ultralytics (Ubuntu) 中找到代碼 

目錄

1、YOLOv5回顧

2、YOLOv8核心介紹

2.1、C2f子產品

2.2、SPPF改進了什麼?

2.3、PAN-FPN改進了什麼?

2.4、Head部分都變了什麼呢?

2.5、損失函數

2.6、樣本的比對

參考文章

1、YOLOv5回顧

這裡粗略回顧一下,這裡直接提供YOLOv5的整理的結構圖吧:

  1. Backbone:CSPDarkNet結構,主要結構思想的展現在C3子產品,這裡也是梯度分流的主要思想所在的地方;
  2. PAN-FPN:雙流的FPN,必須香,也必須快,但是量化還是有些需要圖優化才可以達到最優的性能,比如cat前後的scale優化等等,這裡除了上采樣、CBS卷積子產品,最為主要的還有C3子產品;
  3. Head:Coupled Head+Anchor-base,毫無疑問,YOLOv3、YOLOv4、YOLOv5、YOLOv7都是Anchor-Base的;
  4. Loss:分類用BEC Loss,回歸用CIoU Loss。
YOLOv8來啦 | 詳細解讀YOLOv8的改進子產品!YOLOv5官方出品YOLOv8!1、YOLOv5回顧2、YOLOv8核心介紹參考文章

2、YOLOv8核心介紹

直接上YOLOv8的結構圖吧,小夥伴們可以直接和YOLOv5進行對比,看看能找到或者猜到有什麼不同的地方?

YOLOv8來啦 | 詳細解讀YOLOv8的改進子產品!YOLOv5官方出品YOLOv8!1、YOLOv5回顧2、YOLOv8核心介紹參考文章

  下面就直接揭曉答案吧,具體改進如下:

  1. Backbone:使用的依舊是CSP的思想,不過YOLOv5中的C3子產品被替換成了C2f子產品,實作了進一步的輕量化,同時YOLOv8依舊使用了YOLOv5等架構中使用的SPPF子產品;
  2. PAN-FPN:毫無疑問YOLOv8依舊使用了PAN的思想,不過通過對比YOLOv5與YOLOv8的結構圖可以看到,YOLOv8将YOLOv5中PAN-FPN上采樣階段中的卷積結構删除了,同時也将C3子產品替換為了C2f子產品;
  3. Decoupled-Head:是不是嗅到了不一樣的味道?是的,YOLOv8走向了Decoupled-Head;
  4. Anchor-Free:YOLOv8抛棄了以往的Anchor-Base,使用了Anchor-Free的思想;
  5. 損失函數:YOLOv8使用VFL Loss作為分類損失,使用DFL Loss+CIOU Loss作為分類損失;
  6. 樣本比對:YOLOv8抛棄了以往的IOU比對或者單邊比例的配置設定方式,而是使用了Task-Aligned Assigner比對方式。

2.1、C2f子產品

我們不着急,先看一下C3子產品的結構圖,然後再對比與C2f的具體的差別。針對C3子產品,其主要是借助CSPNet提取分流的思想,同時結合殘差結構的思想,設計了所謂的C3 Block,這裡的CSP主分支梯度子產品為BottleNeck子產品,也就是所謂的殘差子產品。同時堆疊的個數由參數n來進行控制,也就是說不同規模的模型,n的值是有變化的。

YOLOv8來啦 | 詳細解讀YOLOv8的改進子產品!YOLOv5官方出品YOLOv8!1、YOLOv5回顧2、YOLOv8核心介紹參考文章

C3子產品結構圖

其實這裡的梯度流主分支,可以是任何之前你學習過的子產品,比如,美團提出的YOLOv6中就是用來重參子產品RepVGGBlock來替換BottleNeck Block來作為主要的梯度流分支,而百度提出的PP-YOLOE則是使用了RepResNet-Block來替換BottleNeck Block來作為主要的梯度流分支。而YOLOv7則是使用了ELAN Block來替換BottleNeck Block來作為主要的梯度流分支。

C3子產品的Pytorch的實作如下:

class BottleneckC2f(nn.Module):
    # Standard bottleneck
    def __init__(self, c1, c2, shortcut=True, g=1, k=(3, 3), e=0.5):  # ch_in, ch_out, shortcut, kernels, groups, expand
        super().__init__()
        c_ = int(c2 * e)  # hidden channels
        self.cv1 = Conv(c1, c_, k[0], 1)
        self.cv2 = Conv(c_, c2, k[1], 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 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__()
        self.c = int(c2 * e)  # hidden channels
        self.cv1 = Conv(c1, 2 * self.c, 1, 1)
        self.cv2 = Conv((2 + n) * self.c, c2, 1)  # optional act=FReLU(c2)
        self.m = nn.ModuleList(BottleneckC2f(self.c, self.c, shortcut, g, k=((3, 3), (3, 3)), e=1.0) for _ in range(n))

    def forward(self, x):
        y = list(self.cv1(x).split((self.c, self.c), 1))
        y.extend(m(y[-1]) for m in self.m)
        return self.cv2(torch.cat(y, 1))
           

下面就簡單說一下C2f子產品,通過C3子產品的代碼以及結構圖可以看到,C3子產品和名字思路一緻,在子產品中使用了3個卷積子產品(Conv+BN+SiLU),以及n個BottleNeck。

通過C3代碼可以看出,對于cv1卷積和cv2卷積的通道數是一緻的,而cv3的輸入通道數是前者的2倍,因為cv3的輸入是由主梯度流分支(BottleNeck分支)依舊次梯度流分支(CBS,cv2分支)cat得到的,是以是2倍的通道數,而輸出則是一樣的。

不妨我們再看一下YOLOv7中的子產品:

YOLOv8來啦 | 詳細解讀YOLOv8的改進子產品!YOLOv5官方出品YOLOv8!1、YOLOv5回顧2、YOLOv8核心介紹參考文章

YOLOv7通過并行更多的梯度流分支,放ELAN子產品可以獲得更豐富的梯度資訊,進而或者更高的精度和更合理的延遲。

C2f子產品的結構圖如下:

我們可以很容易的看出,C2f子產品就是參考了C3子產品以及ELAN的思想進行的設計,讓YOLOv8可以在保證輕量化的同時獲得更加豐富的梯度流資訊。

YOLOv8來啦 | 詳細解讀YOLOv8的改進子產品!YOLOv5官方出品YOLOv8!1、YOLOv5回顧2、YOLOv8核心介紹參考文章

C2f結構圖

C2f子產品對應的Pytorch實作如下:

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__()
        self.c = int(c2 * e)  # hidden channels
        self.cv1 = Conv(c1, 2 * self.c, 1, 1)
        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))

    def forward(self, x):
        y = list(self.cv1(x).split((self.c, self.c), 1))
        y.extend(m(y[-1]) for m in self.m)
        return self.cv2(torch.cat(y, 1))
           

2.2、SPPF改進了什麼?

SPP結構又被稱為空間金字塔池化,能将任意大小的特征圖轉換成固定大小的特征向量。

接下來我們來詳述一下SPP是怎麼處理滴~

輸入層:首先我們現在有一張任意大小的圖檔,其大小為w * h。

輸出層:21個神經元 -- 即我們待會希望提取到21個特征。

分析如下圖所示:分别對1 * 1分塊,2 * 2分塊和4 * 4子圖裡分别取每一個框内的max值(即取藍框框内的最大值),這一步就是作最大池化,這樣最後提取出來的特征值(即取出來的最大值)一共有1 * 1 + 2 * 2 + 4 * 4 = 21個。得出的特征再concat在一起。

YOLOv8來啦 | 詳細解讀YOLOv8的改進子產品!YOLOv5官方出品YOLOv8!1、YOLOv5回顧2、YOLOv8核心介紹參考文章

 而在YOLOv5中SPP的結構圖如下圖所示:

YOLOv8來啦 | 詳細解讀YOLOv8的改進子產品!YOLOv5官方出品YOLOv8!1、YOLOv5回顧2、YOLOv8核心介紹參考文章

 在YOLOv56.0版本中SPPF替換SPP,二者效果一緻,但前者較後者的執行時間減少至1/2。

YOLOv8來啦 | 詳細解讀YOLOv8的改進子產品!YOLOv5官方出品YOLOv8!1、YOLOv5回顧2、YOLOv8核心介紹參考文章

2.3、PAN-FPN改進了什麼?

我們先看一下YOLOv5以及YOLOv6的PAN-FPN部分的結構圖:

YOLOv5的Neck部分的結構圖如下:

YOLOv8來啦 | 詳細解讀YOLOv8的改進子產品!YOLOv5官方出品YOLOv8!1、YOLOv5回顧2、YOLOv8核心介紹參考文章

YOLOv5的neck結構圖

YOLOv6的Neck部分的結構圖如下:

YOLOv8來啦 | 詳細解讀YOLOv8的改進子產品!YOLOv5官方出品YOLOv8!1、YOLOv5回顧2、YOLOv8核心介紹參考文章

YOLOv6的neck結構圖

我們再看YOLOv8的結構圖:

YOLOv8來啦 | 詳細解讀YOLOv8的改進子產品!YOLOv5官方出品YOLOv8!1、YOLOv5回顧2、YOLOv8核心介紹參考文章

YOLOv8的neck結構圖

可以看到,相對于YOLOv5或者YOLOv6,YOLOv8将C3子產品以及RepBlock替換為了C2f,同時細心可以發現,相對于YOLOv5和YOLOv6,YOLOv8選擇将上采樣之前的1×1卷積去除了,将Backbone不同階段輸出的特征直接送入了上采樣操作。

2.4、Head部分都變了什麼呢?

先看一下YOLOv5本身的Head(Coupled-Head):

YOLOv8來啦 | 詳細解讀YOLOv8的改進子產品!YOLOv5官方出品YOLOv8!1、YOLOv5回顧2、YOLOv8核心介紹參考文章

YOLOv5的head結構圖

而YOLOv8則是使用了Decoupled-Head,同時由于使用了DFL 的思想,是以回歸頭的通道數也變成了4*reg_max的形式:

YOLOv8來啦 | 詳細解讀YOLOv8的改進子產品!YOLOv5官方出品YOLOv8!1、YOLOv5回顧2、YOLOv8核心介紹參考文章

YOLOv8的head結構圖

對比一下YOLOv5與YOLOv8的YAML

YOLOv8來啦 | 詳細解讀YOLOv8的改進子產品!YOLOv5官方出品YOLOv8!1、YOLOv5回顧2、YOLOv8核心介紹參考文章

2.5、損失函數

對于YOLOv8,其分類損失為VFL Loss,其回歸損失為CIOU Loss+DFL的形式,這裡Reg_max預設為16。

VFL主要改進是提出了非對稱的權重操作,FL和QFL都是對稱的。而非對稱權重的思想來源于論文PISA,該論文指出首先正負樣本有不平衡問題,即使在正樣本中也存在不等權問題,因為mAP的計算是主正樣本。

YOLOv8來啦 | 詳細解讀YOLOv8的改進子產品!YOLOv5官方出品YOLOv8!1、YOLOv5回顧2、YOLOv8核心介紹參考文章

q是label,正樣本時候q為bbox和gt的IoU,負樣本時候q=0,當為正樣本時候其實沒有采用FL,而是普通的BCE,隻不過多了一個自适應IoU權重,用于突出主樣本。而為負樣本時候就是标準的FL了。可以明顯發現VFL比QFL更加簡單,主要特點是正負樣本非對稱權重、突出正樣本為主樣本。

針對這裡的DFL(Distribution Focal Loss),其主要是将框的位置模組化成一個 general distribution,讓網絡快速的聚焦于和目标位置距離近的位置的分布。

YOLOv8來啦 | 詳細解讀YOLOv8的改進子產品!YOLOv5官方出品YOLOv8!1、YOLOv5回顧2、YOLOv8核心介紹參考文章

DFL 能夠讓網絡更快地聚焦于目标 y 附近的值,增大它們的機率;

DFL的含義是以交叉熵的形式去優化與标簽y最接近的一左一右2個位置的機率,進而讓網絡更快的聚焦到目标位置的鄰近區域的分布;也就是說學出來的分布理論上是在真實浮點坐标的附近,并且以線性插值的模式得到距離左右整數坐标的權重。

2.6、樣本的比對

标簽配置設定是目标檢測非常重要的一環,在YOLOv5的早期版本中使用了MaxIOU作為标簽配置設定方法。然而,在實踐中發現直接使用邊長比也可以達到一阿姨你的效果。而YOLOv8則是抛棄了Anchor-Base方法使用Anchor-Free方法,找到了一個替代邊長比例的比對方法,TaskAligned。

為與NMS搭配,訓練樣例的Anchor配置設定需要滿足以下兩個規則:

  1. 正常對齊的Anchor應當可以預測高分類得分,同時具有精确定位;
  2. 不對齊的Anchor應當具有低分類得分,并在NMS階段被抑制。基于上述兩個目标,TaskAligned設計了一個新的Anchor alignment metric 來在Anchor level 衡量Task-Alignment的水準。并且,Alignment metric 被內建在了 sample 配置設定和 loss function裡來動态的優化每個 Anchor 的預測。

Anchor alignment metric:

分類得分和 IoU表示了這兩個任務的預測效果,是以,TaskAligned使用分類得分和IoU的高階組合來衡量Task-Alignment的程度。使用下列的方式來對每個執行個體計算Anchor-level 的對齊程度:

YOLOv8來啦 | 詳細解讀YOLOv8的改進子產品!YOLOv5官方出品YOLOv8!1、YOLOv5回顧2、YOLOv8核心介紹參考文章

s 和 u 分别為分類得分和 IoU 值,α 和 β 為權重超參。從上邊的公式可以看出來,t 可以同時控制分類得分和IoU 的優化來實作 Task-Alignment,可以引導網絡動态的關注于高品質的Anchor。

Training sample Assignment:

為提升兩個任務的對齊性,TOOD聚焦于Task-Alignment Anchor,采用一種簡單的配置設定規則選擇訓練樣本:對每個執行個體,選擇m個具有最大t值的Anchor作為正樣本,選擇其餘的Anchor作為負樣本。然後,通過損失函數(針對分類與定位的對齊而設計的損失函數)進行訓練。

參考文章

[1].https://github.com/uyolo1314/ultralytics.

[2].https://github.com/meituan/YOLOv6.

[3].https://arxiv.org/abs/2209.02976.

[4].YOLOv5中的SPP/SPPF結構詳解_tt丫的部落格-CSDN部落格_sppf.

[5].YOLOv8來啦 | 詳細解讀YOLOv8的改進子產品!YOLOv5官方出品YOLOv8,必卷! - 知乎.

[6].https://github.com/ultralytics/ultralytics

繼續閱讀