温州网站改版,中国建造师人才网,html前端网站开发,做网站计入什么科目前言
轻量级神经网络中#xff0c;比较重要的有MobileNet和ShuffleNet#xff0c;其实还有其它的#xff0c;比如SqueezeNet、Xception等。
本博客为MobileNet的前两个版本的理论简介与Keras中封装好的模块的对应实现方案。
国际惯例#xff0c;参考博客#xff1a;
纵…前言
轻量级神经网络中比较重要的有MobileNet和ShuffleNet其实还有其它的比如SqueezeNet、Xception等。
本博客为MobileNet的前两个版本的理论简介与Keras中封装好的模块的对应实现方案。
国际惯例参考博客
纵览轻量化卷积神经网络SqueezeNet、MobileNet、ShuffleNet、Xception
为什么MobileNet及其变体如此之快
【Tensorflow】tf.nn.depthwise_conv2d如何实现深度卷积?
Keras-application中的实现
MobileNetV1论文
MobileNetV2论文
MobileNetV3论文
MobileNetV1
理论
利用深层分离式卷积(Depthwise Separable Convolution)替换传统的卷积操作,深层分离式卷积是由深层卷积(depthwise convolution)和大小为1的卷积核(pointwise convolution)组合
正常的卷积过程为设有M个大小为(P,P)(P,P)(P,P)的输入特征图使用N个大小为(S,S)(S,S)(S,S)的卷积核进行卷积操作可得到N个大小为(Q,Q)(Q,Q)(Q,Q)的输出特征图。计算量是 M×N×S2×Q2M\times N \times S^2\times Q^2 M×N×S2×Q2 MobileNet的卷积过程M个大小为(P,P)(P,P)(P,P)的输入特征图先与MMM个大小为(3,3)(3,3)(3,3)的卷积核分别卷积也就是M个特征图与M个卷积核一一对应第M个卷积核对除第M个特征图外的特征图无其它操作就得到了M个大小为(Q,Q)(Q,Q)(Q,Q)的特征图。随后使用N个大小为(1,1)(1,1)(1,1)的卷积核对这M个特征图卷积得到N个大小为(Q,Q)(Q,Q)(Q,Q)的特征图。计算量是: M×S2×Q2M×N×Q2M\times S^2\times Q^2 M\times N\times Q^2 M×S2×Q2M×N×Q2
上图左边就是常规的卷积操作右边就是MobileNet对应的卷积操作。
此外论文还提供两个控制模型参数或者计算量的操作
Width Multiplier将模型变得更瘦意思就是最终通道数减少在pointwise convolution层操作比如原来N个(1,1)(1,1)(1,1)个卷积核现在变成了N×WidthMultiplierN \times WidthMultiplierN×WidthMultiplierResolution Multiplier将模型的每个特征图变得更小比如某层是240×240240\times240240×240的特征图经过Resolution Multiplier0.5调整后就变成了120×120120\times120120×120大小的特征图论文中是通过这个来调整输入大小。 In practice we implicitly set ρ by setting the input resolution 经评论区提醒关于Resolution Multiplier的描述有所变动。之前描述的是tensorflow里面的验证结果它没有调整特征图大小而是提升了depth wise后的卷积特征图通道数。具体看下面的问题描述与验证。
问题
关于Resolution Multiplier如果按照第三个参考博客计算那么这个值必须是正整数不可能是小于1的小数刚好与tensorflow的实现对应但是论文描述是为了降低计算量一般是小于1的小数所以不可能扩大。这两个理论刚好相反。
我个人理解是这个Resolution Multiplier是降低depthwise卷积得到的每个特征图的大小比如输入10个大小为(224,224)(224,224)(224,224)的特征图经过第一步深度卷积后得到的是10个(112,112)(112,112)(112,112)大小的特征图而非5个(224,224)(224,224)(224,224)大小的特征图。从文章中的公式就能看出来: DK⋅DK⋅αM⋅ρDF⋅ρDFαM⋅αN⋅ρDF⋅ρDFD_K\cdot D_K\cdot \alpha M \cdot \rho D_F\cdot \rho D_F\alpha M\cdot \alpha N\cdot \rho D_F\cdot \rho D_F DK⋅DK⋅αM⋅ρDF⋅ρDFαM⋅αN⋅ρDF⋅ρDF 其中α\alphaα代表输出特征图减少了除了最开始的输入层外的层输入和输出特征图都变成了 α\alphaα 倍所以第二项MMM和NNN都乘了α\alphaα 分别表示输入特征图与输出特征图个数ρ\rhoρ乘在了DFD_FDF而DFD_FDF代表特征图的大小而非个数所以应该是降低了每层depthwise conv后的特征图大小类似于卷积核变大了输出特征图就变小了。
但是Tensorflow代码中验证的结果又和自己的想法不同。验证如下
代码
在Keras中实现此过程的代码如下
def _depthwise_conv_block(inputs, pointwise_conv_filters, alpha,depth_multiplier1, strides(1, 1), block_id1):channel_axis 1 if backend.image_data_format() channels_first else -1pointwise_conv_filters int(pointwise_conv_filters * alpha)if strides (1, 1):x inputselse:x layers.ZeroPadding2D(((0, 1), (0, 1)),nameconv_pad_%d % block_id)(inputs)x layers.DepthwiseConv2D((3, 3),paddingsame if strides (1, 1) else valid,depth_multiplierdepth_multiplier,stridesstrides,use_biasFalse,nameconv_dw_%d % block_id)(x)x layers.BatchNormalization(axischannel_axis, nameconv_dw_%d_bn % block_id)(x)x layers.ReLU(6., nameconv_dw_%d_relu % block_id)(x)x layers.Conv2D(pointwise_conv_filters, (1, 1),paddingsame,use_biasFalse,strides(1, 1),nameconv_pw_%d % block_id)(x)x layers.BatchNormalization(axischannel_axis,nameconv_pw_%d_bn % block_id)(x)return layers.ReLU(6., nameconv_pw_%d_relu % block_id)(x)很清晰的发现是经过DepthWise卷积、BN、Relu、pointwise卷积、BN、Relu。 而且宽度因子alpha起作用的是pointwise卷积的时候而分辨率因子depth_multiplier是对DepthwiseConv起作用的。下面能验证。
然后按照论文Table1复现结构的时候要注意卷积步长刚开始的正常卷积步长(2,2)(2,2)(2,2)后面依次为不使用步长和使用步长的深层分离网络结构。 x _conv_block(img_input, 32, alpha, strides(2, 2))x _depthwise_conv_block(x, 64, alpha, depth_multiplier, block_id1)x _depthwise_conv_block(x, 128, alpha, depth_multiplier,strides(2, 2), block_id2)x _depthwise_conv_block(x, 128, alpha, depth_multiplier, block_id3)x _depthwise_conv_block(x, 256, alpha, depth_multiplier,strides(2, 2), block_id4)x _depthwise_conv_block(x, 256, alpha, depth_multiplier, block_id5)x _depthwise_conv_block(x, 512, alpha, depth_multiplier,strides(2, 2), block_id6)x _depthwise_conv_block(x, 512, alpha, depth_multiplier, block_id7)x _depthwise_conv_block(x, 512, alpha, depth_multiplier, block_id8)x _depthwise_conv_block(x, 512, alpha, depth_multiplier, block_id9)x _depthwise_conv_block(x, 512, alpha, depth_multiplier, block_id10)x _depthwise_conv_block(x, 512, alpha, depth_multiplier, block_id11)x _depthwise_conv_block(x, 1024, alpha, depth_multiplier,strides(2, 2), block_id12)x _depthwise_conv_block(x, 1024, alpha, depth_multiplier, block_id13)验证问题
上面说了关于resolution multiplier的理解论文和tf官方实现不清楚原因所在。
先来验证一下论文3.3节的width Multiplier直接用Keras自带的模型
import tensorflow as tf
mobilenettf.keras.applications.MobileNet(alpha0.25,depth_multiplier1,weightsNone)
#mobilenettf.keras.applications.MobileNet(alpha1,depth_multiplier1,weightsNone)
mobilenet.summary()可以发现在第一次卷积减少了1/41/41/4的特征图后面每次pwpwpw的时候也减少了1/41/41/4的特征图这里的pw就是point wise卷积所以width multiplier是在(1,1)(1,1)(1,1)卷积的时候减少卷积核数量
再来看看resolution multiplier 输入小数的时候 mobilenettf.keras.applications.MobileNet(alpha1,depth_multiplier0.5,weightsNone)直接报错 TypeError: Value passed to parameter shape has DataType float32 not in list of allowed values: int32, int64对比1和2的时候 mobilenettf.keras.applications.MobileNet(alpha1,depth_multiplier1,weightsNone)
#mobilenettf.keras.applications.MobileNet(alpha1,depth_multiplier2,weightsNone)
model.summary()可以发现在dw的时候通道数增大了一倍原理就是在(3,3)(3,3)(3,3)的深度卷积的时候增加了通道数。这让我很郁闷啊论文和tf的代码不一样哎原文是减少是小数。 这个在Keras中也有对应描述: depth_multiplier: depth multiplier for depthwise convolution. This is called the resolution multiplier in the MobileNet paper 貌似Resolution Multiplier就是针对depthwise卷积的 如果有大佬能帮忙解惑将不甚感激昂。 ------------------更新日志2019-8-7--------------------------------- 与评论区讨论了一下这个Resolution Multiplier姑且就认为是改变输入大小吧Keras官方的MobileNet不是默认支持四种输入大小么:
------------------------------------------------------------------------Resolution | ImageNet Acc | Multiply-Adds (M) | Params (M)
------------------------------------------------------------------------
| 1.0 MobileNet-224 | 70.6 % | 529 | 4.2 |
| 1.0 MobileNet-192 | 69.1 % | 529 | 4.2 |
| 1.0 MobileNet-160 | 67.2 % | 529 | 4.2 |
| 1.0 MobileNet-128 | 64.4 % | 529 | 4.2 |
------------------------------------------------------------------------MobileNetV2
理论
文章首先提出了一个概念Linear Bottlenecks这个Bottolenecks在ResNet中出现过戳这篇博客看讲道理就是shortcut连接说明在MobileNetV2引入了ResNet的思想。
作者做了一个假设神经网络中的兴趣流行(manifold of interest)可以被嵌入到低维子空间中个人觉得像是PCA的理论提取主分量就是降维到低维子空间了。比如某卷积层的第ddd个通道的像素所编码的信息实际就存在某个特定的流行空间中而这个流行空间可以嵌入到低维子空间。
基于这个假设降低每一层的维度就能降低操作空间的维度这在MobileNetV1中使用过利用width multiplier减少每个block输出的特征图数目。但是神经网络中的非线性变换打破了这个想法比如Relu经常导致某些神经元坏死将会损失信息。
随后又做了个假设既然Relu在某个通道会丢失信息搞不好在其它通道仍然保存着信息。
总结起来就是作者认为感兴趣的流行一定存在于高维激活空间的一个低维子空间中
如果在Relu激活后仍然保持着非零那就是线性变换。Relu可以保存输入流行的完整信息但是仅仅是在输入流行存在于输入空间的一个低维子空间的前提下。
这样就可以优化网络结构假设兴趣流行在低维空间中我们就可以通过插入linear bottleneck到卷积模块中进而捕捉到这种低维信息。具体插入到哪里看下面右图中带斜线的块就是线性激活块。 左图是标准的Resnet模块先(1,1)(1,1)(1,1)的卷积核卷积然后Relu再用(3,3)(3,3)(3,3)的卷积核卷积然后Relu再用(1,1)(1,1)(1,1)的卷积核卷积再Relu将最终的结果与输入相加。
右图是文章的Inverted Residual带虚线的是不适用非线性激活。先(1,1)(1,1)(1,1)的卷积核卷积注意这里要用大量的卷积核增加特征图数目再用Relu激活再用(3,3)(3,3)(3,3)的深层卷积depthwise conv卷积使用Relu激活再使用(1,1)(1,1)(1,1)的卷积核卷积这里使用较少的卷积核降低输出特征图的数目最后来一次shortcut连接输入和输出特征图。
从上图可以发现传统的Resnet输入和输出通道数都比较大而Invert residual的通道数都比较小只有中间极大增加了通道数是想用大量的通道来确保流行信息被保存下来后面用了深层卷积确保了参数与计算量的降低。
Resnet通常称沙漏形状先减少通道数再增加通道数。Invert Residual刚好相反先增加通道数再降维这就是为啥称为逆Res
初步总结MobilenetV2的特点
依旧使用MobileNetV1的特点使用depthwise和pointwise的卷积但是增加了残差结构连接与残差网的漏斗形状(两边粗中间细)相反MobileNetv2在中间增加了通道数呈现两边细中间粗的形状因为作者相信大量的低维空间能够捕捉到高维特征中有用的信息所以要扩充通道数在Block最后一层为了不破坏提取的信息去掉了Relu激活而直接shortcut连接
结构总结
(1,1)(1,1)(1,1)的pointwise卷积提高通道数结果Relu(3,3)(3,3)(3,3)的depthwise卷积提特征结果Relu(1,1)(1,1)(1,1)的pointwise卷积单纯降维不Relu输入与输出的shortcut
代码
在Keras中做了一下简单的处理
先做常规卷积卷积核大小(3,3)(3,3)(3,3)步长(2,2)(2,2)(2,2)使用valid的卷积方式降低特征图大小
x layers.ZeroPadding2D(paddingcorrect_pad(backend, img_input, 3),nameConv1_pad)(img_input)
x layers.Conv2D(first_block_filters,kernel_size3,strides(2, 2),paddingvalid,use_biasFalse,nameConv1)(x)
x layers.BatchNormalization(axischannel_axis,epsilon1e-3,momentum0.999,namebn_Conv1)(x)
x layers.ReLU(6., nameConv1_relu)(x)其中correct_pad是为了让卷积能够正常进行也就是下式能整除避免特征图变成小数宽度 n−mpaddingstride1\frac{n-mpadding}{stride}1 striden−mpadding1 接下来便是使用Invert Residual模型结构 x _inverted_res_block(x, filters16, alphaalpha, stride1,expansion1, block_id0)x _inverted_res_block(x, filters24, alphaalpha, stride2,expansion6, block_id1)x _inverted_res_block(x, filters24, alphaalpha, stride1,expansion6, block_id2)x _inverted_res_block(x, filters32, alphaalpha, stride2,expansion6, block_id3)x _inverted_res_block(x, filters32, alphaalpha, stride1,expansion6, block_id4)x _inverted_res_block(x, filters32, alphaalpha, stride1,expansion6, block_id5)x _inverted_res_block(x, filters64, alphaalpha, stride2,expansion6, block_id6)x _inverted_res_block(x, filters64, alphaalpha, stride1,expansion6, block_id7)x _inverted_res_block(x, filters64, alphaalpha, stride1,expansion6, block_id8)x _inverted_res_block(x, filters64, alphaalpha, stride1,expansion6, block_id9)x _inverted_res_block(x, filters96, alphaalpha, stride1,expansion6, block_id10)x _inverted_res_block(x, filters96, alphaalpha, stride1,expansion6, block_id11)x _inverted_res_block(x, filters96, alphaalpha, stride1,expansion6, block_id12)x _inverted_res_block(x, filters160, alphaalpha, stride2,expansion6, block_id13)x _inverted_res_block(x, filters160, alphaalpha, stride1,expansion6, block_id14)x _inverted_res_block(x, filters160, alphaalpha, stride1,expansion6, block_id15)x _inverted_res_block(x, filters320, alphaalpha, stride1,expansion6, block_id16)其中alpha是最后一层pointwise的(1,1)(1,1)(1,1)卷积的输出特征图数目expansion是invert residual的第一层pointwise卷积对原始通道数扩充的维度。
注意这里的卷积步长有变化有(1,1)(1,1)(1,1)和(2,2)(2,2)(2,2)所以在步长位(2,2)(2,2)(2,2)的时候要注意使用correct_pad填充特征图避免卷积出小数宽高特征图的情况。
还有一点是Keras实现的时候第1个block并没有做通道数的扩充 if block_id:# Expandx layers.Conv2D(expansion * in_channels,kernel_size1,paddingsame,use_biasFalse,activationNone,nameprefix expand)(x)x layers.BatchNormalization(axischannel_axis,epsilon1e-3,momentum0.999,nameprefix expand_BN)(x)x layers.ReLU(6., nameprefix expand_relu)(x)else:prefix expanded_conv_随后进入(3,3)(3,3)(3,3)的depthwise卷积提取特征注意如果步长是(2,2)(2,2)(2,2)要核对是否能够满足步长卷积 if stride 2:x layers.ZeroPadding2D(paddingcorrect_pad(backend, x, 3),nameprefix pad)(x)x layers.DepthwiseConv2D(kernel_size3,stridesstride,activationNone,use_biasFalse,paddingsame if stride 1 else valid,nameprefix depthwise)(x)x layers.BatchNormalization(axischannel_axis,epsilon1e-3,momentum0.999,nameprefix depthwise_BN)(x)x layers.ReLU(6., nameprefix depthwise_relu)(x)随后直接做投影也就是使用pointwise卷积降低通道数不用Relu: # Projectx layers.Conv2D(pointwise_filters,kernel_size1,paddingsame,use_biasFalse,activationNone,nameprefix project)(x)x layers.BatchNormalization(axischannel_axis,epsilon1e-3,momentum0.999,nameprefix project_BN)(x)最后如果输入输出的通道数相同就shortcut:
if in_channels pointwise_filters and stride 1:return layers.Add(nameprefix add)([inputs, x])
return x为了更清晰理解每层的作用我这里对部分层的特征图大小做了一下输出截图
from keras.applications.mobilenetv2 import MobileNetV2
model MobileNetV2(weightsimagenet)
model.summary()刚开始的卷积-BN-Relu: 随后是最开始的invert residual 块没有维度扩充只有depthwise卷积和pointwise映射 可以发现输出维度由32降低成16。
再看第二块invert residual块 上来就把维度由输入的16个通道提升为96个通道然后使用(3,3)(3,3)(3,3)的卷积步长为(2,2)(2,2)(2,2)得到特征图大小是 113−32156\frac{113-3}{2}156 2113−3156 最后投影的时候又将通道数降低了4倍此时没有shorcut因为输入输出的特征图通道和大小都不同。
再看第三个invert block 同样一上来就把通道数由24扩充6倍到144然后步长为(1,1)(1,1)(1,1)的(3,3)(3,3)(3,3)卷积最后降低通道数此时输入和输出的特征图数目与大小都相同为(56,56,24)(56,56,24)(56,56,24)直接shortcut加起来。
第四个invert block:
老样子24扩充6倍到144用(3,3)(3,3)(3,3)的卷积核步长位(2,2)(2,2)(2,2)卷积最后降低通道数。
总结
主要稍微总结了一下MobileNet的两个版本的设计思路
MobileNetV1使用depthwise卷积和(1,1)(1,1)(1,1)的卷积核代替传统的卷积算法
MobileNetV2在版本1的基础上加入了残差网的思想传统残差网是先将特征图数目变小然后再变大最后shortcut而MobileNetV2之所以称为invert residual就是因为反过来先将特征图数目变大再变小这是因为作者感觉特征图太少会受到Relu的影响表达能力不强那么就使用一堆的特征图去提取特征总有一个能提取到有用信息最后为了避免信息被破话就不用Relu直接shortcut了。
戳这里是V1的实现戳这里是V2的实现都是官方的具体使用方法可以戳Keras官方文档的这里仿写。
后续会继续学习其它的轻量级网络设计方法包括Inception、Xception、ShuflleNet等