在計算機視覺中,卷積是最重要的概念之一。同時研究人員也提出了各種新的卷積或者卷積組合來進行改進,其中有的改進是針對速度、有的是為了加深模型、有的是為了對速度和準确率的trade-off。本文将簡單梳理一下卷積神經網絡中用到的各種卷積核以及改進版本。文章主要是進行一個梳理,着重講其思路以及作用。
- 1. Convolution
- 2. 1x1/Pointwise Convolutions
- 3. Spatial and Cross-Channel Convolutions
- 4. Grouped Convolutions
- 5. Separable Convolutions
- 6. Flattened Convolutions
- 7. Shuffled Grouped Convolutions
- 8. Dilated Convolution(Atrous Convolution)
- 9. Deformable Convolution
- 10. Attention
- 11. Summary
1. Convolution
下圖是一個單通道卷積操作的示意圖:

在深度學習中,卷積的目的是從輸入中提取有用的特征。在圖像進行中,卷積濾波器的選擇範圍非常廣,每種類型的濾波器(比如Sobel算子、Canny算子等)都有助于從輸入圖像中提取不同的方面或者特征,比如水準、垂直、邊緣或對角線等特征。
而在CNN中,不同的特征是通過卷積在訓練過程中自動學習得到的filter的權重得到的。卷積具有權重共享和平移不變性的優點。
下圖是一個單filter的卷積的示意圖:
多個filter效果就如下圖所示:
2. 1x1/Pointwise Convolutions
最初1x1卷積是在Network in Network中提出的,之後1x1convolution最初在GoogLeNet中大量使用,1x1卷積有以下幾個特點:
- 用于降維或者升維,可以靈活控制特征圖filter個數
- 減少參數量,特征圖filter少了,參數量也會減少。
- 實作跨通道的互動和資訊整合。
- 在卷積之後增加了非線性特征(添加激活函數)。
3. Spatial and Cross-Channel Convolutions
最初被使用在Inception子產品中,主要是将跨通道相關性和空間相關性的操作拆分為一系列獨立的操作。
Inception 簡化版本(圖源Xception)
先使用1x1 Convolution來限制通道個數,降低計算量,然後每個分支都是用3x3卷積,最終使用concat的方式融合特征。
pytorch實作(上圖隻是簡化圖,和代碼并不一一對應)
class InceptionA(nn.Module):
def __init__(self, in_channels, pool_features, conv_block=None):
super(InceptionA, self).__init__()
if conv_block is None:
conv_block = BasicConv2d
self.branch1x1 = conv_block(in_channels, 64, kernel_size=1)
self.branch5x5_1 = conv_block(in_channels, 48, kernel_size=1)
self.branch5x5_2 = conv_block(48, 64, kernel_size=5, padding=2)
self.branch3x3dbl_1 = conv_block(in_channels, 64, kernel_size=1)
self.branch3x3dbl_2 = conv_block(64, 96, kernel_size=3, padding=1)
self.branch3x3dbl_3 = conv_block(96, 96, kernel_size=3, padding=1)
self.branch_pool = conv_block(in_channels, pool_features, kernel_size=1)
def _forward(self, x):
branch1x1 = self.branch1x1(x)
branch5x5 = self.branch5x5_1(x)
branch5x5 = self.branch5x5_2(branch5x5)
branch3x3dbl = self.branch3x3dbl_1(x)
branch3x3dbl = self.branch3x3dbl_2(branch3x3dbl)
branch3x3dbl = self.branch3x3dbl_3(branch3x3dbl)
branch_pool = F.avg_pool2d(x, kernel_size=3, stride=1, padding=1)
branch_pool = self.branch_pool(branch_pool)
outputs = [branch1x1, branch5x5, branch3x3dbl, branch_pool]
return outputs
def forward(self, x):
outputs = self._forward(x)
return torch.cat(outputs, 1)
複制
4. Grouped Convolutions
組卷積最初是在AlexNet中提出的,之後被大量應用在ResNeXt網絡結構中,提出的動機就是通過将feature 劃分為不同的組來降低模型計算複雜度。
下圖詳解了組卷積計算過程。
所謂分組就是将輸入feature map的通道進行分組,然後每個組内部進行卷積操作,最終将得到的組卷積的結果Concate到一起,得到輸出的feature map。
ResNeXt是ResNet和Inception的結合,其每個分支都采用的相同的拓撲結構。ResNeXt本質是使用組卷積(Grouped Convolutions),通過基數( cardinality )來控制組的數量。
使用組卷積的優點:
- 訓練效率高。由于卷積被分為幾個不同的組,每個組的計算就可以配置設定給不同的GPU核心來進行計算。這種結構的設計更符合GPU并行計算的要求,這也能解釋為何ResNeXt在GPU上效率要高于Inception子產品。
- 模型效率高。模型參數随着組數或者基數的增加而減少。
-
效果好。分組卷積可能能夠比普通卷積組成的模型效果更優,這是因為濾波器之間的關系是稀疏的,而劃分組以後對模型可以起到一定正則化的作用。從COCO資料集榜單就可以看出來,有很多是ResNeXt101作為backbone的模型在排行榜非常靠前的位置。
組卷積為何效果更好的詳細解釋可以看這篇部落格:https://blog.yani.io/filter-group-tutorial/,其中有比較詳細的解釋。
5. Separable Convolutions
可分離卷積可以分為空間可分離卷積(Spatially Separable Convolutions)和深度可分離卷積(depthwise separable convolution)。
假設feature的size為[channel, height , width]
- 空間也就是指:[height, width]這兩次元組成的。
- 深度也就是指:channel這一次元。
5.1 Spatially Separable Convolutions
簡單來講,空間可分離卷積就是将原nxn的卷積,分開計算變為1xn和nx1兩步。
下邊舉一個例子:
普通的3x3卷積在一個5x5的feature map上是如下圖這樣進行計算:
每個位置需要9次乘法,一共有9個位置,是以整個操作下來就是9x9=81次乘法操作。
如果用空間可分離卷積的話,如下圖所示:
第一步先使用3x1的filter,所需計算量為:15x3=45
第二步使用1x3的filter,所需計算量為:9x3=27
總共需要72次乘法就可以得到最終結果,要小于普通卷積的81次乘法。
推廣一下,假設對nxn的feature map使用kernel size為mxm的卷積,
普通卷積需要計算的乘法次數為:
空間可分離卷積需要計算的乘法次數為:
那麼代價比為:
在n>>m的情況下,這個比值将變為2/m,是以可以極大降低計算量。
雖然空間可分離卷積節省了計算成本,但是一般情況很少用到。原因是并非所有的kernel 都可以分為兩個較小的kernel;空間可分離卷積可能會帶來一定的資訊損失;如果将全部的傳統卷積替換為空間可分離卷積,将影響模型的容量, 這樣得到的訓練結果可能是次優的。
5.2 Depthwise Separable Convolutions
深度可分離卷積在Xception或者MobileNet中大量使用,主要有兩個部分組成:
- Depthwise Convolution: 獨立地施加在每個通道的空間卷積
- Pointwise Convolution: 1x1 convolution,通過深度卷積将通道輸出投影到一個新的通道空間。
下邊是一個深度可分離卷積的pytorch實作:
class DWConv(nn.Module):
def __init__(self, in_plane, out_plane):
super(DWConv, self).__init__()
self.depth_conv = nn.Conv2d(in_channels=in_plane,
out_channels=in_plane,
kernel_size=3,
stride=1,
padding=1,
groups=in_plane)
self.point_conv = nn.Conv2d(in_channels=in_plane,
out_channels=out_plane,
kernel_size=1,
stride=1,
padding=0,
groups=1)
def forward(self, x):
x = self.depth_conv(x)
x = self.point_conv(x)
return x
複制
通過比對代碼,很容易了解下圖的操作過程:
Inception子產品和可分離卷積的差別:
- 可分離卷積是先用Depthwise Convolution, 然後再使用1x1卷積;Inception中是先使用1x1 Convolution,然後再使用Depthwise Convolution。
- 深度可分離卷積實作的時候沒有增加非線性特征(也就是使用激活函數)。
下面再來比較一下所需計算量:
以上圖為例,普通卷積需要的計算量為:
對應128個3x3x3的卷積核移動5x5次的結果。
深度可分離卷積計算量應該分為兩個部分:
第一步:depthwise convolution 有3個3x3x1的kernel移動5x5次。
第二步:1x1 convolution 有128個1x1x3的kernel移動5x5次。
兩步總計10275次乘法,隻有普通卷積計算量的12%左右。
推廣一下,對于一個輸入尺寸為[C,H,W]的feature map, 如果用stride=1、padding=0、kernel size=h(h為奇數),那麼輸出的尺寸為。
普通卷積需要的乘法次數為:
深度可分離卷積所需要乘法次數為:
代價比為:
在Nc>>h的情況下,代價比可約等于h的平方分之一。
同樣,深度可分離卷積也有缺點,通過使用深度可分離卷積替代普通的卷積,可以顯著降低模型的計算量,但是與此同時會導緻模型的容量也顯著降低。這将導緻訓練得到的結果可能也不是最優的。是以,在使用深度可分離卷積的時候要考慮模型容量和計算效率的平衡。
6. Flattened Convolutions
最初在Flattened Convolutional Neural Networks for Feedforward Acceleration中提出,是2015年ICLR的workshop。(連結:https://arxiv.org/abs/1412.5474)
在了解了空間可分離卷積以後,再來看Flattened Convolutions就比較簡單了,Flattened Convolution将标準的卷積核拆分成3個1D卷積核(空間可分離卷積隻拆分HxW次元),可以極大地降低了計算成本。
論文在結論中提到,使用Flattened Convolutions能夠将計算量減少為原來的10倍,并可以達到類似或更高的準确率在CIFAR-10、CIFAR-100和MNIST資料集中。
深度學習中的學習型濾波器具有分布特征值,直接将分離應用在濾波器中會導緻嚴重的資訊損失,過多使用的話會對模型準确率産生一定影響。
7. Shuffled Grouped Convolutions
最初是在ShuffleNet中提出的,使用了pointwise group convolution和channel shuffle兩種操作,能夠在保持精度的同時極大地降低計算量。之前解讀的ThunderNet也是在ShuffleNetV2為基礎進行改進的。
Channel Shuffle操作主要是為了消除原來Grouped Convolution中存在的副作用,也就是輸出feature map的通道僅僅來自輸入通道的一小部分,是以每個濾波器組僅限于學習一些特定的特性,如下圖(a)所示。
Grouped Convolution的這個屬性會阻礙資訊在通道組之間的資訊流動并削弱了模型的表達。通過使用Channel Shuffle可以促進通道間資訊的融合進而解決以上問題。
從上圖中,(a)代表的是組卷積,所有輸出隻和一部分輸入有關(b)代表的是Channel Shuffle組合的方式,不同的組内部進行了重排,都是用到了輸入的一部分(c)代表的是一種與(b)等價的實作方式。
ShuffleNet還用到了pointwise grouped convolution, 作者認為1x1卷積成本也非常高,是以也對1x1卷積使用組卷積,具體子產品化的設計如下圖(b)和(c)所示。
是以實際上用到了三種類型的卷積:
- shuffled grouped convolution = grouped convolution + Channel Shuffle
- pointwise grouped convolution = 1x1 convolution + grouped convolution
- depthwise separable convolution
8. Dilated Convolution(Atrous Convolution)
空洞卷積是在DeepLabv1和《Multi-scale context aggregation by dilated convolutions》中提出的。
空洞卷積是在kernel之間插入空洞,并引入了空洞率,普通卷積如下圖(a)所示,其空洞率為1, (b)中所示的Dilated Convolution的空洞率為2,(c)中空洞率為4。
上圖中淺綠色的正方形塊代表Dilated Convolution對應的感受野, 顔色越深代表其被覆寫的次數越多。而以上三種方法所需要的計算量是相等的,也就是說,空洞卷積能夠在不增加計算量的情況下,增加模型的感受野。并且如果使用多個空洞卷積組成多層結構來建構網絡,有效感受野将呈指數級增長,而所需要的參數數量僅僅呈線性增長。
空洞卷積用于多尺度的上下文資訊,并且不會丢失分辨率,在其應用到語義分割模型後,達到了當時的STOA。
不過也存在幾個問題:
- Gridding Effect:
上圖是具體使用到的結點,可以發現kernel并不連續,并不是所有的點都被計算了,這會導緻損失資訊的連續性,對細粒度資訊處理(pixel-level dense prediction)來說并不友好。
- 上下文資訊多少
dilated convolution可以擷取長距離資訊,使用dilated convolution對大物體的效果會有一定效果,但是對小物體來說并不友好,小物體所需要的感受野并不需要太大。是以如何同時處理好不同大小物體之間的關系是使用空洞卷積的關鍵。
9. Deformable Convolution
可變形卷積(DCN) 是一個特别新穎的想法,最初是為了提升目标檢測模型,提出以後也成為刷榜利器。
DCN提出的動機是為何特征提取形狀一定要是正方形或者矩形,為何不能更加自适應的分布到目标上(如下圖右側所示),進而提出了一種形狀可學習的卷積。
實作過程如下圖所示,在普通卷積之外又添加了一個分支,專門用于學習每個點的偏移量,将原始的卷積和offset結合就形成了可變形卷積。
10. Attention
這裡介紹兩個最經典的注意力機制的子產品,SE Module和CBAM Module。
10.1 Squeeze and Excitation
SENet的核心想法是,每個通道的重要性不是一樣的。基于這個想法,SENet添加了一個子產品,如上圖靠上的分支,這個子產品作用是給每個通道打分,最終将打分的結果與卷積得到的feature map相乘,完成特征通道的權重重配置設定。SENet是通道注意力機制的最經典的實作。
pytorch實作:
class SELayer(nn.Module):
def __init__(self, channel, reduction=16):
super(SELayer, self).__init__()
self.avg_pool = nn.AdaptiveAvgPool2d(1)
self.fc = nn.Sequential(
nn.Linear(channel, channel // reduction, bias=False),
nn.ReLU(inplace=True),
nn.Linear(channel // reduction, channel, bias=False),
nn.Sigmoid()
)
def forward(self, x):
b, c, _, _ = x.size()
y = self.avg_pool(x).view(b, c)
y = self.fc(y).view(b, c, 1, 1)
return x * y.expand_as(x)
複制
其具體實作也非常簡單,值得一提的是,SENet成功拿到了ImageNet2017分類比賽的冠軍。
缺點:不利于并行處理,添加SELayer以後導緻在GPU上運作速度有一定的減慢。
10.2 Convolutional Block Attention Module
CBAM子產品算是比較早的一批将通道注意力機制和空間注意力機制結合起來的模型,通過添加該子產品,能在一定程度上優化feature。
CBAM分為Channel Attention Module 和 Spatial Attention Module:
channel attention module
pytorch實作通道注意力機制:
class ChannelAttention(nn.Module):
def __init__(self, in_planes, rotio=16):
super(ChannelAttention, self).__init__()
self.avg_pool = nn.AdaptiveAvgPool2d(1)
self.max_pool = nn.AdaptiveMaxPool2d(1)
self.sharedMLP = nn.Sequential(
nn.Conv2d(in_planes, in_planes // ratio, 1, bias=False), nn.ReLU(),
nn.Conv2d(in_planes // rotio, in_planes, 1, bias=False))
self.sigmoid = nn.Sigmoid()
def forward(self, x):
avgout = self.sharedMLP(self.avg_pool(x))
maxout = self.sharedMLP(self.max_pool(x))
return self.sigmoid(avgout + maxout)
複制
spatial Attention module
pytorch實作空間注意力機制:
class SpatialAttention(nn.Module):
def __init__(self, kernel_size=7):
super(SpatialAttention, self).__init__()
assert kernel_size in (3,7), "kernel size must be 3 or 7"
padding = 3 if kernel_size == 7 else 1
self.conv = nn.Conv2d(2,1,kernel_size, padding=padding, bias=False)
self.sigmoid = nn.Sigmoid()
def forward(self, x):
avgout = torch.mean(x, dim=1, keepdim=True)
maxout, _ = torch.max(x, dim=1, keepdim=True)
x = torch.cat([avgout, maxout], dim=1)
x = self.conv(x)
return self.sigmoid(x)
複制
最終CBAM子產品選擇将兩個部分進行串聯:
11. Summary
卷積核的設計非常多,以上僅僅是一部分常見的卷積核,以上卷積核可以這樣分類:
- 通道和空間
- Convolution
- 1x1 Convolution
- Spatial and Cross-Channel Convolutions
- 通道相關性(channel)
- Depthwise Separable Convolutions
- Shuffled Grouped Convolutions
- Squeeze and Excitation Network
- Channel Attention Module in CBAM
- 空間相關性(HxW)
- Spatially Separable Convolutions
- Flattened Convolutions
- Dilated Convolutions
- Deformable Convolution
- Spatial Attention Module in CBAM
現在很多CNN模型準确率越來越高,很多研究人員的研究方向也轉向如何在盡可能保證準确率的情況下,盡可能減少模型參數,做好準确率和速度的平衡。
其中ShuffleNet系列效果得到了認可(ShuffleNetV2是騰訊掃一掃項目和ThudnerNet的backbone)。
總結一下效果優異的人工設計的backbone可能會用到以下政策:
- 單一尺寸卷積核用多個尺寸卷積核代替(參考Inception系列)
- 使用可變形卷積替代固定尺寸卷積(參考DCN)
- 大量加入1x1卷積或者pointwise grouped convolution來降低計算量(參考NIN、ShuffleNet)
- 通道權重處理(參考SENet)
- 用深度可分離卷積替換普通卷積(參考MobileNet)
- 使用分組卷積(參考ResNeXt)
- 分組卷積+channel shuffle(參考shuffleNet)
- 使用Residual連接配接(參考ResNet)
最後附上一個18年綜述《Benchmark Analysis of RepresentativeDeep Neural Network Architectures》中的圖,文章也提供了代碼:https://github.com/CeLuigi/models-comparison.pytorch
緻謝:感謝Kunlun Bai,本文中使用了不少便于了解的圖都是出自這位作者。
Reference
https://ikhlestov.github.io/pages/machine-learning/convolutional-layers/
https://zhuanlan.zhihu.com/p/37910136
https://ikhlestov.github.io/pages/machine-learning/convolutions-types/
https://arxiv.org/abs/1610.02357
https://arxiv.org/abs/1704.04861
https://arxiv.org/pdf/1611.05431.pdf
https://arxiv.org/abs/1707.01083
https://towardsdatascience.com/a-comprehensive-introduction-to-different-types-of-convolutions-in-deep-learning-669281e58215
http://fourier.eng.hmc.edu/e161/lectures/convolution/index.html
https://arxiv.org/pdf/1412.7062
https://arxiv.org/abs/1511.07122
https://arxiv.org/abs/1412.5474
https://www.zhihu.com/question/54149221
https://zhuanlan.zhihu.com/p/28749411
https://arxiv.org/pdf/1810.00736.pdf