MoblieNetV1、V2、V3、ViT四种Moblie模型的分析对比

1、MoblieNetV1

2017年提出,论文地址为:https://arxiv.org/pdf/1704.04861.pdf

1.1 相关知识

提到了标准卷积、深度可分卷积、点卷积,并分析了不同卷积结构的计算量,(假设 D k D_k Dk为ksize,M为卷积的输入层通道数,N为卷积的输出层通道数, D f D_f Df位feature map的size)。
Alt text

标准卷积涉及到的kernel数量为M*N(其卷积运算涉及参数共享、局部视野感知,卷积kernel内的全连接涉及channel间的交互融合,多个卷积核涉及多视角提取特征),即针对每一个输出(共N个)都采用M个卷积去做运算(本质就是实现channel间的全连接,每一个连接线都要进行卷积运算),M个卷积的结果加到一起得到一个输出,每一个输出都有一个偏置项。标准卷积的计算量如下(不考虑偏置项):
D k ∗ D k ∗ M ∗ N ∗ D f ∗ D f D_k*D_k*M*N*D_f*D_f DkDkMNDfDf

深度卷积与标准卷积不同,其kernel数量与输入数据的channel相同,其不改变输入数据的通道数。针对每一个输入(共M个),都采用1个卷积去做运算(一个输出对应一个卷积,实现输入与输出的一一对应)。故深度可分卷积的计算量如下:
D k ∗ D k ∗ M ∗ D f ∗ D f D_k*D_k*M*D_f*D_f DkDkMDfDf

点卷积是一种特殊的标准卷积,其ksize为1x1,也就是说其不对数据进行卷积运算,只是对数据进行缩放和平移,并实现通道间的数据交互。故点卷积的计算量如下:
M ∗ N ∗ D f ∗ D f M*N*D_f*D_f MNDfDf

1.2 标准卷积与深度可分离卷积

MobliNetV1认为标准卷积的运算量太大,其本质就是卷积运算提取图像特征,卷积核内进行全连接实现channel间特征的交互融合,故认为可以使用 深度卷积+点卷积 来近似 标准卷积,并将这种结合定义为深度可分离卷积。故此,深度可分离卷积的运算量如下:
D k ∗ D k ∗ M ∗ D f ∗ D f + M ∗ N ∗ D f ∗ D f D_k*D_k*M*D_f*D_f+M*N*D_f*D_f DkDkMDfDf+MNDfDf
传统卷积的计算示意图如下:
Alt text
传统卷积在卷积核为1x1时的计算示意图如下
Alt text

深度可分离卷积与普通卷积的运算量对比如下:
D k ∗ D k ∗ M ∗ D f ∗ D f + M ∗ N ∗ D f ∗ D f D k ∗ D k ∗ M ∗ N ∗ D f ∗ D f = 1 N + 1 D k ∗ D k \frac{D_k*D_k*M*D_f*D_f+M*N*D_f*D_f}{D_k*D_k*M*N*D_f*D_f}=\frac{1}{N}+\frac{1}{D_k*D_k} DkDkMNDfDfDkDkMDfDf+MNDfDf=N1+DkDk1
深度可分卷积的计算示意图如下:
Alt text

在MobliNet中,并不是直接使用深度卷积+点卷积来替换标准卷积。其在深度卷积与点卷积之间还插入了BN层和relu层。

由此推断,直接使用深度卷积+点卷积来替换标准卷积并不能取得优质效果(因为缺少了标准卷积中多个卷积核涉及多视角提取特征)。在中间插入BN层和relu层后,增强了网络的非线性能力。
Alt text

1.3 MobliNetV1

MobliNetV网络结构如下图所示,在一个框内的Conv dw+Conv实际上是在等价于一个标准的conv。需要注意的是,论文图中最后一个Conv dw的stride是s1,不是s2;同时,紫色框中的模块是重复了5次的深度分离卷积
Alt text

完整实现代码如下:

import torch
import torch.nn as nndef conv_bn(in_channel, out_channel, stride = 1):"""传统卷积块:Conv+BN+Act"""return nn.Sequential(nn.Conv2d(in_channel, out_channel, 3, stride, 1, bias=False),nn.BatchNorm2d(out_channel),nn.ReLU6(inplace=True))def conv_dsc(in_channel, out_channel, stride = 1):"""深度可分离卷积:DW+BN+Act + Conv+BN+Act"""return nn.Sequential(nn.Conv2d(in_channel, in_channel, 3, stride, 1, groups=in_channel, bias=False),nn.BatchNorm2d(in_channel),nn.ReLU6(inplace=True),nn.Conv2d(in_channel, out_channel, 1, 1, 0, bias=False),nn.BatchNorm2d(out_channel),nn.ReLU6(inplace=True),)class MobileNetV1(nn.Module):def __init__(self,in_dim=3, num_classes=1000):super(MobileNetV1, self).__init__()self.num_classes = num_classesself.stage1 = nn.Sequential(conv_bn(in_dim, 32, 2),conv_dsc(32, 64, 1), conv_dsc(64, 128, 2),conv_dsc(128, 128, 1),conv_dsc(128, 256, 2),conv_dsc(256, 256, 1), )self.stage2 = nn.Sequential(conv_dsc(256, 512, 2),conv_dsc(512, 512, 1),conv_dsc(512, 512, 1),conv_dsc(512, 512, 1), conv_dsc(512, 512, 1),conv_dsc(512, 512, 1),)self.stage3 = nn.Sequential(conv_dsc(512, 1024, 2),conv_dsc(1024, 1024, 1),)self.avg = nn.AdaptiveAvgPool2d((1,1))self.fc = nn.Linear(1024, self.num_classes)def forward(self, x):x = self.stage1(x)x = self.stage2(x)x = self.stage3(x)x = self.avg(x)x = x.view(-1, 1024)x = self.fc(x)return x

1.4 效果对比

将moblienet的深度分离卷积替换为标准卷积后,可见运算量增加了8倍(使用深度可分卷积,节省了约1/9的计算量),参数量减少了6倍。而精度,仅降低了1%。
Alt text

模型结构验证,移除了模型结构中重复了5次的深度分离卷积,并将标准MoblieNet的参数缩放到原来额0.75倍,保持参数量相同,可以看到拥有更多深度的0.75 MoblieNet模型效果更好。
Alt text

作者又在不同宽度缩放倍数和分辨率下对模型进行测试,得出MoblieNetV1的最佳精度为70.6%。同时也展示了不同分辨率输入下,模型计算量的差异。
Alt text

2、MoblieNetV2

2019年3月提出,论文地址为:https://arxiv.org/pdf/1801.04381.pdf

2.1 背景知识

2015年提出了resnet,并在多项imagenet大赛中取得成绩。然而,在MoblieNetV1中只是研究了标准卷积的轻量化替代。MoblieNetV2的本质是对残差结构的一次分析和改进,首先从以block为单位分析了特征输入输出在relu函数前后的变化,然后论证出内部relu函数要在高维度才能保留特征,而输出前的relu函数需要减少维度,以保留感兴趣的维度。并基于此,对原始的残差结构进行修改,提出倒残差结构。

2.2 倒残差结构论证

将特征图进行低纬嵌入可视化,并对relu函数的输出进行分析。认为在低维情况下,relu函数会截断部分数据特性。而当输出维度较高时,被relu截断的信息在其他维度有所体现,从而保留了原始的流行特性。
Alt text
其认为感兴趣的流形[目标特征]应该位于高维激活空间的低维子空间中,并得出以下特征:1、如果感兴趣的流形在ReLU变换后保持不变,它对应于一个线性变换。2、只有当输入流形位于输入空间的低维子空间时,ReLU才能够保存关于输入流形的完整信息。 作者假设感兴趣的流形是低维的,通过在卷积块中插入线性瓶颈层来捕获。实验证据表明,使用线性层是至关重要的,因为它可以防止非线性破坏太多的信息故此在PW卷积后移除了relu函数

直觉认为残差结构中【漏斗形结构】瓶颈层block中参数少的部分实际上包含了所有必要的信息,而扩展层block中参数多的部分仅仅作为伴随张量非线性转换的实现细节。希望提高梯度在乘数层之间传播的能力。然而,倒置设计【纺锤体结构】的内存效率要高得多。参数低,未必计算快,要靠内存效率
Alt text
MobliNetV2中倒残差bottleneck如下所示,可见中间层参数要多很多。
Alt text
倒残差bottleneck的具体示意如下
Alt text

2.3 网络结构

MoblieNetV2的网络结构如下所示,其中所有的空间卷积的size都为,表头中t表示卷积的扩展倍数(具体见上图的表一),c表示channel的数量,n表示bottleneck的重复次数,s表示conv的步长。从中可以看出,相比于MoblieNetV1,MoblieNetV2网络的深度较深,但通道数更少。
Alt text

对于不同的网络架构,在每个空间分辨率下需要实现的最大通道/内存数(以Kb为单位),对比如下,可见MoblieNetV2对内存的消耗是最低的。
Alt text

与其他模型的bottleneck相比,MoblieNetV2的更加简单高效。单从涉及上看,个人觉得ShuffleNet在残差分支上在使用avg pool会更高效一些
Alt text

在性能上,可以发现ShuffleNet本质上与MoblieNetV2区别不大。但是ShuffleNet的扩展性差一些(增大网络宽带,性能增加不如V2版本)
Alt text

2.4 实现代码

正常的CBR结构,用在MobileNetV2中卷积基的第一个和最后一个

class ConvBNReLU(nn.Sequential):#depthwise conv+BN+relu6,用于构建InvertedResidual。 def __init__(self, in_channel, out_channel, kernel_size=3, stride=1, groups=1): #参数:输入的tensor的通道数,输出通道数,卷积核大小,卷积步距,输入与输出对应的块数(改为输入的层数就是depth wise conv了,详见https://blog.csdn.net/weixin_43572595/article/details/110563397) padding = (kernel_size - 1) // 2#根据卷积核大小获取padding大小 super(ConvBNReLU, self).__init__( nn.Conv2d(in_channel, out_channel, kernel_size, stride, padding, groups=groups, bias=False), #构建depthwise conv卷积,不使用偏置,因为后面有BN nn.BatchNorm2d(out_channel),#BN层,输入参数为输入的tensor的层数 nn.ReLU6(inplace=True)#relu6激活函数,inplace原地操作tensor减少内存占用 )

MobileNetV2所提出的倒残差结构,当不需要进行维度放大时则不使用conv1x1,当使用时则添加。需要注意的是,其使用groups来对卷积进行分组,当groups_num==output_channel时则表示为深度卷积【分组数与通道数相同;此外,block的输出并没有使用relu函数】

class InvertedResidual(nn.Module):#逆向残差结构 def __init__(self, in_channel, out_channel, stride, expand_ratio): #参数:输入的tensor的通道数,输出的通道数,中间depthwise conv的步距,第一个1x1普通卷积的channel放大倍数 super(InvertedResidual, self).__init__() hidden_channel = in_channel * expand_ratio#隐层的输入通道数,对应中间depthwise conv的输入通道数 self.use_shortcut = stride == 1 and in_channel == out_channel #使用shortcut的条件:输入与输出shape一样,即没有缩放(stride=1),维度一样(in_channel = out_channel) layers = []#搜集各个层 if expand_ratio != 1:#这个是由于第一个卷积没有维度放大,因而不需要第一个1x1普通卷积 # 1x1 pointwise conv,第一个普通1x1卷积,升维 layers.append(ConvBNReLU(in_channel,hidden_channel, kernel_size=1)) layers.extend([ # 3x3 depthwise conv ConvBNReLU(hidden_channel, hidden_channel, stride=stride, groups=hidden_channel), # 1x1 pointwise conv(linear)第二个普通1x1卷积,降维 nn.Conv2d(hidden_channel, out_channel, kernel_size=1, bias=False), nn.BatchNorm2d(out_channel),#BN ]) self.conv = nn.Sequential(*layers)#将上面的集成到一起 def forward(self, x):#前向传播过程,直接用上面弄好的conv层 if self.use_shortcut: return x + self.conv(x) else: return self.conv(x) 

完整模型代码 代码参考地址: https://zhuanlan.zhihu.com/p/432471190?utm_id=0&wd=&eqid=aa79af36000a2d65000000046461d5b8

class MobileNetV2(nn.Module):#整体的网络 def __init__(self, num_classes=1000, alpha=1.0, round_nearest=8): #参数:类的个数,各个层的深度缩放系数(width multiplier),深度数需要改成离其最近的倍数 super(MobileNetV2, self).__init__() input_channel = _make_divisible(32 * alpha, round_nearest) #获取原始图片进来后的卷积的输出通道数离round_nearest最近的倍数 last_channel = _make_divisible(1280 * alpha, round_nearest) #获取特征提取部分最后的输出通道数离round_nearest最近的倍数 inverted_residual_setting = [ #配置表,这些都是逆向残差层   t:维度放大倍数,c:该层的输出通道数,n:该层的重复次数,s:该层的中间的depthwise conv的步距(仅限第一个inverted_residual)。 # 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], ] features = []#收集特征提取部分网络层 # conv1 layer,第一层,一个3x3普通卷积,步距为2 features.append(ConvBNReLU(3, input_channel, stride=2)) # building inverted residual residual blockes,遍历上面的配置,构建中间的逆向残差层 for t, c, n, s in inverted_residual_setting: output_channel = _make_divisible(c * alpha, round_nearest) #获取缩放后的通道数离round_nearest最近的倍数 for i in range(n):#构建n层逆向残差 stride = s if i == 0 else 1#只有第一层逆向残差层的depthwise conv才有步距不为1 features.append(InvertedResidual(input_channel, output_channel, stride, expand_ratio=t)) input_channel = output_channel # building last several layers,构建逆向残差后面的1x1普通卷积 features.append(ConvBNReLU(input_channel, last_channel, 1)) # combine feature layers,整合特征提取部分的网络 self.features = nn.Sequential(*features) # building classifier,自适应池化层,参数是输出的tensor的大小 self.avgpool = nn.AdaptiveAvgPool2d((1, 1)) self.classifier = nn.Sequential(#分类层,drop+全连接层 nn.Dropout(0.2), nn.Linear(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)#偏置初始化为0 elif isinstance(m, nn.BatchNorm2d):#BN,(x-bias)/weight nn.init.ones_(m.weight)#初始化为1 nn.init.zeros_(m.bias)#初始化为0 elif isinstance(m, nn.Linear):#全连接层 nn.init.normal_(m.weight, 0, 0.01)#权重初始化为均值为0.方差为0.01的正态分布 nn.init.zeros_(m.bias)#偏置初始化为0 def forward(self, x):#前向传播过程 x = self.features(x)#获得特征图 x = self.avgpool(x)#自适应池化,把每层大小从7x7变成了1x1 x = torch.flatten(x, 1)#展平操作,从dim=1开始 x = self.classifier(x)#通过分类层获取每个类的概率 return x 

3、MoblieNetV3

2019年11月提出,论文地址:https://openaccess.thecvf.com/content_ICCV_2019/papers/Howard_Searching_for_MobileNetV3_ICCV_2019_paper.pdf

3.1 相关知识

设计深度神经网络结构以实现精度和效率之间的最佳权衡一直是近年来一个活跃的研究领域。
在现有的轻量化模型设计中主要利用点卷积和channel shuffle操作进行模型轻量化操作。
在当时使用NAS技术来构建模型已经时极为流行的,并有人提出使用基于梯度优化的可微架构搜索框架,专注于使现有网络适应受限的移动平台。
此外,模型量化、知识蒸馏也成为了模型轻量化的一种技巧被普片应用。
为了优化移动设备上的精度延迟权衡,谷歌团队将NAS技术应用到了MoblieNetV3上。

3.2 模块设计

将MoblieNetV2中的block与SENet中的设计相结合,得到V3的block设计,同时还在DW与PW的连接中额外使用了SE模块。
Alt text
SE模块的实现如下:


class senet(nn.Module):""""""def __init__(self, chennl, reduction=4):super(senet, self).__init__()"""Constructor"""self.avg_pool = nn.AdaptiveAvgPool2d(1)self.fc = nn.Sequential(nn.Linear(chennl, _make_divisible(chennl // reduction, 8)),nn.ReLU(inplace=True), nn.Linear(_make_divisible(chennl // reduction, 8), chennl),h_sigmoid())def forward(self, x):in_chennl, out_chennl, h, w = x.size()y = self.avg_pool(x).view(in_chennl, out_chennl)y = self.fc(y).view(in_chennl, out_chennl, 1, 1)return x * y

*MV3的block的实现代码如下:


class residual(nn.Module):def __init__(self, in_place, scale_place, out_place, k_size, stride, use_se, use_hs):super(residual, self).__init__()"""Constructor"""assert stride in [1, 2]self.identity = in_place == out_place and stride == 1if in_place == scale_place:self.conv = nn.Sequential(nn.Conv2d(scale_place, scale_place, k_size, stride, padding=(k_size - 1) // 2, groups=scale_place, bias=False),nn.BatchNorm2d(scale_place),h_swish() if use_hs else nn.ReLU(inplace=True),senet(scale_place) if use_se else nn.Identity(),nn.Conv2d(scale_place, out_place, 1, 1, 0, bias=False),nn.BatchNorm2d(out_place))else:self.conv = nn.Sequential(nn.Conv2d(in_place, scale_place, 1, 1, 0, bias=False),nn.BatchNorm2d(scale_place),h_swish() if use_sh else nn.ReLU(inplace=True),nn.Conv2d(scale_place, scale_place, k_size, stride, padding=(k_size - 1) // 2, groups=scale_place, bias=False),nn.BatchNorm2d(scale_place),senet(scale_place) if use_se else nn.Identity(), h_swish() if use_hs else nn.ReLU(inplace=True),nn.Conv2d(scale_place, out_place, 1, 1, 0, bias=False), nn.BatchNorm2d(out_place))def foward(self, x):if self.identity:x += self.conv(x)else:x = self.conv(x)return x

同时,为了得到理想的网络结构,谷歌团队在设定优化目标后使用NAS技术进行结构搜索。同时在结构优化外,还进行了分类头优化和激活函数优化。
分类头优化 主要是对MoblieNetV2中最后几个conv和fc层进行重新设计,削减了其中的大量参数和计算量。
Alt text

激活函数优化 将swish x函数用hard-swish x来进行近似。与hard-sigmoid函数的近似不同,hard-swish消除了由于近似函数不同实现所造成的潜在的数值精度损失。
Alt text
Alt text
h_swish的实现代码如下:

import torch
import torch.nn as nn
import torch.nn.functional as F########################################################################
class h_sigmoid(nn.Module):""""""#----------------------------------------------------------------------def __init__(self, inplace=True):super(h_sigmoid, self).__init__()"""Constructor"""self.relu_6 = nn.ReLU6(inplace=inplace)#----------------------------------------------------------------------def forward(self, x):""""""x = self.relu_6(x + 3) / 6return x########################################################################
class h_swish(nn.Module):""""""#----------------------------------------------------------------------def __init__(self, inplace=True):super(h_swish, self).__init__()"""Constructor"""self.h_sigmoid = h_sigmoid(inplace=inplace)#----------------------------------------------------------------------def forward(self, x):""""""x = x * self.h_sigmoid(x)return x

3.3 模型结构

MobileNetV3-Large的模型结构如下所示
Alt text
MobileNetV3-Small的模型结构如下所示,因为整个模型结构都是使用NAS搜索而来,故Large与Small不存在结构相似性。
Alt text
这种NAS技术,在2019-2021年风头无两,这并不能代表未来,只是在比拼算力。后续的很多模型研究,都还是喜欢使用宽度、深度系数对模型进行缩放操作。

实现代码参考自:https://blog.csdn.net/qq_43318374/article/details/125883126

import torch
import torch.nn as nn
import torch.nn.functional as F
#----------------------------------------------------------------------
def conv_3_x_3_bn(in_place, out_place, stride):""""""return nn.Sequential(nn.Conv2d(in_place, out_place, 3, stride, 1, bias=False),nn.BatchNorm2d(out_place),h_swish())#----------------------------------------------------------------------
def conv_1_x_1_bn(in_place, out_place):""""""return nn.Sequential(nn.Conv2d(in_place, out_place, 1, 1, 0, bias=False),nn.BatchNorm2d(out_place),h_swish())
########################################################################
class mobilentv3(nn.Module):""""""#----------------------------------------------------------------------def __init__(self, net_cfg, mode = False, num_class=False, fpn_ids=False, width_mult=1):super(mobilentv3, self).__init__()"""Constructor"""self.net_cfg = net_cfgself.fpn_ids = fpn_idsin_chennl = _make_divisible(16 * width_mult, 8)layers = [conv_3_x_3_bn(3, in_channl, 2)]block = residualfor k, scale, chennl, stride, use_se, use_hs, in self.net_cfg:out_place = _make_divisible(chennl * width_mult)scale_place = _make_divisible(in_channl * scale)layers.append(block(in_chennl, scale_place, out_place, k, stride, use_se, use_hs))in_chennl = out_placeself.features = nn.Sequential(*layers)self.last_conv = conv_1_x_1_bn(in_chennl, scale_place)self.avg_pool = nn.AdaptiveAvgPool2d((1, 1))output_channel = {'large': 1280, 'small': 1024}output_channel = _make_divisible(output_channel[mode] * width_mult, 8) if width_mult > 1.0 else output_channel[mode]self.classifi = num_class != Noneif self.classifi:self.classifier = nn.Sequential(nn.Linear(scale_place, output_channel),h_swish(),nn.Dropout(0.2), nn.Linear(output_channel, num_class))self._init_weights()#----------------------------------------------------------------------def forward(self, x):""""""fnp_layers = []for i, l in enumerate(self.features):x = l(x)if self.fnp_ids:if i in fnp_ids:fnp_layers.append(x)if self.classifi:x = self.last_conv(x)x = self.avg_pool(x)x = x.view(x.size(0), -1)x = self.classifier(x)return xelse:fnp_layers.insert(0, x)return fnp_layers#----------------------------------------------------------------------def _init_weights(self):""""""for m in self.modules():if isinstance(m, nn.Conv2d):n = m.kernel_size[0] * m.kernel_size[1] * m.out_channelsm.weight.data.normal_(0, math.sqrt(2. / n))if m.bias is not None:m.bias.data.zero_()elif isinstance(m, nn.BatchNorm2d):m.weight.data.fill_(1)m.bias.data.zero_()elif isinstance(m, nn.Linear):n = m.weight.size(1)m.weight.data.normal_(0, 0.01)m.bias.data.zero_()def mobilenetv3_large(**kwargs):"""Constructs a MobileNetV3-Large model"""cfgs = [# k, sclae, c, stride SE, HS[3,   1,  16, 1, 0, 0],[3,   4,  24, 2, 0, 0],[3,   3,  24, 1, 0, 0],[5,   3,  40, 2, 1, 0],[5,   3,  40, 1, 1, 0],[5,   3,  40, 1, 1, 0], # P3 5[3,   6,  80, 2, 0, 1],[3, 2.5,  80, 1, 0, 1],[3, 2.3,  80, 1, 0, 1],[3, 2.3,  80, 1, 0, 1], # P4 9[3,   6, 112, 1, 1, 1],[3,   6, 112, 1, 1, 1],[5,   6, 160, 2, 1, 1],[5,   6, 160, 1, 1, 1],[5,   6, 160, 1, 1, 1] # P5 14]return MobileNetV3(cfgs, mode='large', **kwargs)def mobilenetv3_small(**kwargs):"""Constructs a MobileNetV3-Small model"""cfgs = [# k, scale, c, stride SE, HS[3,    1,  16, 2, 1, 0],[3,  4.5,  24, 2, 0, 0],[3, 3.67,  24, 1, 0, 0],[5,    4,  40, 2, 1, 1],[5,    6,  40, 1, 1, 1],[5,    6,  40, 1, 1, 1],[5,    3,  48, 1, 1, 1],[5,    3,  48, 1, 1, 1],[5,    6,  96, 2, 1, 1],[5,    6,  96, 1, 1, 1],[5,    6,  96, 1, 1, 1],]return MobileNetV3(cfgs, mode='small', **kwargs)      

3.4 模型性能

从延时上对比,v3貌似比v2强很多,
Alt text
同时在与其它模型对比中,MobileNetV3看着比其他同类模型要强。
Alt text

在下游任务中应用来看,V3比V2实际上没有优化效果。
目标检测对比,V3与V2具备类似的性能
Alt text

语义分割检测对比,V3与V2具备类似的性能
Alt text
基于以上来看,应该是V3模型结构是由NAS建设而来(模型结构可能只适应于检索的imagenet数据集),并未通过人工先验设计(所设计的模型结构可以适用于更多下游任务),故在下游任务效果提升有限。

4、MoblieVit

发表时间:2022.03.04 论文地址:https://blog.csdn.net/a486259/article/details/131485111?spm=1001.2014.3001.5501
csdn翻译:https://blog.csdn.net/a486259/article/details/131485111?spm=1001.2014.3001.5501

4.1 相关知识

1、现在基于self-attention的模型,特别是Vision Transformer,是卷积神经网络(CNNs)学习视觉表征的替代方法,Transformer正在视觉方面逐步取代cnn。
2、目前ViT的发展趋势是通过增加参数量来提升性能,故此现在的一些轻量化ViT模型在移动端上,性能总是低于一些轻量型的cnn。
3、ViT缺乏了cnn固有的归纳偏置,故需要更多的参数
4、现有的CNN+ViT混合模型,权重比较大,仍然需要更多的数据增强

4.2 核心设计

现有的轻量化cnn模型在处理空间特征上是局部的,MobileViT将Transformer视为卷积的一部分;利用卷积(例如,通用和简单的训练)和Transformer(例如,全局处理)的优点来构建轻量级和通用的ViTs。

1、MobliVit将标准卷积定义为:展开、局部处理、折叠
2、将卷积中的局部处理与使用Transformer的全局处理取代
3、MobileViT通过简单的训练即可达到轻量cnn的水平

MoblieVit block的设计如下,其在block内先使用conv提取图像的局部特征对应蓝色箭头线,然后再使用transformer将图像划分为patch提取patch间的全局图像特征对应黄色箭头线。寻常混合模型只是在深层使用transformer提取图像特征包括特征之间细节,而MoblieVit block的这种设定降低了transformer的训练难度,它的transformer的目的是提取patch间的全局关联性。
Alt text

MoblieVit表面使用多尺度进行模型训练有利于模型快速收敛,且精度会高一些。并提出在多尺度训练时,imgsize小则使用大batchsize,imgsize大则使用小batchsize充分有效的利用了gpu

4.3 网络设计

MoblieVit的网络设计如下所示,需要注意的有以下两点:
1、MoblieVit使用MV2block,并未使用MV3block(其中在DW与PW之间使用了SE),并不适合在block中嵌入transformer
2、在stage设计中,MoblieVit只在32、16、8的尺度下使用MobileViT block重复次数比较多,并未在大尺度特征图上使用transformer,充分的节省了计算量
3、MoblieVit在进行下采样时使用MV2block,未使用transformer进行下采样。
Alt text

4.3 实施效果

与MoblieNetV1、MoblieNetV2、MoblieNetV3等模型相比,MoblieNetVit现状要强很多。通过以下图中,可以显著的看到MV2是比MV3是要略强一些的,MV3仅是解决了延时问题
Alt text

与其他同类型的混合模型相比,MolienetVit要强一大截。这预计是模型设计思路差异导致,其他混合模型都是前面使用cnn、后面使用transform,而MVit将卷积进行采用,融合了transform的特性
Alt text

5、扩展思考

5.1 MoblieNet的本质

从MoblieNetV1、MoblieNetV2、MoblieNetV3到MoblieNetVit,模型系列经过了4次的迭代发展,但不离本质就是对conv的近似替代。先对conv的功能进行定义和拆分,然后对每一个子功能进行实现。试图以一个低参数高flop的block来替换掉原来的cnn层;在MoblieNet中,都是使用conv进行下采样,并没有使用池化层。

MoblieNetV1提出了conv可以拆分为深度卷积和点卷积的组合,用深度分离卷积来近似标准卷积,虽然有一定的精度损失(越一个点),但能极大的降低参数量和提升flop;

MoblieNetV2提出残差结构在深度分离卷积的应用,并对残差结构进行分析指出使用倒残差纺锤体结构可以提升模型性能(对特征流形进行分析,认为relu函数会截断特征表现应该用于channel大的部分,而block为纺锤体结构,其输出通道较少,故不需要使用激活函数);

MoblieNetV3并未提出深刻的见解,只是略微修改了V2block的结构,将NAS技术应用到了V2结构中;然后提出对swish激活函数 x ∗ σ ( x ) x* σ(x) xσ(x)其中 σ可以是relu、sigmoid等的近似。 感觉就像来凑数的

MoblieNetVit则又将标准卷积定义为:展开、局部处理、折叠,然后针对每个步骤分别使用conv和transformer进行实现。以conv提取局部特征,以transformer提取patch间的全局信息 这种设定降低了transformer的训练难度,从而能使MoblieNetVit在一众混合模型中脱颖而出。

5.2 MoblieNet的泛化性

这里主要集合MoblieNetVit论文中的图片进行分析,毋庸置疑的是MoblieNetVit为最强。但,MoblieNetV2、MoblieNetV3孰优孰劣却有待争议。V2和V3在同一年出品,时间相差无几。从V3的论文图片来看,限定flop的情况下v3是大幅度优于v2的,但那只是在imagenet上的结论。

在MoblieNetVit中给出的图片中可以看出,v1、v2、v3差异并不多,v3在参数增加后性能会略超v2;同时在V3的论文中也未体现出显著的精度优势,而是延时上的优势,而在MoblieNetVit所展示的也仅仅是精度上的优势。故此推断,MoblieNetVit在速度上有优化空间;此外,硬件性能的发展拉低了移动端模型的性能差异
Alt text
Alt text
在MV3和MVit中给出的图片中可以看出,v1、v2、v3差异并不多; 基于此可以基本确定,v1、v2、v3的迭代是升级了模型在ImageNet数据集上的精度,但并未提升模型在实际应用中的泛化能力,其核心只是提升了模型在移动端的运行速度
Alt text
Alt text

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

软考:中级软件设计师:存储管理,分区存储,页式存储,逻辑地址,物理地址

软考:中级软件设计师:存储管理,分区存储 提示:系列被面试官问的问题,我自己当时不会,所以下来自己复盘一下,认真学习和总结,以应对未来更多的可能性 关于互联网大厂的笔试面试,都是…

Apollo、RocketMQ加载顺序问题

在SpringCloudAlibaba框架中,因Nacos配置中心管理权限过于简单,决定用Apollo代替Nacos配置中心,但在启动时,Nacos、Redis等配置读取正常,RocketMQ由于启动过早,无法从Apollo读取自己的服务地址配置。 报错…

7.7~7.8学习总结

StringBuider:线程不安全,效率高 StringBuffer:线程安全,效率低; 用法举例: class TWC {public static void main(String []args){StringBuilder sbnew StringBuilder("小麻子爱吃粑粑");Syst…

redis的主从复制,哨兵和cluster集群

一、redis的高可用 在web服务器中,高可用是指服务器可以正常访问的时间,衡量的标准是在多长时间内可以提供正常服务(99.9%、99.99%、99.999%等等)。 高可用的计算公式是1-(宕机时间)/(宕机时间运行时间)有…

常见排序算法—面试编程题2023

常见排序算法—面试编程题2023 最近在看一些面试题,发现很多面试过程中都会要求手写排序编程题,经过一番查找整理,可以快速学习和使用相关排序算法题,通俗易懂,手撕代码吊打面试官。 一、冒泡排序 冒泡排序 是一种简…

C语言实现三子棋

三子棋 1. 三子棋玩法2. 程序设计思路2.1 准备工作2.2 主函数设计2.3 创建菜单界面2.4 设计棋盘2.4.1 初始化棋盘2.4.2 打印棋盘 2.5 下棋2.5.1 玩家下棋2.5.2 电脑下棋 2.6 判断输赢2.8 头文件函数声明 结束语 1. 三子棋玩法 三子棋的玩法很简单,两个人依次在9宫格…

【ElasticSearch】ES案例:旅游酒店搜索

文章目录 一、项目分析二、需求1:酒店搜索功能三、需求2:添加过滤功能四、需求3:我附近的酒店五、需求4:置顶花广告费的酒店 一、项目分析 启动hotel-demo项目,访问localhost:servicePort,即可访问static下…

【CANopen】周立功轻松入门CANopen笔记

前言 想学习些新东西了,原本想直接学学Ethercat,但是简单看了看对象字典啥的概念一头雾水的,决定先从CANopen开始,Ethercat看着头疼。Etehrcat和CANopen有挺多类似的地方。感谢ZLG的这个入门笔记,我似乎是看懂了些&am…

ITIL 4服务连续性管理实践

一、目的和描述 关键信息 服务连续性管理实践的目的是确保灾难发生时,服务的可用性和性能能够保持在足够的水平。本实践提供了一个框架机制,利用产生有效响应的能力来构建组织的弹性,以保障关键利益相关者的利益,还有组织的声誉…

数据库视图与索引经典题

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 视图与索引视图:定义视图创建视图删除视图查询视图视图的作用 索引索引的概念索引的类型设计索引 视图与索引 视图: 视图是从一个或几个基…

chatgpt生成pygame opengl实现旋转用图片填充的3d三角形

import pygame from pygame.locals import * from OpenGL.GL import * from OpenGL.GLU import *def draw_triangle():vertices ((0, 2, 0), # 顶点1(-2, -2, 0), # 顶点2(2, -2, 0) # 顶点3)tex_coords ((1, 2), # 顶点1的纹理坐标(1, 1), # 顶点2的纹理坐标(2, …

HTML期末作业-精仿故宫模板(HTML+CSS+JavaScript)

期末作业完成!我仿了故宫官网,老师给了90分。现在分享给大家! 首页包含功能: 轮播图:在首页顶部设置一个可自动轮播的图片展示区域,展示多张宣传图片或产品图片,提升页面的视觉效果和吸引力。…