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

news2024/11/26 18:38:18

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 nn


def 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_classes
        self.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 == 1
        
        if 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) / 6
        
        return 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_cfg
        self.fpn_ids = fpn_ids
        
        in_chennl = _make_divisible(16 * width_mult, 8)
        layers = [conv_3_x_3_bn(3, in_channl, 2)]
        block = residual
        
        for 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_place
        
        self.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 != None
        if 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 x
        else:
            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_channels
                m.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.coloradmin.cn/o/718492.html

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

相关文章

在VMware Workstation虚拟机上安装centos服务,并使用xshell连接centos服务。

一、安装VMware Workstation 可前往http://www.kkx.net/soft/22239.html,附带有下载链接,和对应的破解码。 二、下载centos .iso格式文件 前往https://mirrors.aliyun.com/centos/7/isos/x86_64/下载。 以下两个,选择哪一个都行。 记得要下…

【MySQL进阶篇】学习笔记

文章目录 MySQL进阶学习前言1、存储引擎1.1 MySQL体系结构概览1.2 存储引擎介绍1.3 常见存储引擎的特点1.3.1 InnoDB的特点1.3.2 MyISAM1.3.3 Memory1.3.4 总结 2、索引2.1 索引介绍2.2 索引的结构2.2.1 前置知识2.2.2 索引结构的种类 2.3 索引的分类2.4 索引的使用2.4.1 索引的…

Vue3 通过ref获取 el-dialog 内容(组件)中的dom元素的问题

问题描述: 项目中,使用el-dialog组件展示内容,内容是自己封装的一个组件,组件中通过ref获取dom元素然后进行绘制echarts图形的。 问题是:在弹出el-dialog组件后,echarts图形没有渲染出来。刚开始还以为是自…

ArcPy学习心得系列(5)遥感数据中值滤波与均值滤波实现方法(不计入NoDATA)

在数据处理与应用的过程中,我们难免会遇到一些低质量的遥感数据,低质量遥感数据一般是由于天气因素导致的,在云量较多时,卫星传感器所采集到的地面信息被云层所遮挡,导致遥感图像成像过程中产生了较多噪声,对遥感数据的精确度造成了一定影响,如果不解决这些数据中的噪声…

超详细IDEA连接本地数据库以及使用教程

文章目录 前言一、IDEA连接本地数据库二、IDEA使用数据库操作 前言 IDEA的项目要想连接本地数据库,前提就是本地已经配置有数据库,并且可以使用 附Windows10 安装配置数据库教程: 超详细MySQL(免安装版)安装与配置教程 一、IDEA连接本地数据库 点击IDE…

什么是视频|孪生视频孪生,打造实时实景数字孪生应用

元宇宙概念下,虚拟世界和虚拟空间的讨论日益增多,其相关技术的应用和发展也备受关注。在众多技术手段中,数字孪生技术以虚实结合为主,架起虚拟世界与现实世界之间沟通的桥梁。 视频孪生更侧重从真实角度出发,打造实时…

【大数据之Hive】十九、Hive之文件格式和压缩

1 Hadoop压缩概述 Hive中的压缩算法与Hadoop中的压缩算法保持一致,可以把Hive当作Hadoop的一个客户端。 【大数据之Hadoop】十八、MapReduce之压缩 2 Hive文件格式 Hive表中常用的数据存储格式:text file(行式存储)、orc&#x…

Vue3 ElementPlus写的导航栏

Vue3 ElementPlus Vuex Router写的导航栏,效果如图: 导航条路径那里是自动的。 链接: https://pan.baidu.com/s/1pjqgR9QGbB2ZYIECPwNIoQ?pwdk5ew 提取码: k5ew 复制这段内容后打开百度网盘手机App,操作更方便哦

Android进阶之路 - 背景阴影、阴影背景

不知道你是不是也经常听到这些话:你这个没有阴影效果;你这个阴影太浓了;你这个阴影太粗了;你这个阴影太实了;你这个阴影颜色也不对,你这个阴影… 在正式开发中,临近上线前有个环节叫UI验收&…

Vector - CAPL - 数据库和CAPL_02

DBLookup 动态读取数据库中的信息 //Transmitter 属性以及数据库属性只能通过 DBLookup 动态读取。 //返回数据库中存储的 DLC on message * {int myAttributeValue;myAttributeValue DBLookup(this).MyAttribute;write(this.Transmitter); // compiler errorwrite(DBLookup(…

AR试穿试戴相关SDK或平台

1.火山引擎 链接 咨询过平台收费比较高几十万一年而且还是起步价 2.Geenee 链接 geenee在衣服、裤子、头饰以及鞋子方面可以实现试穿。 3.Wanna 链接 Wanna 试衣、试包、试鞋及手表都可以,我试过鞋子的试穿效果还不错 4.DeepAR …

sqlite维护命令复习学习

前面已经看了一些sqlite命令,例如查看表名,查看表结构等;下面继续看一下; 查看全部表名; 查看单个或全部的表结构; 输出表结构和数据; 使用.output 把查询结果定向到1.txt; 重新定向…

Java设计模式系列--观察者模式写法2:JDK

原文网址:Java设计模式系列--观察者模式写法2:JDK_IT利刃出鞘的博客-CSDN博客 简介 说明 本文用示例介绍观察者模式的一种写法:JDK。 JDK的观察者模式简介 在 Java 中,java.util.Observable 类和 java.util.Observer 接口定义…

Elsevier: Expert Systems With Applications 经验分享

目录 序时间线投稿返稿录用模板下载链接 序 这是一篇Elsevier旗下Expert Systems With Applications(ESWA)期刊的投稿经验分享。虽然是ESWA,但对于Elsevier旗下的其它期刊也适用,他们都可以用一套模板。 需要注意的是不同期刊会有一些特别的要求&#…

13 直接存储器访问DMA(基于STM32HAL库)

目录 DMA-直接存储器访问控制器 DMA概览 DMA的作用 DMA框图 DMA外设要点概括 DMA功能对比 STMF10x DMA具体内容 DMA主要特性 DMA中断 DMA请求映像 DMA的使用步骤 HAL库中的DMA功能实例 句柄结构体介绍(以DMA为例) 外设初始化结构体介绍 具体…

多元回归预测 | Matlab基于粒子群算法优化深度置信网络(PSO-DBN)的数据回归预测,matlab代码回归预测,多变量输入模型

文章目录 效果一览文章概述部分源码参考资料效果一览 文章概述 多元回归预测 | Matlab基于基于粒子群算法优化深度置信网络(PSO-DBN)的数据回归预测,matlab代码回归预测,多变量输入模型,多变量输入模型 评价

聊聊不同集群的微服务如何通过feign调用

前言 之前业务部门的某项目微服务调用关系如下图 后因业务改造需要,该项目需要将服务A部署到另外一个集群,但服务A仍然需要能调用到服务B,调用关系如下图 之前调用方式是负责服务B的开发团队提供相应的feign客户端包给到服务A开发团队&…

三种Linux内核代码在线阅读工具

记录一下 1 . 可在线阅读uboot,kernel,busybox(rootfs),可搜索字符串,函数跳 https://lxr.missinglinkelectronics.com/ 界面如下: 2. 显示界面跟代码编辑器很像,同样可以函数跳转 https://elixir.boot…

如何让罗技29方向盘像视频中的那样转动起来?

​​​​​​​[vlog]Autoware Carla G29 自动驾驶仿真_哔哩哔哩_bilibili 话接上文,在我之前一篇博客中已经讲解了如何给罗技29方向盘装上力反馈,也就是在拨动方向盘的时候感觉有一个力组织你过度的拨动方向盘,其实它真正的用处是用于实现对…

【Web3】认识区块链

目录 区块链特征 区块链类型 区块链的概念 区块链特征 去中心化:区块链是由一个分布在多个参与者之间的网络组成,没有中央机构或中介控制整个系统。所有参与者共同维护和验证账本的完整性,减少了单点故障和集中式控制的风险。共识机制&…