目標檢測之YOLO系列

freshZero_發表於2018-12-18

YOLO

整體結構

在這裡插入圖片描述
原始碼

def _build_net(self):
        """build the network"""
        if self.verbose:
            print("Start to build the network ...")
        self.images = tf.placeholder(tf.float32, [None, 448, 448, 3])
        net = self._conv_layer(self.images, 1, 64, 7, 2)
        net = self._maxpool_layer(net, 1, 2, 2)
        net = self._conv_layer(net, 2, 192, 3, 1)
        net = self._maxpool_layer(net, 2, 2, 2)
        net = self._conv_layer(net, 3, 128, 1, 1)
        net = self._conv_layer(net, 4, 256, 3, 1)
        net = self._conv_layer(net, 5, 256, 1, 1)
        net = self._conv_layer(net, 6, 512, 3, 1)
        net = self._maxpool_layer(net, 6, 2, 2)
        net = self._conv_layer(net, 7, 256, 1, 1)
        net = self._conv_layer(net, 8, 512, 3, 1)
        net = self._conv_layer(net, 9, 256, 1, 1)
        net = self._conv_layer(net, 10, 512, 3, 1)
        net = self._conv_layer(net, 11, 256, 1, 1)
        net = self._conv_layer(net, 12, 512, 3, 1)
        net = self._conv_layer(net, 13, 256, 1, 1)
        net = self._conv_layer(net, 14, 512, 3, 1)
        net = self._conv_layer(net, 15, 512, 1, 1)
        net = self._conv_layer(net, 16, 1024, 3, 1)
        net = self._maxpool_layer(net, 16, 2, 2)
        net = self._conv_layer(net, 17, 512, 1, 1)
        net = self._conv_layer(net, 18, 1024, 3, 1)
        net = self._conv_layer(net, 19, 512, 1, 1)
        net = self._conv_layer(net, 20, 1024, 3, 1)
        net = self._conv_layer(net, 21, 1024, 3, 1)
        net = self._conv_layer(net, 22, 1024, 3, 2)
        net = self._conv_layer(net, 23, 1024, 3, 1)
        net = self._conv_layer(net, 24, 1024, 3, 1)
        net = self._flatten(net)
        net = self._fc_layer(net, 25, 512, activation=leak_relu)
        net = self._fc_layer(net, 26, 4096, activation=leak_relu)
        net = self._fc_layer(net, 27, self.S*self.S*(self.C+5*self.B))
        self.predicts = net

YOLO的核心思想

  • YOLO全稱是you only look once。之前的目標檢測演算法,R-CNN等是將目標檢測問題轉換為region proposal+ object classification的問題來解決。相對於之前的檢測演算法的一個關鍵的思想改進是,將目標檢測問題視為一個迴歸問題,通過一個網路實現訓練和預測,極大的提升了檢測速度。

實現方法

  • 直接將原始圖片分割成互不重合的小方塊,然後通過卷積最後生產這樣大小的特徵圖,可以認為特徵圖的每個元素也是對應原始圖片的一個小方塊,然後用每個元素來可以預測那些中心點在該小方格內的目標,這就是Yolo演算法的樸素思想。
    在這裡插入圖片描述

損失函式

在這裡插入圖片描述

訓練

  • 先將網路中前20層卷積層後面加上一個平均池化層和一個全連線層在ImageNet上面1000個類別的分類資料集上進行預訓練。使用的是DarkNet framework
  • 將模型轉換為檢測模型,在之後加入四層卷積層和兩層全連線層,作為最終的檢測模型。為了提高檢測效果,將輸入圖片從224x224大小增加為448x448,在預訓練的時候採用的輸入圖片大小是224x224
  • 使用leaky Relu,作為啟用函式

預測

  • 設定置信度閾值,過濾掉置信度小於該閾值的box,保留置信度較高的預測框
  • 對留下來的預測框採用NMS演算法,得到最終的預測結果。這裡有兩種思路,一種是區分類別分別採用NMS演算法;另一種是對所有預測框同等對待。區別類別採用NMS演算法理論上來說效果更好,但是很多實現都是對所有預測框同等對待,實際效果並差別不大,主要原因可能是在同一個位置出現不同類別的概率較小。

優缺點

  • 訓練和預測都是end-to-end,所以YOLO演算法比較簡潔且速度快
  • YOLO對整個圖片做卷積,在目標檢測時有更大的視野,不容易對背景誤判
  • 模型泛化能力強,比如可以對藝術畫作進行檢測
  • 雖然每個格子可以預測 B 個 bounding box,但是最終只選擇只選擇 IOU 最高的 bounding box 作為物體檢測輸出,即每個格子最多隻預測出一個物體。當物體佔畫面比例較小,如影象中包含畜群或鳥群時,每個格子包含多個物體,但卻只能檢測出其中一個。這是 YOLO 方法的一個缺陷。這方面可以看SSD,採用的多尺度單元格。
  • 在寬高比方面泛化率底,無法定位不尋常比例的物體,定位不如proposal+classification方法準確

YOLO-V2

論文 https://arxiv.org/pdf/1612.08242.pdf
主要包含內容YOLOv2相對於YOLOv1的改進,以及在YOLOv2基礎上YOLO9000的訓練過程,wordtree的構建。

模型結構

在這裡插入圖片描述

程式碼

def darknet(images, n_last_channels=425):
    """Darknet19 for YOLOv2"""
    net = conv2d(images, 32, 3, 1, name="conv1")
    net = maxpool(net, name="pool1")
    net = conv2d(net, 64, 3, 1, name="conv2")
    net = maxpool(net, name="pool2")
    net = conv2d(net, 128, 3, 1, name="conv3_1")
    net = conv2d(net, 64, 1, name="conv3_2")
    net = conv2d(net, 128, 3, 1, name="conv3_3")
    net = maxpool(net, name="pool3")
    net = conv2d(net, 256, 3, 1, name="conv4_1")
    net = conv2d(net, 128, 1, name="conv4_2")
    net = conv2d(net, 256, 3, 1, name="conv4_3")
    net = maxpool(net, name="pool4")
    net = conv2d(net, 512, 3, 1, name="conv5_1")
    net = conv2d(net, 256, 1, name="conv5_2")
    net = conv2d(net, 512, 3, 1, name="conv5_3")
    net = conv2d(net, 256, 1, name="conv5_4")
    net = conv2d(net, 512, 3, 1, name="conv5_5")
    shortcut = net
    net = maxpool(net, name="pool5")
    net = conv2d(net, 1024, 3, 1, name="conv6_1")
    net = conv2d(net, 512, 1, name="conv6_2")
    net = conv2d(net, 1024, 3, 1, name="conv6_3")
    net = conv2d(net, 512, 1, name="conv6_4")
    net = conv2d(net, 1024, 3, 1, name="conv6_5")
    # ---------
    net = conv2d(net, 1024, 3, 1, name="conv7_1")
    net = conv2d(net, 1024, 3, 1, name="conv7_2")
    # shortcut
    shortcut = conv2d(shortcut, 64, 1, name="conv_shortcut")
    shortcut = reorg(shortcut, 2)
    net = tf.concat([shortcut, net], axis=-1)
    net = conv2d(net, 1024, 3, 1, name="conv8")
    # detection layer
    net = conv2d(net, n_last_channels, 1, batch_normalize=0,
                 activation=None, use_bias=True, name="conv_dec")
    return net

改進策略

YOLO模型雖然速度較快,但是相對於R-CNN,在檢測定位精度上面較差。YOLOv2主要是針對檢測精度上面進行了一系列的改進。
在這裡插入圖片描述

訓練

  • 在ImageNet上面預訓練模型,先採用的輸入時224x224訓練160個epochs;然後調整輸入為448x448,finetune,10個epochs;更改Darknet-19的分類模型為檢測模型,在檢測資料集上進行finetune網路。
  • 為了保證最終得到的特徵圖維度大小為奇數,在檢測模型中採用的輸入圖片的大小是416x416,採用總步長為32,則最終的特徵圖大小為13x13,這樣保證了特徵圖正好只有一箇中心位置。
  • loss函式
    在這裡插入圖片描述

YOLO9000

聯合ImageNet的龐大的分類資料集,以及coco的檢測資料集,訓練得到可以檢測9000+類別的YOLO9000模型。這是這篇文章中最大的貢獻。主要採用的方法是採用聯合兩個資料集構建wordtree的方法
在COCO和ImageNet資料集上進行聯合訓練遇到的第一問題是兩者的類別並不是完全互斥的,比如"Norfolk terrier"明顯屬於"dog",所以作者提出了一種層級分類方法(Hierarchical classification),主要思路是根據各個類別之間的從屬關係(根據WordNet)建立一種樹結構WordTree,結合COCO和ImageNet建立的WordTree如下圖所示:
在這裡插入圖片描述

在這裡插入圖片描述
在訓練時,如果是檢測樣本,按照YOLOv2的loss計算誤差,而對於分類樣本,只計算分類誤差。在預測時,YOLOv2給出的置信度就是 Pr(physical \space object) ,同時會給出邊界框位置以及一個樹狀概率圖。在這個概率圖中找到概率最高的路徑,當達到某一個閾值時停止,就用當前節點表示預測的類別。通過聯合訓練策略,YOLO9000可以快速檢測出超過9000個類別的物體,總體mAP值為19,7%。

YOLOv3

改進點

  • 引入殘差模型,特徵圖提取模型深度從19增加到53
  • 引入特徵金字塔網路結構,實現多尺度檢測,YOLOv3採用3個尺度特徵圖:13x13,26x26,52x52

YOLOv3結構

在這裡插入圖片描述

與其他模型對比結果

在這裡插入圖片描述

在這裡插入圖片描述

參考:
https://arxiv.org/pdf/1506.02640.pdf
https://arxiv.org/pdf/1612.08242.pdf
https://pjreddie.com/media/files/papers/YOLOv3.pdf
https://zhuanlan.zhihu.com/p/32525231
https://blog.csdn.net/guleileo/article/details/80581858
https://zhuanlan.zhihu.com/p/35325884

相關文章