在这里插入图片描述

前言

论文地址:https://arxiv.org/abs/1907.09595.

源码: https://github.com/tensorflow/tpu/tree/master/models/official/mnasnet/mixnet.

创新: 提出MixConv(Mixed Depthwise Conv Kernels),混合的深度卷积核。就是使用不同大小的卷积核对深度卷积的不同channel分组处理。也可以看作是 分组深度卷积 + Inception结构 的多种卷积核混用。

一、背景

首先区分一下三个概念:普通卷积、深度卷积、深度可分离卷积。

  1. 普通卷积大家都清楚就是对整个feature map的所有channel同时进行卷积操作。

  2. 深度卷积和普通卷积的区别在于,深度卷积并不是对所有的channel同时进行卷积操作的,它是对每个(几个)channel单独分开来进行操作的。

  3. 深度可分离卷积又是在深度卷积的操作上加了点卷积(1x1)的操作,使参数更少。具体可以查看MobileNet网络。

\qquad 深度卷积在现代卷积中越来越受欢迎,如MobileNets、ShuffleNets、NASNet、AmoebaNet、MnasNet和EfficientNet等都使用到了深度卷积。与普通卷积不同,深度卷积核分别应用于每个单独的通道channel。在设计具有深度卷积的卷积神经网络时,一个重要但常常被忽略的因素是 卷积核大小。虽然常用的做法是简单地使用3x3卷积核,但是最近的研究结果表明,更大的核尺寸,如5 × 5 卷积核和7 × 7 卷积核,可能会提高模型的精度和效率。

\qquad 什么?大的卷积核真的能提高网络的精度?这个情况似乎是和我们以往掌握的知识是相互驳斥的。因为从VGG开始我们就使用大量的 3x3 的卷积来替代 5x5 和 7x7 卷积了,理由也很清楚:降低网络的参数,减少计算量。但是大的卷积核真的总是提高精度吗?作者为了搞清楚这个问题,做了如下实验:
在这里插入图片描述
\qquad 可以看到,适当的增大卷积核的大小,如增加到 5x5,7x7 确实可以有效的提高网络的精度,但是过度的增加卷积核的大小又会马上损害精度。作者是这样解释这个现象的:适当的增加卷积核的大小,以牺牲部分参数和计算为代价来获取具有更多细节的高分辨率模式,提高网络的精度;但是当卷积核大小过大了,这也符合卷积神经网络的最初直觉:在核大小等于输入分辨率的极端情况下,ConvNet简单地成为一个全连接的网络,这是众所周知的次品。

\qquad 综上,我们得出结论:我们既需要大卷积核来捕获高分辨率模式,也需要小卷积核来捕获低分辨率模式,以获得更好的模型精度和效率。

\qquad 而且近年来的一些研究表明,多尺度融合在计算机视觉里面是非常重要的,以往的融合方式大多都是通过改变网络的结构的方式如:Inceptions、Inception-ResNet、ResNeXt、NASNet、DenseNet 和 FPN等。但是我们的MixConv是一种通过使用不同的卷积核来获得不同尺度的feature map,再对它们进行融合(concat),而不改变网络的结构。这种优点类似与Inception结构,但是Inception结构的多种不同大小的卷积核是普通卷积,而这里是深度卷积(channel分组操作)。

\qquad 所以,作者就提出了一种新型的卷积形式,MixConv(Mixed Depthwise Conv Kernels),混合的深度卷积核,很好理解:就是使用不同大小的卷积核对深度卷积的不同channel分组处理。

二、MixConv

2.1、数学描述

如下图表示普通深度卷积。 X ( h , w , c ) X^{(h, w, c)} X(h,w,c) 代表输入 feature map,三个参数分别代表输入 feature map 的 高,宽,channel。 W ( k , k , c , m ) W^{(k, k, c, m)} W(k,k,c,m)代表 深度卷积核 。kxk代表卷积核的大小,c代表输入feature map的channel,mc代表输出feature map的channel。
在这里插入图片描述
如下公式代表MixConv。输入feature map通常会分成多个feature map 即: < X ^ ( h , w , c 1 ) , X ^ ( h , w , c 2 ) , … , X ^ ( h , w , c g ) > <\hat{X}^{(h, w, c_1)},\hat{X}^{(h, w, c_2)},…,\hat{X}^{(h, w, c_g)}> <X^(h,w,c1)X^(h,w,c2)X^(h,w,cg)> 所有的feature map有相同的 h 核 w,channel数相加为c,即: c 1 + c 2 + c 3 + … + c g = c c1 + c2 + c3 + … + c_g = c c1+c2+c3++cg=c。相应的 深度卷积核W 也会被分为几个部分即: < W ^ ( k 1 , k 1 , c 1 , m ) , W ^ ( k 2 , k 2 , c 2 , m ) , … , W ^ ( k g , k g , c g , m ) > <\hat{W}^{(k_1, k_1, c_1, m)},\hat{W}^{(k_2, k_2, c_2, m)},…,\hat{W}^{(k_g, k_g, c_g, m)}> <W^(k1,k1,c1,m)W^(k2,k2,c2,m)W^(kg,kg,cg,m)>
在这里插入图片描述

2.2、图解

在这里插入图片描述
如图右边为MixConv,输入Tensor(feature map)的channel会被分为几组,分别与几个大小不同的卷积核进行计算,再将得到的输出feature map进行concat操作,得到最终的输出feature map。

2.3、设计MixConv的几个关键点

1、组数 g 的选择

一开始我们认为 g=4 最好,后来利用 NAS 技术发现 g=1~5各组组合起来效果会更好。

2、每组的卷积核的大小的选择

原则:

  1. 每组的卷积核的大小必须不同
  2. 卷积核的大小一般以 3x3 开始,以2往上加。即:2i+1,i=1, 2, 3… 具体要多少个自己定,一般不超过 9x9。

3、每组的channel个数

channel的分组方式有两种

  1. Equal partition(平均划分):每组的channel数相同。
  2. Exponential partition(指数划分): 分组的channel数呈指数变化,即 2 i 2^i 2i。且卷积核较小的分配到更多的channel,以减少参数。

另外,作者还做了大量关于空洞卷积的实验,但是效果并不是很好,所有就不说了。

三、Pytorch实现

\qquad 这个pytorch实现并不是我看源码实现的,而是在YOLO V5的网络实验部分作者实现的,所以相比于源码以及作者的设计思路是有一点的出入的。

class MixConv2d(nn.Module):
    # Mixed Depthwise Conv https://arxiv.org/abs/1907.09595
    def __init__(self, c1, c2, k=(1, 3), s=1, equal_ch=True):
        """
        :params c1: 输入feature map的通道数
        :params c2: 输出的feature map的通道数(这个函数的关键点就是对c2进行分组)
        :params k: 混合的卷积核大小 其实一般是[3, 5, 7...]用的比较多的
        :params s: 步长 stride
        :params equal_ch: 通道划分方式 有均等划分和指数划分两种方式  默认是均等划分
        """
        super(MixConv2d, self).__init__()
        groups = len(k)
        if equal_ch:  # 均等划分通道
            i = torch.linspace(0, groups - 1E-6, c2).floor()  # c2 indices
            c_ = [(i == g).sum() for g in range(groups)]  # intermediate channels
        else:  # 指数划分通道
            b = [c2] + [0] * groups
            a = np.eye(groups + 1, groups, k=-1)
            a -= np.roll(a, 1, axis=1)
            a *= np.array(k) ** 2
            a[0] = 1
            c_ = np.linalg.lstsq(a, b, rcond=None)[0].round()  # solve for equal weight indices, ax = b

        self.m = nn.ModuleList([nn.Conv2d(c1, int(c_[g]), k[g], s, k[g] // 2, bias=False) for g in range(groups)])
        self.bn = nn.BatchNorm2d(c2)
        self.act = nn.LeakyReLU(0.1, inplace=True)

    def forward(self, x):
        # 这里核原论文略有出入,这里加了一个shortcut操作
        return x + self.act(self.bn(torch.cat([m(x) for m in self.m], 1)))

\qquad 因为这篇论文的核心主要就是MixConv结构的设计,所以我并没有继续学习它的网络构建(MixNet)部分。感兴趣的也可以自己去学习下它的源码: https://github.com/tensorflow/tpu/tree/master/models/official/mnasnet/mixnet.

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐