轻量化网络:MobileNet-V2(转载)

原文链接https://blog.youkuaiyun.com/u011995719/article/details/79135818

MobileNetV2:
《Inverted Residuals and Linear Bottlenecks: Mobile Networks for Classification, Detection and Segmentation》
于2018年1月公开在arXiv(美[ˈɑ:rkaɪv]) :https://arxiv.org/abs/1801.04381

MobileNetV2是对MobileNetV1的改进,同样是一个轻量化卷积神经网络。

创新点:

1. Inverted residuals,通常的residuals block是先经过一个1*1的Conv layer,把feature map的通道数“压”下来,再经过3*3 Conv layer,最后经过一个1*1 的Conv layer,将feature map 通道数再“扩张”回去。即先“压缩”,最后“扩张”回去。
而 inverted residuals就是 先“扩张”,最后“压缩”。为什么这么做呢?请往下看。

2.Linear bottlenecks,为了避免Relu对特征的破坏,在residual block的Eltwise sum之前的那个 1*1 Conv 不再采用Relu,为什么?请往下看。

创新点全写在论文标题上了!

由于才疏学浅,对本论文理论部分不太明白,所以选取文中重要结论来说明MobileNet-V2。

先看看MobileNetV2 和 V1之间有啥不同
原图链接
这里写图片描述

主要是两点:

  1. Depth-wise convolution之前多了一个1*1的“扩张”层,目的是为了提升通道数,获得更多特征;
  2. 最后不采用Relu,而是Linear,目的是防止Relu破坏特征。

再看看MobileNetV2的block 与ResNet 的block:
原图链接
这里写图片描述
主要不同之处就在于,ResNet是:压缩”→“卷积提特征”→“扩张”,MobileNetV2则是Inverted residuals,即:“扩张”→“卷积提特征”→ “压缩”

正文:

MobileNet-V1 最大的特点就是采用depth-wise separable convolution来减少运算量以及参数量,而在网络结构上,没有采用shortcut的方式。
Resnet及Densenet等一系列采用shortcut的网络的成功,表明了shortcut是个非常好的东西,于是MobileNet-V2就将这个好东西拿来用。

拿来主义,最重要的就是要结合自身的特点,MobileNet的特点就是depth-wise separable convolution,但是直接把depth-wise separable convolution应用到 residual block中,会碰到如下问题:

1.DWConv layer层提取得到的特征受限于输入的通道数,若是采用以往的residual block,先“压缩”,再卷积提特征,那么DWConv layer可提取得特征就太少了,因此一开始不“压缩”,MobileNetV2反其道而行,一开始先“扩张”,本文实验“扩张”倍数为6。 通常residual block里面是 “压缩”→“卷积提特征”→“扩张”,MobileNetV2就变成了 “扩张”→“卷积提特征”→ “压缩”,因此称为Inverted residuals

2.当采用“扩张”→“卷积提特征”→ “压缩”时,在“压缩”之后会碰到一个问题,那就是Relu会破坏特征。为什么这里的Relu会破坏特征呢?这得从Relu的性质说起,Relu对于负的输入,输出全为零;而本来特征就已经被“压缩”,再经过Relu的话,又要“损失”一部分特征,因此这里不采用Relu,实验结果表明这样做是正确的,这就称为Linear bottlenecks

MobileNet-V2网络结构

附赠苏师兄的prototxt:
https://github.com/suzhenghang/MobileNetv2/tree/master/.gitignore
另外一位朋友的prototxt:
https://github.com/austingg/MobileNet-v2-caffe

这里写图片描述

其中:t表示“扩张”倍数,c表示输出通道数,n表示重复次数,s表示步长stride。
先说两点有误之处吧:
1. 第五行,也就是第7~10个bottleneck,stride=2,分辨率应该从28降低到14;如果不是分辨率出错,那就应该是stride=1;
2. 文中提到共计采用19个bottleneck,但是这里只有17个。

Conv2d 和avgpool和传统CNN里的操作一样;最大的特点是bottleneck,一个bottleneck由如下三个部分构成:

这里写图片描述

这就是之前提到的inverted residuals结构,一个inverted residuals结构的Multiply Add=
h*w*d’ * 1*1*td’ +
h*w*td’ * k*k*1 +
h*w*t d’ * 1*1*d” =
h*w*d’*t(d’+ k*k + d”)

特别的,针对stride=1 和stride=2,在block上有稍微不同,主要是为了与shortcut的维度匹配,因此,stride=2时,不采用shortcut。 具体如下图:
这里写图片描述

可以发现,除了最后的avgpool,整个网络并没有采用pooling进行下采样,而是利用stride=2来下采样,此法已经成为主流,不知道是否pooling层对速度有影响,因此舍弃pooling层?是否有朋友知道那篇论文里提到这个操作?

看看MobileNet-V2 分类时,inference速度:

这里写图片描述

这是在手机的CPU上跑出来的结果(Google pixel 1 for TF-Lite)

同时还进行了目标检测和图像分割实验,效果都不错,详细请看原文。

### MobilenetV2架构 MobilenetV2是一种高效的卷积神经网络,专为移动设备设计,在保持高精度的同时显著减少了计算量和参数数量。该网络引入了倒残差结构(Inverted Residuals)和线性瓶颈(Linear Bottlenecks),这些特性使得模型更加轻量化且易于部署[^4]。 #### 架构特点 - **倒残差模块**:传统残差连接位于较宽层之后,而MobilenetV2则相反,将窄层置于更宽的膨胀层之前。 - **逐点卷积与深度可分离卷积组合**:通过先执行1×1卷积再做3×3深度可分离卷积的方式减少运算次数。 - **ReLU6激活函数**:为了防止数值溢出并加速收敛速度,采用ReLU6作为默认激活器。 ```python import torch.nn as nn class ConvBNReLU(nn.Sequential): def __init__(self, in_planes, out_planes, kernel_size=3, stride=1, groups=1): padding = (kernel_size - 1) // 2 super(ConvBNReLU, self).__init__( nn.Conv2d(in_planes, out_planes, kernel_size, stride, padding, groups=groups, bias=False), nn.BatchNorm2d(out_planes), nn.ReLU6(inplace=True) ) ``` ### 实现代码 以下是使用PyTorch框架构建MobilenetV2的一个简化版本: ```python def _make_divisible(v, divisor, min_value=None): """ This function is taken from the original tf repo. It ensures that all layers have a channel number that is divisible by 8 """ if min_value is None: min_value = divisor new_v = max(min_value, int(v + divisor / 2) // divisor * divisor) # Make sure that round down does not go down by more than 10%. if new_v < 0.9 * v: new_v += divisor return new_v class InvertedResidual(nn.Module): def __init__(self, inp, oup, stride, expand_ratio): super(InvertedResidual, self).__init__() hidden_dim = int(round(inp * expand_ratio)) self.use_res_connect = stride == 1 and inp == oup layers = [] if expand_ratio != 1: # pw layers.append(ConvBNReLU(inp, hidden_dim, kernel_size=1)) layers.extend([ # dw ConvBNReLU(hidden_dim, hidden_dim, stride=stride, groups=hidden_dim), # pw-linear nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False), nn.BatchNorm2d(oup), ]) self.conv = nn.Sequential(*layers) def forward(self, x): if self.use_res_connect: return x + self.conv(x) else: return self.conv(x) class MobileNetV2(nn.Module): def __init__(self, num_classes=1000, width_mult=1.0): super(MobileNetV2, self).__init__() block = InvertedResidual input_channel = 32 last_channel = 1280 inverted_residual_setting = [ # t, c, n, s [1, 16, 1, 1], [6, 24, 2, 2], [6, 32, 3, 2], [6, 64, 4, 2], [6, 96, 3, 1], [6, 160, 3, 2], [6, 320, 1, 1], ] # building first layer input_channel = _make_divisible(input_channel * width_mult, 8) self.last_channel = _make_divisible(last_channel * max(1.0, width_mult), 8) features = [ConvBNReLU(3, input_channel, stride=2)] # building inverted residual blocks for t, c, n, s in inverted_residual_setting: output_channel = _make_divisible(c * width_mult, 8) for i in range(n): stride = s if i == 0 else 1 features.append(block(input_channel, output_channel, stride, expand_ratio=t)) input_channel = output_channel # building last several layers features.append(ConvBNReLU(input_channel, self.last_channel, kernel_size=1)) # make it nn.Sequential self.features = nn.Sequential(*features) # building classifier self.classifier = nn.Sequential( nn.Dropout(0.2), nn.Linear(self.last_channel, num_classes), ) # weight initialization for m in self.modules(): if isinstance(m, nn.Conv2d): nn.init.kaiming_normal_(m.weight, mode='fan_out') if m.bias is not None: nn.init.zeros_(m.bias) elif isinstance(m, nn.BatchNorm2d): nn.init.ones_(m.weight) nn.init.zeros_(m.bias) elif isinstance(m, nn.Linear): nn.init.normal_(m.weight, 0, 0.01) nn.init.zeros_(m.bias) def forward(self, x): x = self.features(x) x = x.mean([2, 3]) # global average pooling x = self.classifier(x) return x ``` ### 预训练模型下载 对于希望直接利用已有的预训练权重来加快开发进度或提高性能的研究者来说,可以直接从官方资源获取预训练好的MobilenetV2模型。例如,在PyTorch环境中可以通过`torchvision.models.mobilenet_v2(pretrained=True)`轻松加载ImageNet上预先训练过的模型实例[^2]。 ### 使用教程 当获得了一个预训练的MobilenetV2模型后,可以根据具体应用场景调整最后一层全连接层以适应新的类别数,并继续微调整个网络或者仅冻结前面几层只更新新增加的部分。此外还可以探索其他高级技巧如数据增强、正则化方法等进一步提升泛化能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值