Mask-RCNN最詳細(xì)解讀

作者:stone
https://zhuanlan.zhihu.com/p/37998710
本文已授權(quán),未經(jīng)允許,不得二次轉(zhuǎn)載
最近在做一個(gè)目標(biāo)檢測(cè)項(xiàng)目,用到了Mask RCNN。我僅僅用了50張訓(xùn)練照片,訓(xùn)練了1000步之后進(jìn)行測(cè)試,發(fā)現(xiàn)效果好得令人稱奇。就這個(gè)任務(wù),很久之前用yolo v1訓(xùn)練則很難收斂。不過(guò)把它們拿來(lái)比當(dāng)然不公平,但我更想說(shuō)的是,mask RCNN效果真的很好。
所以這篇文章來(lái)詳細(xì)地總結(jié)一下Mask RCNN。
Mask RCNN沿用了Faster RCNN的思想,特征提取采用ResNet-FPN的架構(gòu),另外多加了一個(gè)Mask預(yù)測(cè)分支??梢?jiàn)Mask RCNN綜合了很多此前優(yōu)秀的研究成果。為了更好地講解Mask RCNN,我會(huì)先回顧一下幾個(gè)部分:
Faster RCNN
ResNet-FPN
ResNet-FPN+Fast RCNN
回顧完之后講ResNet-FPN+Fast RCNN+Mask,實(shí)際上就是Mask RCNN。
一、Faster RCNN
Faster RCNN是兩階段的目標(biāo)檢測(cè)算法,包括階段一的Region proposal以及階段二的bounding box回歸和分類。用一張圖來(lái)直觀展示Faster RCNN的整個(gè)流程:

Faster RCNN使用CNN提取圖像特征,然后使用region proposal network(RPN)去提取出ROI,然后使用ROI pooling將這些ROI全部變成固定尺寸,再喂給全連接層進(jìn)行Bounding box回歸和分類預(yù)測(cè)。
這里只是簡(jiǎn)單地介紹了Faster RCNN前向預(yù)測(cè)的過(guò)程,但Faster RCNN本身的細(xì)節(jié)非常多,比一階段的算法復(fù)雜度高不少,并非三言兩語(yǔ)能說(shuō)得清。如果對(duì)Faster RCNN算法不熟悉,想了解更多的同學(xué)可以看這篇文章:一文讀懂Faster RCNN,這是我看過(guò)的解釋得最清晰的文章。link:https://zhuanlan.zhihu.com/p/31426458
二、ResNet-FPN
多尺度檢測(cè)在目標(biāo)檢測(cè)中變得越來(lái)越重要,對(duì)小目標(biāo)的檢測(cè)尤其如此?,F(xiàn)在主流的目標(biāo)檢測(cè)方法很多都用到了多尺度的方法,包括最新的yolo v3。Feature Pyramid Network (FPN)則是一種精心設(shè)計(jì)的多尺度檢測(cè)方法,下面就開(kāi)始簡(jiǎn)要介紹FPN。
FPN結(jié)構(gòu)中包括自下而上,自上而下和橫向連接三個(gè)部分,如下圖所示。這種結(jié)構(gòu)可以將各個(gè)層級(jí)的特征進(jìn)行融合,使其同時(shí)具有強(qiáng)語(yǔ)義信息和強(qiáng)空間信息,在特征學(xué)習(xí)中算是一把利器了。

FPN實(shí)際上是一種通用架構(gòu),可以結(jié)合各種骨架網(wǎng)絡(luò)使用,比如VGG,ResNet等。Mask RCNN文章中使用了ResNNet-FPN網(wǎng)絡(luò)結(jié)構(gòu)。如下圖:

ResNet-FPN包括3個(gè)部分,自下而上連接,自上而下連接和橫向連接。下面分別介紹。
自下而上
從下到上路徑??梢悦黠@看出,其實(shí)就是簡(jiǎn)單的特征提取過(guò)程,和傳統(tǒng)的沒(méi)有區(qū)別。具體就是將ResNet作為骨架網(wǎng)絡(luò),根據(jù)feature map的大小分為5個(gè)stage。stage2,stage3,stage4和stage5各自最后一層輸出conv2,conv3,conv4和conv5分別定義為?
?,他們相對(duì)于原始圖片的stride是{4,8,16,32}。需要注意的是,考慮到內(nèi)存原因,stage1的conv1并沒(méi)有使用。
自上而下和橫向連接
自上而下是從最高層開(kāi)始進(jìn)行上采樣,這里的上采樣直接使用的是最近鄰上采樣,而不是使用反卷積操作,一方面簡(jiǎn)單,另外一方面可以減少訓(xùn)練參數(shù)。橫向連接則是將上采樣的結(jié)果和自底向上生成的相同大小的feature map進(jìn)行融合。具體就是對(duì)?
?中的每一層經(jīng)過(guò)一個(gè)conv 1x1操作(1x1卷積用于降低通道數(shù)),無(wú)激活函數(shù)操作,輸出通道全部設(shè)置為相同的256通道,然后和上采樣的feature map進(jìn)行加和操作。在融合之后還會(huì)再采用3*3的卷積核對(duì)已經(jīng)融合的特征進(jìn)行處理,目的是消除上采樣的混疊效應(yīng)(aliasing effect)。
實(shí)際上,上圖少繪制了一個(gè)分支:M5經(jīng)過(guò)步長(zhǎng)為2的max pooling下采樣得到 P6,作者指出使用P6是想得到更大的anchor尺度512×512。但P6是只用在 RPN中用來(lái)得到region proposal的,并不會(huì)作為后續(xù)Fast RCNN的輸入。
總結(jié)一下,ResNet-FPN作為RPN輸入的feature map是?
?,而作為后續(xù)Fast RCNN的輸入則是?
?。
三、ResNet-FPN+Fast RCNN

將ResNet-FPN和Fast RCNN進(jìn)行結(jié)合,實(shí)際上就是Faster RCNN的了,但與最初的Faster RCNN不同的是,F(xiàn)PN產(chǎn)生了特征金字塔?
?,而并非只是一個(gè)feature map。金字塔經(jīng)過(guò)RPN之后會(huì)產(chǎn)生很多region proposal。這些region proposal是分別由?
?經(jīng)過(guò)RPN產(chǎn)生的,但用于輸入到Fast RCNN中的是?
,也就是說(shuō)要在?
?中根據(jù)region proposal切出ROI進(jìn)行后續(xù)的分類和回歸預(yù)測(cè)。問(wèn)題來(lái)了,我們要選擇哪個(gè)feature map來(lái)切出這些ROI區(qū)域呢?實(shí)際上,我們會(huì)選擇最合適的尺度的feature map來(lái)切ROI。具體來(lái)說(shuō),我們通過(guò)一個(gè)公式來(lái)決定寬w和高h(yuǎn)的ROI到底要從哪個(gè)
?來(lái)切:

這里224表示用于預(yù)訓(xùn)練的ImageNet圖片的大小。?
?表示面積為?
?的ROI所應(yīng)該在的層級(jí)。作者將?
?設(shè)置為4,也就是說(shuō)?
?的ROI應(yīng)該從?
?中切出來(lái)。假設(shè)ROI的scale小于224(比如說(shuō)是112 * 112),?
?,就意味著要從更高分辨率的?
?中產(chǎn)生。另外 k 值會(huì)做取整處理,防止結(jié)果不是整數(shù)。
這種做法很合理,大尺度的ROI要從低分辨率的feature map上切,有利于檢測(cè)大目標(biāo),小尺度的ROI要從高分辨率的feature map上切,有利于檢測(cè)小目標(biāo)。
四、ResNet-FPN+Fast RCNN+mask
我們?cè)龠M(jìn)一步,將ResNet-FPN+Fast RCNN+mask,則得到了最終的Mask RCNN,如下圖:

Mask RCNN的構(gòu)建很簡(jiǎn)單,只是在ROI pooling(實(shí)際上用到的是ROIAlign,后面會(huì)講到)之后添加卷積層,進(jìn)行mask預(yù)測(cè)的任務(wù)。
下面總結(jié)一下Mask RCNN的網(wǎng)絡(luò):
骨干網(wǎng)絡(luò)ResNet-FPN,用于特征提取,另外,ResNet還可以是:ResNet-50,ResNet-101,ResNeXt-50,ResNeXt-101;
頭部網(wǎng)絡(luò),包括邊界框識(shí)別(分類和回歸)+mask預(yù)測(cè)。頭部結(jié)構(gòu)見(jiàn)下圖:

五、ROI Align
實(shí)際上,Mask RCNN中還有一個(gè)很重要的改進(jìn),就是ROIAlign。Faster R-CNN存在的問(wèn)題是:特征圖與原始圖像是不對(duì)準(zhǔn)的(mis-alignment),所以會(huì)影響檢測(cè)精度。而Mask R-CNN提出了RoIAlign的方法來(lái)取代ROI pooling,RoIAlign可以保留大致的空間位置。
為了講清楚ROI Align,這里先插入兩個(gè)知識(shí),雙線性插值和ROI pooling。
1.雙線性插值
在講雙線性插值之前,還得看最簡(jiǎn)單的線性插值。
線性插值
已知數(shù)據(jù)?
?與?
?,要計(jì)算?
?區(qū)間內(nèi)某一位置?
?在直線上的?
?值,如下圖所示。

計(jì)算方法很簡(jiǎn)單,通過(guò)斜率相等就可以構(gòu)建y和x之間的關(guān)系,如下:
?
仔細(xì)看就是用?
?和?
?,?
?的距離作為一個(gè)權(quán)重(除以?
?是歸一化的作用),用于?
?和?
?的加權(quán)。這個(gè)思想很重要,因?yàn)橹懒诉@個(gè)思想,理解雙線性插值就非常簡(jiǎn)單了。
雙線性插值
雙線性插值本質(zhì)上就是在兩個(gè)方向上做線性插值。

如圖,假設(shè)我們想得到P點(diǎn)的插值,我們可以先在x方向上,對(duì)?
?和?
?之間做線性插值得到?
?,
?同理可得。然后在y方向上對(duì)?
?和?
?進(jìn)行線性插值就可以得到最終的P。其實(shí)知道這個(gè)就已經(jīng)理解了雙線性插值的意思了,如果用公式表達(dá)則如下(注意?
?前面的系數(shù)看成權(quán)重就很好理解了)。
首先在?x?方向進(jìn)行線性插值,得到

然后在?y?方向進(jìn)行線性插值,得到

這樣就得到所要的結(jié)果?
?

參考:維基百科:雙線性插值
https://zh.wikipedia.org/wiki/%E5%8F%8C%E7%BA%BF%E6%80%A7%E6%8F%92%E5%80%BC
2.ROIpooling
ROI pooling就不多解釋了,直接通過(guò)一個(gè)例子來(lái)形象理解。假設(shè)現(xiàn)在我們有一個(gè)8x8大小的feature map,我們要在這個(gè)feature map上得到ROI,并且進(jìn)行ROI pooling到2x2大小的輸出。

假設(shè)ROI的bounding box為?
?。如圖:

將它劃分為2x2的網(wǎng)格,因?yàn)镽OI的長(zhǎng)寬除以2是不能整除的,所以會(huì)出現(xiàn)每個(gè)格子大小不一樣的情況。

進(jìn)行max pooling的最終2x2的輸出為:

最后以一張動(dòng)圖形象概括之:

參考:Region of interest pooling explained
https://deepsense.ai/region-of-interest-pooling-explained/
3. ROI Align
在Faster RCNN中,有兩次整數(shù)化的過(guò)程:
region proposal的xywh通常是小數(shù),但是為了方便操作會(huì)把它整數(shù)化。
將整數(shù)化后的邊界區(qū)域平均分割成 k x k 個(gè)單元,對(duì)每一個(gè)單元的邊界進(jìn)行整數(shù)化。
兩次整數(shù)化的過(guò)程如下圖所示:

事實(shí)上,經(jīng)過(guò)上述兩次整數(shù)化,此時(shí)的候選框已經(jīng)和最開(kāi)始回歸出來(lái)的位置有一定的偏差,這個(gè)偏差會(huì)影響檢測(cè)或者分割的準(zhǔn)確度。在論文里,作者把它總結(jié)為“不匹配問(wèn)題”(misalignment)。
為了解決這個(gè)問(wèn)題,ROI Align方法取消整數(shù)化操作,保留了小數(shù),使用以上介紹的雙線性插值的方法獲得坐標(biāo)為浮點(diǎn)數(shù)的像素點(diǎn)上的圖像數(shù)值。但在實(shí)際操作中,ROI Align并不是簡(jiǎn)單地補(bǔ)充出候選區(qū)域邊界上的坐標(biāo)點(diǎn),然后進(jìn)行池化,而是重新進(jìn)行設(shè)計(jì)。
下面通過(guò)一個(gè)例子來(lái)講解ROI Align操作。如下圖所示,虛線部分表示feature map,實(shí)線表示ROI,這里將ROI切分成2x2的單元格。如果采樣點(diǎn)數(shù)是4,那我們首先將每個(gè)單元格子均分成四個(gè)小方格(如紅色線所示),每個(gè)小方格中心就是采樣點(diǎn)。這些采樣點(diǎn)的坐標(biāo)通常是浮點(diǎn)數(shù),所以需要對(duì)采樣點(diǎn)像素進(jìn)行雙線性插值(如四個(gè)箭頭所示),就可以得到該像素點(diǎn)的值了。然后對(duì)每個(gè)單元格內(nèi)的四個(gè)采樣點(diǎn)進(jìn)行maxpooling,就可以得到最終的ROIAlign的結(jié)果。

需要說(shuō)明的是,在相關(guān)實(shí)驗(yàn)中,作者發(fā)現(xiàn)將采樣點(diǎn)設(shè)為4會(huì)獲得最佳性能,甚至直接設(shè)為1在性能上也相差無(wú)幾。事實(shí)上,ROI Align 在遍歷取樣點(diǎn)的數(shù)量上沒(méi)有ROIPooling那么多,但卻可以獲得更好的性能,這主要?dú)w功于解決了misalignment的問(wèn)題。
六、損失
Mask RCNN定義多任務(wù)損失:

和?
?與faster rcnn的定義沒(méi)有區(qū)別。需要具體說(shuō)明的是?
?,假設(shè)一共有K個(gè)類別,則mask分割分支的輸出維度是?
?, 對(duì)于?
?中的每個(gè)點(diǎn),都會(huì)輸出K個(gè)二值Mask(每個(gè)類別使用sigmoid輸出)。需要注意的是,計(jì)算loss的時(shí)候,并不是每個(gè)類別的sigmoid輸出都計(jì)算二值交叉熵?fù)p失,而是該像素屬于哪個(gè)類,哪個(gè)類的sigmoid輸出才要計(jì)算損失(如圖紅色方形所示)。并且在測(cè)試的時(shí)候,我們是通過(guò)分類分支預(yù)測(cè)的類別來(lái)選擇相應(yīng)的mask預(yù)測(cè)。這樣,mask預(yù)測(cè)和分類預(yù)測(cè)就徹底解耦了。
這與FCN方法是不同,F(xiàn)CN是對(duì)每個(gè)像素進(jìn)行多類別softmax分類,然后計(jì)算交叉熵?fù)p失,很明顯,這種做法是會(huì)造成類間競(jìng)爭(zhēng)的,而每個(gè)類別使用sigmoid輸出并計(jì)算二值損失,可以避免類間競(jìng)爭(zhēng)。實(shí)驗(yàn)表明,通過(guò)這種方法,可以較好地提升性能。
七、代碼
我用到的代碼是github上star最多的Mask RCNN代碼:Mask R-CNN for object detection and instance segmentation on Keras and TensorFlow
https://github.com/matterport/Mask_RCNN
由于篇幅所限,不會(huì)在本文中講解代碼。但會(huì)由我的一個(gè)同事(?@深度眸知乎用戶)視頻講解,視頻即將錄制,錄好之后我會(huì)把視頻鏈接發(fā)在這里,感興趣的可以關(guān)注。如果對(duì)視頻內(nèi)容有什么需求,歡迎留言。
參考
文章中有些圖片來(lái)自medium博主:Jonathan Hui

