CV学习笔记-MobileNet

news2024/11/26 12:51:24

MobileNet

文章目录

  • MobileNet
    • 1. MobileNet概述
    • 2. 深度可分离卷积(depthwise separable convolution)
      • 2.1 深度可分离卷积通俗理解
      • 2.2 深度可分离卷积对于参数的优化
    • 3. MobileNet网络结构
    • 4. 代码实现
      • 4.1 卷积块
      • 4.2 深度可分离卷积块
      • 4.3 MobileNet定义
      • 4.4 完整代码

1. MobileNet概述

  网络专注于移动端或者嵌入式设备中的轻量级CNN,相比于传统卷积神经网络,在准确率小幅度降低的前提下大大减少模型参数与运算量。

  传统卷积与DW卷积(Depthwise Conv)的差异,在传统卷积中,每个卷积核的channel与输入特征矩阵的channel相等(每个卷积核都会与输入特征矩阵的每一个维度进行卷积运算),输出特征矩阵channel等于卷积核的个数。而在DW卷积中,每个卷积核的channel都是等于1的(每个卷积核只负责输入特征矩阵的一个channel,卷积核的个数=输入特征矩阵的channel数=输出特征矩阵的channel数)

  MobileNet模型是Google针对手机等嵌入式设备提出的一种轻量级的深层神经网络,其使用的核心思想便是深度可分离卷积 depthwise separable convolution

2. 深度可分离卷积(depthwise separable convolution)

2.1 深度可分离卷积通俗理解

  传统的三通道卷积过程如下:

三通道卷积

  那么深度可分离卷积能用到轻量级的网络中,保证效率减少参数量,其具体是如何巧妙设计优化的,下图为深度可分离卷积的运作过程。

点卷积和深度卷积

  深度可分离卷积分为两个部分,Depthwise Convolution (深度卷积)、Pointwise Convolution(点卷积),这是两个不同的卷积过程,传统三通道卷积的过程是一个卷积核的厚度有三层,每一层与输入的feature map的每一层做加权求和,从而生成输出的一个通道,故输出通道数取决于卷积核的数量,卷积核的通道数等同于输入feature map的通道数(对齐)

传统卷积卷积核与输入矩阵的对应关系

  而Depthwise Convolution不同,其卷积核的厚度只有1,对于输入的feature map(特征图谱)的每一个通道,都有一个不同的厚度为1的卷积核相对应(卷积核数量与输入通道数对应),每个卷积核都只对对应通道的feature map做卷积操作,这样得到的每一个通道的卷积操作得到一个1通道的feature map,这样深度卷积之后的输出通道数与输入通道数一致。下图中绿色、红色、黄色表示的不同的通道数为1的卷积核。

深度卷积

  点卷积(Pointwise Convolution)是采用 1 × 1 1\times1 1×1大小的卷积核(此时的卷积核是传统卷积核,其通道数与输入通道数相同)分别遍历输入的所有通道,得到的输出通道取决于卷积核的个数,点卷积与传统卷积方式相同,只是卷积核使用 1 × 1 1\times1 1×1,起到一个改变通道数的作用,直观意义上讲就是加厚输出的feature map,其实际的意义在于由于深度卷积的方式对于特征提取不够明显,再通过点卷积进行一些信息补偿,在保证参数量减少的情况下,尽可能的让网络的效果保持一个可观的水平。

点卷积

2.2 深度可分离卷积对于参数的优化

  举例说明优化的效果,由于MobileNet是用于嵌入式设备等受限的设备上的网络,其参数肯定是大幅度降低的。

  有下列场景:
  使用 3 × 3 3\times3 3×3大小的卷积层,输入通道数为16,输出通道数为32.

  使用传统卷积的方式,需要32个 3 × 3 3\times3 3×3的卷积核对16个通道中的每一层数据进行卷积计算,最后得到32个输出通道的feature map,所需要的参数量为:
16 × 32 × 3 × 3 = 4608 16\times 32\times 3\times 3=4608 16×32×3×3=4608
  采用深度可分离卷积,由于输入的通道数为16,那么采用16个 3 × 3 3\times3 3×3大小的卷积核对16个通道的feature map分别卷积,得到一个16通道的输出feature map,为了得到所需要的32通道的输出,采用点卷积的方式,使用32个 1 × 1 1\times1 1×1大小的卷积核遍历16个通道的输入feature map,那么参数量为:
16 × 3 × 3 + 16 × 32 × 1 × 1 = 656 16\times 3\times 3+16\times 32\times 1\times 1=656 16×3×3+16×32×1×1=656
  通过上述实验对比,参数量确实大大减少。

注意:深度可分离卷积指的是深度卷积(Depthwise Conv)+点卷积(Pointwise Conv),而并不是一个dw(Depthwise的缩写)就是一个深度可分离卷积,这个概念混淆度很高

3. MobileNet网络结构

一个深度可分离卷积的结构如下所示:

深度可分离卷积

  首先对输入做深度卷积(Depthwise Conv),深度卷积的特点是是卷积核的个数与处理的输入通道数一致同时每个卷积核通道数都是1,每个1通道卷积核去卷积输入feature map里的一个通道的数据,导致深度卷积处理之后输入与输出的通道数总是不变的。

  深度卷积之后就是BN层,BN层为Batch Normalization,对数据做归一化处理,再通过激活函数,接下来是一个 1 × 1 1\times1 1×1的Conv层,其就是点卷积的层,使用 1 × 1 1\times1 1×1的卷积核进行卷积操作,改变通道数,再进行归一化和激活输出。

BN的作用以及应用过程已经在笔者的博文《CV学习笔记-Inception》中详细介绍,不再赘述。

MobileNet的网络结构图如下所示

MobileNet

  在此不进行逐层分析,仅对初始几层进行分析,后面的读者可以自行推导,计算过程一致。

  首先输入为 224 × 224 × 3 224\times 224\times 3 224×224×3,卷积核的shape为 3 × 3 × 3 × 32 3\times 3\times 3\times 32 3×3×3×32,通过卷积核的shape,能够看出本次卷积操作采用的是一个传统的卷积计算,卷积核的通道数为3,个数为32,那么得到的输出为 112 × 112 × 32 112\times 112\times 32 112×112×32(stride步长为2),此处的通道数32与卷积采用的卷积核个数32对应,之后经过Conv dw层,这个是深度卷积层,dw即Depthwise,输入feature map为 112 × 112 × 32 112\times 112\times 32 112×112×32,所用卷积核shape为 3 × 3 × 32 3\times 3\times 32 3×3×32注意与上面一层的卷积层相区别,少了一个维度,因为每一个卷积核的通道数为1,每个1通道卷积核只需要卷积feature map中的一个通道的数据,故经过深度卷积之后的输出通道数不变,输出的feature map为 112 × 112 × 32 112\times 112\times 32 112×112×32,然后通过一个点卷积层,卷积核shape为 1 × 1 × 32 × 64 1\times 1\times 32\times 64 1×1×32×64,即使用64个 1 × 1 × 1 × 32 1\times 1\times 1 \times 32 1×1×1×32的卷积核进行卷积操作,点卷积卷积的方式与传统的卷积相同,只是在尺寸上采用的 1 × 1 1\times 1 1×1的卷积核,用点卷积的方式改变通道数,输出的feature map shape为 112 × 112 × 64 112\times 112\times 64 112×112×64,接下来就可以留给读者推算了,深度可分离卷积部分已经作出了分析。

4. 代码实现

4.1 卷积块

  代码按照传统的Conv-BN-Relu组合拳,进行卷积块的定义padding采用same方式

def _conv_block(inputs, filters, kernel=(3, 3), strides=(1, 1)):
    x = Conv2D(filters, kernel,
               padding='same',
               use_bias=False,
               strides=strides,
               name='conv1')(inputs)
    x = BatchNormalization(name='conv1_bn')(x)
    return Activation(relu6, name='conv1_relu')(x)

4.2 深度可分离卷积块

  深度可分离卷积分为深度卷积和点卷积,x = DepthwiseConv2D([params])即为深度卷积块的定义,使用的是DepthwiseConv2D(),在深度卷积完成BN+Relu后,紧接着接上了一个点卷积模块,x = Conv2D(pointwise_conv_filters,(1, 1)),使用 1 × 1 1\times 1 1×1卷积核来改变通道数。

def _depthwise_conv_block(inputs, pointwise_conv_filters,
                          depth_multiplier=1, strides=(1, 1), block_id=1):

    x = DepthwiseConv2D((3, 3),
                        padding='same',
                        depth_multiplier=depth_multiplier,
                        strides=strides,
                        use_bias=False,
                        name='conv_dw_%d' % block_id)(inputs)

    x = BatchNormalization(name='conv_dw_%d_bn' % block_id)(x)
    x = Activation(relu6, name='conv_dw_%d_relu' % block_id)(x)

    x = Conv2D(pointwise_conv_filters, (1, 1),
               padding='same',
               use_bias=False,
               strides=(1, 1),
               name='conv_pw_%d' % block_id)(x)
    x = BatchNormalization(name='conv_pw_%d_bn' % block_id)(x)
    return Activation(relu6, name='conv_pw_%d_relu' % block_id)(x)

4.3 MobileNet定义

  按照MobileNet的网络结构,将其复现成python程序,整合的就是上面定义的卷积模块和深度可分离卷积模块。(预训练权重文件可以在网络上下载)

def MobileNet(input_shape=[224,224,3],
              depth_multiplier=1,
              dropout=1e-3,
              classes=1000):


    img_input = Input(shape=input_shape)

    # 224,224,3 -> 112,112,32
    x = _conv_block(img_input, 32, strides=(2, 2))

    # 112,112,32 -> 112,112,64
    x = _depthwise_conv_block(x, 64, depth_multiplier, block_id=1)

    # 112,112,64 -> 56,56,128
    x = _depthwise_conv_block(x, 128, depth_multiplier,
                              strides=(2, 2), block_id=2)
    # 56,56,128 -> 56,56,128
    x = _depthwise_conv_block(x, 128, depth_multiplier, block_id=3)

    # 56,56,128 -> 28,28,256
    x = _depthwise_conv_block(x, 256, depth_multiplier,
                              strides=(2, 2), block_id=4)
    
    # 28,28,256 -> 28,28,256
    x = _depthwise_conv_block(x, 256, depth_multiplier, block_id=5)

    # 28,28,256 -> 14,14,512
    x = _depthwise_conv_block(x, 512, depth_multiplier,
                              strides=(2, 2), block_id=6)
    
    # 14,14,512 -> 14,14,512
    x = _depthwise_conv_block(x, 512, depth_multiplier, block_id=7)
    x = _depthwise_conv_block(x, 512, depth_multiplier, block_id=8)
    x = _depthwise_conv_block(x, 512, depth_multiplier, block_id=9)
    x = _depthwise_conv_block(x, 512, depth_multiplier, block_id=10)
    x = _depthwise_conv_block(x, 512, depth_multiplier, block_id=11)

    # 14,14,512 -> 7,7,1024
    x = _depthwise_conv_block(x, 1024, depth_multiplier,
                              strides=(2, 2), block_id=12)
    x = _depthwise_conv_block(x, 1024, depth_multiplier, block_id=13)

    # 7,7,1024 -> 1,1,1024
    x = GlobalAveragePooling2D()(x)
    x = Reshape((1, 1, 1024), name='reshape_1')(x)
    x = Dropout(dropout, name='dropout')(x)
    x = Conv2D(classes, (1, 1),padding='same', name='conv_preds')(x)
    x = Activation('softmax', name='act_softmax')(x)
    x = Reshape((classes,), name='reshape_2')(x)

    inputs = img_input

    model = Model(inputs, x, name='mobilenet_1_0_224_tf')
    model_name = 'mobilenet_1_0_224_tf.h5'
    model.load_weights(model_name)

    return model

4.4 完整代码

  本代码完成了一个图片的识别,图片如下,可以重命名为elephant.jpg输入网络运行。

美洲象

#-------------------------------------------------------------#
#   MobileNet的网络部分
#-------------------------------------------------------------#
import warnings
import numpy as np

# from keras.preprocessing import image

from keras.models import Model
from keras.layers import DepthwiseConv2D,Input,Activation,Dropout,Reshape,BatchNormalization,GlobalAveragePooling2D,GlobalMaxPooling2D,Conv2D
from keras.applications.imagenet_utils import decode_predictions
from keras.utils import image_utils as image
from keras import backend as K


def MobileNet(input_shape=[224,224,3],
              depth_multiplier=1,
              dropout=1e-3,
              classes=1000):


    img_input = Input(shape=input_shape)

    # 224,224,3 -> 112,112,32
    x = _conv_block(img_input, 32, strides=(2, 2))

    # 112,112,32 -> 112,112,64
    x = _depthwise_conv_block(x, 64, depth_multiplier, block_id=1)

    # 112,112,64 -> 56,56,128
    x = _depthwise_conv_block(x, 128, depth_multiplier,
                              strides=(2, 2), block_id=2)
    # 56,56,128 -> 56,56,128
    x = _depthwise_conv_block(x, 128, depth_multiplier, block_id=3)

    # 56,56,128 -> 28,28,256
    x = _depthwise_conv_block(x, 256, depth_multiplier,
                              strides=(2, 2), block_id=4)
    
    # 28,28,256 -> 28,28,256
    x = _depthwise_conv_block(x, 256, depth_multiplier, block_id=5)

    # 28,28,256 -> 14,14,512
    x = _depthwise_conv_block(x, 512, depth_multiplier,
                              strides=(2, 2), block_id=6)
    
    # 14,14,512 -> 14,14,512
    x = _depthwise_conv_block(x, 512, depth_multiplier, block_id=7)
    x = _depthwise_conv_block(x, 512, depth_multiplier, block_id=8)
    x = _depthwise_conv_block(x, 512, depth_multiplier, block_id=9)
    x = _depthwise_conv_block(x, 512, depth_multiplier, block_id=10)
    x = _depthwise_conv_block(x, 512, depth_multiplier, block_id=11)

    # 14,14,512 -> 7,7,1024
    x = _depthwise_conv_block(x, 1024, depth_multiplier,
                              strides=(2, 2), block_id=12)
    x = _depthwise_conv_block(x, 1024, depth_multiplier, block_id=13)

    # 7,7,1024 -> 1,1,1024
    x = GlobalAveragePooling2D()(x)
    x = Reshape((1, 1, 1024), name='reshape_1')(x)
    x = Dropout(dropout, name='dropout')(x)
    x = Conv2D(classes, (1, 1),padding='same', name='conv_preds')(x)
    x = Activation('softmax', name='act_softmax')(x)
    x = Reshape((classes,), name='reshape_2')(x)

    inputs = img_input

    model = Model(inputs, x, name='mobilenet_1_0_224_tf')
    model_name = 'mobilenet_1_0_224_tf.h5'
    model.load_weights(model_name)

    return model

def _conv_block(inputs, filters, kernel=(3, 3), strides=(1, 1)):
    x = Conv2D(filters, kernel,
               padding='same',
               use_bias=False,
               strides=strides,
               name='conv1')(inputs)
    x = BatchNormalization(name='conv1_bn')(x)
    return Activation(relu6, name='conv1_relu')(x)


def _depthwise_conv_block(inputs, pointwise_conv_filters,
                          depth_multiplier=1, strides=(1, 1), block_id=1):

    x = DepthwiseConv2D((3, 3),
                        padding='same',
                        depth_multiplier=depth_multiplier,
                        strides=strides,
                        use_bias=False,
                        name='conv_dw_%d' % block_id)(inputs)

    x = BatchNormalization(name='conv_dw_%d_bn' % block_id)(x)
    x = Activation(relu6, name='conv_dw_%d_relu' % block_id)(x)

    x = Conv2D(pointwise_conv_filters, (1, 1),
               padding='same',
               use_bias=False,
               strides=(1, 1),
               name='conv_pw_%d' % block_id)(x)
    x = BatchNormalization(name='conv_pw_%d_bn' % block_id)(x)
    return Activation(relu6, name='conv_pw_%d_relu' % block_id)(x)

def relu6(x):
    return K.relu(x, max_value=6)

def preprocess_input(x):
    x /= 255.
    x -= 0.5
    x *= 2.
    return x

if __name__ == '__main__':
    model = MobileNet(input_shape=(224, 224, 3))

    img_path = 'elephant.jpg'
    img = image.load_img(img_path, target_size=(224, 224))
    x = image.img_to_array(img)
    x = np.expand_dims(x, axis=0)
    x = preprocess_input(x)
    print('Input image shape:', x.shape)

    preds = model.predict(x)
    print(np.argmax(preds))
    print('Predicted:', decode_predictions(preds,1))  # 只显示top1

实验输出:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/373253.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

linux下使用vscode和cmake高效管理c++项目简明教程

安装vscode及c环境配置可以参见:https://blog.csdn.net/fangshuo_light/article/details/123635576   首先,创建工程目录,并在vscode中打开该文件夹,在里面创建如下文件夹: include:用于存放.h文件src&a…

HBase 一文读懂

本文基于《尚硅谷大数据技术之HBase》编写。HBase 简介HBase定义HBase是一种分布式、可扩展、支持海量数据存储的NoSQL数据库。HBase数据模型HBase的数据模型同关系型数据库(RDMS)很类似,数据存储在一张表中,有行有列。但从HBase的…

c语言tips-大端小端存储介绍和使用union判断大小端

1. 大小端介绍 大端(Big Endian)和小端(Little Endian)是两种CPU或者计算机系统存储数据的方式。 在大端系统中,数据的高位字节(MSB)存储在内存地址的低位,低位字节(LSB…

Linux系统下搭建maven环境

文章目录前述从官网下载安装包安装 maven修改maven配置修改环境变量测试前述 安装 maven 环境前,需要先安装 java 环境,如果没有安装 java 环境,可以参考:https://blog.csdn.net/weixin_45583303/article/details/118631855 从官…

maven的仓库配置、指定jdk编译版本、相关编译命令简介、scope依赖的范围以及依赖的传递性

目录 1、配置阿里云提供的镜像仓库 2、指定jdk编译版本 3、执行 Maven 的构建命令 3.1、清理操作 3.2、编译操作 3.3、测试操作 3.4、打包操作 3.5、安装操作 4、scope依赖的范围 5、依赖的传递性 5.1、概念 5.2、传递的原则 1、配置阿里云提供的镜像仓库 将下面 m…

C++STL之list的模拟实现

目录 一.list准备 二. iterator迭代器 1._list_iterator 2.begin()、end() 3.const_begin()、const_end() 4.!&& 5. && -- 6.operator* 7.operator-> 三.Modify(修改) 1.insert() 2.erase() 3.push_back() && push_front() 4.pop_bac…

MySql触发器学习

文章目录1 触发器1.1介绍1.2 创建触发器1.2 删除触发器1.3查看触发器1 触发器 1.1介绍 触发器是与表有关的数据库对象,指在 insert/update/delete 之前或之后,触发并执行触发器中定义的SQL语句集合。触发器的这种特性可以协助应用在数据库端确保数据的…

(十八)操作系统-进程互斥的软件实现方法

文章目录一、知识总览二、单标志法三、双标志先检查法四、双标志后检查法五、Peterson算法六、总结一、知识总览 二、单标志法 算法思想:两个进程在访问临界区后,会把使用临界区的权限转交给另一个进程。也就是说每个进程进入临界区的权限只能被另一个进…

Guna Charts WinForm 1.0.8 Crack

Guna Charts 16 图表 在 16 种不同的图表类型中可视化您的数据。 Guna Charts 反应灵敏 轻松响应屏幕尺寸的变化。 Guna Charts 实时图表 创建实时数据仪表板现在非常容易。 Guna Charts 混合图表类型 混合多种图表类型,例如条形图和折线图/面积图。 Guna Charts…

MS9122是一款USB单芯片投屏器,内部集成了USB2 0 控制器和数据收发模块、HDMI 数据接口和音视频处理模块。MS9122可以通过USB接口显示

MS9122是一款USB单芯片投屏器,内部集成了USB2.0 控制器和数据收发模块、HDMI 数据接口和音视频处理模块。MS9122可以通过USB接口显示或者扩展PC、智能手机、平板电脑的显示信息到更大尺寸的显示设备,支持HDMI视频接口。 主要功能特征 HDMI v1.4兼容 最大…

【java基础】包装类,自动装箱和自动拆箱

文章目录基本介绍包装类自动装箱自动拆箱包装类注意事项包装类比较包装器内容不可变基本介绍 有时,需要将int这样的基本类型转换为对象。所有的基本类型都有一个与之对应的类。 例如,Integer类对应基本类型int。通常,这些类称为包装器&#…

网上这么多IT的培训机构,我们该怎么选?

说实话,千万不要把这个答案放在网上来找,因为你只能得到别人觉得合适的或者机构的广告;当然个人的培训经历可以听一听的,毕竟不靠谱的机构也有,比如让你交一两万去上线上课程或者一百号来人坐一起看视频,这…

【django】django-simpleui配置后,后台显示空白页解决方法

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 django后台显示空白页解决方法 1. 正文 添加完simpleui以后,后台显示一片空白,一脸问号??? …

MacBook Pro 休眠后五国,自动重启报错

看了网上很多情况,都说是系统的原因,2018年款的Mac已经过了保修期去过天才吧说花4k 换主板解决,花个几千块去解决这个问题不如折旧加钱再买个新的总之先用应急的办法1.电池偏好设置,接通电源时勾选“ 当显示器关闭时,防…

类的加载过程(生命周期)

类的加载过程(生命周期) 一、装载:通过一个类的全限定名获取定义此类的二进制字节流将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构在内存中生成一个代表这个类的java.lang.Class对象(将字节码加载到内存中),作为…

扫地机器人(蓝桥杯C/C++)

题目描述 小明公司的办公区有一条长长的走廊,由 NN 个方格区域组成,如下图所示。 走廊内部署了 KK 台扫地机器人,其中第 ii 台在第 A_iAi​ 个方格区域中。已知扫地机器人每分钟可以移动到左右相邻的方格中,并将该区域清扫干净。…

DevOps落地与转型:提升研发效能的方法与实践

❤️作者主页:小虚竹 ❤️作者简介:大家好,我是小虚竹。Java领域优质创作者🏆,CSDN博客专家🏆,华为云享专家🏆,掘金年度人气作者🏆,阿里云专家博主&#x1f3…

使用 OpenCV 将图像转换为铅笔素描的 8 个步骤

使用 OpenCV 从彩色图像创建铅笔素描图像这个项目是我为 LetsGrowMore 的数据科学实习生创建的任务。LetsGrowMore :https://letsgrowmore.in/vip/目录什么是 OpenCV?第 1 步:读取图像第 2 步:将图像转换为灰度第 3 步&#xff1a…

MarkDown中写UML图的方法

目录序UML图之顺序图顺序图的四个要素关于消息箭头的语法Mermaid中顺序图的简单例子样例用小人表示对象为对象设置别名激活对象UML图之类图类图中常见的关系关于不同类型关系的语法Mermaid中类图的简单例子样例类定义的两种方式为类定义成员双向关系的表示多重性关系的表示UML之…

毕业设计 基于51单片机的手机蓝牙控制8位LED灯亮灭设计

基于51单片机的手机蓝牙控制8位LED灯亮灭设计1、项目简介1.1 系统构成1.2 系统功能2、部分电路设计2.1 STC89C52单片机核心系统电路设计2.2 LED电路设计2.3 蓝牙模块电路设计3、部分代码展示3.1 定时器初始化以及中断处理3.2 串口初始化3.3 串口中断处理1、项目简介 选题指导&…