(pytorch-深度学习系列)卷积神经网络中的填充(padding)和步幅(stride)
卷積神經網絡中的填充(padding)和步幅(stride)
之前寫過一篇blog,描述CNN網絡層的輸入和輸入尺寸的計算關系,但是并沒有描述的很全面,這里全面描述了影響輸出尺寸的兩個超參數padding和stride,查閱了相關資料,編碼理解了pytorch中CNN網絡的輸入輸出關系。
對于CNN網絡,一般來說,假設輸入形狀是nh×nwn_h\times n_wnh?×nw?,卷積核窗口形狀是kh×kwk_h\times k_wkh?×kw?,那么輸出形狀將會是
(nh?kh+1)×(nw?kw+1).(n_h-k_h+1) \times (n_w-k_w+1).(nh??kh?+1)×(nw??kw?+1).
所以卷積層的輸出形狀由輸入形狀和卷積核窗口形狀決定。卷積層還有兩個超參數,即填充和步幅。它們可以對給定形狀的輸入和卷積核改變輸出形狀。
填充(padding)
填充(padding)是指在輸入高和寬的兩側填充元素(通常是0元素)。
對于輸入:
input=[012345678]input = \begin{bmatrix} 0 & 1 & 2 \\ 3 & 4 &5 \\ 6 & 7 & 8 \end{bmatrix}input=???036?147?258????
我們在原輸入高和寬的兩側分別添加了值為0的元素,使得輸入高和寬從3變成了5,并導致輸出高和寬由2增加到4:
[012345678]→[0000000120034500678000000]\begin{bmatrix} 0 & 1 & 2 \\ 3 & 4 &5 \\ 6 & 7 & 8 \end{bmatrix} \rightarrow \begin{bmatrix} 0 & 0&0&0&0\\ 0&0&1&2&0 \\ 0&3&4&5&0\\0&6&7&8&0\\0&0&0&0&0\end{bmatrix} ???036?147?258????→???????00000?00360?01470?02580?00000????????
一般來說,如果在高的兩側一共填充php_hph?行,在寬的兩側一共填充pwp_wpw?列,那么輸出形狀將會是
(nh?kh+ph+1)×(nw?kw+pw+1),(n_h-k_h+p_h+1)\times(n_w-k_w+p_w+1),(nh??kh?+ph?+1)×(nw??kw?+pw?+1),
也就是說,輸出的高和寬會分別增加php_hph?和pwp_wpw?。
[0000000120034500678000000]?[0123]=[03849192510213743166780]\begin{bmatrix} 0 & 0&0&0&0\\ 0&0&1&2&0 \\ 0&3&4&5&0\\0&6&7&8&0\\0&0&0&0&0\end{bmatrix} * \begin{bmatrix} 0&1\\2&3 \end{bmatrix} = \begin{bmatrix} 0&3&8&4\\9 &19& 25& 10\\21 &37& 43& 16\\6 &7 &8 &0 \end{bmatrix}???????00000?00360?01470?02580?00000?????????[02?13?]=?????09216?319377?825438?410160??????
在很多情況下,我們會設置
ph=kh?1pw=kw?1p_h=k_h-1 \\ p_w=k_w-1ph?=kh??1pw?=kw??1
來使輸入和輸出具有相同的高和寬。這樣會方便在構造網絡時推測每個層的輸出形狀。假設這里khk_hkh?是奇數,我們會在高的兩側分別填充ph/2p_h/2ph?/2行。如果khk_hkh?是偶數,一種可能是在輸入的頂端一側填充?ph/2?\lceil p_h/2\rceil?ph?/2?行,而在底端一側填充?ph/2?\lfloor p_h/2\rfloor?ph?/2?行。在寬的兩側填充同理。
卷積神經網絡經常使用奇數高寬的卷積核,如1、3、5和7,所以兩端上的填充個數相等。對任意的二維數組X,設它的第i行第j列的元素為X[i,j]。當兩端上的填充個數相等,并使輸入和輸出具有相同的高和寬時,我們就知道輸出Y[i,j]是由輸入以X[i,j]為中心的窗口同卷積核進行互相關計算得到的。
下面的我們創建一個高和寬為3的二維卷積層,然后設輸入高和寬兩側的填充數分別為1。給定一個高和寬為8的輸入,我們發現輸出的高和寬也是8。
import torch from torch import nn# 定義一個函數來計算卷積層。它對輸入和輸出做相應的升維和降維 def comp_conv2d(conv2d, X):X = X.view((1, 1) + X.shape)# (1, 1)代表批量大小和通道數,均為1Y = conv2d(X)return Y.view(Y.shape[2:]) # 排除不關心的前兩維:批量和通道# 注意這里是兩側分別填充1行或列,所以在兩側一共填充2行或列 conv2d = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=3, padding=1)X = torch.rand(8, 8) comp_conv2d(conv2d, X).shape輸出:
torch.Size([8, 8])當卷積核的高和寬不同時,可以通過設置高和寬上不同的填充數使輸出和輸入具有相同的高和寬。
# 使用高為5、寬為3的卷積核。在高和寬兩側的填充數分別為2和1 conv2d = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=(5, 3), padding=(2, 1)) comp_conv2d(conv2d, X).shape輸出不變
步幅(stride)
卷積窗口從輸入數組的最左上方開始,按從左往右、從上往下的順序,依次在輸入數組上滑動。我們將每次滑動的行數和列數稱為步幅(stride)。
目前我們看到的例子里,在高和寬兩個方向上步幅均為1, 我們也可以使用更大的步幅。
在下面的問題中:
在高上步幅為3、在寬上步幅為2。可以看到,輸出第一列第二個元素時,卷積窗口向下滑動了3行,而在輸出第一行第二個元素時卷積窗口向右滑動了2列。當卷積窗口在輸入上再向右滑動2列時,由于輸入元素無法填滿窗口,無結果輸出。
[0000000120034500678000000]?[0123]=[[0123]?[0000][0123]?[0012][0123]?[0600][0123]?[7700]]=[0868]\begin{bmatrix} 0 & 0&0&0&0\\ 0&0&1&2&0 \\ 0&3&4&5&0\\0&6&7&8&0\\0&0&0&0&0\end{bmatrix} * \begin{bmatrix} 0&1\\2&3 \end{bmatrix} = \begin{bmatrix} {\begin{bmatrix} 0&1\\2&3 \end{bmatrix}* \begin{bmatrix} 0&0\\0&0 \end{bmatrix} \quad \begin{bmatrix} 0&1\\2&3 \end{bmatrix}* \begin{bmatrix} 0&0\\1&2 \end{bmatrix}}\\ \\ \\ {\begin{bmatrix} 0&1\\2&3 \end{bmatrix}* \begin{bmatrix} 0&6\\0&0 \end{bmatrix} \quad \begin{bmatrix} 0&1\\2&3 \end{bmatrix}* \begin{bmatrix} 7&7\\0&0 \end{bmatrix}} \end{bmatrix} = \begin{bmatrix} 0&8 \\ 6&8\end{bmatrix}???????00000?00360?01470?02580?00000?????????[02?13?]=?????????[02?13?]?[00?00?][02?13?]?[01?02?][02?13?]?[00?60?][02?13?]?[70?70?]??????????=[06?88?]
一般來說,當高上步幅為shs_hsh?,寬上步幅為sws_wsw?時,輸出形狀為
?(nh?kh+ph+sh)/sh?×?(nw?kw+pw+sw)/sw?.\lfloor(n_h-k_h+p_h+s_h)/s_h\rfloor \times \lfloor(n_w-k_w+p_w+s_w)/s_w\rfloor.?(nh??kh?+ph?+sh?)/sh??×?(nw??kw?+pw?+sw?)/sw??.
如果設置
ph=kh?1pw=kw?1p_h=k_h-1\\p_w=k_w-1ph?=kh??1pw?=kw??1
那么輸出形狀將簡化為
?(nh+sh?1)/sh?×?(nw+sw?1)/sw?\lfloor(n_h+s_h-1)/s_h\rfloor \times \lfloor(n_w+s_w-1)/s_w\rfloor?(nh?+sh??1)/sh??×?(nw?+sw??1)/sw??
更進一步,如果輸入的高和寬能分別被高和寬上的步幅整除,那么輸出形狀將是
(nh/sh)×(nw/sw)(因為上式中是向下取整)(n_h/s_h) \times (n_w/s_w) (因為上式中是向下取整)(nh?/sh?)×(nw?/sw?)(因為上式中是向下取整)
·
·
我們令高和寬上的步幅均為2,從而使輸出的高和寬減半。
輸出:
torch.Size([4, 4])代入公式,這里應該這么算:
?(nh?kh+ph+sh)/sh?×?(nw?kw+pw+sw)/sw?=?(8?3+1×2+2)/2?×?(8?3+1×2+2)/2?=4×4\lfloor(n_h-k_h+p_h+s_h)/s_h\rfloor \times \lfloor(n_w-k_w+p_w+s_w)/s_w\rfloor = \\\lfloor(8-3+1\times2+2)/2\rfloor \times \lfloor(8-3+1\times2+2)/2\rfloor = 4 \times 4?(nh??kh?+ph?+sh?)/sh??×?(nw??kw?+pw?+sw?)/sw??=?(8?3+1×2+2)/2?×?(8?3+1×2+2)/2?=4×4
再算一個稍微復雜點的:
conv2d = nn.Conv2d(1, 1, kernel_size=(3, 5), padding=(0, 1), stride=(3, 4)) comp_conv2d(conv2d, X).shape輸出:
torch.Size([2, 2])代入公式,這里應該這么算:
?(nh?kh+ph+sh)/sh?×?(nw?kw+pw+sw)/sw?=?(8?3+0×2+3)/3?×?(8?5+1×2+4)/4?=2×2\lfloor(n_h-k_h+p_h+s_h)/s_h\rfloor \times \lfloor(n_w-k_w+p_w+s_w)/s_w\rfloor = \\\lfloor(8-3+0\times2+3)/3\rfloor \times \lfloor(8-5+1\times2+4)/4\rfloor = 2 \times 2?(nh??kh?+ph?+sh?)/sh??×?(nw??kw?+pw?+sw?)/sw??=?(8?3+0×2+3)/3?×?(8?5+1×2+4)/4?=2×2
總結
以上是生活随笔為你收集整理的(pytorch-深度学习系列)卷积神经网络中的填充(padding)和步幅(stride)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Membership Inference
- 下一篇: 人类繁荣的数学:数学的哈欠