在自己设置网络的时候需要考虑到每一个Conv或者Pooling的下采样率,所以我们需要对padding进行设置。
卷积和池化后形状计算公式分别如下:
没有空洞卷积的计算:
S h a p e i c o n v / p o o l = S h a p e i i n + 2 p i − k i s i + 1 \mathrm{Shape}_i^{\mathrm{conv / pool }}=\frac{\mathrm{Shape}_i^{\mathrm{in}}+2p_i-k_i}{s_i}+1 Shapeiconv/pool=siShapeiin+2pi−ki+1
带有空洞卷积后的计算:
S h a p e i d i a l a t e d c o n v = S h a p e i i n + 2 p i − d i × ( k i − 1 ) s i + 1 \mathrm{Shape}_i^{\mathrm{dialated \ conv}}=\frac{\mathrm{Shape}_i^{\mathrm{in}}+2p_i-d_i\times (k_i-1)}{s_i}+1 Shapeidialated conv=siShapeiin+2pi−di×(ki−1)+1
卷积: 向下取整
池化: 向上取整
要想方便的控制输出feature map的shape,我们可以采用控制单个数的方法,即只需控制stride
而不用考虑kernel_size
或者pooling
。
一般有两种方式:
方法1:
padding = (kernel_size - 1) // 2
局限性:kernel_size只能是奇数
方法2:
padding = (kernel_size * 2 + 1) // 2
没有局限性
为了更加直观使用这两个公式,我们举个例子:
self.conv = nn.Sequential(
# PwConv
conv_layer(inp, exp, kernel_size=(1, 1), stride=(1, 1), padding=(0, 0), bias=False),
norm_layer(exp),
nlin_layer(inplace=True),
# DwConv
conv_layer(exp, exp, kernel_size=kernel,
stride=stride,
padding=((kernel - 1) // 2, (kernel - 1) // 2),
groups=exp, bias=False),
norm_layer(exp),
SELayer(exp),
nlin_layer(inplace=True),
# PwConv
conv_layer(exp, oup, kernel_size=(1, 1), stride=(1, 1), padding=(0, 0), bias=False),
norm_layer(oup),
)
以上是MobileNet v3的Bottleneck中的卷积部分,可以看到该模块是由PwConv -> DwConv -> PwConv
组成。
对于PwConv来说,kernel_size=1, stride=1
, 代入公式 padding =(kernel_size -1)//2
,我们可以求出:
p a d d i n g = ( k e r n e l s i z e − 1 ) / / 2 = ( 1 − 1 ) / / 2 = 0 \mathrm{padding} = (\mathrm{kernel \ size} - 1) // 2 \\ = (1-1) // 2 \\ =0 padding=(kernel size−1)//2=(1−1)//2=0
所以对于PwConv,padding设置为0
即可。
对于DWConv来说,kernel_size我们是想控制的,一般是3×3、5×5卷积
,stride我们也是想控制的,因为有些时候我们需要让它下采样,有些时候又不想让它下采样,所以我们将padding设置为padding =(kernel_size -1)//2
,这样的好处就是:
我们无需关心padding了,我们可以随意设置卷积核的大小,此时padding默认为same
。
stride=1
时,不进行下采样stride=2
时,进行2倍下采样