Vanilla Convolution 普通卷積
卷積通道數:
- 卷積核的數量決定輸出的張量的通道數nnn,輸入的張量和每一個核Kernel做卷積運算得到一個channel的輸出。
- 輸入通道數CinC_{in}Cin?決定每一個卷積核的通道數
卷積輸出feature map的尺寸的計算公式:
- W_out=Win+2Pw?KwSw+1W\_out = \frac{W_{in} + 2P_w - K_w}{S_w} + 1W_out=Sw?Win?+2Pw??Kw??+1
- H_out=Hin+2Ph?KhSh+1H\_out = \frac{H_{in} + 2P_h - K_h}{S_h} + 1H_out=Sh?Hin?+2Ph??Kh??+1
其中,輸入的尺寸為 (Win,Hin)(W_{\text{in}}, H_{\text{in}})(Win?,Hin?),卷積核的尺寸為 (Kw,Kh)(K_w, K_h)(Kw?,Kh?),步幅為 (Sw,Sh)(S_w, S_h)(Sw?,Sh?),填充為 (Pw,Ph)(P_w, P_h)(Pw?,Ph?),輸出的尺寸為 (Wout,Hout)(W_{\text{out}}, H_{\text{out}})(Wout?,Hout?)
Group Convolution 分組卷積
Group convolution(分組卷積)最早是在AlexNet(Alex Krizhevsky等人于2012年提出的深度神經網絡模型)中引入的。在 AlexNet中,作者們使用了分組卷積來將計算分布到多個GPU上。
計算
Group Convolution :將輸入的 feature map張量 在 channel 的維度上進行分組,然后再對每個分組 分別進行卷積操作,再將各個輸出沿channel維度拼接在一起。
參數量
Group Convolution 優點為可減小參數量。
如下圖,假設輸入尺寸為 Cin×H×WC_{in} \times H \times WCin?×H×W,卷積核尺寸為 K×KK \times KK×K,輸出的 Channel 數為 nnn,我們對于相同的輸入和輸出尺寸,分別使用 普通卷積 和 分組卷積來進行操作,并對比兩種卷積方式的參數量。
- 普通卷積:每個Kernel參數量為K×K×CinK \times K \times C_{in}K×K×Cin? 一共有nnn個Kernel,總共參數量為:
K×K×Cin×nK \times K \times C_{in} \times n K×K×Cin?×n - 分組卷積:將輸入分為ggg個組,每個組進入普通卷積的channel數為 Cing\frac{C_{in}}{g}gCin??,要保證最后沿channel維度拼接后為nnn,所以每個普通卷積的數維度為ng\frac{n}{g}gn?,所以每個組用的卷積核參數量為 K×K×Cing×ngK \times K \times \frac{C_{in}}{g} \times \frac{n}{g}K×K×gCin??×gn?,一共ggg個組,所以總參數量為:
(K×K×Cing×ng)×g=K×K×Cin×ng(K \times K \times \frac{C_{in}}{g} \times \frac{n}{g} ) \times g = K \times K \times C_{in}\times \frac{n}{g} (K×K×gCin??×gn?)×g=K×K×Cin?×gn?
所以分組卷積是普通卷積參數量的1g\frac{1}{g}g1?。
代碼實現
- 使用普通卷積方法
torch.nn.conv2d()
,通過參數groups
指定組數 - 注意:
in_channels
和out_channels
都必須可以被groups
整除,否則會報錯, 類似 :ValueError: in_channels must be divisible by groups
import torch
import torch.nn as nnconv = nn.Conv2d(in_channels=10, out_channels=15, kernel_size=3, groups=5, stride=1, padding=1)
output = conv(torch.rand(1, 10, 20, 20))
print(output.shape) # torch.Size([1, 15, 20, 20])
Depth-wise Convolution 逐深度卷積
是分組卷積的特例:當Group Convolution 的分組數量 ggg 最大,等于輸入channel 數CinC_{in}Cin?時,就變為了 Depth-wise Convolution逐深度卷積。
計算
將輸入的每個通道(channel)獨立地進行 普通卷積運算(CinC_{in}Cin?和CoutC_{out}Cout?都為1),每個通道使用一個單獨的卷積核進行處理,即為 DWConvolution
具體做法如下:
- 將輸入特征圖按照 channel 進行分組,每個 channel 一個組,即 g=Cing = C_{in}g=Cin?
- 每個 channel (即每個 group ) 使用一個獨立的卷積核(大小為 1×k×k1 \times k \times k1×k×k )對其對應通道做卷積運算,輸入輸出通道均為 1
和Group Convolution的優點一樣,參數量和計算量小。
代碼實現
nn.Conv2d
的參數 out_channels
和 groups
都設置為等于 in_channels
import torch
import torch.nn as nnconv = nn.Conv2d(in_channels=10, out_channels=10, kernel_size=3, groups=10, stride=1, padding=1)
output = conv(torch.rand(1, 10, 20, 20))
print(output.shape) # torch.Size([1, 10, 20, 20])
Point-wise Convolution 逐點卷積
是特殊的普通卷積,kernel_size=1x1
,也就是我們經常看到的一乘一卷積 conv 1x1
,
因為感受野是1x1,沒有融合垂直于channl維度的平面上的信息,所以它通常用來組合每個像素各個通道之間的特征信息。
優點 :參數量和計算量小
代碼實現:將 nn.Conv2d
的參數 kernel_size
設置為等于 1
import torch
import torch.nn as nnconv = nn.Conv2d(in_channels=10, out_channels=10, kernel_size=1, stride=1)
output = conv(torch.rand(1, 10, 20, 20))
print(output.shape) # torch.Size([1, 10, 20, 20])
Depth-wise Separable Convolution 深度可分離卷積
提出背景
Depth-wise Separable Convolution(深度可分離卷積)最早是由Google的研究團隊在2014年提出的。該方法首次出現在論文《MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications》中。
計算
Depth-wise Separable Convolution 是
- 先做一個 Depth-wise Convolution ,后面再做一個 Point-wise Convolution
- 或者 先做一個 Depth-wise Convolution ,后面再做一個 Point-wise Convolution
計算量
Depth-wise Separable Convolution 的優勢是 極大的減小了卷積的計算量。
這里還是對于相同的輸入和輸出尺寸,對比普通卷積核深度可分離卷積的計算量。
輸入尺寸DF×DF×MD_F\times D_F \times MDF?×DF?×M,輸出通道數為NNN
1、普通卷積計算量
對于普通卷積,計算量公式:
Dk?Dk?M?N?DF?DF(1)D_k \cdot D_k \cdot M \cdot N \cdot D_F \cdot D_F \tag{1} Dk??Dk??M?N?DF??DF?(1)
2、深度可分離卷積(Depth-wise Separable Convolution)計算量
計算量分為兩部分:
- Depth-wise 卷積:計算量為 Dk?Dk?M?DF?DFD_k \cdot D_k \cdot M \cdot D_F \cdot D_FDk??Dk??M?DF??DF?
- Point-wise 卷積:計算量為 (1?1?M?DF?DF)?N(1\cdot 1\cdot M \cdot D_F \cdot D_F) \cdot N(1?1?M?DF??DF?)?N
深度可分離卷積計算量公式:
Dk?Dk?M?DF?DF+M?N?DF?DF(2)D_k \cdot D_k \cdot M \cdot D_F \cdot D_F + M \cdot N \cdot D_F \cdot D_F \tag{2} Dk??Dk??M?DF??DF?+M?N?DF??DF?(2)
3、計算量對比(公式(2) ÷ 公式(1))
為對比普通卷積與深度可分離卷積的計算量,用作商法比大小,將公式(2)除以公式(1),推導得:
Dk?Dk?M?DF?DF+M?N?DF?DFDk?Dk?M?N?DF?DF=1N+1Dk2(3)\frac{D_k \cdot D_k \cdot M \cdot D_F \cdot D_F + M \cdot N \cdot D_F \cdot D_F}{D_k \cdot D_k \cdot M \cdot N \cdot D_F \cdot D_F} = \frac{1}{N} + \frac{1}{D_k^2} \tag{3} Dk??Dk??M?N?DF??DF?Dk??Dk??M?DF??DF?+M?N?DF??DF??=N1?+Dk2?1?(3)
4、典型場景(Dk=3D_k = 3Dk?=3,即 3×3 卷積)
實際常用 3×3 卷積(Dk=3D_k = 3Dk?=3 ),代入公式(3)得:
1N+1Dk2=1N+19\frac{1}{N} + \frac{1}{D_k^2} = \frac{1}{N} + \frac{1}{9} N1?+Dk2?1?=N1?+91?
5、結論
理論上,普通卷積計算量是深度可分離卷積的 8~9 倍(因NNN為輸出channel數往往很大,所以 1N\frac{1}{N}N1? 通常很小,主導項為 $ \frac{1}{9} $ ,故整體約為 9 倍 )。
Dilation/Atrous Convolution 膨脹卷積/空洞卷積
計算與代碼實現
- 普通卷積輸出的每個像素是輸入的連續的像素與卷積核運算得到的。
- 膨脹卷積輸出的每個像素是輸入的有間隔的像素與Kernel運算得到的。
作用:增大卷積核的感受野。
感受野(Receptive Field)指CNN網絡中,每個特征圖的每個像素點對應輸入圖像的區域大小。
代碼實現:指定nn.Conv2d()
的膨脹率參數 dilation
- 當 dilation=1 時,沒有間隔,是普通卷積
- 當 dilation=2 時,卷積核元素中間間隔一個像素
import torch
import torch.nn as nnconv = nn.Conv2d(in_channels=10, out_channels=10, kernel_size=3, stride=1, padding=2, dilation=2)
output = conv(torch.rand(1, 10, 20, 20))
print(output.shape) # torch.Size([1, 10, 20, 20])
padding參數設置
要保證輸入輸出的大小尺寸不變
- 普通卷積:步長stride=1,填充padding=1,即原圖多加一圈像素。
- 膨脹卷積:步長stride=1,填充padding=2,加兩圈像素。
缺點:網格效應Gridding Effect
由于膨脹卷積是一種稀疏的采樣方式,當多個膨脹卷積疊加時,有些像素根本沒有被利用到,會損失信息的連續性與相關性,進而影響分割、檢測等要求較高的任務。
比如這里連續用了兩次膨脹卷積得到的feature map中的一個綠色的像素,回溯回去是由紫色像素得到的。所以如果整個網絡只有兩層膨脹卷積的話,相當于只有原圖的四分之一的像素用到了。
Transposed Convolution 轉置卷積
轉置卷積也是卷積,只不過轉置卷積 是一種上采樣操作
如下圖的轉置卷積所示,輸入圖像尺寸為 2x2, 輸出圖像的尺寸為 4x4。
參數設置
如何設置 轉置卷積 的 stride 和 padding
已知 input 和 kernel 求 output
Deformable Convolution 可變形卷積
論文:《Deformable Convolutional Networks》
普通卷積公式:
y(p0)=∑pn∈Rw(pn)?x(p0+pn)\mathbf{y}(\mathbf{p}_0) = \sum_{\mathbf{p}_n \in \mathcal{R}} \mathbf{w}(\mathbf{p}_n) \cdot \mathbf{x}(\mathbf{p}_0 + \mathbf{p}_n) y(p0?)=pn?∈R∑?w(pn?)?x(p0?+pn?)
- y(p0)\boldsymbol{y(p_0)}y(p0?):輸出特征圖在位置 p0\boldsymbol{p_0}p0? 處的值
- w(pn)\boldsymbol{w(p_n)}w(pn?):卷積核(權重)在位置 pn\boldsymbol{p_n}pn? 處的參數
- x(p0+pn)\boldsymbol{x(p_0 + p_n)}x(p0?+pn?):輸入數據在位置 p0+pn\boldsymbol{p_0 + p_n}p0?+pn? 處的原始值
- R\mathcal{R}R:卷積核的作用范圍(即卷積核覆蓋的所有位置集合,決定卷積的“視野”)
簡單說,就是輸出位置的值 = 卷積核權重與輸入對應區域的加權求和。
可變性卷積公式:y(p0)=∑pn∈Rw(pn)?x(p0+pn+Δpn)\mathbf{y}(\mathbf{p}_{0})=\sum_{\mathbf{p}_{n}\in\mathcal{R}}\mathbf{w}(\mathbf{p}_{n})\cdot\mathbf{x}(\mathbf{p}_{0}+\mathbf{p}_{n}+\Delta\mathbf{p}_{n})y(p0?)=pn?∈R∑?w(pn?)?x(p0?+pn?+Δpn?)
- y(p0)\mathbf{y}(\mathbf{p}_0)y(p0?):輸出特征圖在位置 p0\mathbf{p}_0p0? 處的像素值
- R\mathcal{R}R:卷積核的固定感受野范圍(如 3×3 卷積的 9 個位置集合)
- w(pn)\mathbf{w}(\mathbf{p}_n)w(pn?):卷積核在固定位置 pn\mathbf{p}_npn? 處的權重參數
- x(?)\mathbf{x}(\cdot)x(?):輸入特征圖
- Δpn\Delta\mathbf{p}_nΔpn?:偏移量表示卷積核第 nnn 個采樣點相對于固定位置 pn\mathbf{p}_npn? 的偏移。偏移量 Δpn\Delta\mathbf{p}_nΔpn?是學習到的值,是浮點型數據,由圖像經過普通卷積計算得到。