计算机系统应用教程网站

网站首页 > 技术文章 正文

深度学习之重读经典(六)MobileNet

btikc 2024-10-15 09:07:07 技术文章 11 ℃ 0 评论

01 引 言

自AlexNet后,深度学习技术不断发展,各种网络模型的结构不断被发现,如用多个小核卷积模拟大核卷积的VGGNet模型,引入残差结构以避免模型退化、梯度消失的ResNet模型等,深度学习模型在图像识别上的精度也是逐步提高,研究的对象也从单一的图像分类扩展到了抠图(语义分割)、目标定位等应用上,且取得了一些喜人的进展。但是这类模型对内存和计算需求较大,限制其只能在云端服务器上运行。在当前的技术条件下,减小模型对计算资源的依赖有三种方式:1)使用小模型2)模型量化技术3)模型蒸馏技术本文以MobileNet图像分类为例,简述如何在精度接近无损的情况下,降低模型的计算量与参数量。

02 前置知识

分组卷积

常见的卷积使用多个卷积核(以下图为例,卷积核维度为[6,12,k,k],k代表卷积核大小)生成了多张特征图,而分组卷积(Group Convolution)首先将卷积分成n个组,再对组内的图像/特征图进行卷积,以下图为例,卷积核的大小为[3,2,4,k,k],表示将输入特征如图分成3组,对每个组内使用卷积。使用分组卷积后,参数量变为原来的1/3(6×12×k2/ 3×2×4×k2),计算量(考虑偏置情况下,计算公式为2Cin×Cout×k2×Hout×Wout)变为原来的1/3。使用分组卷积后参数和计算量将会变为1/n(n为组数)。

?

计算量与分辨率和隐藏层通道数量关系

卷积神经网络想要提高精度可以通过提高图像分辨率和提高中间特征图的数量(即卷积核数量)来达到。但是图像分辨率加一倍,会使得模型的计算量翻四倍,特征图数量的翻倍会导致模型的计算量翻一倍,这都会使模型需要更多的计算量。平衡二者之间的关系可以在计算量相差不大的情况下提高识别的精度。


03 实操方法

卷积计算量的压缩

MobileNet出现之前,大多使用3×3或是5×5卷积作为常用卷积,当通过重复堆叠后其参数量也变得相当可观,如VGG16权重大小有450M,ResNet152层权重有644M,其在运行时的计算量更是不容小觑。因此减少卷积层的参数和计算量可以大幅度降低模型参数量,提高计算速度。卷积的基础操作是每个通道的卷积核与输入图像逐点相乘再相加,而中间结果少则几十层,多则成百上千层的通道数量是卷积计算量大幅增加的原因之一。在此之前分组卷积的提出,可以一定程度上降低卷积层的参数量和计算量;1×1的卷积由于其特殊性在特征图大小不变情况下实现通道间特征的重映射。仔细一想,当使用分组卷积(分组数量等于特征图数量)加上1×1卷积,二者可以完整的替代普通卷积,且参数和计算量将大幅减少。MobileNet中称该卷积为深度可分离卷积(Depthwise Separable Conv,其中两部分分别为逐深度卷积和逐点卷积)。使用该卷积后计算量大约是原始卷积的1/8~1/9。计算流程如下图所示。

?

特征通道数与分辨率的缩放

在前置知识中了解到,模型的计算量和通道数量p、分辨率的平方(H×W)成正比。若引入宽度因子α、分辨率因子ρ,用α控制输入和输出的特征图通道数量,理论上可以使计算量和参数降低a2倍,用ρ控制特征图的尺寸后,参数量和计算量大约可以降低ρ2倍。在MobileNet论文中,通过实验验证了上述两个方法的可行性。从表中可以看出,使用深度可分离卷积后参数量大约降低了1/10,通过宽度缩放后,参数量降低了一半,再使用分辨率缩放后保持保持参数量不变情况下,计算量降低了一半。

?


04 PyTorch实现

MobileNet的具体结构如下,在结构上并没有使用reset中的恒等链接,都是相似结构的重复堆叠,PyTorch的实现如下:

?

import torch
import torch.nn  as  nn

def DepthwiseSeparableConv(in_channel,out_channel,kernel_size = 3,stride = 1):
    return nn.Sequential(
        nn.Conv2d(in_channels=in_channel,out_channels=in_channel,
                  groups=in_channel,
                  kernel_size=kernel_size,
                  stride=stride,padding=1,
                  ),
        nn.BatchNorm2d(in_channel),
        nn.ReLU6(inplace=True),
        nn.Conv2d(in_channels=in_channel, out_channels=out_channel, kernel_size=1,
                  stride=1,
                  ),
        nn.BatchNorm2d(out_channel),
        nn.ReLU6(inplace=True)
    )

class MobileNet(nn.Module):

    def __init__(self,in_channel = 3,class_nums = 1000):
        super(MobileNet, self).__init__()
        self.project = nn.Sequential(
            nn.Conv2d(in_channels=in_channel,out_channels=32,kernel_size=3,stride=2,padding=1),
            nn.BatchNorm2d(32),
            nn.ReLU6(inplace=True)
        )
        convs = []
        convs += [
            DepthwiseSeparableConv(32, 64, stride=1),
            DepthwiseSeparableConv(64, 128, stride=2),
            DepthwiseSeparableConv(128, 128, stride=1),
            DepthwiseSeparableConv(128, 256, stride=2),
            DepthwiseSeparableConv(256,256, stride=1),
            DepthwiseSeparableConv(256, 512, stride=2)
        ]
        for i in range(5):
            convs.append(
                DepthwiseSeparableConv(512, 512, stride=1)
            )
        convs += [
            DepthwiseSeparableConv(512, 1024, stride=2),
            DepthwiseSeparableConv(1024, 1024, stride=1)
        ]

        self.dws = nn.Sequential(*convs)
        self.avg_pool = nn.AvgPool2d(kernel_size=7,stride = 1)
        self.linear = nn.Linear(in_features=1024,out_features=class_nums)
        self.drop = nn.Dropout(p = 0.2)
        self.softmax = nn.Softmax(dim = 1)
        self.init_params()


    def init_params(self):
        for m in self.modules():
            if isinstance(m,nn.Conv2d):
                nn.init.kaiming_normal_(m.weight,mode='fan_out', nonlinearity='relu')
                if m.bias is not None:
                    nn.init.constant_(m.bias, 0)
            elif isinstance(m,nn.Linear):
                nn.init.normal_(m.weight, 0, 0.01)
                nn.init.constant_(m.bias, 0)

    def forward(self,x):
        x = self.project(x)
        x = self.dws(x)
        x = self.avg_pool(x)
        x = x.view(x.size(0),-1)
        x = self.drop(x)
        x = self.linear(x)
        out = self.softmax(x)
        return out

if __name__ == "__main__":
    model = MobileNet(3,10).to('cuda')
    x = torch.rand((1,3,224,224)).to('cuda')
    model(x) 

完整训练流程见:

https://github.com/PopSmartTech/deeplearning


05 总结

MobileNet通过深度可分离卷积,特征图通道数量和图像分辨率的缩放方法,在精度损失可控范围内大幅度的降低了模型的计算量和参数量,给模型的轻量化提供了新的设计思路。使得深度学习的智能化技术开始在端侧设备、边缘设备上开始应用,推动了应用智能化的进程。


参考资料:

[1] MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications https://arxiv.org/abs/1704.04861


本文所有文字版权均属“宝略科技”,更多内容请搜索关注【宝略科技】微信公众号~

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表