美文网首页
[论文笔记]Learning Versatile Filters

[论文笔记]Learning Versatile Filters

作者: 祁晏晏 | 来源:发表于2019-10-08 17:13 被阅读0次

    一句话介绍:使用多用卷积核构建轻量网络模型
    优势:不改变原来的网络结构,只需要换一下卷积的接口
    论文地址
    开源代码
    原作者的知乎专栏有简单介绍

    一、背景知识介绍

    1. 感受野

    感受野在卷积中是个非常重要的概念。大的感受野允许神经元在一个广阔的维度上发现其变换规律,但感知不够精确。小的感受野则允许神经元发现细节。所以为了提取复杂而精确的特征,有必要整合有大感受野和小感受野的神经元。

    对不同尺寸的输入信息直接使用不同尺寸的卷积核是个很方法,但内存开销的增加也不容忽视。最重要的是,由于同一层网络中不同尺寸的卷积核有不同的感受野,他们检测的信息会有一定的冗余,进一步说明了卷积核信息之间的关联性。

    2. 传统卷积核

    • 输入数据x,尺寸为H\times W \times c,分别为高度,宽度和通道数。
    • 一个卷积核f,尺寸为d \times d \times cd \times d表示卷积核的尺寸。
    • 输出数据y,假设相应的输出尺寸为H' * W' * 1
      y = f * x,*表示卷积运算

    其时间复杂度为O(cd^2H'W')

    二、空间多用卷积核

    1. 概括

    一句话介绍其思想:针对尺寸大于3*3的卷积核,通过逐步舍弃边缘的元素,抽取出多个不同感受野的二级卷积核,得到更多的特征图。

    以下图为例,初始为5*5的卷积核,分别保留5*53*31*1位置的元素,可以得到3个二级卷积核,得到3倍的特征图。(反过来说就是,原来要x个卷积核才能得到的输出,现在只需要x/3个卷积核了)

    image.png

    2. 具体分析

    • 输入数据x,尺寸为H\times W \times c,分别为高度,宽度和通道数。
    • 一个卷积核f,尺寸为d \times d \times cd \times d表示卷积核的尺寸。
    • 输出数据y,假设相应的输出尺寸为H' * W' * 1

    我们将传统卷积里的卷积核f \in \mathbb{R}^{d \times d}作为主卷积核,从中推导出一系列的二级卷积核{f_1, f_2, \cdots, f_s},其中s= \lceil d/2 \rceil。推导规则如下:

    1. 定义掩膜M
      M_i(p,q,c) = \begin{cases} 1, & if\ p,q \geq i |p,q \leq d+1-i, \cr 0, & otherwise \end{cases}

    2. 定义二级卷积核为f_i = M_i \circ f
      \circ 表示元素对应相乘。分析这个二级卷积核,f_1表示f本身,f_2舍弃了f最外层的元素, f_s只有f的最内层元素。
      假设主卷积核尺寸为5 \times 5, 则可以生成三个二级卷积核,尺寸为别为5 \times 53 \times 31 \times 1

    3. 从二级卷积核中得到输出
      y = [(M_1 \circ f) * x + b_1, \cdots, (M_s \circ f) * x + b_1], \\ s.t. s=\lceil d/2 \rceil,\ \{Mi\}^s_{i=1} \in \{0,1\}^{d \times d \times c}
      这里的b_1, b_2, \cdots, b_s表示偏置

    通过这种方式,我们可以同时得到s个不同感受野的二级卷积核,输出的尺寸是使用传统卷积的s倍

    3. 与传统卷积的比较

    上文可知,空间多用卷积核在不增加卷积核数量的情况下获得更多的特征图。

    • 输入数据x,尺寸为H\times W \times c,分别为高度,宽度和通道数。
    • 一个卷积核f,尺寸为d \times d \times cd \times d表示卷积核的尺寸。
    • 输出数据y,假设相应的输出尺寸为H' * W' * n

    对于输出y \in \mathbb{R}^{H' \times W' \times n}

    1. 传统方法需要n个卷积核,每个主卷积核的尺寸为d \times d \times c, 其总的空间消耗为O(d^2cn),计算复杂度为O(d^2cH'W'n)

    2. 使用本文方法需要s个主卷积核,每个主卷积核的尺寸为d \times d \times c,其总的空间消耗为O(d^2cn/s),计算复杂度为O(\sum^s_{i=1}(d-2i+2)^2cH'W'n/s)。当s>2时,空间和时间消耗都能显著降低

    4. 为什么能这么做

    前面提到了一个感受野的问题。能这样做的依据如下。

    1. 卷积核可视化就能发现存在大量冗余,利用主卷积核生成二级卷积核能一定程度上消除这种冗余
    2. 感受野的问题。多个尺寸的二级卷积核使得既有大感受野,又有小的感受野,更得于后续的任务。

    5. 补充

    Tips:
    使用空间多用卷积核的过程中,多个二级卷积核使用了相同的stride和padding,理由有二。一,由二级卷积核生成的特征图尺寸需要保持一致;二,二级卷积核的中心元素是一样的,s维特征是x中某个特征像素的多尺度表示。

    Discuss:
    上文提到的卷积方式,最后是将多个卷积核的结果做了一个concat操作,作者在discuss中有提到如果做的是add操作会如何,这有待进一步的探究。

    三、通道多用卷积核

    1. 概括

    通道多用卷积核本质上和空间多用卷积核是一样的。区别在于,一个是基于平面生成二级卷积核(会有不同的尺寸),一个是在基于通道生成二级卷积核(尺寸一样,对应的通道不一样)。


    image.png

    2. 具体过程

    使用通道多用卷积核得到的输出为
    y = [f_1 * x + b_1, f_2 *x + b_2, \cdots, f_n * x + b_n], \\ s.t. \forall i, f_i \in \mathbb{R}^{d \times d \times c}, n=(c-\hat{c})/g+1
    g是通道上的stride,\hat{c}是二级卷积核的通道数
    f_i是主卷积核在给定\hat{c}和g的情况下得到的第i个二级卷积核
    因此,一个卷积核会被同时使用n次以产生更多的特征图。

    假设初始卷积核1个,尺寸为5*5*24, 生成了一个特征图;g=1, \hat{c}=23, 则生成了两个二级卷积核,尺寸为5*5*23,只在对应通道执行计算,舍弃不能计算的部分,可得到两个特征图。

    四、实验

    1. MNIST上的结果

    空间多用卷积核
    baseline是LeNet, Versatile-Model 1表示的是空间多用卷积核采用Add操作(我觉得这个可以忽略),Versatile-Model 2和3使用了空间多用卷积核,区别在于Model 3的bias(b_1, b_2, \cdots, b_s)是共享的。

    image.png

    卷积核可视化结果


    image.png

    原始卷积核还是有比较多冗余的,本文使用的卷积核则差异较大,结构更复杂。

    通道多用卷积核
    不同取值下的结果对比

    image.png

    2. ImageNet 2012上的结果

    image.png
    Versatile-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 
    

    相关文章

      网友评论

          本文标题:[论文笔记]Learning Versatile Filters

          本文链接:https://www.haomeiwen.com/subject/zctbpctx.html