最近在利用SSD检测物体时,由于实际项目要求,需要对模型进行轻量化,所以考虑利用轻量网络替换原本的骨架VGG16,查找一些资料后最终采用了google开源的mobileNetV2。这里对学习mobileNet系列的过程做一些总结。mobileNetV1是由google在2017年发布的一个轻量级深度神经网络,其主要特点是采用深度可分离卷积替换了普通卷积,2018年提出的mobileNetV2在V1的基础上引入了线性瓶颈 (Linear Bottleneck)和倒残差 (Inverted Residual)来提高网络的表征能力。
1.mobileNetV1
mobileNet V1是一种体积较小、计算量较少、适用于移动设备的卷积神经网络。mobileNet V1的主要创新点是用深度可分离卷积(depthwise separable convolution)代替普通的卷积,并使用宽度乘数(width multiply)减少参数量,不过减少参数的数量和操作的同时也会使特征丢失导致精度下降。
1.1 普通卷积和深度可分离卷积
标准的卷积过程如图1,卷积核做卷积运算时得同时考虑对应图像区域中的所有通道(channel),而深度可分离卷积对不同的通道采用不同的卷积核进行卷积,如图2所示它将普通卷积分解成了深度卷积(Depthwise Convolution)和逐点卷积(Pointwise Convolution)两个过程,这样可以将通道(channel)相关性和空间(spatial)相关性解耦。原文中给出的深度可分离卷积后面都接了一个BN和ReLU层。
图1 普通卷积 图2 深度可分离卷积
1.1.1 标准卷积核
设输入特征维度为DF*DF*M,M为通道数。标准卷积核的参数为DK*DK*M*N,DK为卷积核大小,M为输入的通道数, N为输出的通道数。卷积后输出维度为:DF*DF*N。卷积过程中每个卷积核对图像区域进行DF*DF次扫描,每次扫描的深度为M(channel),每个通道需要DK*DK次加权求和运算, 所以理论计算量(floating point operatios FLOPs)为:N*DF*DF*M*Dk*DK。
1.1.2 深度可分离卷积
- 深度卷积:设输入特征维度为DF*DF*M,M为通道数。卷积核的参数为DK*DK*1*M。输出深度卷积后的特征维度为:DF*DF*M。卷积时每个通道只对应一个卷积核(扫描深度为1),所以 FLOPs为:M*DF*DF*DK*DK。
- 逐点卷积:输入为深度卷积后的特征,维度为DF*DF*M。卷积核参数为1*1*M*N。输出维度为DF*DF*N。卷积过程中对每个特征做1*1的标准卷积, FLOPs为:N*DF*DF*M。
1.1.3 深度可分离卷积的优势
- 参数量:关系到模型大小,通常参数用float32表示,所以模型大小一般时参数量的4倍。标准卷积的参数量为Dk*Dk*M*N(M为输入通道数, N为输出通道数),深度卷积的参数量为DK*DK*M,逐点卷积的参数量为1*1*M*N,所以深度可分离卷积相对于标准卷积的参数量为(DK*DK*M + M*N)/ DK*DK*M*N = 1/N + 1/DK*DK。
- 计算量: 可以用来衡量算法/模型的复杂度, 通常只考虑乘加操作(Multi-Adds)的数量,而且只考虑 CONV 和 FC 等参数层的计算量,忽略 BN 和PReLU 等等。一般情况,CONV 和 FC 层也会忽略仅纯加操作的计算量,如 bias 偏置加和 shotcut 残差加等。标准卷积的计算量为:N*DF*DF*M*DK*DK,深度可分离卷积的计算量为M*DF*DF*DK*DK+N*DF*DF*M。所以深度可分离卷积的计算量相比于标准卷积为(M*DF*DF*DK*DK+N*DF*DF*M)/ N*DF*DF*M*DK*DK = 1/N + 1/DK*DK。
- 区域和通道分离: 深度可分离卷积将以往普通卷积操作同时考虑通道和区域改变(卷积先只考虑区域,然后再考虑通道),实现了通道和区域的分离。
举个例子:
假设输入特征的维度为224*224*3,卷积核大小为3*3,输出通道数为2,设置pad=1,stride=1,则如下图(图a)所示,标准卷积的输出维度为224*224*2。参数量为3*3*3*2=54,计算量为2*224*224*3*3*3=2709504。
在深度卷积过程中(图b),输入为224*224*3,卷积核参数为3*3*1*3,每个通道做3*3的卷积,收集了每个通道的空间特征(Depthwise特征),输出特征维度为224*224*3。
接着进入逐点卷积(图c),卷积核参数为1*1*3*2,对Depthwise特征做2个1*1的普通卷积,这样相当于收集了每个点的特征,输出维度为224*224*2。
深度可分离卷积的参数量为3*3*3 + 3*2 = 33,计算量为3*224*224*3*3 + 2*224*224*3 = 1655808,33/54=1655808 / 2709504 = 0.61, 可以看出参数量和计算量减少至标准卷积的0.61倍。
图a 标准卷积过程 图b 深度卷积 图c 逐点卷积
1.2 mobileNetV1网络结构
mobileNetV1的网络结构如Table 1.前面的卷积层中除了第一层为标准卷积层外,其他都是深度可分离卷积(Conv dw + Conv/s1),卷积后接了一个7*7的平均池化层,之后通过全连接层,最后利用Softmax激活函数将全连接层输出归一化到0-1的一个概率值,根据概率值的高低可以得到图像的分类情况。
1.3 超参数
宽度因子α(Width Multiplier)
对于深度可分离卷积层,输入的通道数M乘上一个宽度因子α变为αM,输出通道数变为αN,其中α区间为(0,1],此时深度可分离卷积的参数量为:DK*DK*αN + αM*αN = α*α(1/α *DK*DK*N + M*N),计算量变为αM*DF*DF*DK*DK+αN*DF*DF*αM = α*α (1/α*M*DF*DF*DK*DK+N*DF*DF*M),所以参数量和计算量差不多都变为原来的α*α倍。
分辨率因子ρ (Resolution Multiplier)
ρ改变输入层的分辨率,所以深度可分离卷积的参数量不变,但计算量为M*ρDF*ρDF*DK*DK+N*ρDF*ρDF*M = ρ*ρ(M*DF*DF*DK*DK+N*DF*DF*M),即计算量变为原来的ρ*ρ倍。
1.4 mobileNetV1 实现(基于框架keras / pytorch)
- pytorch实现: https://github.com/marvis/pytorch-mobilenet
- keras实现: https://github.com/Hedlen/Mobilenet-Keras/blob/master/model/mobilenet.py
import torch
import torch.nn as nn
def conv3x3(in_planes, out_planes, stride=1, padding=1):
return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride, padding=padding, groups=in_planes, bias=False)
# why no bias: 如果卷积层之后是BN层,那么可以不用偏置参数,可以节省内存
def conv1x1(in_planes, out_planes):
return nn.Conv2d(in_planes, out_planes, kernel_size=1, stride=1, bias=False)
class DPBlock(nn.Module):
'''
Depthwise convolution and Pointwise convolution.
'''
def __init__(self, in_planes, out_planes, stride=1):
super(DPBlock, self).__init__() # 调用基类__init__函数初始化
self.conv1 = conv3x3(in_planes, out_planes, stride)
self.bn1 = nn.BatchNorm2d(in_planes)
self.relu = nn.ReLU(inplace=True)
self.conv2 = conv1x1(in_planes, out_planes)
self.bn2 = nn.BatchNorm2d(out_planes)
def forward(self, x):
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
out = self.relu(out)
return out
class mobileNetV1Net(nn.Module):
def __init__(self, block, num_class=1000):
super(mobileNetV1Net, self).__init__()
self.model = nn.Sequential(
conv3x3(3, 32, 2),
nn.BatchNorm2d(32),
nn.ReLU(inplace=True)
block(32, 64, 1),
block(64, 128, 2),
block(128, 128, 1),
block(128, 256, 2),
block(256, 256, 1),
block(256, 512, 2),
block(512, 512, 1),
block(512, 512, 1),
block(512, 512, 1),
block(512, 512, 1),
block(512, 512, 1),
block(512, 1024, 2),
block(1024, 1024, 2),
nn.AvgPool2d(7)
)
self.fc = nn.Linear(1024, num_class)
def forward(self, x):
x = self.model(x)
x = x.view(-1, 1024) # reshape
out = self.fc(x)
return out
mobileNetV1 = mobileNetV1Net(DPBlock)
1.5 参考链接
- 深度可分离卷积核MobileNet_v1
- MobileNetV1 & MobileNetV2 简介
- 从MobileNet V1到MobileNet V2
- pytorch实现MobileNet
TRANSLATE with x
English
Arabic | Hebrew | Polish |
Bulgarian | Hindi | Portuguese |
Catalan | Hmong Daw | Romanian |
Chinese Simplified | Hungarian | Russian |
Chinese Traditional | Indonesian | Slovak |
Czech | Italian | Slovenian |
Danish | Japanese | Spanish |
Dutch | Klingon | Swedish |
English | Korean | Thai |
Estonian | Latvian | Turkish |
Finnish | Lithuanian | Ukrainian |
French | Malay | Urdu |
German | Maltese | Vietnamese |
Greek | Norwegian | Welsh |
Haitian Creole | Persian | |
TRANSLATE with
COPY THE URL BELOW
Back
EMBED THE SNIPPET BELOW IN YOUR SITE
Enable collaborative features and customize widget: Bing Webmaster Portal
Back
此页面的语言为中文(简体)
翻译为
- 中文(简体)
- 中文(繁体)
- 丹麦语
- 乌克兰语
- 乌尔都语
- 亚美尼亚语
- 俄语
- 保加利亚语
- 克罗地亚语
- 冰岛语
- 加泰罗尼亚语
- 匈牙利语
- 卡纳达语
- 印地语
- 印尼语
- 古吉拉特语
- 哈萨克语
- 土耳其语
- 威尔士语
- 孟加拉语
- 尼泊尔语
- 布尔语(南非荷兰语)
- 希伯来语
- 希腊语
- 库尔德语
- 德语
- 意大利语
- 拉脱维亚语
- 挪威语
- 捷克语
- 斯洛伐克语
- 斯洛文尼亚语
- 旁遮普语
- 日语
- 普什图语
- 毛利语
- 法语
- 波兰语
- 波斯语
- 泰卢固语
- 泰米尔语
- 泰语
- 海地克里奥尔语
- 爱沙尼亚语
- 瑞典语
- 立陶宛语
- 缅甸语
- 罗马尼亚语
- 老挝语
- 芬兰语
- 英语
- 荷兰语
- 萨摩亚语
- 葡萄牙语
- 西班牙语
- 越南语
- 阿塞拜疆语
- 阿姆哈拉语
- 阿尔巴尼亚语
- 阿拉伯语
- 韩语
- 马尔加什语
- 马拉地语
- 马拉雅拉姆语
- 马来语
- 马耳他语
- 高棉语
随时将中文(简体)翻译为PRO
一律不翻译中文(简体)