# RoI Pooling 系列方法介紹(文末附源碼)

作者簡介

CW,廣東深圳人,畢業於中山大學(SYSU)數據科學與計算機學院,畢業後就業於騰訊計算機系統有限公司技術工程與事業群(TEG)從事Devops工作,期間在AI LAB實習過,實操過道路交通元素與醫療病例圖像分割、視頻實時人臉檢測與表情識別、OCR等項目。

目前也有在一些自媒體平台上參與外包項目的研發工作,項目專註於CV領域(傳統圖像處理與深度學習方向均有)。

前言

RoI Pooling 是目標檢測任務中的常見手段,最早在 Faster R-CNN 中提出,作用是將一系列大小不同的 RoI 投影至特徵圖上,然後通過池化操作將它們處理為一致大小,從而方便後面的網絡層進行處理(歷史原因,以前的網絡結構中最後幾層往往是全連接層,因此需要固定的輸入尺寸),同時起到了加速計算的作用。

本文先對 RoI Pooling 進行介紹,該方法由於量化誤差而帶來了精度上的損失,後來有大神基於該方法提出了 RoI Align 和 Precise RoI Pooling,本文後半部分會讓大夥瞧瞧這倆個傢伙的玩法。

文末會附上以上三部分相應的源碼鏈接,其中 RoI Pooling 和 RoI Align 是自己手寫的純 py 實現,作為學習參考使用;Precise RoI Pooling 是作者的原版,基於 cuda 編寫,在使用時需要編譯。

本文框架

RoI Pooling — 將不同的尺寸變為一致

RoI Align — 沒有量化誤差

Precise RoI Pooling — 無需超參,每個像素點均有梯度貢獻

一、Rol Pooling——將不同的尺寸變為一致

先來概述下 RoI Pooling 的操作:

i). RoI 的尺寸通常是對應輸入圖像的,特徵圖是輸入圖像經過一系列卷積層後的輸出,因此,首先將 RoI 映射到特徵圖上的對應區域位置;

ii). 最終需要將尺寸不一的 RoI 變為固定的 n x n 大小,於是將 RoI 平均劃分為 n x n 個區域;

iii). 取每個劃分而來的區域的最大像素值,相當於對每個區域做 max pooling 操作,作為每個區域的「代表」,這樣每個 RoI 經過操作後就變為 n x n 大小。

結合一個例子說明下 RoI Pooling 帶來的量化誤差:

如下圖,假設輸入圖像經過一系列卷積層下採樣32倍後輸出的特徵圖大小為8×8,現有一 RoI 的左上角和右下角坐標(x, y 形式)分別為(0, 100) 和 (198, 224),映射至特徵圖上後坐標變為(0, 100 / 32)和(198 / 32,224 / 32),由於像素點是離散的,因此向下取整後最終坐標為(0, 3)和(6, 7),這裡產生了第一次量化誤差。

假設最終需要將 RoI 變為固定的2×2大小,那麼將 RoI 平均劃分為2×2個區域,每個區域長寬分別為 (6 – 0 + 1) / 2 和 (7 – 3 + 1) / 2 即 3.5 和 2.5,同樣,由於像素點是離散的,因此有些區域的長取3,另一些取4,而有些區域的寬取2,另一些取3,這裡產生了第二次量化誤差。

二、RoI Align——沒有量化誤差

RoI Align 是在 Mask R-CNN 中提出來的,基本流程和 RoI Pooling 一致,但是沒有量化誤差,下面結合一個例子來說明:

如上圖,輸入圖像分辨率為800×800,其中一個 RoI 大小為 665×665,輸入圖像經過 VGG16 下採樣32倍後輸出分辨率為25×25的特徵圖。

1). 將 RoI 映射至特徵圖上,大小為 (665/32)x( 665/32) 即 20.78×20.78,注意這裡不進行取整;

2). 最終需要將 RoI 輸出為7×7大小,因此將 20.78×20.78大小的 RoI 均分為7×7個區域,每個區域大小為2.97×2.97,注意這裡也沒有取整操作;

3). RoI Align 需要設置一個超參,代表每個區域的採樣點數,即每個區域取幾個點來計算「代表」這個區域的值,通常為4;

4). 對每個劃分後的區域長寬各劃分為一半,「十字交叉」變為4等份,取每份中心點位置作為其「代表」,中心點位置的像素值利用雙線性插值計算獲得,這樣就得到4個中心點像素值,採樣點數為4就是這個意思;

2.97×2.97區域劃分為4等份,每份利用雙線性插值計算其中心點像素值5).每個2.97×2.97的區域都有4個中心點像素值,它們分別取4個中心點像素值中的最大值作為其「代表」,這樣7×7個區域就產生7×7個值,最終將 RoI 變為了7×7大小。
**三、Precise RoI Pooling ——無需超參 **

RoI Align 雖然沒有量化損失,但是卻需要設置超參,對於不同大小的特徵圖和 RoI 而言這個超參的取值難以自適應,於是就有人提出 Precise RoI Pooling 來解決這一問題,真是人才輩出吶!

Precise RoI Pooling 和 RoI Align 類似,將 RoI 映射到特徵圖以及劃分區域時都沒有量化操作,不同的是,Precise RoI Pooling 沒有再次劃分子區域,而是對每個區域計算積分後取均值來「代表」每個區域,因而不需要進行採樣。

另外,由上述公式可知,區域內的每點在反向傳播中對梯度都是有貢獻的,而對 RoI Align 和 RoI Pooling 來說,只有區域內最大值那點才對梯度有貢獻,相當於「浪費」了大部分的點。

四、總結

以上操作的原理不難理解,很多人看完後或許都有這樣的feel——咦,挺簡單的嘛!但是如果試着從代碼層面去實現的話或許就會發現不是那麼容易了,特別是,要能應用到生活場景中。

這裡我手擼的 RoI Pooling 和 RoI Align 也僅是基於原理去實現的,用作學習參考,真正業界上應用的通常是用C或C++實現和編譯的,純 py 的版本通常由於性能原因難以落地到工程中。

另外,這裡附上的 Precise RoI Pooling 源碼是原作者的版本,但是我使用時一直出現編譯錯誤,各位大俠可以試試,如果有類似問題或者解決方案希望可以和我一起探討,謝謝!