1. 程式人生 > >深度學習(目標檢測)---從RCNN到SSD,這應該是最全的一份目標檢測演算法盤點

深度學習(目標檢測)---從RCNN到SSD,這應該是最全的一份目標檢測演算法盤點

目標檢測是很多計算機視覺任務的基礎,不論我們需要實現影象與文字的互動還是需要識別精細類別,它都提供了可靠的資訊。本文對目標檢測進行了整體回顧,第一部分從RCNN開始介紹基於候選區域的目標檢測器,包括Fast R-CNN、Faster R-CNN 和 FPN等。第二部分則重點討論了包括YOLO、SSD和RetinaNet等在內的單次檢測器,它們都是目前最為優秀的方法。

機器之心之前已經討論過非常多的目標檢測演算法,對計算機視覺感興趣的讀者也可以結合以前的文章加強理解。

  • 深度學習目標檢測模型全面綜述:Faster R-CNN、R-FCN和SSD

  • 從零開始PyTorch專案:YOLO v3目標檢測實現

  • 像玩樂高一樣拆解Faster R-CNN:詳解目標檢測的實現過程

  • 後RCNN時代的物體檢測及例項分割進展

  • 物體檢測演算法全概述:從傳統檢測方法到深度神經網路框架

基於候選區域的目標檢測器

滑動視窗檢測器

自從 AlexNet 獲得 ILSVRC 2012 挑戰賽冠軍後,用 CNN 進行分類成為主流。一種用於目標檢測的暴力方法是從左到右、從上到下滑動視窗,利用分類識別目標。為了在不同觀察距離處檢測不同的目標型別,我們使用不同大小和寬高比的視窗。

從RCNN到SSD,這應該是最全的一份目標檢測演算法盤點

滑動視窗(從右到左,從上到下)

我們根據滑動視窗從影象中剪下影象塊。由於很多分類器只取固定大小的影象,因此這些影象塊是經過變形轉換的。但是,這不影響分類準確率,因為分類器可以處理變形後的影象。

從RCNN到SSD,這應該是最全的一份目標檢測演算法盤點

將影象變形轉換成固定大小的影象

變形影象塊被輸入 CNN 分類器中,提取出 4096 個特徵。之後,我們使用 SVM 分類器識別類別和該邊界框的另一個線性迴歸器。

從RCNN到SSD,這應該是最全的一份目標檢測演算法盤點

滑動視窗檢測器的系統工作流程圖。

下面是虛擬碼。我們建立很多視窗來檢測不同位置的不同目標。要提升效能,一個顯而易見的辦法就是減少視窗數量。

for window in windows patch = get_patch(image, window) 
results = detector(patch)

選擇性搜尋

我們不使用暴力方法,而是用候選區域方法(region proposal method)建立目標檢測的感興趣區域(ROI)。在選擇性搜尋(selective search,SS)中,我們首先將每個畫素作為一組。然後,計算每一組的紋理,並將兩個最接近的組結合起來。但是為了避免單個區域吞噬其他區域,我們首先對較小的組進行分組。我們繼續合併區域,直到所有區域都結合在一起。下圖第一行展示瞭如何使區域增長,第二行中的藍色矩形代表合併過程中所有可能的 ROI。

從RCNN到SSD,這應該是最全的一份目標檢測演算法盤點

圖源:van de Sande et al. ICCV'11

R-CNN

R-CNN 利用候選區域方法建立了約 2000 個 ROI。這些區域被轉換為固定大小的影象,並分別饋送到卷積神經網路中。該網路架構後面會跟幾個全連線層,以實現目標分類並提煉邊界框。

從RCNN到SSD,這應該是最全的一份目標檢測演算法盤點

使用候選區域、CNN、仿射層來定位目標。

以下是 R-CNN 整個系統的流程圖:

從RCNN到SSD,這應該是最全的一份目標檢測演算法盤點

通過使用更少且更高質量的 ROI,R-CNN 要比滑動視窗方法更快速、更準確。

ROIs = region_proposal(image)for ROI in ROIs 
patch = get_patch(image, ROI) 
results = detector(patch)

邊界框迴歸器

候選區域方法有非常高的計算複雜度。為了加速這個過程,我們通常會使用計算量較少的候選區域選擇方法構建 ROI,並在後面使用線性迴歸器(使用全連線層)進一步提煉邊界框。

從RCNN到SSD,這應該是最全的一份目標檢測演算法盤點

使用迴歸方法將藍色的原始邊界框提煉為紅色的。

Fast R-CNN

R-CNN 需要非常多的候選區域以提升準確度,但其實有很多區域是彼此重疊的,因此 R-CNN 的訓練和推斷速度非常慢。如果我們有 2000 個候選區域,且每一個都需要獨立地饋送到 CNN 中,那麼對於不同的 ROI,我們需要重複提取 2000 次特徵。

此外,CNN 中的特徵圖以一種密集的方式表徵空間特徵,那麼我們能直接使用特徵圖代替原圖來檢測目標嗎?

從RCNN到SSD,這應該是最全的一份目標檢測演算法盤點

從RCNN到SSD,這應該是最全的一份目標檢測演算法盤點

直接利用特徵圖計算 ROI。

Fast R-CNN 使用特徵提取器(CNN)先提取整個影象的特徵,而不是從頭開始對每個影象塊提取多次。然後,我們可以將建立候選區域的方法直接應用到提取到的特徵圖上。例如,Fast R-CNN 選擇了 VGG16 中的卷積層 conv5 來生成 ROI,這些關注區域隨後會結合對應的特徵圖以裁剪為特徵圖塊,並用於目標檢測任務中。我們使用 ROI 池化將特徵圖塊轉換為固定的大小,並饋送到全連線層進行分類和定位。因為 Fast-RCNN 不會重複提取特徵,因此它能顯著地減少處理時間。

從RCNN到SSD,這應該是最全的一份目標檢測演算法盤點

將候選區域直接應用於特徵圖,並使用 ROI 池化將其轉化為固定大小的特徵圖塊。

以下是 Fast R-CNN 的流程圖:

從RCNN到SSD,這應該是最全的一份目標檢測演算法盤點

在下面的虛擬碼中,計算量巨大的特徵提取過程從 For 迴圈中移出來了,因此速度得到顯著提升。Fast R-CNN 的訓練速度是 R-CNN 的 10 倍,推斷速度是後者的 150 倍。

feature_maps = process(image)
ROIs = region_proposal(feature_maps)
for ROI in ROIs 
patch = roi_pooling(feature_maps, ROI) 
results = detector2(patch)

Fast R-CNN 最重要的一點就是包含特徵提取器、分類器和邊界框迴歸器在內的整個網路能通過多工損失函式進行端到端的訓練,這種多工損失即結合了分類損失和定位損失的方法,大大提升了模型準確度。

ROI 池化

因為 Fast R-CNN 使用全連線層,所以我們應用 ROI 池化將不同大小的 ROI 轉換為固定大小。

為簡潔起見,我們先將 8×8 特徵圖轉換為預定義的 2×2 大小。

  • 下圖左上角:特徵圖。

  • 右上角:將 ROI(藍色區域)與特徵圖重疊。

  • 左下角:將 ROI 拆分為目標維度。例如,對於 2×2 目標,我們將 ROI 分割為 4 個大小相似或相等的部分。

  • 右下角:找到每個部分的最大值,得到變換後的特徵圖。

從RCNN到SSD,這應該是最全的一份目標檢測演算法盤點

輸入特徵圖(左上),輸出特徵圖(右下),ROI (右上,藍色框)。

按上述步驟得到一個 2×2 的特徵圖塊,可以饋送至分類器和邊界框迴歸器中。

Faster R-CNN

Fast R-CNN 依賴於外部候選區域方法,如選擇性搜尋。但這些演算法在 CPU 上執行且速度很慢。在測試中,Fast R-CNN 需要 2.3 秒來進行預測,其中 2 秒用於生成 2000 個 ROI。

feature_maps = process(image)
ROIs = region_proposal(feature_maps) # Expensive!
for ROI in ROIs 
patch = roi_pooling(feature_maps, ROI) 
results = detector2(patch)

Faster R-CNN 採用與 Fast R-CNN 相同的設計,只是它用內部深層網路代替了候選區域方法。新的候選區域網路(RPN)在生成 ROI 時效率更高,並且以每幅影象 10 毫秒的速度執行。

從RCNN到SSD,這應該是最全的一份目標檢測演算法盤點

Faster R-CNN 的流程圖與 Fast R-CNN 相同。

從RCNN到SSD,這應該是最全的一份目標檢測演算法盤點

外部候選區域方法代替了內部深層網路。

候選區域網路

候選區域網路(RPN)將第一個卷積網路的輸出特徵圖作為輸入。它在特徵圖上滑動一個 3×3 的卷積核,以使用卷積網路(如下所示的 ZF 網路)構建與類別無關的候選區域。其他深度網路(如 VGG 或 ResNet)可用於更全面的特徵提取,但這需要以速度為代價。ZF 網路最後會輸出 256 個值,它們將饋送到兩個獨立的全連線層,以預測邊界框和兩個 objectness 分數,這兩個 objectness 分數度量了邊界框是否包含目標。我們其實可以使用迴歸器計算單個 objectness 分數,但為簡潔起見,Faster R-CNN 使用只有兩個類別的分類器:即帶有目標的類別和不帶有目標的類別。

從RCNN到SSD,這應該是最全的一份目標檢測演算法盤點

對於特徵圖中的每一個位置,RPN 會做 k 次預測。因此,RPN 將輸出 4×k 個座標和每個位置上 2×k 個得分。下圖展示了 8×8 的特徵圖,且有一個 3×3 的卷積核執行運算,它最後輸出 8×8×3 個 ROI(其中 k=3)。下圖(右)展示了單個位置的 3 個候選區域。

從RCNN到SSD,這應該是最全的一份目標檢測演算法盤點

此處有 3 種猜想,稍後我們將予以完善。由於只需要一個正確猜想,因此我們最初的猜想最好涵蓋不同的形狀和大小。因此,Faster R-CNN 不會建立隨機邊界框。相反,它會預測一些與左上角名為「錨點」的參考框相關的偏移量(如x、y)。我們限制這些偏移量的值,因此我們的猜想仍然類似於錨點。

從RCNN到SSD,這應該是最全的一份目標檢測演算法盤點

要對每個位置進行 k 個預測,我們需要以每個位置為中心的 k 個錨點。每個預測與特定錨點相關聯,但不同位置共享相同形狀的錨點。

從RCNN到SSD,這應該是最全的一份目標檢測演算法盤點

這些錨點是精心挑選的,因此它們是多樣的,且覆蓋具有不同比例和寬高比的現實目標。這使得我們可以以更好的猜想來指導初始訓練,並允許每個預測專門用於特定的形狀。該策略使早期訓練更加穩定和簡便。

從RCNN到SSD,這應該是最全的一份目標檢測演算法盤點

Faster R-CNN 使用更多的錨點。它部署 9 個錨點框:3 個不同寬高比的 3 個不同大小的錨點框。每一個位置使用 9 個錨點,每個位置會生成 2×9 個 objectness 分數和 4×9 個座標。

從RCNN到SSD,這應該是最全的一份目標檢測演算法盤點

圖源:https://arxiv.org/pdf/1506.01497.pdf

R-CNN 方法的效能

如下圖所示,Faster R-CNN 的速度要快得多。

從RCNN到SSD,這應該是最全的一份目標檢測演算法盤點

基於區域的全卷積神經網路(R-FCN)

假設我們只有一個特徵圖用來檢測右眼。那麼我們可以使用它定位人臉嗎?應該可以。因為右眼應該在人臉影象的左上角,所以我們可以利用這一點定位整個人臉。

從RCNN到SSD,這應該是最全的一份目標檢測演算法盤點

如果我們還有其他用來檢測左眼、鼻子或嘴巴的特徵圖,那麼我們可以將檢測結果結合起來,更好地定位人臉。

現在我們回顧一下所有問題。在 Faster R-CNN 中,檢測器使用了多個全連線層進行預測。如果有 2000 個 ROI,那麼成本非常高。

feature_maps = process(image)
ROIs = region_proposal(feature_maps)
for ROI in ROIs 
patch = roi_pooling(feature_maps, ROI) 
class_scores, box = detector(patch) # Expensive! 
class_probabilities = softmax(class_scores)

R-FCN 通過減少每個 ROI 所需的工作量實現加速。上面基於區域的特徵圖與 ROI 是獨立的,可以在每個 ROI 之外單獨計算。剩下的工作就比較簡單了,因此 R-FCN 的速度比 Faster R-CNN 快。

feature_maps = process(image)
ROIs = region_proposal(feature_maps) 
score_maps = compute_score_map(feature_maps)
for ROI in ROIs V = region_roi_pool(score_maps, ROI) 
class_scores, box = average(V) # Much simpler! 
class_probabilities = softmax(class_scores)

現在我們來看一下 5 × 5 的特徵圖 M,內部包含一個藍色方塊。我們將方塊平均分成 3 × 3 個區域。現在,我們在 M 中建立了一個新的特徵圖,來檢測方塊的左上角(TL)。這個新的特徵圖如下圖(右)所示。只有黃色的網格單元 [2, 2] 處於啟用狀態。

從RCNN到SSD,這應該是最全的一份目標檢測演算法盤點

在左側建立一個新的特徵圖,用於檢測目標的左上角。

我們將方塊分成 9 個部分,由此建立了 9 個特徵圖,每個用來檢測對應的目標區域。這些特徵圖叫作位置敏感得分圖(position-sensitive score map),因為每個圖檢測目標的子區域(計算其得分)。

從RCNN到SSD,這應該是最全的一份目標檢測演算法盤點

生成 9 個得分圖

下圖中紅色虛線矩形是建議的 ROI。我們將其分割成 3 × 3 個區域,並詢問每個區域包含目標對應部分的概率是多少。例如,左上角 ROI 區域包含左眼的概率。我們將結果儲存成 3 × 3 vote 陣列,如下圖(右)所示。例如,vote_array[0][0] 包含左上角區域是否包含目標對應部分的得分。

從RCNN到SSD,這應該是最全的一份目標檢測演算法盤點

將 ROI 應用到特徵圖上,輸出一個 3 x 3 陣列。

將得分圖和 ROI 對映到 vote 陣列的過程叫作位置敏感 ROI 池化(position-sensitive ROI-pool)。該過程與前面討論過的 ROI 池化非常接近。

從RCNN到SSD,這應該是最全的一份目標檢測演算法盤點

將 ROI 的一部分疊加到對應的得分圖上,計算 V[i][j]。

在計算出位置敏感 ROI 池化的所有值後,類別得分是其所有元素得分的平均值。

從RCNN到SSD,這應該是最全的一份目標檢測演算法盤點

ROI 池化

假如我們有 C 個類別要檢測。我們將其擴充套件為 C + 1 個類別,這樣就為背景(非目標)增加了一個新的類別。每個類別有 3 × 3 個得分圖,因此一共有 (C+1) × 3 × 3 個得分圖。使用每個類別的得分圖可以預測出該類別的類別得分。然後我們對這些得分應用 softmax 函式,計算出每個類別的概率。

以下是資料流圖,在我們的案例中,k=3。

從RCNN到SSD,這應該是最全的一份目標檢測演算法盤點

總結

我們首先了解了基礎的滑動視窗演算法:

for window in windows 
patch = get_patch(image, window) 
results = detector(patch)

然後嘗試減少視窗數量,儘可能減少 for 迴圈中的工作量。

ROIs = region_proposal(image)
for ROI in ROIs 
patch = get_patch(image, ROI) 
results = detector(patch)

單次目標檢測器

第二部分,我們將對單次目標檢測器(包括 SSD、YOLO、YOLOv2、YOLOv3)進行綜述。我們將分析 FPN 以理解多尺度特徵圖如何提高準確率,特別是小目標的檢測,其在單次檢測器中的檢測效果通常很差。然後我們將分析 Focal loss 和 RetinaNet,看看它們是如何解決訓練過程中的類別不平衡問題的。

單次檢測器

Faster R-CNN 中,在分類器之後有一個專用的候選區域網路。

從RCNN到SSD,這應該是最全的一份目標檢測演算法盤點

Faster R-CNN 工作流

基於區域的檢測器是很準確的,但需要付出代價。Faster R-CNN 在 PASCAL VOC 2007 測試集上每秒處理 7 幀的影象(7 FPS)。和 R-FCN 類似,研究者通過減少每個 ROI 的工作量來精簡流程。

feature_maps = process(image)
ROIs = region_proposal(feature_maps)
for ROI in ROIs 
patch = roi_align(feature_maps, ROI) 
results = detector2(patch) # Reduce the amount of work here!

作為替代,我們是否需要一個分離的候選區域步驟?我們可以直接在一個步驟內得到邊界框和類別嗎?

feature_maps = process(image)
results = detector3(feature_maps) 
# No more separate step for ROIs

讓我們再看一下滑動視窗檢測器。我們可以通過在特徵圖上滑動視窗來檢測目標。對於不同的目標型別,我們使用不同的視窗型別。以前的滑動視窗方法的致命錯誤在於使用視窗作為最終的邊界框,這就需要非常多的形狀來覆蓋大部分目標。更有效的方法是將視窗當做初始猜想,這樣我們就得到了從當前滑動視窗同時預測類別和邊界框的檢測器。

從RCNN到SSD,這應該是最全的一份目標檢測演算法盤點

基於滑動視窗進行預測

這個概念和 Faster R-CNN 中的錨點很相似。然而,單次檢測器會同時預測邊界框和類別。例如,我們有一個 8 × 8 特徵圖,並在每個位置做出 k 個預測,即總共有 8 × 8 × k 個預測結果。

從RCNN到SSD,這應該是最全的一份目標檢測演算法盤點

64 個位置

在每個位置,我們有 k 個錨點(錨點是固定的初始邊界框猜想),一個錨點對應一個特定位置。我們使用相同的 錨點形狀仔細地選擇錨點和每個位置。

從RCNN到SSD,這應該是最全的一份目標檢測演算法盤點

使用 4 個錨點在每個位置做出 4 個預測。

以下是 4 個錨點(綠色)和 4 個對應預測(藍色),每個預測對應一個特定錨點。

從RCNN到SSD,這應該是最全的一份目標檢測演算法盤點

4 個預測,每個預測對應一個錨點。

在 Faster R-CNN 中,我們使用卷積核來做 5 個引數的預測:4 個引數對應某個錨點的預測邊框,1 個引數對應 objectness 置信度得分。因此 3× 3× D × 5 卷積核將特徵圖從 8 × 8 × D 轉換為 8 × 8 × 5。

從RCNN到SSD,這應該是最全的一份目標檢測演算法盤點

使用 3x3 卷積核計算預測。

在單次檢測器中,卷積核還預測 C 個類別概率以執行分類(每個概率對應一個類別)。因此我們應用一個 3× 3× D × 25 卷積核將特徵圖從 8 × 8 × D 轉換為 8 × 8 × 25(C=20)。

從RCNN到SSD,這應該是最全的一份目標檢測演算法盤點

每個位置做出 k 個預測,每個預測有 25 個引數。

單次檢測器通常需要在準確率和實時處理速度之間進行權衡。它們在檢測太近距離或太小的目標時容易出現問題。在下圖中,左下角有 9 個聖誕老人,但某個單次檢測器只檢測出了 5 個。

SSD

SSD 是使用 VGG19 網路作為特徵提取器(和 Faster R-CNN 中使用的 CNN 一樣)的單次檢測器。我們在該網路之後新增自定義卷積層(藍色),並使用卷積核(綠色)執行預測。

從RCNN到SSD,這應該是最全的一份目標檢測演算法盤點

同時對類別和位置執行單次預測。

然而,卷積層降低了空間維度和解析度。因此上述模型僅可以檢測較大的目標。為了解決該問題,我們從多個特徵圖上執行獨立的目標檢測。

從RCNN到SSD,這應該是最全的一份目標檢測演算法盤點

使用多尺度特徵圖用於檢測。