深度学习之图像分类(十)-- MobileNetV1 网络结构

木卯 于 2021-09-03 发布

深度学习之图像分类(十)MobileNetV1 网络结构

本节学习 MobileNetV1 网络结构。学习视频源于 Bilibili

img0

1. 前言

在传统卷积神经网络中,内存需求大,运算量打,导致无法在移动设备以及嵌入式设备上运行,例如在手机上等等。VGG16 大概有 490M 模型参数,ResNet152 大概有 644M 模型参数。为了服务于社会,网络不能仅仅停留在实验室。为了推动神经网络应用于现实世界,MobileNet 氤氲而生。

MobileNet 是由google团队在 2017 年提出的,专注于移动端或者嵌入式设备中的轻量级 CNN 网络。相比于传统的卷积神经网络而言,在准确率小幅降低的前提下,大大减少了模型的参数与运算量。MobileNetV1 相比 VGG16 准确率低了 0.9%,但是模型参数量仅仅有 1/32。其原始论文为 MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications

MobileNetV1 网络中的亮点包括:

2. DW 卷积

在传统卷积中,卷积核的channel与输入特征矩阵的channel是一致的,卷积核的个数对应于输出特征矩阵的channel。而在 DW 卷积中,每个卷积核的 channel 都是为 1 的,也就是对输入特征图的每一个 channel 单独进行卷积。既然每一个卷积核负责输入通道的一个 channel,那么卷积核的个数就是输入通道 channel 的个数,输出特征图通道 channel 数也和输入特征矩阵的通道 channel 数一致。如果要升通道数,那就 n 个卷积核对应一个输入矩阵的 channel,输出矩阵的通道数为输入矩阵通道数的 n 倍。这其实就是 group 数等于输入通道数的组卷积。但是实际上,mobileNet 是靠 PW 卷积去升通道数的。

img1

3. Depthwise Separable Conv 深度可分卷积操作

Depthwise Separable Conv 由两部分组成,DW 卷积以及 PW 卷积。其实 PW 卷积就是 $1 \times 1$ 卷积。先通过 DW 卷积,再通过 PW 卷积。

img2

Depthwise Separable Conv 能节省多少参数呢?我们来做一个计算。普通卷积的计算量为 $D_k \times D_k \times M \times N \times D_F \times D_F$, 而先使用 DW 卷积,再使用 PW 卷积的计算量为 $D_k \times D_k \times M \times D_F \times D_F + M \times N \times D_F \times D_F$。所以说理论上其为普通卷积的 $\frac{1}{N} + \frac{1}{D_k^2}$,通常 $D_k = 3$,即使用 $3 \times 3$ 卷积,而 $N$ 通常为几十上百,所以理论上普通卷积计算量为 DW + PW 的 8 到 9 倍。

img3

(悄悄说,我们最近的研究中,正交构造法得到的窗导数就是这个深度可分卷积模型!)

4. MobileNetV1 网络结构

MobileNetV1 网络结构如下图所示,其中 Conv 表示普通卷积,Conv dw 表示 DW 卷积操作。在实验结果中,$\alpha$ 控制使用卷积核的倍率,$\beta$ 控制输入图像尺寸。当卷积核数量取 0.75 时,正确率降低了两个点,但是模型参数量核计算量少了近一半。同样的,减少输入尺寸从 224 到 192 时,计算量也有大幅降低,而正确率降低较少。我们可以基于自己的应用和资源设定 $\alpha$ 和 $\beta$ 选择合适的模型。此时卷积的计算量变为 $D_k \times D_k \times \alpha \times M \times \beta \times D_F \times \beta \times D_F + \alpha \times M \times \alpha \times N \times \beta \times D_F \times \beta \times D_F$。按理说只有输出 $N$ 才乘 $\alpha$ 呀,DW 卷积保通道数,但是实际上输入 $M$ 因为上一层的原因乘上了 $\alpha$。

img4

可以看到,MobileNetV1 有 95% 的计算量和 74.59% 的参数集中在 1 x 1 的卷积层上,而且几乎另外的参数也都在全连接层上。

img5

具体 MobileNetV1 实验部分结果就不在赘述了。值得指出的是,很多人在使用时发现训练完之后,DW 卷积核很容易废掉,即卷积核参数大部分为 0。针对这个问题在 MobileNetV2 会有改善。

5. 代码

MobileNetV1 实现代码如下所示:

# 非官方实现

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

        def conv_bn(inp, oup, stride):    # 第一层传统的卷积:conv3*3+BN+ReLU
            return nn.Sequential(
                nn.Conv2d(inp, oup, 3, stride, 1, bias=False),
                nn.BatchNorm2d(oup),
                nn.ReLU(inplace=True)
            )

        def conv_dw(inp, oup, stride):      # 其它层的depthwise convolution:conv3*3+BN+ReLU+conv1*1+BN+ReLU
            return nn.Sequential(
                nn.Conv2d(inp, inp, 3, stride, 1, groups=inp, bias=False),
                nn.BatchNorm2d(inp),
                nn.ReLU(inplace=True),

                nn.Conv2d(inp, oup, 1, 1, 0, bias=False),
                nn.BatchNorm2d(oup),
                nn.ReLU(inplace=True),
            )

        self.model = nn.Sequential(
            conv_bn(  3,  32, 2),   # 第一层传统的卷积
            conv_dw( 32,  64, 1),   # 其它层depthwise convolution
            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