轻量化网路之MobileNet V1

1、深度可分离卷积

标准卷积在卷积时,同时考虑了图像的区域与通道信息,那么为什么不能分开考虑区域与通道呢?

基于此想法,诞生了深度可分离卷积(Depthwise Separable Convolution),将卷积的过程分为逐通道卷积逐点1×1卷积两步。

1.1 逐通道卷积

  • 卷积核参数量少。卷积核参数量为Ci×3×3,远少于标准卷积Ci×3×3×Co的数量;

  • 通道之间相互独立,没有各通道间的特征融合,这也是逐通道卷积的核心思想,输出特征的每一个点只对应输入特征一个通道上的3×3大小的特征,而不是标准卷积中Ci×3×3大小;

  • 由于只在通道间进行卷积,导致输入与输出特征图的通道数相同,无法改变通道数。

1.2 1x1卷积

由于逐通道卷积通道间缺少特征的融合,并且通道数无法改变,因此后续还需要继续连接一个逐点的1×1的卷积,有两个作用:

  • 一方面可以融合不同通道间的特征;
  • 同时也可以改变特征图的通道数。

1.3 参数量计算

图a的标准卷积,使用一个和输入数据通道数相同的卷积核执行逐个通道卷积后求和获得一个通道的输出,计算量为M \times D_k \times D_k,其中M代表输入的通道数,D_k为卷积核的宽和高,一个卷积核处理输入数据时的计算量为:D_k \times D_k \times M \times D_F \times D_F ,其中D_F为输入的宽和高,如果在某一个卷积层使用N个卷积核,那么这一卷积层的计算量为:D_k \times D_k \times M \times D_F \times D_F \times N

图b、c是深度可分离卷积,首先使用一组通道数为1的卷积核,每次只处理一个输入通道,并且这一组卷积核的个数是和输入通道数相同的。执行完上面的深度卷积后,再使用通道数为输入数据通道数的大小为1x1的卷积来组合之前输出的特征图,将最终输出通道数变为一个指定的数量。

  • 理论计算

一组和输入通道数相同的2D卷积核(通道数为1,即深度卷积或者说分组卷积)的运算量为: D_k \times D_k \times M \times D_F \times D_F

而3D(标准卷积)的卷积核1x1的运算量为:1 \times 1 \times M \times D_F \times D_F \times N ,因此这种组合方式的总计算量为:

D_k \times D_k \times M \times D_F \times D_F + 1 \times 1 \times M \times D_F \times D_F \times N;

因此,深度可分离卷积相比于标准卷积计算量的比例为:

\frac{D_k \times D_k \times M \times D_F \times D_F + 1 \times 1 \times M \times D_F \times D_F \times N}{D_k \times D_k \times M \times D_F \times D_F \times N} = \frac{1}{N} + \frac{1}{D_k^{2}}

  • 举例计算

假设输入特征图尺寸为112x112,输入通道数64,卷积核大小3x3,卷积核个数128。

标准卷积计算:3 * 3 * 64 * 112 * 112 * 128 = 924844032

深度可分离卷积计算:3 * 3 * 64 * 112 * 112 + 1 * 1 * 64 * 112 * 112 * 128 = 109985792

二者的比例为:\frac{109985792}{924844032} = 0.1189

可以看到将一个标准卷积换成深度可分离卷积之后模型的计算量减少了9倍。

2、网络结构

特点:

  • 除了第一层为标准卷积外都采用深度可分离卷积;
  • 最后一层全连接没有激活函数直接送入到 Softmax 层,其他的标准卷积和深度可分离卷积都接入BN和ReLU;
  • 在深度可分离卷积的两层后面都要引入 BN 和 ReLU
  • 第一层标准卷积和深度可分离卷积通过stride=2的卷积实现下采样;
  • 最后一个平均池化层将空间分辨率减小为1;
  • 如果将Depthwise卷积层和Pointwise卷积层算成不同层的话,MobileNet V1一共有28层。

MobileNet网络的有效性:

  • 仅仅通过上文中减少网络乘加操作对于网络加速是不够的,同时要确保这些乘加操作能够高效实现也很重要。例如,非结构化的稀疏矩阵操作通常不比密集矩阵运算快,除非是非常稀疏的矩阵。

  • MobileNet V1的模型结构几乎将全部的计算复杂度放到了1x1卷积中。


可以看到,95% 的计算时间都花费在 1×1 卷积上,75% 的参数量也都集中在 1×1 卷积层,其余的参数则主要来自全连接层。

  • 1 x 1卷积 、3 x 3卷积、5 x 5卷积都可以通过高度优化的通用矩阵乘法(GEMM)来实现,但是3x3卷积和5x5卷积等需要使用im2col在内存中对数据进行重排,以将其映射到GEMM可以使用的方式。而1x1卷积不需要在内存中重排就可以直接被GEMM(最优化的数值线性代数算法之一)实现。MobileNet绝大部分使用1x1卷积,因此可以充分利用GEMM算法,来实现高效计算,加速网络。

3、宽度乘子:更瘦的模型

尽管基本的 MobileNet 结构已经非常小、时延很低,但具体的使用场景可能需要模型更小更快。为了构建这些计算量更少更小的模型,作者引入了一个简单的参数\alpha,称之为宽度乘子(width multiplier),它的作用是统一让网络中的每一层都更瘦

针对某一层网络和\alpha,输入通道数从M变成\alpha M,输出通道数从N变成\alpha N,这样这一层的计算代价就变为:
D_k \times D_k \times \alpha M \times D_F \times D_F + \alpha M \times D_F \times D_F \times \alpha N

宽度乘子可以以大致\alpha ^{2}的速率减少计算代价和参数量,可以用来设计一个更小的网络来平衡准确率、时延和模型大小这三者。

4、分辨率乘子:减弱表示能力

另一个减少计算代价的超参数是分辨率乘子(resolution multiplier)\rho
将它应用在输入图像上,然后每一层的表示能力都相应减少。实际中,作者是通过设置输入分辨率来确定这个参数的。

宽度乘子和分辨率乘子结合在一起,某一层网络的计算代价就变为了:
D_k \times D_k \times \alpha M \times \rho D_F \times \rho D_F + \alpha M \times \rho D_F \times \rho D_F \times \alpha N

分辨率乘子可以以\rho^{2}的速率减少计算代价。下面的例子可以很好地展示深度可分离卷积以及两个超参数是怎么减少网络的计算代价和参数量的。

5、实验结果

采用深度卷积的网络相对于标准卷积节省了巨大的计算代价和参数量,但准确率却仅仅降低了 1%。

相较于移除表一中特征图大小为 14×14×512 的五层网络,宽度因子\alpha=0.75的更瘦网络比更浅的网络准确率高出 3 个百分点。

随着宽度因子的降低,模型的准确率平滑降低,直到值太小 \alpha=0.25时才有显著下降。


随着分辨率因子的降低,模型的准确率始终保持平滑降低。

相较于 GoogleNet 和 VGG,MobileNet 的准确率接近于 VGG ,超越了 GoogleNet,但模型大小的计算代价都小了很多。

可以使用PyTorch来搭建一个MobileNet V1网络。

6、代码

class MobileNetV1(nn.Module):
    def __init__(self):
        super(MobileNet, self).__init__()

        def conv_bn(dim_in, dim_out, stride):
            return nn.Sequential(
                nn.Conv2d(dim_in, dim_out, 3, stride, 1, bias=False),
                nn.BatchNorm2d(dim_out),
                nn.ReLU(inplace=True)
            )

        def conv_dw(dim_in, dim_out, stride):
            return nn.Sequential(
                nn.Conv2d(dim_in, dim_in, 3, stride, 1,
                             groups= dim_in, bias=False),
                nn.BatchNorm2d(dim_in),
                nn.ReLU(inplace=True),
                nn.Conv2d(dim_in, dim_out, 1, 1, 0, bias=False),
                nn.BatchNorm2d(dim_out),
                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

总体上,MobileNet v1利用深度可分离的结构牺牲了较小的精度,带来了计算量与网络层参数的大幅降低,从而也减小了模型的大小,方便应用于移动端。

但MobileNet v1也有其自身结构带来的缺陷,主要有以下两点:

  1. 模型结构较为复古,采用了与VGGNet类似的卷积简单堆叠,没有采用残差、特征融合等先进的结构。

  2. 深度分解卷积中各通道相互独立,卷积核维度较小,输出特征中只有较少的输入特征,再加上ReLU激活函数,使得输出很容易变为0,难以恢复正常训练,因此在训练时部分卷积核容易被训练废掉。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。