07MobileNet V1的原理与代码

引言

在深度学习的世界里,如何在保证模型效能的同时减少其复杂性,一直是研究人员追求的目标。随着移动设备的普及,MobileNetV1应运而生,提供了一种在移动和嵌入式设备上运行高效深度学习模型的可能。

MobileNet V1 由谷歌在 2017 年 4 月提出,核心原理是利用深度可分离卷积(Depthwise Separable Convolution)来构建轻量级的深度神经网络。这种设计显著减少了模型的参数数量和计算量,同时保持了较高的准确率。

深度可分离卷积

深度可分离卷积将传统的卷积操作分解为两个更简单的步骤:

深度卷积(Depthwise Convolution):对每个输入通道使用不同的滤波器进行卷积,实现空间特征的提取。

逐点卷积(Pointwise Convolution):将深度卷积的输出结果通过1x1的卷积核进行组合,实现通道间的组合。

在这里插入图片描述

和普通网络比具体有什么区别呢?

标准卷积网络

对于 12×12×3的输入图像,即图像尺寸为 12×12,通道数为3,对图像进行 5×5卷积,没有填充(padding)且步长为1。如果我们只考虑图像的宽度和高度,使用 5×5卷积来处理 12×12大小的输入图像,t最终可以得到一个 8×8的输出特征图。为了获取更多特征假设使用256个卷积核来学习256个不同类别的特征。此时参数量达到(不含偏置项):
参数量 = 卷积核高度 × 卷积核宽度 × 输入通道数 × 输出通道数
参数量 = 5 × 5 × 3 × 256= 983040
乘法操作个数 = 卷积核高度 × 卷积核宽度 × 输入通道数 × 输出特征图宽度 × 输出特征图高度 × 输出通道数
乘法操作个数 = 5 × 5 × 3 × 8 × 8 × 256= 1228800
**加粗样式**

深度卷积

首先使用5×5的卷积核进行逐通道运算,分别提取输入图像中3个 channel 的特征得到3个 8×8×1的输出特征图,即8×8×3的输出特征图。
参数量 =5 × 5 × 3 = 75
乘法操作个数 = 5 × 5 × 3 × 8 × 8= 4800

此时会发现通道数不变,缺少通道间的特征融合,故而需要逐点卷积,其作用可以理解为升维或降维。
在这里插入图片描述

逐点卷积

再深度卷积的基础上使用 1×1卷积核进行卷积,此时可以选择3通道对上文 8×8×3的特征图进行运算 得到8×8×1的输出特征图 ,若使用256个1×1的3通道卷积,则可得到 8×8×256的特征图。
乘法操作个数(PW)= 8 × 8 × 3 × 256= 49152
参数量(PW)= 3 × 256= 768

在这里插入图片描述

核心层

在这里插入图片描述

网络瘦身其他方法

下采样(Stride):在某些深度可分离卷积块中,会使用步长大于1的卷积来减小特征图的空间尺寸,从而减少计算量,加速模型并保持感受野大小。

宽度乘数(Width Multiplier, α):整个网络中,宽度乘数被用来按比例缩小网络的宽度,即减少每层的滤波器数量,从而进一步减少模型的参数量和计算成本。

分辨率乘数(Resolution Multiplier, β):控制输入图像的分辨率,间接控制中间层feature map的大小。允许用户在精度和速度之间进行权衡。

全局平均池化(Global Average Pooling, GAP):在网络的尾部,使用GAP层替代全连接层,以减少参数量并保持平移不变性。GAP层将每个通道的空间维度平均化为一个值,得到一个形状为(1x1xC)的特征向量,其中C是通道数。

💻代码

import tensorflow as tf
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Conv2D, DepthwiseConv2D, BatchNormalization, ReLU, Dense, GlobalAveragePooling2D, Dropout, Activation

# 定义深度可分卷积块
def BottleneckV1(inputs, in_channels, out_channels, stride):
    # DW卷积层
    x = DepthwiseConv2D(kernel_size=3, padding='same', strides=stride, depth_multiplier=1, use_bias=False)(inputs)
    x = BatchNormalization()(x) # 批量归一化
    x = ReLU(max_value=6)(x)  # ReLU6激活函数

    # PW卷积层
    x = Conv2D(out_channels, kernel_size=1, strides=1, use_bias=False)(x)
    x = BatchNormalization()(x) # 批量归一化
    x = ReLU(max_value=6)(x)  # ReLU6激活函数

    return x

# MobileNetV1模型定义
def MobileNetV1(num_classes=5):
    inputs = Input(shape=(224, 224, 3)) # 输入层
    x = Conv2D(32, kernel_size=3, strides=(2, 2), padding='same', use_bias=False)(inputs)
    x = BatchNormalization()(x) # 批量归一化
    x = ReLU(max_value=6)(x)  # ReLU6激活函数

    # 以下是MobileNet V1的深度可分卷积架构层
    x = BottleneckV1(x, 32, 64, (1, 1))  # Bottleneck模块
    x = BottleneckV1(x, 64, 128, (2, 2)) # 随着模块的增加,步长和输出通道数可能会变化
    x = BottleneckV1(x, 128, 128, (1, 1)) # 重复Bottleneck模块建模深度网络

    x = BottleneckV1(x, 128, 256, (2, 2))
    x = BottleneckV1(x, 256, 256, (1, 1))

    # 重复的深度卷积和逐点卷积模块
    x = BottleneckV1(x, 256, 512, (2, 2))
    for _ in range(5):  # 512输出通道数的Bottleneck重复5次  
        x = BottleneckV1(x, 512, 512, (1, 1))  

    x = BottleneckV1(x, 512, 1024, (2, 2)) # 步长为2的Bottleneck模块,增加输出尺寸


    x = BottleneckV1(x, 1024, 1024, (1, 1)) # 输出通道数为1024的Bottleneck模块



    x = GlobalAveragePooling2D()(x)  # 全局平均池化层  
    x = Dense(1024)(x)  # 全连接层  
    x = Dropout(0.2)(x)  # Dropout防止过拟合  
    x = Dense(num_classes)(x)  # 全连接层,设置输出类别数  
    outputs = Activation('softmax')(x)  # Softmax激活函数获取概率分布



    model = Model(inputs, outputs) # 定义模型的输入输出  
    return model  

# 创建MobileNetV1模型实例  
model = MobileNetV1(num_classes=5)  
model.summary()  # 输出模型结构  

# 编译模型,准备训练  
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])


  • 12
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值