MobileNetV1详细原理(含torch源码)

目录

MobileNetV1原理

MobileNet V1的网络结构如下:

为什么要设计MobileNet:

MobileNetV1的主要特点如下:

MobileNetV1的创新点:

MobileNetV1源码(torch版)

训练10个epoch的效果


MobileNetV1原理

        MobileNet V1是一种轻量级的卷积神经网络,能够在保持较高准确率的情况下具有较少的参数量和计算时间。它是由Google的研究人员在2017年提出的,并成为当时最流行的轻量级模型之一。

        MobileNet V1的核心思想是通过深度分离卷积来减少模型的参数量和计算时间。与标准卷积不同,深度分离卷积将空间卷积和通道卷积分为两个独立的卷积层,这使得网络更加高效。具体来说,在深度分离卷积中,首先使用一个空间卷积,然后使用一个通道卷积来提取特征。这与标准卷积相比可以减少参数数量并加速运算。

MobileNet V1的网络结构如下:

        MobileNet V1由序列卷积和1x1卷积两个部分组成。序列卷积包括13个深度可分离卷积层,每个层都包括一个3x3的卷积和一个批量归一化层(BN层),并且在卷积之后使用了ReLU6激活函数。最后,1x1卷积层用于生成最终的特征向量,并使用全局平均池化来缩小特征图的大小。在最后一层之后,使用一个全连接层来进行分类。MobileNet V1可以根据需要使用不同的输入分辨率,其超参数取决于输入分辨率和需要的精度。

为什么要设计MobileNet:

        Mobilenetv1是一种轻量级的深度神经网络模型,设计的目的是在保持较高的精度的同时减小模型的大小和计算量,使其适合于移动设备的推理任务。在过去,大部分深度神经网络模型都是基于卷积神经网络(CNN)进行设计的,这些模型往往非常庞大(比如VGG16/VGG19),因此不能直接应用于手机或其他嵌入式设备上。同时,运行这些大型模型所需要的计算资源也很昂贵。

        为了解决这个问题,Google Brain团队提出了Mobilenetv1。Mobilenetv1是基于深度可分离卷积(depthwise separable convolution)的设计,它将标准的卷积层分成深度卷积层和逐点卷积层两个部分,用较少的参数和计算量达到了相当不错的准确率。具体来说,深度卷积层用于在每个通道上执行空间卷积,而逐点卷积层(Pointwise Convolution)用于在不同通道之间执行线性变换。这种设计可以减少计算量和模型大小,并使得Mobilenetv1在移动设备上能够运行得更快。

        除此之外,Mobilenetv1还使用了其他一些技巧来进一步缩小模型。例如,通过扩张系数(expansion factor)来控制输出通道数和输入通道数之间的关系,从而精细控制模型的大小和复杂度;通过残差连接(Residual Connection)来提高信息流动,从而提高模型的准确性和训练速度。

        综合来说,Mobilenetv1是一种非常出色的深度神经网络模型,它在保持较高精确度的同时,大大减小了模型大小和计算量,使得它更容易嵌入到移动和嵌入式设备中。

MobileNetV1的主要特点如下:

  1. 轻量级:MobileNetv1的模型参数量非常少,只有4.2M,比起其他深度神经网络模型如VGG16、ResNet等模型,模型大小大大减小,更适合移动设备等资源受限环境下进行应用。

  2. 深度可分离卷积:MobileNetv1主要使用了深度可分离卷积,即将标准卷积分解成一个深度卷积和一个逐点卷积两个部分,分离后分别进行卷积操作,可以大大减少计算量和参数数量,从而实现轻量化的目的。

  3. 使用卷积核大小为1x1的卷积层和全局平均池化层:MobileNetv1使用了大量的1x1卷积层和全局平均池化层来代替传统的卷积层,可以减少特征图的空间尺寸,从而减少计算量和参数数量。

  4. 加入线性层和ReLU6激活函数:为了减少梯度消失的现象,MobileNetv1在每个深度可分离卷积结构后加入一个线性层和ReLU6激活函数,同时提高模型的非线性能力。

  5. 高性能:MobileNetv1在性能表现方面也做得很好,准确率达到了当时的state-of-the-art水平,同时模型具有高效率的特点,能够在较短的时间内完成较为复杂的任务。

MobileNetV1的创新点:

  1. Depthwise Separable Convolution(深度可分离卷积)

        MobileNetV1使用Depthwise Separable Convolution代替了传统的卷积操作。Depthwise Separable Convolution分为两个步骤,首先进行深度卷积,然后进行点卷积。深度卷积可以在每个输入通道上进行滤波操作,而点卷积使用1×1卷积来对每个通道进行线性组合。这样可以减少运算量以及减小模型的大小,同时也可以提高模型的精度和鲁棒性。

     2. Width Multiplier(宽度乘法参数)

        MobileNetV1引入了width multiplier的概念,可以通过调整宽度乘数来控制模型的大小和计算量。宽度乘数是作用于每一层的通道数目,可以取0到1的任意值。当宽度乘数为1时,模型与原始模型一致,而当宽度乘数小于1时,模型会变得更轻巧。

     3. Global Depthwise Pooling(全局深度池化)

        MobileNetV1使用Global Depthwise Pooling代替了全连接层。全局深度池化是在每个通道上进行求和操作,并将结果作为输出。这样可以有效地减少模型的参数量和计算量,提高模型的速度和精度。

        总的来说,MobileNetV1在模型轻量化方面具有显著的创新,可以在计算资源有限的设备上进行高效的推理操作,成为了移动设备上的高效神经网络模型。

MobileNetV1源码(torch版)

数据集运行代码时自动下载,如果网络比较慢,可以自行点击我分享的链接下载cifar数据集。

链接:百度网盘
提取码:kd9a 

# -*- coding: utf-8 -*-
import torch
import torch.nn as nn
from torch.utils.data import DataLoader
from torchvision.datasets import CIFAR10
from torchvision.transforms import transforms
from torch.autograd import Variable


class DepthwiseSeparableConv(nn.Module):
    def __init__(self, in_channels, out_channels):
        super(DepthwiseSeparableConv, self).__init__()

        self.depthwise_conv = nn.Conv2d(in_channels, in_channels, kernel_size=3, padding=1, groups=in_channels)
        self.pointwise_conv = nn.Conv2d(in_channels, out_channels, kernel_size=1)
        self.relu = nn.ReLU(inplace=True)

    def forward(self, x):
        x = self.depthwise_conv(x)
        x = self.pointwise_conv(x)
        x = self.relu(x)
        return x


class MobileNetV1(nn.Module):
    def __init__(self, num_classes=1000):
        super(MobileNetV1, self).__init__()

        self.conv1 = nn.Conv2d(3, 32, kernel_size=3, stride=2, padding=1)
        self.relu = nn.ReLU(inplace=True)

        self.dw_separable_conv1 = DepthwiseSeparableConv(32, 64)
        self.dw_separable_conv2 = DepthwiseSeparableConv(64, 128)
        self.dw_separable_conv3 = DepthwiseSeparableConv(128, 128)
        self.dw_separable_conv4 = DepthwiseSeparableConv(128, 256)
        self.dw_separable_conv5 = DepthwiseSeparableConv(256, 256)
        self.dw_separable_conv6 = DepthwiseSeparableConv(256, 512)
        self.dw_separable_conv7 = DepthwiseSeparableConv(512, 512)
        self.dw_separable_conv8 = DepthwiseSeparableConv(512, 512)
        self.dw_separable_conv9 = DepthwiseSeparableConv(512, 512)
        self.dw_separable_conv10 = DepthwiseSeparableConv(512, 512)
        self.dw_separable_conv11 = DepthwiseSeparableConv(512, 512)
        self.dw_separable_conv12 = DepthwiseSeparableConv(512, 1024)
        self.dw_separable_conv13 = DepthwiseSeparableConv(1024, 1024)

        self.avg_pool = nn.AdaptiveAvgPool2d((1, 1))
        self.fc = nn.Linear(1024, num_classes)

    def forward(self, x):
        x = self.conv1(x)
        x = self.relu(x)

        x = self.dw_separable_conv1(x)
        x = self.dw_separable_conv2(x)
        x = self.dw_separable_conv3(x)
        x = self.dw_separable_conv4(x)
        x = self.dw_separable_conv5(x)
        x = self.dw_separable_conv6(x)
        x = self.dw_separable_conv7(x)
        x = self.dw_separable_conv8(x)
        x = self.dw_separable_conv9(x)
        x = self.dw_separable_conv10(x)
        x = self.dw_separable_conv11(x)
        x = self.dw_separable_conv12(x)
        x = self.dw_separable_conv13(x)

        x = self.avg_pool(x)
        x = torch.flatten(x, 1)
        x = self.fc(x)

        return x
def main():
    train_data = CIFAR10('cifar',train=True,transform = transforms.ToTensor())
    data = DataLoader(train_data,batch_size=128,shuffle=True)

    device = torch.device("cuda")
    net = MobileNetV1(num_classes=10).to(device)
    print(net)
    cross = nn.CrossEntropyLoss().to(device)
    optimizer = torch.optim.Adam(net.parameters(),0.001)
    for epoch in range(10):
        for img,label in data:
            img = Variable(img).to(device)
            label = Variable(label).to(device)
            output = net.forward(img)
            loss = cross(output,label)
            loss.backward()
            optimizer.zero_grad()
            optimizer.step()
            pre = torch.argmax(output,1)
            num = (pre == label).sum().item()
            acc = num / img.shape[0]
        print("epoch:",epoch + 1)
        print("loss:",loss.item())
        print("acc:",acc)
    pass


if __name__ == '__main__':
    main()

        上述代码中,我使用的是CIFAR-10数据集,通过训练MobileNet V1对图像进行分类。在训练过程中,我使用Adam优化器和交叉熵损失函数,并在训练后使用验证集评估模型的性能。

        其中,模型中使用了Depthwise Separable Convolution,它包含一层深度卷积和一层1x1卷积。深度卷积用于处理输入数据的不同通道,1x1卷积用于将不同通道的特征图合并成更多的通道。这个操作可以有效地减少参数数量和计算量,并提高模型的性能。

        另外,模型还使用了AdaptiveAvgPool2d,该层可以自适应地将输入特征图的大小调整为任意大小,并对每个子区域进行平均池化操作。这可以使模型对输入图像的尺寸具有更强的鲁棒性。

        通过MobileNet V1,我们可以在保持较高精度的同时具有较少的参数量和计算时间,在计算资源受限的情况下尤其有用。

训练10个epoch的效果

  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
下面是一个基于torch实现的mobilenetv3代码示例: ```python import torch import torch.nn as nn class Hswish(nn.Module): def forward(self, x): relu6 = nn.ReLU6(inplace=True) return x * relu6(x + 3) / 6 class Hsigmoid(nn.Module): def forward(self, x): return nn.functional.relu6(x + 3) / 6 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), Hswish() ) class SqueezeExcitation(nn.Module): def __init__(self, in_planes, se_planes): super(SqueezeExcitation, self).__init__() self.se = nn.Sequential( nn.AdaptiveAvgPool2d(1), nn.Conv2d(in_planes, se_planes, 1), Hswish(), nn.Conv2d(se_planes, in_planes, 1), Hsigmoid() ) def forward(self, x): return x * self.se(x) class InvertedResidual(nn.Module): def __init__(self, inp, oup, stride, expand_ratio): super(InvertedResidual, self).__init__() hidden_dim = round(inp * expand_ratio) self.use_res_connect = stride == 1 and inp == oup layers = [] if expand_ratio != 1: layers.append(ConvBNReLU(inp, hidden_dim, kernel_size=1)) layers.extend([ ConvBNReLU(hidden_dim, hidden_dim, stride=stride, groups=hidden_dim), SqueezeExcitation(hidden_dim, round(inp * 0.25)), nn.Conv2d(hidden_dim, oup, 1, 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 MobileNetV3(nn.Module): def __init__(self, num_classes=1000, mode='large', width_mult=1.0): super(MobileNetV3, self).__init__() self.cfgs = [ # k, t, c, SE, s [3, 16, 16, 0, 1], [3, 64, 24, 0, 2], [3, 72, 24, 0, 1], [5, 72, 40, 1, 2], [5, 120, 40, 1, 1], [5, 120, 40, 1, 1], [3, 240, 80, 0, 2], [3, 200, 80, 0, 1], [3, 184, 80, 0, 1], [3, 184, 80, 0, 1], [3, 480, 112, 1, 1], [3, 672, 112, 1, 1], [5, 672, 160, 1, 2], [5, 960, 160, 0, 1], [5, 960, 160, 1, 1] ] if mode == 'large': interverted_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], ] last_channel = 1280 elif mode == 'small': interverted_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, 192, 2, 2], [6, 320, 1, 1], ] last_channel = 1024 else: raise NotImplementedError input_channel = int(16 * width_mult) self.last_channel = int(last_channel * width_mult) if width_mult > 1.0 else last_channel # building first layer self.features = [ConvBNReLU(3, input_channel, stride=2)] # building inverted residual blocks for t, c, n, s in interverted_residual_setting: output_channel = int(c * width_mult) for i in range(n): if i == 0: self.features.append(InvertedResidual(input_channel, output_channel, s, t)) else: self.features.append(InvertedResidual(input_channel, output_channel, 1, t)) input_channel = output_channel # building last several layers self.features.append(ConvBNReLU(input_channel, self.last_channel, kernel_size=1)) self.features.append(nn.AdaptiveAvgPool2d(1)) # make it nn.Sequential self.features = nn.Sequential(*self.features) # building classifier self.classifier = nn.Sequential( nn.Linear(self.last_channel, 1280), Hswish(), nn.Dropout(0.2), nn.Linear(1280, num_classes), ) self._initialize_weights() def forward(self, x): x = self.features(x) x = x.view(-1, self.last_channel) x = self.classifier(x) return x def _initialize_weights(self): for m in self.modules(): if isinstance(m, nn.Conv2d): nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') 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) ``` 以上是一个标准的mobilenetv3实现,包括了基本的模块,如InvertedResidual,SqueezeExcitation等等。同时也包括了两种模式的实现,即'mode=large'和'mode=small',以及相应的参数width_mult,用于控制模型的宽度。在实现时,我们参考了官方的实现以及其他开源实现,并进行了一些修改和调整,以便更好地适应我们的需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱笑的男孩。

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值