经典轻量级神经网络(1)MobileNet V1及其在Fashion-MNIST数据集上的应用

经典轻量级神经网络(1)MobileNet V1及其在Fashion-MNIST数据集上的应用

1 MobileNet V1的简述

自从2017年由谷歌公司提出,MobileNet可谓是轻量级网络中的Inception,经历了一代又一代的更新。

  1. MobileNet 应用了Depthwise 深度可分离卷积来代替常规卷积,从而降低计算量,减少模型参数。
  2. MobileNet 不仅产生了小型网络,还重点优化了预测延迟。与之相比,有一些小型网络虽然网络参数较少,但是预测延迟较大。
  3. 论文下载地址: https://arxiv.org/pdf/1704.04861.pdf

1.1 深度可分离卷积

对于传统的卷积层,单个输出feature 这样产生:

  • 首先由一组滤波器对输入的各通道执行滤波,生成滤波feature 。这一步仅仅考虑空间相关性。

  • 然后计算各通道的滤波feature 的加权和,得到单个feature

    这里不同位置处的通道加权和的权重不同,这意味着在空间相关性的基础上,叠加了通道相关性。

Depthwise 深度可分离卷积打破了空间相关性和通道相关性的混合:

  • 首先由一组滤波器对输入的各通道执行滤波,生成滤波feature 。这一步仅仅考虑空间相关性。
  • 然后执行1x1 卷积来组合不同滤波feature 。这里不同位置处的通道加权和的权重都相同,这意味着这一步仅仅考虑通道相关性。
  • depthwise 卷积的参数数量和计算代价都是常规卷积的 1/8 到 1/9。

传统卷积和深度可分离卷积的区别可参考下面博客:

Pytorch常用的函数(三)深度学习中常见的卷积操作详细总结_undo_try的博客-CSDN博客

1.2 网络结构

1.2.1 V1卷积层

在这里插入图片描述

上图左边是标准卷积层,右边是V1的卷积层。V1的卷积层,首先使用3×3的深度卷积提取特征,接着是一个BN层,随后是一个ReLU层,在之后就会逐点卷积,最后就是BN和ReLU了。这也很符合深度可分离卷积,将左边的标准卷积拆分成右边的一个深度卷积和一个逐点卷积

注意:深度可分离卷积里面的ReLU,是ReLU6。

在这里插入图片描述

上图左边是普通的ReLU,对于大于0的值不进行处理,右边是ReLU6,当输入的值大于6的时候,返回6,relu6“具有一个边界”。作者认为ReLU6作为非线性激活函数,在低精度计算下具有更强的鲁棒性

标准卷积核深度可分离卷积层到底对结果有什么样的影响?

在这里插入图片描述

从上图可以看到使用深度可分离卷积与标准卷积,参数和计算量能下降为后者的九分之一到八分之一左右。但是准确率只有下降极小的1%

1.2.2 网络结构

MobileNeet 网络结构如下表所示。其中:

  • Conv 表示标准卷积,Conv dw 表示深度可分离卷积。
  • 所有层之后都跟随BNReLU (除了最后的全连接层,该层的输出直接送入到softmax 层进行分类)。
  • 先是一个3x3的标准卷积,s2进行下采样。然后就是堆积深度可分离卷积,并且其中的部分深度卷积会利用s2进行下采样。然后采用平均池化层将feature变成1x1,根据预测类别大小加上全连接层,最后是一个softmax层。整个网络有28层,其中深度卷积层有13层。
  • 与训练大模型相反,训练MobileNet 时较少的采用正则化和数据集增强技术,因为MobileNet 是小模型,而小模型不容易过拟合。

在这里插入图片描述

整个计算量基本集中在1x1卷积上。对于参数也主要集中在1x1卷积,除此之外还有就是全连接层占了一部分参数。

  • Conv 1x1 包含了所有的1x1 卷积层,包括可分离卷积中的1x1 卷积。
  • Conv DW 3x3 仅包括可分离卷积中的 3x3 卷积。

在这里插入图片描述

1.3 宽度乘子、分辨率乘子

尽管基本的MobileNet 架构已经很小,延迟很低,但特定应用需要更快的模型。为此MobileNet 引入了两个超参数:宽度乘子、分辨率乘子。

宽度乘子width multiplier ,记做a 。实际上是减小每层网络的输入、输出 feature map 的通道数量。

  • 宽度乘子应用于第一层(是一个全卷积层)的输出通道数上。这也影响了后续所有Depthwise可分离卷积层的输入feature map通道数、输出feature map通道数。

    这可以通过直接调整第一层的输出通道数来实现。

  • 它大概以 a^2的比例减少了参数数量,降低了计算量。

  • 通常将其设置为:0.25、0.5、0.75、1.0 四档。

分辨率乘子resolution multiplier,记做p 。其作用是:降低输出的feature map 的尺寸。

  • 分辨率乘子应用于输入图片上,改变了输入图片的尺寸。这也影响了后续所有Depthwise可分离卷积层的输入feature map 尺寸、输出feature map 尺寸。

    这可以通过直接调整网络的输入尺寸来实现。

  • 它不会改变模型的参数数量,但是大概以p^2 的比例降低计算量。

如果模型同时实施了宽度乘子和分辨率乘子,则模型大概以 a^2 的比例减少了参数数量,大概以 a^2p^2 的比例降低了计算量。

假设输入feature map 尺寸为14x14,通道数为 512 ;卷积尺寸为3x3;输出feature map 尺寸为14x14,通道数为512

层类型 乘-加操作(百万) 参数数量(百万)
常规卷积 462 2.36
深度可分离卷积 52.3 0.27
a=0.75 的深度可分离卷积 29.6 0.15
a=0.75,p=0.714 的深度可分离卷积 15.1 0.15

1.4 模型的性能

1.4.1 更瘦的模型和更浅的模型的比较

在计算量和参数数量相差无几的情况下,采用更瘦的MobileNet 比采用更浅的MobileNet 更好。

  • 更瘦的模型:采用a=0.75 宽度乘子( 表示模型的通道数更小)。
  • 更浅的模型:删除了MobileNet5x Conv dw/s 部分(即:5层 feature size=14x14@512 的深度可分离卷积)。
模型 ImageNet Accuracy 乘-加操作(百万) 参数数量(百万)
更瘦的MobileNet 68.4% 325 2.6
更浅的MobileNet 65.3% 307 2.9

1.4.2 不同宽度乘子及分辨率乘子的比较

随着a降低,模型的准确率一直下降(a=1 表示基准 MobileNet)。

with multiplier ImageNet Accuracy 乘-加 操作(百万) 参数数量(百万)
1.0 70.6% 569 4.2
0.75 68.4% 325 2.6
0.5 63.7% 149 1.3
0.25 50.6% 41 0.5

同样,随着 p的降低,模型的准确率一直下降( p=1表示基准MobileNet)。

resolution ImageNet Accuracy 乘-加 操作(百万) 参数数量(百万)
224x224 70.6% 569 4.2
192x192 69.1% 418 4.2
160x160 67.2% 290 4.2
128x128 64.4% 186 4.2

1.4.3 MobileNet和其它模型的比较

作者将V1与大型网络GoogleNet和VGG16进行了比较。

可以发现,作为轻量级网络的V1在计算量小于GoogleNet,参数量差不多是在一个数量级的基础上,在分类效果上比GoogleNet还要好,这就是要得益于深度可分离卷积了。VGG16的计算量参数量比V1大了30倍,但是结果也仅仅只高了1%不到。

在这里插入图片描述

瘦身的MobileNet(宽度乘子a=0.75 ,分辨率乘子p=0.714 )和 Squeezenet 模型大小差不多,但是准确率更高,计算量小了 22 倍。

在这里插入图片描述

2 MobileNet V1在Fashion-MNIST数据集上的应用示例

2.1 创建MobileNet V1网络模型

我们实现一个简化版本的模型。

import torch.nn as nn
import torch

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

        def conv_bn(inp, oup, stride):
            """
               标准卷积块
            """
            return nn.Sequential(
                nn.Conv2d(in_channels=inp, out_channels=oup, kernel_size=3, stride=stride, padding=1, bias=False),
                nn.BatchNorm2d(oup),
                nn.ReLU(inplace=True)
            )

        def conv_dw(inp, oup, stride):
            """
               深度可分离卷积
            """
            return nn.Sequential(
                # 深度卷积
                nn.Conv2d(
                    in_channels=inp,
                    out_channels=inp, # out_channels=in_channels
                    kernel_size=3,
                    stride=stride,
                    padding=1,
                    groups=inp,      # groups=in_channels
                    bias=False
                ),
                nn.BatchNorm2d(inp),
                nn.ReLU(inplace=True),

                # 逐点卷积
                nn.Conv2d(
                    in_channels=inp,
                    out_channels=oup,
                    kernel_size=1,
                    stride=1,
                    padding=0,
                    bias=False
                ),
                nn.BatchNorm2d(oup),
                nn.ReLU(inplace=True),
            )

        self.model = nn.Sequential(
            # conv_bn(3, 32, 2),
            conv_bn(1, 32, 2),
            conv_dw(32, 64, 1),  # 深度卷积层有13层
            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)
        self.fc = nn.Linear(1024, 10)

    def forward(self, x):
        x = self.model(x)
        x = x.view(-1, 1024)
        x = self.fc(x)
        return x

if __name__ == '__main__':
    net = MobileNetV1()
    X = torch.rand(size=(1, 1, 224, 224), dtype=torch.float32)
    for layer in net.model:
        X = layer(X)
        print(layer.__class__.__name__, 'output shape:', X.shape)

equential output shape: torch.Size([1, 32, 112, 112])
Sequential output shape: torch.Size([1, 64, 112, 112])
Sequential output shape: torch.Size([1, 128, 56, 56])
Sequential output shape: torch.Size([1, 128, 56, 56])
Sequential output shape: torch.Size([1, 256, 28, 28])
Sequential output shape: torch.Size([1, 256, 28, 28])
Sequential output shape: torch.Size([1, 512, 14, 14])
Sequential output shape: torch.Size([1, 512, 14, 14])
Sequential output shape: torch.Size([1, 512, 14, 14])
Sequential output shape: torch.Size([1, 512, 14, 14])
Sequential output shape: torch.Size([1, 512, 14, 14])
Sequential output shape: torch.Size([1, 512, 14, 14])
Sequential output shape: torch.Size([1, 1024, 7, 7])
Sequential output shape: torch.Size([1, 1024, 7, 7])
AvgPool2d output shape: torch.Size([1, 1024, 1, 1])

2.2 读取Fashion-MNIST数据集

# 我们将图片大小设置224×224
# 训练机器内存有限,将批量大小设置为64
batch_size = 64

train_iter,test_iter = get_mnist_data(batch_size,resize=224)

2.3 在GPU上进行模型训练

from _07_MobileNetV1 import MobileNetV1

# 初始化模型
net = MobileNetV1()

lr, num_epochs = 0.1, 10
train_ch(net, train_iter, test_iter, num_epochs, lr, try_gpu())

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_44665283/article/details/131564874