Sparse R-CNN: 徹底Sparse!青出於藍的R-CNN家族新晉小生,稀疏的目標候選+稀疏的交互機制 = E2E
前言
這段時間的paper不是E2E(End-to-End)就是Transformer,什麼都拿Transformer往上套,然後個個都聲稱自己E2E,看得CW都有點「審美疲勞」..
吾以為,Transformer並非關鍵,更不是E2E的必需所在,從NLP拓展到CV來應用,可以表揚下Transformer從單純的「器」發展成為一種「術」,但是否成為「道」還請拭目以待。至於是否E2E,有個很直觀的關鍵點在於是否幹掉了NMS,而追溯下導致NMS的原因,無非還是Label-Assignment!因為當下流行的大多數目標檢測方法都是one-to-many的標籤分配策略,這是導致NMS的最直接原因之一。
在近期眾多E2E系列的目標檢測方法中,CW之所以選中了 Sparse R-CNN 這個R-CNN家族的新晉小生,是因為它並未用到Transformer,同時又實現了E2E,原因也正如上面所說:它採用的是one-to-one的Label-Assignment。
有點搞笑的是,雖說它隸屬於R-CNN家族,但從思想以及方法(甚至代碼實現)上來看,其更像是 DETR: End-to-End Object Detection with Transformers 的小弟,為何?因為它採用了DETR的optimal bipartite matching(二分匹配)的標籤分配策略和set prediction形式,同時又借鑒了DETR中learnable object query的思想,從而無需設計密集的目標候選(如anchor)。當然,它的得意之處在於摒棄了DETR中讓object query和全局特徵圖(密集)交互(即每個object query都和特徵圖的每個位置交互計算,這本質上屬於dense)的Transformer attention機制,而是提出了一種稀疏(sparse)的交互形式。
基於上述,Sparse R-CNN之所以自稱’sparse’在於兩方面:sparse candidates & sparse feature interaction。
本文目錄
研究動機與主要貢獻
整體架構與算法pipeline
具體方法
i). Learnable Proposal Boxes: 稀疏的目標候選
ii). Learnable Proposal Features: 更給力地表徵物體特徵
iii). Dynamic Instance Interactive Head: 稀疏的特徵交互
iv). Cascade R-CNN Head: 級聯大法好,Coarse-to-Fine
總結與思考
研究動機與主要貢獻
在正式講解Sparse R-CNN的方法前,先來「吹吹水」,了解下作者的研究動機是什麼,這其實是很重要的一part。只有發現了以往方法的問題所在,你才能(創造性地去)解決問題!可惜的是,從小到大,學校往往只是不問緣由地教給我們解決問題的手段,卻根本沒有教我們如何發現問題。因此,在學習一種新方法的同時,還需要了解這種方法誕生的背景,再倒回來評估新方法的合理性,甚至提出自己的猜想並且進行實驗,最終再對這個過程總結歸納。CW認為,不斷重複這樣的學習方式,能夠有效地培養我們的洞察力和創造力。
作者很優秀,他發現當前目標檢測的主流方法往往存在dense屬性:anchor-boxes(anchor-based系列)、 reference-points(anchor-free系列)、dense RoIs(2-stage系列),這伴隨着諸如以下的「麻煩事」:
1. prior candidates(如anchors)的設計;
2. one(gt)-to-many(positive)的標籤分配策略;
3. nms後處理(由於第2點)
於是,作者很自然地想到能不能設計一種sparse的框架。幸運的是,DETR的出現給出了一種啟發:candidates可以是一組sparse的learnable object queries,標籤分配策略是one-to-one的optimal bipartite matching,這樣就無需nms。然而,如前文所述,DETR在特徵交互計算時本質上也是使用了dense的方法。抓住這點,作者覺得可以發paper了(原諒我說得那麼露骨哈哈哈)!除了sparse candidates,他還想實現sparse feature interaction,結合自身天賦並且通過努力,最終提出了Sparse R-CNN。作者也自豪地認為,他延續了目標檢測方法的「生態史」:dense -> dense-to-sparse -> (thoroughly)sparse:
Sparse R-CNN的創新點有以下:
1. 使用可學習的proposal boxes充當RoI角色,從而無需RPN;
2. 引入維度更高(相比proposal boxes)的可學習proposal features,用於彌補粗糙的proposal boxes提取出來的RoI features不足以表徵豐富的物體特徵(如姿態和位置等)的缺點;
3. 改進了原生的r-cnn head,設計出dynamic instance interactive head,主要用於對RoI feautures與proposal features執行一對一的稀疏交互(而非DETR那種全局密集交互),其中前者相當於Key、後者相當於Query的角色
整體來看,其特點和貢獻也多多:沒有anchors和reference points、沒有rpn、無需正負樣本採樣、無需nms後處理、效果比他家族老大Faster R-CNN好、收斂速度遠遠快於他老哥DETR。在標準的COCO benchmark上使用ResNet-50 FPN單模型在標準3 x training schedule的情況下達到了44.5 AP 和 22 FPS。
整體架構與算法pipeline
吹了一波,是時候講下Sparse R-CNN是怎麼操作的了。先概述下整體的架構設計,並且把算法pipeline過一遍,然後再具體到各方面去剖析吧。
既然Sparse R-CNN隸屬於R-CNN家族,那麼它的網絡設計原型就和R-CNN系列相似:第一階段先得到RoI(只不過這裡不需要RPN,而是直接設置一個可學習的嵌入向量,同時也沒有對RoI做前背景的二分類和採樣);第二階段結合backbone提取的feature map通過池化得到統一大小的RoI特徵圖,輸入檢測頭部做最終的分類和回歸預測。
以上是它在R-CNN家族中繼承的性質,至於其它方面,概括地來說,主要以下幾點:
(1). Backbone是ResNet,Neck使用FPN;
(2). Head使用一組級聯的Dynamic Instance Interactive Head,這個頭部是對原R-CNN Head的改進。在級聯組中,上一個head的輸出特徵和預測框分別作為下一個head的Proposal Features和Proposal Boxes。另外,Proposal Features在與RoI Features交互之前會先經過Multi-Head-Self-Attention(此處和Transformer的Decoder十分類似,在交互之後還會經過FFN,即整個過程是:self-attention+cross-attention+ffn);
(3). 訓練的損失函數是基於optimal bipartite matching的set prediction loss,沿用了DETR那一套,只不過在代碼實現中,針對使用focal loss的情況做了改動。
整體的框架設計基本就這樣,現在來過下算法的pipeline:
i. 設置N個可學習候選框Proposal Boxes用於提供RoI坐標(可使用Pytorch的nn.Embedding(N, 4)實現);
ii. 設置N個可學習的候選實例特徵Proposal Features來表徵更豐富的物體信息(特徵),例如姿態和形狀等等(可使用Pytorch的nn.Embedding(N, hidden_dim)實現);
iii. 將任意大小的圖片輸入CNN,得到輸出特徵圖(包含多尺度的FPN特徵);
iv. 通過RoIPooling(Align)將每個Proposal Boxes池化到統一大小的RoI Features;
v. 將RoI Features與Proposal Features進行一對一交互,從而增強前景特徵;
vi. 增強後的特徵Object Features作為表徵各個目標對象的特徵,經過全連接層得到固定大小的特徵向量,輸出N個無序集合,每個集合中包括預測類別和預測框;
vii. 採用Casecase R-CNN的級聯思想,不斷對預測框進行refine。其中,前一階段的預測框和Object Features分別作為下一階段的Proposal Boxes和Proposal Features
訓練期間,對每個級聯階段的輸出信息都使用匈牙利雙邊匹配計算分類及回歸loss來進行訓練(深監督)。
具體方法
pipeline過完,現在來具體講講裏面的主要部分。當然,這次同樣按照CW一貫的風格,會結合核心代碼來解析。
Learnable Proposal Boxes: 稀疏的目標候選
可學習Proposal Boxes維度是(N,4),其中N是預設的超參,代表每張圖片最多可檢測出多少個物體,也就是目標候選數量;4對應的是候選框坐標信息(cxcywh or xyxy)。這些設置作者經過了實驗測試:
考慮到性能與訓練時間,最終作者選擇將N設置為300。
-center表示將proposal boxes初始化在圖像中心位置,中心坐標為(0.5,0.5),wh全部設置為0.1(圖像尺寸為1,這裡是將wh設置為圖像的0.1倍),即所有框的大小都是原圖的0.01(0.1×0.1)倍,最終1個proposal box表示為(0.5,0.5,0.1,0.1);
-Image表示將proposal boxes初始化為圖像本身,此時size=1,於是表示為(0.5,0.5,1,1);
-Grid表示RoI按照類似anchor一樣密集排列在原圖上,例如[(0,0,0.1,0.1), (0,1,0.1,0.1), …, (32/圖片w,32/圖片h,0.1,0.1)…],和G-CNN中使用的方法相同;
-Random表示中心坐標和寬、高均採用高斯分佈隨機初始化
而Proposal Boxes的初始化方式對性能的影響相對較小,這應該得益於可學習性,使得整體框架也更靈活和魯棒。最終,作者採用了和DETR相同的表示方式:歸一化的cxcywh值,值域是0-1。
需要注意的是,Proposal Boxes是不包括batch信息的,也就是說這個(N,4)矩陣存儲的不是當前一張圖片信息,而是要學習整個數據集相關的RoI統計信息,學到的是訓練集中潛在的目標物體位置的統計分佈,其被視作對圖像中目標物體最可能存在區域的初始猜測。
作者認為,使用RPN來得到RoI是非常「奢侈」的(相當於多加了一個模型做預測),而RoI的主要作用是提供豐富的候選框位置,保證召回率,並不需要十分精確,只要滿足合理的相關分佈即可。因此,作者覺得能得到一個合理的和數據集相關的統計信息就足夠了,最終就採用了這可學習的Proposal Boxes來充當RoI的角色,從而在整體架構中無需RPN。
作者採用了Pytorch的nn.Embedding進行代碼實現:
也可以用以下這種方式:
self.init_proposal_boxes = nn.Parameter(torch.Tensor(self.num_proposals, 4))
Learnable Proposal Features: 更給力地表徵物體特性
可學習的Proposal Features維度是(N,256),其中N的意義和Proposal Boxes中一致,它和Proposal Boxes是一對一的關係,同時也表徵了整個數據集實例特徵的統計信息。
之所以引入Proposal Features,是因為作者考慮到僅靠4d的Proposal Boxes提供的RoI Features難免過於粗糙,不足以表徵物體深層次的特徵信息(如物體姿態和形狀等),於是有必要額外引入這個高維度(256d)的Proposal Features,目的是希望通過這個可學習的嵌入向量編碼更豐富的實例的特徵。
Proposal Features在這裡類似於DETR中Object Query的角色,Object Query在DETR中是可學習的位置編碼(position encoding),指導Decoder關注全局特徵圖的哪些位置,同時全局特徵圖還要加上位置編碼,否則性能會大幅下降。然而,在Sparse R-CNN中,Proposal Features和Proposal Boxes(對應的RoI Features)一對一進行交互(而非DETR中讓Object Query和全局特徵圖的每個位置進行交互),並且Proposal Boxes本身已包含了在全局特徵圖中的位置信息,Proposal Features則作為Proposal Boxes對應(位置)的物體的豐富特徵,因而在不需要空間位置編碼的同時也能夠實現特徵過濾與增強。
同樣地,對於Proposal Features的代碼實現,作者也採用了Pytorch的nn.Embedding:
Dynamic Instance Interactive Head: 稀疏的特徵交互
該模塊是對R-CNN Head的改進:在RoI池化後插入了動態實例交互(Dynamic Instance Iteractive)模塊,用於將RoI Features和Proposal Features進行一對一交互,目的是實現特徵過濾和增強。
前面說過,Proposal Features的角色相當於DETR的Object Query,由於Object Query是和全局特徵圖(作為Key)交互,和全局特徵圖一樣,RoI Features也提供了位置信息,因而RoI Features也可看作是Key。但是DETR中的交互是為了讓各目標物體關注其在全局特徵圖中的有效特徵位置,而這裡的RoI Features如前面所述已經包含了在全局特徵圖的對應位置信息,這些特徵本身就對應各個局部位置,同時和Proposal Features又是一對一交互,那麼這裡的交互究竟是在做甚?
其實,RoI Features中本身還有「更進一步」的位置信息:RoI Features是池化特徵,通常為統一的7×7大小,這7×7個bin就是進一步的位置信息!
因此,這裡Proposal Features和RoI Features進行交互是為了關注7×7個bin中對前景更有貢獻的那些位置,從而更有利於之後的分類和回歸。
核心思想已明確,現在舉個例子來形象說明下整個交互過程:
1). 暫時不考慮batch對應的維度,假設RoI Features的shape是(300,7,7,256),300是proposals個數,7×7是統一後的池化特徵大小,256是表示每個特徵空間位置的表徵向量維度;Proposal Features的shape是(300,256);
2). 將Proposal Features先經過自注意力模塊,這是為了推理出各物體相互之間的關係(和DETR中一樣);
3). 然後由Proposal Features生成卷積參數:使用全連接層將最後一維由256變為2x64x256,接着切分成shape為(300,256,64)和(300,64,256)兩部分,這也是稱為「動態交互」的原因,因其參數是動態生成的;
4). 接着進行交互:將shape為(300,7×7,256)的RoI Features按序和以上兩部分進行矩陣乘法,輸出的shape是(300,7×7,256),這個結果就隱含了各目標對應的7×7個位置中哪些位置才是應該關心的,對前景更有貢獻的位置將有更高的輸出值。注意,在第一個維度(300)上RoI Features和Proposal Features是一對一進行交互計算的!因此這也是稱為「實例級交互」的原因;
(以上3&4這種交互操作稱為動態卷積,作者是受到 Dynamic filter networks 啟發)
5). 最後,這個結果(先經過全連接層變換維度)還要加(element-wise add)在Proposal Feautures上(並且歸一化),得到過濾和增強後的特徵表示,作為抽象的物體特徵。
仔細品味下以上過程,不覺得和Transformer的Decoder十分相似嗎!?其中2是一樣的,將Query先經過self-attention;3+4實質上就是Query和Key的交互計算,為的是實現特徵過濾和增強,只不過這裡將Multi-Head-Attention替換成動態卷積的方式(同時也沒有Value,因為這裡計算出來的結果本身就是過濾和增強後的特徵表示,而非權重係數,所以不需要將計算結果應用在Value上);5就相當於是Add&Norm。所以說嘛,Sparse R-CNN就像是DETR的小老弟,雖然沒有用Transformer,但套路是一樣的。
作者通過實驗證實了2~4過程對性能帶來的提升:
以上過程的代碼實現如下:
其中N代表batch size,nr_boxes代表預設的目標數量(300),d_model是嵌入維度(256)。至於自注意力層和實例交互則如下:
現在來看看最關鍵的動態卷積是如何實現的:
代碼非常簡單易懂,CW的注釋應該已經足夠說明了。返回的features就是上面pro_features2。
交互之後還會經過一個FFN(前向反饋網絡),真的和DETR那套太像了..
其實連作者也覺得像,於是他還與Transformer對比了一把:
結果證明,它這個動態頭部比較牛逼。
最後,既然它是個頭部,那麼肯定需要進行預測(分類+回歸):
代碼也非常直觀易懂,看注釋就OK。
這一節該說的就這些了,最後我想吐槽下這節最上面那幅圖。從圖上看,每個RoI Features和Proposal Features一對一交互後都會分別送入獨立的頭部進行預測,作者在paper中也是這麼說的:
Each RoI feature is fed into its own exclusive head for object location and classification, where each head is conditioned on specific proposal feature.
但實際是,代碼中並不是這麼寫的!通過上述就可以知道,想要仔細印證的話也可以自己去看看這部分源碼:DynamicHead,如果是CW看錯了,還望反饋給我,並且狠狠抽我一巴,謝謝!
Cascade R-CNN Head: 級聯大法好,Coarse-to-Fine
級聯大法好哇,作者在paper中也是這麼說的:
Iteratively updating the boxes is an intuitive idea to improve its performance.
對於級聯本身,並沒有什麼好說的,就拿上一個頭部的輸出送入到下一頭部再進行預測唄,整個相當於是由粗到細(Coarse-to-Fine)的過程。另外,每個頭部的參數是獨立的。但是,關鍵就在於應該拿上一個頭部輸出的什麼送入到下一個頭部?
理所當然地,我們會想到預測框,但僅僅如此的話,作者發現帶來的性能提升並不明顯:
作者觀察到,一個候選框對應的目標在整個級聯迭代過程中通常是不變的,那麼為何不把上一個頭部輸出的目標特徵也一併送入下一個頭部呢!畢竟這些目標特徵可是編碼了豐富的物體特徵信息(如姿態、形狀和位置等)啊!
這麼想之後,作者也試了一把,果然,飛漲了11.7個點(見上圖中Feature reuse打鉤那行)!
此外還有個細節,需要看代碼才知道:
注意到紅框部分,上一個head的預測框在輸入到下一個head前要取消梯度!這樣的話,就只有第一個head的梯度能夠回傳至proposal boxes,而後面的head只能讓proposal features進行學習。對於這個問題,CW是這麼看的:
你想想,Sparse R-CNN是R-CNN家族的,也就是說它帶有2-stage性質。第一階段會學習RoI,但第二階段RoI已經作為先驗的角色(相當於anchor),也是不再進行學習的。作者在這裡的設置或多或少也有這樣的味道。
這麼看來,Proposal Boxes和RPN的RoI一樣,只需提供一個粗糙的結果即可,後面會有第二階進行精調(這裡是級聯head,不斷學習Proposal Features)。
在github上看到有的人疑惑:即使是第一個head,Proposal Boxes由於經過了RoI Pooling/Align,因此這部分是無法回傳梯度的,那麼到底是怎麼讓Proposal Boxes進行學習的呢?
RoI Pooling/Align確實無法回傳梯度,但是在bbox解碼時會需要Proposal Boxes參與計算啊,這時候就能夠回傳梯度了,Proposal Boxes也是在此獲得學習的機會的(其實Faster R-CNN在第二階段也可以的,只不過其將解碼過程寫在no_grad過程里了,而這裡並沒有)。這部分代碼就是上一節展示的預測部分中的self.apply_deltas()方法,和常規的bbox解碼無異,這裡就不再展示了。
另外,作者還實驗了級聯頭部的數量對最終性能的影響:
根據實驗結果,最終選擇級聯6個頭部。
總結與思考
我們知道,R-CNN系列的2-stage方法通常有更高的精度,但是檢測速度也相對不如1-stage,畢竟其需要前一個階段(RPN)來預測RoI。然而,Sparse R-CNN受到DETR的learnable object query啟發,直接開掛般設置一組可學習的嵌入向量作為RoI,從而幹掉了RPN,也是十分大膽!另外,在最近萬物皆Transformer的形勢下,Sparse R-CNN保持了自己的個性,使用動態卷積的方式來做交互計算,並且是局部而非Transformer般的全局交互(因此也不需要全局空間的位置編碼)!進一步提升了計算效率,也徹底地做到了SPARSE(必須大寫以表揚下~)。這篇paper的思想給R-CNN方法乃至E2E Object Detection領域無疑都是能帶來創造性的啟發。
另外,CW突然想到一個點,如上所述,雖然Sparse R-CNN在沒有加入空間位置編碼的情況下依然能做到特徵過濾和增強,但是不妨猜想下,如果是加入「局部」空間位置編碼呢:即對池化特徵7×7個bin附加對應的位置編碼(這個位置編碼看作是數據集池化特徵中隱含的局部位置的統計分佈),會不會達到更強的特徵過濾和增強效果?如果有興趣,各位哥們兒也可是實驗下,同時也歡迎反饋交流!