基础知识储备:
一、深度可分离卷积(Depthwise Separable Convolution)
MobileNet的核心是深度可分离卷积(Depthwise Separable Convolution),深度可分离卷积是卷积神经网络(CNN)中一种高效的卷积操作,主要用于降低计算复杂度和模型参数数量。它由两个独立的步骤组成:深度卷积和逐点卷积。这种方法是 MobileNet、Xception 等轻量级神经网络架构的核心组成部分。
下面是对深度可分离卷积的详细解析。
1. 标准卷积回顾
在标准卷积中,卷积操作涉及到同时对所有输入通道进行卷积,输出为每个通道的加权和。假设输入特征图有 C_in个通道,输出特征图有 C_out 个通道,卷积核大小为 (K),则计算的复杂度为:
FLOPs (Floating Point Operations):浮点运算次数
FLOPs = H*W* K*K*C_in*C_out
其中 (H) 和 (W) 是输入特征图的高度和宽度。
2. 深度卷积(Depthwise Convolution)
深度卷积对每个输入通道单独进行卷积操作,即对于每个输入通道,单独使用一个卷积核。对于深度卷积,每个输入通道使用一个独立的卷积核进行卷积,亦即使用 C_in个的卷积核处理 C_in个输入通道,卷积核大小为K*K,由于输入是 H*W像素,有C_in 个通道,所以:
FLOPs = H*W* K*K*C_in
3. 逐点卷积(Pointwise Convolution)
逐点卷积是用 (1*1) 的卷积核对深度卷积的输出进行处理,结合所有的通道信息。逐点卷积的输入是深度卷积的输出,输出是设置的输出通道数。
逐点卷积的 FLOPs = H*W* 1*1*C_in*C_out
4. 深度可分离卷积的整合
深度可分离卷积就是将上述两个步骤整合在一起。首先通过深度卷积进行特征提取,然后通过逐点卷积进行通道的融合。这种方法在保留大部分信息的同时,极大地减少了计算负担和模型参数。深度可分离卷积与标准卷积的网络结构对比图:
5. 计算复杂度比较
例如,有一个卷积层H=10,W=10,K=3,C_in=3, C_out=10,
那么标准卷积的FLOPs = H*W* K*K*C_in*C_out=10*10*3*3*3*10=27000 。这表示在这个卷积层中大约需要进行 27000 次浮点运算。
深度可分离卷积的FLOPs分为两部分:深度卷积的 FLOPs和逐点卷积的 FLOPs。
深度卷积的 FLOPs = H*W* K*K*C_in=10*10*3*3*3=2700
逐点卷积的 FLOPs = H*W* 1*1*C_in*C_out=10*10*1*1*3*10=3000
深度可分离卷积的总的FLOPs = 2700+3000=5700
简而言之,深度可分离卷积的计算复杂度显著低于标准卷积。
6. 结论
深度可分离卷积是一种高效的卷积操作,能够在确保分类准确率的同时,显著减少计算量和模型大小。这使得它在移动端和嵌入式设备上的应用极具吸引力,是许多现代轻量级神经网络的基础。
二、MobileNet
MobileNet是一种深度学习模型,专门设计用于在移动设备和嵌入式设备上进行高效的图像分类和目标检测。它是在2017年由 Google 提出的,旨在在保持较高准确率的同时,减少计算资源消耗和模型大小,从而实现快速的推理。
1. 设计背景
移动设备的需求:随着机器学习的普及,特别是在移动设备上的应用,需要一种轻量级的神经网络,以便在处理能力有限的设备上执行模型。
准确性与效率的权衡:MobileNet V1 试图在模型大小、速度和准确率之间找到一个良好的平衡点。
2. 主要构建模块
MobileNet V1 的核心思想是使用深度可分离卷积(Depthwise Separable Convolution),这一操作把标准卷积分解为两步:
深度卷积(Depthwise Convolution):对每个输入通道单独执行卷积操作。
逐点卷积(Pointwise Convolution):使用 1x1 卷积来结合深度卷积的输出。
这种方法大大减少了计算量和参数数量,从而提高了模型的效率。
3. 模型架构
MobileNet V1 的基本结构如下:
输入层:接收输入图像,通常为 224x224 像素大小及 RGB 通道。
标准卷积层:初始的标准卷积层,用于提取基础特征。
深度可分离卷积层:多个 stacked layers,通过大量的深度可分离卷积层进行特征提取,每层由深度卷积和逐点卷积组成。
全局平均池化:在最后一层以全局平均池化来减少模型的大小。
全连接层:最后的全连接层,用于分类。
4. 计算复杂度
MobileNet V1 通过引入深度可分离卷积显著降低了模型的计算复杂度。
FLOPs(每秒浮点运算次数):在输入为 224x224x3 图像时,MobileNet V1 的 FLOPs 约为 569 万,显著低于许多其他主流模型。MobileNet V1 的参数数量也相对较少,约在 4-6 百万之间,具体取决于使用的宽度乘子(Width Multiplier)。
5. 应用领域
MobileNet V1 已经广泛应用于各种计算机视觉任务,包括:
图像分类
物体检测(与 SSD 等方法结合使用)
实时图像分析
视频处理
6. 结论
MobileNet V1 为在计算资源有限的环境中应用深度学习提供了一种有效的解决方案。其引入的深度可分离卷积成为了轻量级网络设计中的一项重要技术,并极大地影响了后续轻量级模型的设计,如 MobileNet V2 和 MobileNet V3。
代码实现:
新建mobileNet.py
import torch
import torch.nn as nn
import torch.nn.functional as F
class MobileNet(nn.Module):
def conv_dw(self, in_channels, out_channels, stride): # 定义深度可分离卷积
return nn.Sequential( # Sequential是一个容器,它可以包含一系列的神经网络层(layers),并按顺序执行它们
nn.Conv2d(in_channels, in_channels, kernel_size=3, # 定义深度卷积
stride=stride, padding=1, groups=in_channels, bias=False),
nn.BatchNorm2d(in_channels),
nn.ReLU(),
nn.Conv2d(in_channels, out_channels, kernel_size=1, # 定义逐点卷积,卷积核是1*1
stride=1, padding=0, bias=False),
nn.BatchNorm2d(out_channels),
nn.ReLU()
)
def __init__(self, num_classes=10):
super(MobileNet, self).__init__()
self.conv1 = nn.Sequential( # 定义第1个卷积层
nn.Conv2d(3, 32, kernel_size=3, stride=1, padding=1), # 输入通道为3,输出通道为32,卷积核大小为3x3,步长为1,填充为1
nn.BatchNorm2d(32), # 批量归一化
nn.ReLU() # 激活函数
)
self.conv2_1 = self.conv_dw(32, 32, 1) # 定义第2个卷积层的第一次卷积
self.conv2_2 = self.conv_dw(32, 64, 2) # 定义第2个卷积层的第二次卷积
self.conv2_3 = self.conv_dw(64, 64, 1) # 定义第2个卷积层的第三次卷积
self.conv2_4 = self.conv_dw(64, 128, 2) # 定义第2个卷积层的第四次卷积
self.conv2_5 = self.conv_dw(128, 128, 1) # 定义第2个卷积层的第五次卷积
self.conv2_6 = self.conv_dw(128, 256, 2) # 定义第2个卷积层的第六次卷积
self.conv2_7 = self.conv_dw(256, 256, 1) # 定义第2个卷积层的第七次卷积
self.conv2_8 = self.conv_dw(256, 512, 2) # 定义第2个卷积层的第八次卷积
self.fc = nn.Linear(512, num_classes) # 全连接层
def forward(self, x): # 定义前向传播
out = self.conv1(x) # 第1个卷积层
out = self.conv2_1(out) # 第2个卷积层的第一次卷积
out = self.conv2_2(out) # 第2个卷积层的第二次卷积
out = self.conv2_3(out) # 第2个卷积层的第三次卷积
out = self.conv2_4(out) # 第2个卷积层的第四次卷积
out = self.conv2_5(out) # 第2个卷积层的第五次卷积
out = self.conv2_6(out) # 第2个卷积层的第六次卷积
out = self.conv2_7(out) # 第2个卷积层的第七次卷积
out = self.conv2_8(out) # 第2个卷积层的第八次卷积
out = F.avg_pool2d(out, 2) # 最大池化,池化核大小为2,out2,步长为2
out = out.view(-1, 512) # 将特征图展开
out = self.fc(out) # 全连接层
return out
def mobilenetv1_small():
return MobileNet()
if __name__ == '__main__':
net = MobileNet()
print(net)
input = torch.randn(1, 3, 32, 32)
out = net(input)
print(out.size())
用新建的MobileNet网络进行训练
同样的,将之前的train.py脚本中的
net = resnet().to(device),改为:
net =mobilenetv1_small().to(device),即可运行开始训练: