Depthwise separable convolution
深度級可分離卷積其實是一種可分解卷積操作(factorized convolutions).將卷積分解成兩個更小的操作:
- depthwise convolution
- pointwise convolution
在標準卷積中,其卷積核作用于所有輸入通道上(input channel).如輸出圖片為:(6,6,3),原來卷積操作是:(4,4,3,5).4X4的卷積大小,3是卷積的通道數(shù),5是卷積核的數(shù)量.其輸出尺寸為,其輸出特征為(3,3,5),如圖所示(同一個卷積核作用在所有通道上):
不同通道結(jié)果相加得到該卷積核的感受野的值.
深度分離卷積(depthwise convolution),針對每個輸入通道采用不同的卷積核瓶籽,就是說一個卷積核對應(yīng)一個輸入通道.如圖所示:
輸入有3個通道,對應(yīng)著有3個大小為(4,4,1) (4,4,1)(4,4,1)的深度卷積核祟同,卷積結(jié)果共有3個大小為(3,3,1) (3,3,1)(3,3,1)
最后,采用pointwise convolution.pointwise convolution其實就是普通的卷積悼沈,只不過其采用1x1的卷積核。1 X 1的卷積核作用 用于將不同通道的值相加.
depthwise convolution先將通道(深度)分別進行卷積操作,再用pointwise convolution(1X1的卷積)進行通道間的卷積感受野值相加 最終結(jié)果與標準卷積類似.
官方示意圖如下所示:
計算量估計
假設(shè)輸入特征圖大小為:,而輸出特征圖大小為:
假設(shè)特征圖的width與height,與輸出圖大小一樣,兩者差別在于通道.
標準卷積
對于標準卷積():
每個卷積核的乘積計算量:
單個通道輸出特征圖邊長所需的乘積計算量:
單個卷積核跨通道值相乘:
N個卷積:
depthwise convolution
對于每一個通道,都有一個卷積,所以其計算量為:
pointwise convolution
1*1卷積,其計算量為:
最后比較depthwise separable convolution和標準卷積如下:
一般情況下 N 比較大,那么如果采用3x3卷積核的話巩步,depthwise separable convolution相較標準卷積可以降低大約9倍的計算量靖避。其實,后面會有對比择克,參數(shù)量也會減少很多。
mobilenet v1 結(jié)構(gòu)
基礎(chǔ)結(jié)構(gòu)如圖:
原始的mobilenet v1 用于imagenet分類任務(wù)中,整個網(wǎng)絡(luò)有28層.
mobilenet 超參數(shù)
- width multiplier
- resolution multiplier
width multiplier主要按照比例減少通道數(shù):
其記為,取值范圍從(0,1]
總計算量變?yōu)?
DK · DK · αM · DF · DF + αM · αN · DF · DF
分辨率因子用來控制輸入的分辨率,記為:
DK · DK · αM · ρDF · ρDF + αM · αN · ρDF · ρDF
pytorch實現(xiàn):
在torch.nn.Conv2d中,group參數(shù)用來控制是否對輸入的每個通道單獨設(shè)置卷積:
- At groups=1, all inputs are convolved to all outputs.(groups=1的時候,為標準卷積)
- At groups=2, the operation becomes equivalent to having two conv layers side by side, each seeing half the input channels, and producing half the output channels, and both subsequently concatenated.(=2時,用兩個卷積核,一個卷積核看一半,最后concat)
- At groups= in_channels, each input channel is convolved with its own set of filters.(每個通道放一個卷積核,這就是我們要的depthwise convolution)
所以,論文中的mobilenet基礎(chǔ)結(jié)構(gòu)為:
def conv_dw(inp, oup, stride):
return nn.Sequential(
nn.Conv2d(inp, inp, 3, stride, 1, groups=inp, bias=False),
nn.BatchNorm2d(inp),
nn.ReLU(inplace=True),
nn.Conv2d(inp, oup, 1, 1, 0, bias=False),
nn.BatchNorm2d(oup),
nn.ReLU(inplace=True),
)
整個mobilenet v1的論文結(jié)構(gòu)代碼為:
class MobileNet(nn.Module):
def __init__(self):
super(MobileNet, self).__init__()
def conv_bn(inp, oup, stride):
return nn.Sequential(
nn.Conv2d(inp, oup, 3, stride, 1, bias=False),
nn.BatchNorm2d(oup),
nn.ReLU(inplace=True)
)
def conv_dw(inp, oup, stride):
return nn.Sequential(
nn.Conv2d(inp, inp, 3, stride, 1, groups=inp, bias=False),
nn.BatchNorm2d(inp),
nn.ReLU(inplace=True),
nn.Conv2d(inp, oup, 1, 1, 0, bias=False),
nn.BatchNorm2d(oup),
nn.ReLU(inplace=True),
)
self.model = nn.Sequential(
conv_bn( 3, 32, 2),
conv_dw( 32, 64, 1),
conv_dw( 64, 128, 2),
conv_dw(128, 128, 1),
conv_dw(128, 256, 2),
conv_dw(256, 256, 1),
conv_dw(256, 512, 2),
conv_dw(512, 512, 1),
conv_dw(512, 512, 1),
conv_dw(512, 512, 1),
conv_dw(512, 512, 1),
conv_dw(512, 512, 1),
conv_dw(512, 1024, 2),
conv_dw(1024, 1024, 1),
nn.AvgPool2d(7),
)
self.fc = nn.Linear(1024, 1000)
def forward(self, x):
x = self.model(x)
x = x.view(-1, 1024)
x = self.fc(x)
return x