原始圖片中的ROI如何映射到到feature map?
鏈接: https://zhuanlan.zhihu.com/p/24780433
在SPP-net中的難點一曾提到:ROI如何對應到feature map?
這個地方遇到不少坑,看了很多資料都沒有太明白,感覺太繞。
先數數遇到的坑:
- 《Spatial Pyramid Pooling in Deep Convolutional Networks for Visual Recognition》原文是這樣寫的,一臉懵逼。
- 找了張圖是這樣畫的:有那麼點意思,好像是從前向後推出各個層的感受野,可是還是不懂爲啥這樣。
- 這兩張圖,看的有點摸不着頭腦
- 接着找了何凱明在ICCV2015上演講的PPT:《 iccv2015_tutorial_convolutional_feature_maps_kaiminghe.》,算是有了點眉目。可是還是不造咋回事,只知道有那麼個公式,卻不知道怎麼推出來的,滿心的疑惑。
迴歸正題
最後找到一篇靠譜的文章 卷積神經網絡物體檢測之感受野大小計算 - machineLearning - 博客園,它給出了一個不錯的啓發,還附帶了代碼,最關鍵的是它給出了參考鏈接。 於是我終於在參考鏈接找到了這篇 Concepts and Tricks In CNN(長期更新) 最佳博文,不僅清晰易懂,而且公式詳細。(不過感覺略有不足,所以下面就詳細介紹一下這個大坑)
【先說說感受野的計算】
回憶一下我之前在 卷積神經網絡(CNN)簡介 裏就曾經畫圖推導過的一個公式(這個公式很常見,可是竟然很少有人去講它怎麼來的,當時我在寫 CNN簡介就順便畫圖推導了一下,沒細看的同學可以回頭看看)
原文裏提到:隱藏層邊長(輸出的邊長) = (W - K + 2P)/S + 1
(其中 W是輸入特徵的大小,K是卷積核大小,P是填充大小,S是步長(stride))
爲了理解方便我就把公式先用英文寫一下:
output field size = ( input field size - kernel size + 2*padding ) / stride + 1
(output field size 是卷積層的輸出,input field size 是卷積層的輸入)
反過來問你: 卷積層的輸入(也即前一層的感受野) = ?
答案必然是: input field size = (output field size - 1)* stride - 2*padding + kernel size
再重申一下:卷積神經網絡CNN中,某一層輸出結果中一個元素所對應的輸入層的區域大小,被稱作感受野receptive field。感受野的大小是由kernel size,stride,padding , outputsize 一起決定的。
從Concepts and Tricks In CNN(長期更新) 裏截張圖你感受一下:
公式化一下:
- 對於Convolution/Pooling layer(博文作者忘了加上padding本文在這裏補上):
- 對於 Neuronlayer(ReLU/Sigmoid/..) : (顯然如此)
上面只是給出了 前一層在後一層的感受野,如何計算最後一層在原始圖片上的感受野呢? 從後向前級聯一下就可以了(先計算最後一層到倒數第二層的感受野,再計算倒數第二層到倒數第三層的感受野,依次從後往前推導就可以了)
卷積神經網絡物體檢測之感受野大小計算 - machineLearning
- 博客園 中用如下核心代碼計算了 Alexnet zf-5和VGG16網絡每層輸出feature map的感受野大小。
net_struct = {'alexnet': {'net':[[11,4,0],[3,2,0],[5,1,2],[3,2,0],[3,1,1],[3,1,1],[3,1,1],[3,2,0]],
'name':['conv1','pool1','conv2','pool2','conv3','conv4','conv5','pool5']},
'vgg16': {'net':[[3,1,1],[3,1,1],[2,2,0],[3,1,1],[3,1,1],[2,2,0],[3,1,1],[3,1,1],[3,1,1],
[2,2,0],[3,1,1],[3,1,1],[3,1,1],[2,2,0],[3,1,1],[3,1,1],[3,1,1],[2,2,0]],
'name':['conv1_1','conv1_2','pool1','conv2_1','conv2_2','pool2','conv3_1','conv3_2',
'conv3_3', 'pool3','conv4_1','conv4_2','conv4_3','pool4','conv5_1','conv5_2','conv5_3','pool5']},
'zf-5':{'net': [[7,2,3],[3,2,1],[5,2,2],[3,2,1],[3,1,1],[3,1,1],[3,1,1]],
'name': ['conv1','pool1','conv2','pool2','conv3','conv4','conv5']}}
imsize = 224
def outFromIn(isz, net, layernum):#從前向後算輸出維度
totstride = 1
insize = isz
for layer in range(layernum):
fsize, stride, pad = net[layer]
outsize = (insize - fsize + 2*pad) / stride + 1
insize = outsize
totstride = totstride * stride
return outsize, totstride
def inFromOut(net, layernum):#從後向前算感受野 返回該層元素在原始圖片中的感受野
RF = 1
for layer in reversed(range(layernum)):
fsize, stride, pad = net[layer]
RF = ((RF -1)* stride) + fsize
return RF
【再談談感受野上面的座標映射 (Coordinate Mapping)】
爲了完整性直接摘錄博客內容了:
通常,我們需要知道網絡裏面任意兩個feature map之間的座標映射關係(一般是中心點之間的映射),如下圖,我們想得到map 3上的點p3映射回map 2所在的位置p2(橙色框的中心點)
計算公式:
- 對於 Convolution/Pooling layer:
- 對於Neuronlayer(ReLU/Sigmoid/..) :
上面是計算任意一個layer輸入輸出的座標映射關係,如果是計算任意feature map之間的關係,只需要用簡單的組合就可以得到,下圖是一個簡單的例子:
最後說一下 前面那張PPT裏的公式。
- 對於上面 A simple solution:其實也是何凱明在SPP-net中採用的方法。其實就是巧妙的化簡一下公式 令每一層的padding都爲
- 當 爲奇數 所以
- 當 爲偶數 所以
- 而 是座標值,不可能取小數 所以基本上可以認爲 。公式得到了化簡:感受野中心點的座標只跟前一層 有關。
- 對於上面的 A general solution: 其實就是把 公式 級聯消去整合一下而已。
SPP-net的ROI映射做法詳解
SPP-net 是把原始ROI的左上角和右下角 映射到 feature map上的兩個對應點。 有了feature map上的兩隊角點就確定了 對應的 feature map 區域(下圖中橙色)。
如何映射?
左上角的點(x,y)映射到 feature map上的 : 使得 在原始圖上感受野(上圖綠色框)的中心點
與(x,y)儘可能接近。
對應點之間的映射公式是啥?
- 就是前面每層都填充padding/2 得到的簡化公式 :
- 需要把上面公式進行級聯得到 其中
- 對於feature map 上的 它在原始圖的對應點爲
- 論文中的最後做法:把原始圖片中的ROI映射爲 feature map中的映射區域(上圖橙色區域)其中 左上角取:, ;右下角的點取: 界取y'的x值:。 下圖可見 , 的作用效果分別是增加和減少。也就是 左上角要向右下偏移,右下角要想要向左上偏移。個人理解採取這樣的策略是因爲論文中的映射方法(左上右下映射)會導致feature map上的區域反映射回原始ROI時有多餘的區域(下圖左邊紅色框是比藍色區域大的)
參考:
《Spatial Pyramid Pooling in Deep Convolutional Networks for Visual Recognition》
Concepts and Tricks In CNN(長期更新)
卷積神經網絡物體檢測之感受野大小計算 - machineLearning
- 博客
iccv2015_tutorial_convolutional_feature_maps_kaiminghe.