一句话介绍:使用多用卷积核构建轻量网络模型
优势:不改变原来的网络结构,只需要换一下卷积的接口
论文地址
开源代码
原作者的知乎专栏有简单介绍
一、背景知识介绍
1. 感受野
感受野在卷积中是个非常重要的概念。大的感受野允许神经元在一个广阔的维度上发现其变换规律,但感知不够精确。小的感受野则允许神经元发现细节。所以为了提取复杂而精确的特征,有必要整合有大感受野和小感受野的神经元。
对不同尺寸的输入信息直接使用不同尺寸的卷积核是个很方法,但内存开销的增加也不容忽视。最重要的是,由于同一层网络中不同尺寸的卷积核有不同的感受野,他们检测的信息会有一定的冗余,进一步说明了卷积核信息之间的关联性。
2. 传统卷积核
- 输入数据x,尺寸为,分别为高度,宽度和通道数。
- 一个卷积核f,尺寸为,表示卷积核的尺寸。
- 输出数据y,假设相应的输出尺寸为
,*表示卷积运算
其时间复杂度为
二、空间多用卷积核
1. 概括
一句话介绍其思想:针对尺寸大于3*3
的卷积核,通过逐步舍弃边缘的元素,抽取出多个不同感受野的二级卷积核,得到更多的特征图。
以下图为例,初始为5*5
的卷积核,分别保留5*5
、3*3
、1*1
位置的元素,可以得到3个二级卷积核,得到3倍的特征图。(反过来说就是,原来要x个卷积核才能得到的输出,现在只需要x/3个卷积核了)
2. 具体分析
- 输入数据x,尺寸为,分别为高度,宽度和通道数。
- 一个卷积核f,尺寸为,表示卷积核的尺寸。
- 输出数据y,假设相应的输出尺寸为
我们将传统卷积里的卷积核作为主卷积核,从中推导出一系列的二级卷积核,其中。推导规则如下:
-
定义掩膜
-
定义二级卷积核为
表示元素对应相乘。分析这个二级卷积核,表示本身,舍弃了最外层的元素, 只有的最内层元素。
假设主卷积核尺寸为, 则可以生成三个二级卷积核,尺寸为别为,, -
从二级卷积核中得到输出
这里的表示偏置
通过这种方式,我们可以同时得到s个不同感受野的二级卷积核,输出的尺寸是使用传统卷积的s倍
3. 与传统卷积的比较
上文可知,空间多用卷积核在不增加卷积核数量的情况下获得更多的特征图。
- 输入数据x,尺寸为,分别为高度,宽度和通道数。
- 一个卷积核f,尺寸为,表示卷积核的尺寸。
- 输出数据y,假设相应的输出尺寸为
对于输出
-
传统方法需要n个卷积核,每个主卷积核的尺寸为, 其总的空间消耗为,计算复杂度为
-
使用本文方法需要s个主卷积核,每个主卷积核的尺寸为,其总的空间消耗为,计算复杂度为。当s>2时,空间和时间消耗都能显著降低
4. 为什么能这么做
前面提到了一个感受野的问题。能这样做的依据如下。
- 卷积核可视化就能发现存在大量冗余,利用主卷积核生成二级卷积核能一定程度上消除这种冗余
- 感受野的问题。多个尺寸的二级卷积核使得既有大感受野,又有小的感受野,更得于后续的任务。
5. 补充
Tips:
使用空间多用卷积核的过程中,多个二级卷积核使用了相同的stride和padding,理由有二。一,由二级卷积核生成的特征图尺寸需要保持一致;二,二级卷积核的中心元素是一样的,s维特征是x中某个特征像素的多尺度表示。
Discuss:
上文提到的卷积方式,最后是将多个卷积核的结果做了一个concat操作,作者在discuss中有提到如果做的是add操作会如何,这有待进一步的探究。
三、通道多用卷积核
1. 概括
通道多用卷积核本质上和空间多用卷积核是一样的。区别在于,一个是基于平面生成二级卷积核(会有不同的尺寸),一个是在基于通道生成二级卷积核(尺寸一样,对应的通道不一样)。
image.png
2. 具体过程
使用通道多用卷积核得到的输出为
g是通道上的stride,是二级卷积核的通道数
是主卷积核在给定和g的情况下得到的第i个二级卷积核
因此,一个卷积核会被同时使用n次以产生更多的特征图。
假设初始卷积核1个,尺寸为5*5*24
, 生成了一个特征图;g=1, =23, 则生成了两个二级卷积核,尺寸为5*5*23
,只在对应通道执行计算,舍弃不能计算的部分,可得到两个特征图。
四、实验
1. MNIST上的结果
空间多用卷积核
baseline是LeNet, Versatile-Model 1表示的是空间多用卷积核采用Add操作(我觉得这个可以忽略),Versatile-Model 2和3使用了空间多用卷积核,区别在于Model 3的bias()是共享的。
卷积核可视化结果
image.png
原始卷积核还是有比较多冗余的,本文使用的卷积核则差异较大,结构更复杂。
通道多用卷积核
不同取值下的结果对比
2. ImageNet 2012上的结果
image.pngVersatile-AlexNet用的是空间多用卷积核,Versatile v2-AlexNet在Versatile-AlexNet基础上使用了的通道多用卷积核。
3. 和其它轻量化网络的比较
image.png五、代码探究
作者开源了代码,使用的是pytorch0.4,定义了多用卷积接口VConv2d
,能替代任何CNN网络中的nn.Conv2d
源码如下:
class VConv2d(nn.modules.conv._ConvNd):
"""
Versatile Filters
Paper: https://papers.nips.cc/paper/7433-learning-versatile-filters-for-efficient-convolutional-neural-networks
"""
def __init__(self, in_channels, out_channels, kernel_size, stride=1,
padding=0, dilation=1, groups=1, bias=True, delta=0, g=1):
kernel_size = _pair(kernel_size)
stride = _pair(stride)
padding = _pair(padding)
dilation = _pair(dilation)
super(VConv2d, self).__init__(
in_channels, out_channels, kernel_size, stride, padding, dilation,
False, _pair(0), groups, bias)
self.s_num = int(np.ceil(self.kernel_size[0]/2)) # s in paper, 空间多用卷积核的数量
self.delta = delta # c-\hat{c} in paper, 为0的时候表示不使用通道卷积核
self.g = g # g in paper,表示通道上的stride
self.weight = nn.Parameter(torch.Tensor(
int(out_channels/self.s_num/(1+self.delta/self.g)), in_channels // groups, *kernel_size)) # weight的维度(卷积核数量,每组的通道数,卷积核的维度)
self.reset_parameters()
def forward(self, x):
x_list = []
s_num = self.s_num # 空间多用卷积核的数量
ch_ratio = (1+self.delta/self.g) # 通道多用卷积核的数量
ch_len = self.in_channels - self.delta # 通道多用卷积核的通道数
for s in range(s_num):
for start in range(0, self.delta+1, self.g):
# 取出卷积核的相关数据
weight1 = self.weight[:, :ch_len, s:self.kernel_size[0]-s, s:self.kernel_size[0]-s]
# s:self.kernel_size[0]-s可以取出某一尺寸的卷积核
# 取出输入数据中需要用来计算的相关部分
if self.padding[0]-s < 0:
h = x.size(2) # x:(batch_size, 通道数,宽,高)
x1 = x[:,start:start+ch_len,s:h-s,s:h-s] # 通道+尺度(?)上的舍弃
padding1 = _pair(0)
else:
x1 = x[:,start:start+ch_len,:,:]
padding1 = _pair(self.padding[0]-s)
# 执行卷积计算
x_list.append(F.conv2d(x1, weight1, self.bias[int(self.out_channels*(s*ch_ratio+start)/s_num/ch_ratio):int(self.out_channels*(s*ch_ratio+start+1)/s_num/ch_ratio)], self.stride,
padding1, self.dilation, self.groups)) # 保存结果
x = torch.cat(x_list, 1) # 拼接
return x
网友评论