文章目录
1.MobileNetV1的介绍
传统卷积神经网络, 内存需求大、 运算量大导致无法在移动设备以及嵌入式设备上运行.VGG16的权重大小有450M,而ResNet中152层的模型,其权重模型644M,这么大的内存需求是明显无法在嵌入式设备上进行运行的。而网络应该服务于生活,所以轻量级网络的很重要的。
MobileNet网络是由google团队在2017年提出的,专注于移动端或者嵌入式设备中的轻量级CNN网络。相比传统卷积神经网络,在准确率小幅降低的前提下大大减少模型参数与运算量。(相比VGG16准确率减少了0.9%,但模型参数只有VGG的1/32)
MobileNet_v1的亮点:
- Depthwise Convolution( 大大减少运算量和参数数量)
- 增加超参数 增加超参数α 、β
(其中α是控制卷积层卷积核个数的超参数,β是控制输入图像的大小)
2.MobileNetV1的结构
传统的卷积
- 卷积核channel=输入特征矩阵channel
- 输出特征矩阵channel=卷积核个数
1)DW卷积(Depthwise Conv)
- 卷积核channel=1
- 输入特征矩阵channel=卷积核个数=输出特征矩阵channel
也就是DW卷积中的每一个卷积核,只会和输入特征矩阵的一个channel进行卷积计算,所以输出的特征矩阵就等于输入的特征矩阵。
2)PW卷积(Pointwise Conv)
其实PW卷积和普通的卷积类似,只是采用了1x1的卷积核,输出的特征矩阵channel的个数与使用的卷积核数相等,而输入特征矩阵的channel的个数与卷积核的channel数相等。所以其就是一个普通的卷积。
一般来说,以上的PW卷积与DW卷积是放在一起操作的,共同组成深度可分卷积操作。
3)深度可分卷积操作(Depthwise Separable Conv)
深度可分卷积操作是有两步分组成,一部分是DW卷积(Depthwise Conv),另外一部分是PW卷积(Pointwise Conv)
两者的计算量对比:
- DSC:Dk * Dk * M * Df * Df + M * N * Df * Df
- 普通:Dk * Dk * M * N * Df * Df
理论上普通卷积计算量是 DW+PW 的8到9倍
3.MobileNetV1的性能统计
- Multiply-Add计算量
- α-Width Multiplier(卷积核个数的倍率)
- β-Resolution Multiplier(图像尺寸的大小)
4.MobileNetV1的pytorch实现
MobileNetV1模型结构
参考代码
import torch
import torch.nn as nn
# 定义DSC结构:DW+PW操作
def BottleneckV1(in_channels, out_channels, stride):
# 深度可分卷积操作模块: DSC卷积 = DW卷积 + PW卷积
return nn.Sequential(
# dw卷积,也是RexNeXt中的组卷积,当分组个数等于输入通道数时,输出矩阵的通道输也变成了输入通道数时,组卷积就是dw卷积
nn.Conv2d(in_channels=in_channels,out_channels=in_channels,kernel_size=3,stride=stride,padding=1,groups=in_channels),
nn.BatchNorm2d(in_channels),
nn.ReLU6(inplace=True),
# pw卷积,与普通的卷积一样,只是使用了1x1的卷积核
nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=1, stride=1),
nn.BatchNorm2d(out_channels),
nn.ReLU6(inplace=True)
)
# 定义MobileNetV1结构
class MobileNetV1(nn.Module):
def __init__(self, num_classes=5):
super(MobileNetV1, self).__init__()
# torch.Size([1, 3, 224, 224])
self.first_conv = nn.Sequential(
nn.Conv2d(in_channels=3,out_channels=32,kernel_size=3,stride=2,padding=1),
nn.BatchNorm2d(32),
nn.ReLU6(inplace=True),
)
# torch.Size([1, 32, 112, 112])
# 叠加的基本结构是: DW+PW(DW用来减小尺寸stride=2实现,PW用来增加通道out_channels增加实现)
self.bottleneck = nn.Sequential(
BottleneckV1(32, 64, stride=1), # torch.Size([1, 64, 112, 112]), stride=1
BottleneckV1(64, 128, stride=2), # torch.Size([1, 128, 56, 56]), stride=2
BottleneckV1(128, 128, stride=1), # torch.Size([1, 128, 56, 56]), stride=1
BottleneckV1(128, 256, stride=2), # torch.Size([1, 256, 28, 28]), stride=2
BottleneckV1(256, 256, stride=1), # torch.Size([1, 256, 28, 28]), stride=1
BottleneckV1(256, 512, stride=2), # torch.Size([1, 512, 14, 14]), stride=2
BottleneckV1(512, 512, stride=1), # torch.Size([1, 512, 14, 14]), stride=1
BottleneckV1(512, 512, stride=1), # torch.Size([1, 512, 14, 14]), stride=1
BottleneckV1(512, 512, stride=1), # torch.Size([1, 512, 14, 14]), stride=1
BottleneckV1(512, 512, stride=1), # torch.Size([1, 512, 14, 14]), stride=1
BottleneckV1(512, 512, stride=1), # torch.Size([1, 512, 14, 14]), stride=1
BottleneckV1(512, 1024, stride=2), # torch.Size([1, 1024, 7, 7]), stride=2
BottleneckV1(1024, 1024, stride=1), # torch.Size([1, 1024, 7, 7]), stride=1
)
# torch.Size([1, 1024, 7, 7])
self.avg_pool = nn.AvgPool2d(kernel_size=7,stride=1) # torch.Size([1, 1024, 1, 1])
self.linear = nn.Linear(in_features=1024,out_features=num_classes)
self.dropout = 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)
nn.init.constant_(m.bias,0)
elif isinstance(m, nn.Linear) or isinstance(m, nn.BatchNorm2d):
nn.init.constant_(m.weight, 1)
nn.init.constant_(m.bias, 0)
def forward(self, x):
x = self.first_conv(x) # torch.Size([1, 32, 112, 112])
x = self.bottleneck(x) # torch.Size([1, 1024, 7, 7])
x = self.avg_pool(x) # torch.Size([1, 1024, 1, 1])
x = x.view(x.size(0),-1) # torch.Size([1, 1024])
x = self.dropout(x)
x = self.linear(x) # torch.Size([1, 5])
out = self.softmax(x) # 概率化
return x
if __name__=='__main__':
model = MobileNetV1()
# print(model)
input = torch.randn(1, 3, 224, 224)
out = model(input)
print(out.shape)
后续:MobileNetV1出现的问题
对于DW卷积,训练完之后,会出现部分卷积核会费掉的问题,既卷积核参数大部分为零,也就是表示其实DW卷积没有起到多大的作用。对于这个问题,MobileNetV2有一定的改善。
参考:
https://www.bilibili.com/video/BV1yE411p7L7