Pytorch.nn.Conv2d詳解
首先看一下這個類的定義:
class Conv2d(_ConvNd):
# 初始化函式,這裡主要了解有哪些引數傳進來就可以了
def __init__(
self,
in_channels: int,
out_channels: int,
kernel_size: _size_2_t,
stride: _size_2_t = 1,
padding: _size_2_t = 0,
dilation: _size_2_t = 1,
groups: int = 1,
bias: bool = True,
padding_mode: str = 'zeros' # TODO: refine this type
):
#————————————————看到這就可以了————————————————————
kernel_size = _pair(kernel_size)
stride = _pair(stride)
padding = _pair(padding)
dilation = _pair(dilation)
super(Conv2d, self).__init__(
in_channels, out_channels, kernel_size, stride, padding, dilation,
False, _pair(0), groups, bias, padding_mode)
def _conv_forward(self, input, weight):
if self.padding_mode != 'zeros':
return F.conv2d(F.pad(input, self._reversed_padding_repeated_twice, mode=self.padding_mode),
weight, self.bias, self.stride,
_pair(0), self.dilation, self.groups)
return F.conv2d(input, weight, self.bias, self.stride,
self.padding, self.dilation, self.groups)
# 前向傳播計算
def forward(self, input: Tensor) -> Tensor:
return self._conv_forward(input, self.weight)
上面就是Cov2d這個類的原始碼實現,是不是感覺程式碼特別少?是不是突然對掌握它信心倍增!
從整體上來看:
Conv2d是一個類,它包含了做卷積運算所需要的引數(__init__函式),以及卷積操作(forward函式)。
再來看一下它的詳細引數:
一共九個引數,一般用前三個就可以處理一般的任務:
in_channels
:輸入通道數目out_channels
:輸出通道數目kernel_size
:卷積核大小,如果輸入是一個值,比如 3 3 3,那麼卷積核大小就是 3 × 3 3 \times 3 3×3 ,如果不想卷積核寬和高相等,還可以輸入tuple型別資料,比如: ( 3 , 5 ) (3, 5) (3,5)stride
:步長大小,跟上面卷積核引數一樣,如果輸入是一個值,比如 2 2 2 ,步長就是 2 × 2 2 \times 2 2×2 ,還可以輸入元組 ( 2 , 1 ) (2, 1) (2,1) ,表示卷積核每次向右移動 1 1 1 個步長,向下移動 2 2 2 個步長。padding
:填充,參數列示在周圍補0的情況。補0的方向為上、下、左、右四個方向。如果是輸入是單個值,比如1,就是在上下左右四個方向補一圈0。如果輸入是元組比如(2,1)
,則表示在上方補兩行,左邊補兩列,下方補一行,右邊補一列。發現規律沒有。第一個值控制上、左兩個方向的填充,第二個值控制下、右兩個方向的填充。dilation
:進行擴充套件卷積需要的引數。groups
:進行分組卷積需要的引數。(有需要自行深入瞭解)bias
:偏置,布林型別,預設為True
,即增加一個學習的偏置項。padding_mode
:填充的模式,預設是zero
,還可以選擇reflect
、replicate
、circular
。(有需要自行深入瞭解)
首先是關於輸入通道數 in_channels
和輸出通道數 out_channels
的解釋:
如果對通道是什麼還不太理解可以去看我的這篇部落格:如何理解卷積神經網路中的通道(channel)
如果卷積核輸入的是原始的圖片,那麼我們可以根據圖片型別決定圖片的輸入通道數,如果是RGB圖片, in_channels=3
,如果是灰度影像,in_channels=1
。輸出通道數需要我們自己指定,具體指定多少需要根據經驗。
如果是第二層或者更多層卷積,當前層的輸入通道數就是上一層卷積的輸出通道數,當前層的輸出通道數需要自己指定。
關於卷積核的解釋:
這裡的卷積核引數只是指定卷積核的寬度和高度,對於多通道的卷積,程式碼內部會根據輸入通道數初始化對應的卷積核數目,即:如果輸入是3通道,那麼就會有三個我們指定寬高的卷積核做相應的卷積操作。
關於步長的解釋:
步長指的是卷積核在輸入矩陣的上每次移動幾步。移動方向由兩個,向右或者向下。分別由兩個值指定。
關於填充的解釋:
填充的主要目的是為了充分利用輸入圖片的邊緣資訊。
關於為什麼需要填充,可以參考這篇部落格:CNN基礎知識——卷積(Convolution)、填充(Padding)、步長(Stride)
關於擴張(dilation)引數的解釋:
本質上就是擴大卷積核,比如將 3 × 3 3 \times 3 3×3 的擴大為 7 × 7 7 \times 7 7×7 的,然後對擴大的部分用0進行填充,
目的是擴大感受野,捕獲多尺度上下文資訊。
具體怎麼擴張,詳細請參考:
【1】如何理解擴張卷積(dilated convolution)
【2】總結-空洞卷積(Dilated/Atrous Convolution)
關於分組(group)的解釋:
對於一般的卷積神經網路,假設通道數有100個,那麼我們需要100個卷積核對這100個通道分別做卷積操作,然後求和,最後得到1個特徵圖。如果我們需要得到30個特徵圖,就需要30*100=3000個卷積核。引數量非常大。
而對於分組卷積,依然假設通道數有100個,我們分成兩組,每組50個通道數,分別對每組單獨做卷積操作,每組需要50個卷積核,總的卷積核數目沒變,但是我們每組可以得到2個特徵圖。如果我們分成4組,每組25個通道數,最終可以得到4個特徵圖。
也就是說,我們在不增加引數量的情況下,得到了更多的特徵圖。這是分組的優勢之一,對於分組的通道,我們可以在不同的GPU上並行訓練,加速訓練過程。
更詳細的解釋參考:
卷積網路基礎知識—Group Convolution分組卷積
關於偏置項的解釋:
這個項是個布林型別值,如果為True,就會在訓練的時候加上偏置項,反之,不會。預設是True。
關於填充模式的解釋:
我們一般情況下對於填充的資料,預設是0。還可以選擇常數填充、映象填充、重複填充.。
常數填充就是指定一個常數值進行填充,如果為0,等價於零填充;
映象填充是指對影像邊緣進行映象對稱的填充;
重複填充指的是用影像邊緣的畫素值進行填充。
OK,到這裡就對所有的引數解釋完了。下面我們解釋一下,經過一次卷積操作之後,影像形狀如何進行變化。
卷積神經網路的輸入: N , C i n , H i n , W i n N,C_{in},H_{in},W_{in} N,Cin,Hin,Win
卷積神經網路的輸出: N , C o u t , H o u t , W o u t N,C_{out},H_{out},W_{out} N,Cout,Hout,Wout
其中, N N N 表示批處理的大小,即batch_size。 C i n C_{in} Cin 和 C o u t C_{out} Cout 分別表示輸入和輸出通道數。 H i n H_{in} Hin 和 W i n W_{in} Win 表示輸入圖片的高和寬,以畫素為單位。同理, H o u t H_{out} Hout 和 W o u t W_{out} Wout 表示輸出圖片的高和寬。
OK,我們來看一下輸入輸出是怎麼變化的。
首先是batch_size,卷積神經網路並不會改變這個引數;然後是
C
i
n
C_{in}
Cin 和
C
o
u
t
C_{out}
Cout 這兩個引數是我們初始化的時候就指定的;實際上卷積神經網路改變的是圖片的尺寸,圖片尺寸變化公式為:
H
o
u
t
=
⌊
H
i
n
+
2
×
p
a
d
d
i
n
g
⌊
0
⌋
−
d
i
l
a
t
i
o
n
⌊
0
⌋
×
(
k
e
r
n
e
l
_
s
i
z
e
⌊
0
⌋
−
1
)
s
t
r
i
d
e
⌊
0
⌋
+
1
⌋
H_{out}=\lfloor \frac{H_{in} + 2 \times padding\lfloor 0 \rfloor - dilation \lfloor 0 \rfloor \times (kernel\_size\lfloor 0 \rfloor - 1)}{stride\lfloor 0 \rfloor} + 1 \rfloor
Hout=⌊stride⌊0⌋Hin+2×padding⌊0⌋−dilation⌊0⌋×(kernel_size⌊0⌋−1)+1⌋
W o u t = ⌊ W i n + 2 × p a d d i n g ⌊ 1 ⌋ − d i l a t i o n ⌊ 1 ⌋ × ( k e r n e l _ s i z e ⌊ 1 ⌋ − 1 ) s t r i d e ⌊ 1 ⌋ + 1 ⌋ W_{out}=\lfloor \frac{W_{in} + 2 \times padding\lfloor 1 \rfloor - dilation \lfloor 1 \rfloor \times (kernel\_size\lfloor 1 \rfloor - 1)}{stride\lfloor 1 \rfloor} + 1 \rfloor Wout=⌊stride⌊1⌋Win+2×padding⌊1⌋−dilation⌊1⌋×(kernel_size⌊1⌋−1)+1⌋
然後我們用一段程式碼測試一下:
import torch
import torch.nn as nn
# in_channels=16, out_channels=33, kernel_size=(3, 5)
m = nn.Conv2d(16, 33, (3, 5), stride=(2, 1), padding=(4, 2), dilation=(3, 1))
print(m)
# 我們隨機構造輸入資料
# N=20, C_in=16, H_in=50, W_in=100
# 對應上面說的 (N, C, H, W)
input = torch.randn(20, 16, 50, 100)
output = m(input)
print(output.shape)
結果:
Conv2d(16, 33, kernel_size=(3, 5), stride=(2, 1), padding=(4, 2), dilation=(3, 1))
torch.Size([20, 33, 26, 100])
上面的torch.size表示輸出的形狀:
b a t c h _ s i z e = 20 batch\_size=20 batch_size=20
o u t _ c h a n n e l = 33 out\_channel=33 out_channel=33
H o u t = 26 H_{out}=26 Hout=26
W o u t = 100 W_{out}=100 Wout=100
我們帶入資料實際計算一下:
H
o
u
t
=
⌊
50
+
2
×
4
−
3
×
(
3
−
1
)
−
1
2
+
1
⌋
=
26
H_{out}= \lfloor \frac{50 + 2 \times 4 - 3 \times (3 - 1) - 1}{2} + 1\rfloor=26
Hout=⌊250+2×4−3×(3−1)−1+1⌋=26
W o u t = ⌊ 100 + 2 × 2 − 1 × ( 5 − 1 ) − 1 1 + 1 ⌋ = 100 W_{out}=\lfloor \frac{100 + 2 \times 2 - 1 \times (5 - 1) - 1}{1} + 1 \rfloor = 100 Wout=⌊1100+2×2−1×(5−1)−1+1⌋=100
最後我們有必要了解一下Conv2d的兩個變數:
~Conv2d.weight
其形狀為: ( o u t _ c h a n n e l s , i n _ c h a n n e l s g r o u p , k e r n e l _ s i z e [ 0 ] , k e r n e l _ s i z e [ 1 ] ) (out\_channels, \frac{in\_channels}{group},kernel\_size[0], kernel\_size[1]) (out_channels,groupin_channels,kernel_size[0],kernel_size[1])
資料從 U ( − k , k ) \mathcal{U}(-\sqrt{k}, \sqrt{k}) U(−k,k) 中取樣,其中 k = g r o u p s C i n ∗ ∏ i = 0 1 k e r n e l _ s i z e [ i ] k=\frac{groups}{C_{in}*\prod_{i=0}^1kernel\_size[i]} k=Cin∗∏i=01kernel_size[i]groups
~Conv2d.bias
其形狀為: ( o u t _ c h a n n e l s ) (out\_channels) (out_channels)
資料從 U ( − k , k ) \mathcal{U}(-\sqrt{k}, \sqrt{k}) U(−k,k) 中取樣,其中 k = g r o u p s C i n ∗ ∏ i = 0 1 k e r n e l _ s i z e [ i ] k=\frac{groups}{C_{in}*\prod_{i=0}^1kernel\_size[i]} k=Cin∗∏i=01kernel_size[i]groups
還是上面的程式碼,我們可以檢視這個卷積神經網路的權重和偏置的形狀:
import torch
import torch.nn as nn
# in_channels=16, out_channels=33, kernel_size=(3, 5)
m = nn.Conv2d(16, 33, (3, 5), stride=(2, 1), padding=(4, 2), dilation=(3, 1))
print(m)
# 我們隨機構造輸入資料
# N=20, C_in=16, H_in=50, W_in=100
# 對應上面說的 (N, C, H, W)
input = torch.randn(20, 16, 50, 100)
output = m(input)
print(m.weight.shape)
print(m.bias.shape)
輸出:
torch.Size([33, 16, 3, 5])
torch.Size([33])
相關文章
- http協議/cookie詳解/session詳解HTTP協議CookieSession
- Java註解最全詳解(超級詳細)Java
- Java註解詳解Java
- Lombok 註解詳解Lombok
- @FeignClient註解詳解client
- Java 註解詳解Java
- ECharts 詳解Echarts
- Dialogment詳解
- hibernate詳解
- 詳解bind
- 詳解GOPATHGo
- nginx 詳解Nginx
- HTTP 詳解HTTP
- StreamingContext詳解GCContext
- JavaScript this詳解JavaScript
- promise詳解Promise
- DiskBasedCache詳解
- ReentrantLock詳解ReentrantLock
- Redis詳解Redis
- epoll詳解
- typeid詳解
- TLS 詳解TLS
- webpack詳解Web
- 列表詳解
- kubectl詳解
- HTML詳解HTML
- Callback詳解
- TCP詳解TCP
- JSON詳解JSON
- DiffUtil詳解
- LVS詳解
- JDBC詳解JDBC
- concurrentHashMap詳解HashMap
- BART詳解
- CSharp詳解CSharp
- ulimit詳解MIT
- JNDI詳解
- HiveQL詳解Hive