暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

经典卷积架构的PyTorch实现:MobileNet V1

南极Python 2021-05-31
2092

MobileNet V1

MobileNet V1 有点像VGG,它们的网络结构都是单分支的,通俗点说就是:一条路走到底。

只不过,相比于VGG,MobileNet V1 大量使用了深度可分离卷积,在模型的预测能力变化很小的前提下,极大地提升了模型的速度,从而推进了深度学习模型在移动端的使用。

ps:关于深度可分离卷积,可以查看这篇文章;关于VGG,可以查看这篇文章

MobileNet V1 的网络结构如下:

其中,Conv
表示普通卷积,Conv dw
表示逐通道卷积,s1
表示卷积步长为1,s2
表示卷积步长为2。

PyTorch 实现 MobileNet V1

对于普通的卷积,即上面结构图中的Conv
直接调用torch.nn.Conv2d
就可以了;

而在上面的网络结构图中,每一个蓝色框起来的两部分组合起来就是深度可分离卷积,它包括了逐通道卷积(Conv dw
)和逐点卷积(Conv
,kernel_size=1),其结构如下(左侧是普通卷积,右侧是深度可分离卷积):

根据这个结构,就可以写代码实现深度可分离卷积了:

import torch
import torch.nn as nn

#深度可分离卷积
class Depth_Separable_Conv(nn.Module):
    
    def __init__(self,in_channels,out_channels,stride=1):
        super().__init__()
        self.in_channels=in_channels
        self.out_channels=out_channels
        self.stride=stride
        
        self.dw_conv=self.depth_wise_conv()#逐通道卷积:groups=in_channels
        self.pw_conv=self.point_wise_conv()#逐点卷积,即普通1x1卷积
        
    #逐通道卷积:groups=in_channels
    def depth_wise_conv(self):
        return nn.Sequential(
            nn.Conv2d(in_channels=self.in_channels,out_channels=self.in_channels,kernel_size=3,stride=self.stride,padding=1,groups=self.in_channels,bias=False),
            nn.BatchNorm2d(self.in_channels),
            nn.ReLU()
            )
    
    #逐点卷积,即普通1x1卷积
    def point_wise_conv(self):
        return nn.Sequential(
            nn.Conv2d(in_channels=self.in_channels,out_channels=self.out_channels,kernel_size=1,stride=1,padding=0,groups=1,bias=False),
            nn.BatchNorm2d(self.out_channels),
            nn.ReLU()
            )
    
    def forward(self,x):
        x=self.dw_conv(x)
        x=self.pw_conv(x)
        return x

根据总的网络结构图,可以发现其中的point wise conv
,即1x1
卷积的stride始终为1,因此在上面的代码中就把这个超参数固定为1了。而对于depth wise conv
,其stride不固定,因此使用的是传入的stride参数值。

有了上述结构,现在来实现整个MobileNet V1:

class MobileNetV1(nn.Module):
    
    def __init__(self,num_classes=1000,img_channels=3):
        super().__init__()
        #输出类别数
        self.num_classes=num_classes
        #输入图片的通道数
        self.img_channels=img_channels
        #网络结构
        self.m=nn.Sequential(
                    nn.Conv2d(in_channels=self.img_channels,out_channels=32,kernel_size=3,stride=2,padding=1,groups=1,bias=False),
                    nn.BatchNorm2d(32),
                    nn.ReLU(),

                    #Depth_Separable_Conv的参数顺序:in_channels,out_channels,stride
                    Depth_Separable_Conv(32,64,1),
                    Depth_Separable_Conv(64,128,2),
                    Depth_Separable_Conv(128,128,1),
                    Depth_Separable_Conv(128,256,2),
                    Depth_Separable_Conv(256,256,1),
                    Depth_Separable_Conv(256,512,2),

                    #重复5次
                    Depth_Separable_Conv(512,512,1),
                    Depth_Separable_Conv(512,512,1),
                    Depth_Separable_Conv(512,512,1),
                    Depth_Separable_Conv(512,512,1),
                    Depth_Separable_Conv(512,512,1),

                    Depth_Separable_Conv(512,1024,2),
                    Depth_Separable_Conv(1024,1024,2),

                    nn.AdaptiveAvgPool2d((1,1)),
                    nn.Flatten(start_dim=1),
                    nn.Linear(in_features=1024,out_features=self.num_classes)
                )
        
    def forward(self,x):
        x=self.m(x)
        return x

上述代码就是根据网络结构图,堆叠相应的卷积块,从完成了MobileNet V1网络的搭建。每一个卷积块都和网络结构图中的相应模块是对应的,因此强烈建议对照着网络结构图阅读以上代码,就自然清晰明了了。

注意,在卷积块堆叠完成后,我使用了更常用的自适应平均池化来代替平均池化,以便更能适应不同大小的输入。

现在来测试一下:

关于MobileNet V1的介绍就到这里,后续将对其改进版本,即MobileNet V2,MobileNet V3进行实现,敬请期待~

参考:

  • [1] https://arxiv.org/pdf/1704.04861.pdf




南极Python交流群已成立,长按下方二维码添加我的微信,备注加群即可,欢迎进群学习交流(划水


              原创不易,感谢点赞,分享和在看的你!

文章转载自南极Python,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论