1. 程式人生 > >深度學習中的感受野計算

深度學習中的感受野計算

概念

感受野(receive field)是指當前feature map中的一個原子點P與輸入層中多少個原子個數相關的問題,假設輸入層相關的點為(RF*RF)個(假設長寬兩個方向上做同樣尺度的操作),即輸入層中RF*RF個原子值的變化會影響當前feature map中的指定原子P,而輸入層中其他原子變化並不會影響這個指定原子P。顯然區域性感受野只跟kernel size有關,與stride無關。這也是卷積層的兩個重要特性之一(區域性感受野和引數共享)。顯然RF>=1.

當然感受野也可以作為一個相對概念,即不一定是相對於輸入層的感受野,你也可以定義任意層l1相對於另外任意層L2的相對域(只需要滿足約束條件:L2不能再L1層的後面,檔L1==L2時,RF==1).

問題:求任意層的輸出Feature map上的感受野F(則感受原子點個數即為F*F).

首先我們來分析比較簡單的一種情況:

    輸入層通過第1層(type = conv,kernel_size =3,stride =1),再通過第2層(type = pool,kernel_size =2,stride =2),求第2層的輸出feature map 在輸入層的感受野F.

    顯然,這是很簡單的模型,第一層對於輸入層的感受野為3(只跟kernel_size有關),第二層對第一層的感受野為2(只跟kernel_size有關),那第二層對輸入層呢?動手畫一畫,就知道第2層對輸入層的感受野為4*4。我們在分析的過程中,是不是現求第二層對第一層的感受野,然後結合第一層對輸入層的感受野,算出第二層對輸入層的感受野。這就等價一個遞迴關係。

現在來看一般情況

假設第i層上對第j層的區域性感受野為F(i,j),顯然i>=j.假定輸入層為第0層。

則現在問題轉化為求F(i,0)的問題。由上面分析可知F(i,i)=1,現只需要求出F(i,j) 與F(i,j-1)層的關係,即可通過F(i,i)求出F(i,0).

 通過簡單情況和畫圖分析,可得出遞迴關係式,F(i,j-1) = kernel_size_j + (F(i,j)-1)*stride_j,kernel_size_j表示的第j層的kernel_size,stride_j表示第j層的stride.這個式子分為兩部分,一部分是指kernel_size_j,即區域性感受野,另一部分是stride,可理解為當前層在每多一個原子,上一層的感受野多增加stride個(在第一個區域性感受野的基礎上增加的)(只考慮一個方向的大小)。

則由遞迴關係和F(i,i) =1,可求出F(i,0).注意F(i,0)和F(i-1,0)並沒有任何關係

感受野(receptive field)是怎樣一個東西呢,從CNN視覺化的角度來講,就是輸出featuremap某個節點的響應對應的輸入影象的區域就是感受野。

比如我們第一層是一個3*3的卷積核,那麼我們經過這個卷積核得到的featuremap中的每個節點都源自這個3*3的卷積核與原影象中3*3的區域做卷積,那麼我們就稱這個featuremap的節點感受野大小為3*3

如果再經過pooling層,假定卷積層的stride是1,pooling層大小2*2,stride是2,那麼pooling層節點的感受野就是4*4

有幾點需要注意的是,padding並不影響感受野,stride隻影響下一層featuremap的感受野,size影響的是該層的感受野。

至於如何計算感受野,我的建議是top to down的方式。下面我拿一個例子來算算

 
若沒有邊緣填充,padding=VALID,計算公式如下:
O=ceil((W-K+1)/S)
若有邊緣填充,padding=SAME,計算公式如下:
O=ceil(W/S)
O是輸出尺寸,W是輸入尺寸的長或寬,K是過濾器尺寸,P是邊緣填充,S是步長,ceil()表示向上取整函式。


type    size   stride
conv1    3     1
pool1    2     2
conv2    3     1
pool2    2     2
conv3    3     1
conv4    3     1     
pool3    3     2


感受野計算公式:

(最後一層感受野大小 -1 )×stride + size(過濾器)
pool3感受野:
pool3  1
conv4  3
conv3  5
pool3  10
conv2  12
pool2  24
conv1  26

若沒有邊緣填充:

conv4  輸出為2*2     輸入: (w - 2 + 1)/2 = 2 -->  w=5  :5*5

conv3  輸出為5*5     輸入: (w - 3 + 1)/1 = 5 -->  w=7  :7*7

pool2  輸出為7*7     輸入:  (w - 2 + 1)/2 = 7 -->  w=15  :15*15

conv2  輸出為15*15   輸入:  (w - 3 + 1)/1 = 15 --> w=17 :17*17

pool1  輸出為17*17   輸入:  (w - 2 + 1)/2 = 17 --> w=35 :35*35
 
conv1  輸出為35*35   輸入:  (w - 3 + 1)/1 = 35 --> w=37 :37*37

若有邊緣填充:

conv4  輸出為2*2     輸入: 2*2

conv3  輸出為2*2     輸入: 2*2

pool2  輸出為2*2     輸入: 4*4

conv2  輸出為4*4   輸入:   4*4

pool1  輸出為4*4  輸入:   8*8
 
conv1  輸出為 8*8   輸入:    8*8

為啥說感受野與ping無關   輸入的影象小於感受野很疑惑  求解

計算感受野:

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
 
if __name__ == '__main__':
    print ("layer output sizes given image = %dx%d" % (imsize, imsize))
 
for net in net_struct.keys():
        print ('************net structrue name is %s**************'% net)
        for i in range(len(net_struct[net]['net'])):
            p = outFromIn(imsize,net_struct[net]['net'], i+1)
            rf = inFromOut(net_struct[net]['net'], i+1)
            print ("Layer Name = %s, Output size = %3d, Stride = % 3d, RF size = %3d" % (net_struct[net]['name'][i], p[0], p[1], rf))