先引出题目,占个坑,以后慢慢填。
mobilenet 也算是提出有一段时间了,网上也不乏各种实现版本,其中,谷歌已经开源了Tensorflow的全部代码,无奈自己几乎不熟悉Tensorflow,还是比较钟爱Caffe平台,因而一直在关心这方面。
单纯的Mobilenet分类不是关注重点,如何将其应用到目标检测网络才是关键,目前基本看好的思路就是Mobilenet+SSD,github上已经有至少如下项目涉及到这方面:
https://github.com/chuanqi305/MobileNet-SSD
https://github.com/zeusees/SSD_License_Plate_Detection
https://github.com/canteen-man/MobileNet-SSD-Focal-loss
https://github.com/cooliscool/LISA-on-SSD-mobilenet
https://github.com/FreeApe/VGG-or-MobileNet-SSD
接下来的时间,我将会尽可能进行进行分析验证,目的是寻找并试验出好的解决方案,并且期待能成功训练其他数据集。。
Mobilenet的速度是很快的,如果配上Depthwise layer,在TitanX应该能达到150fps,如果能将检测精度提升到70%以上,将会是一个很好的检测网络。
实现方案一
项目地址:MobileNet-SSD
几个月前接触到了这个project,当时chuanqi大神在Caffe平台上初步实现了Mobilenet-SSD,本人自然是很惊喜的,接下来就时不时和大神一起探讨,在其指导下,我在VOC数据集也能训练出大约72%的精度。现在这个项目趋于稳定,根据github上的描述,最终精度是72.7%,也很不错了。下面简单记录一下运行和训练方法。
模型分析
通过分析Mobilenet的模型结构和MobileNet-SSD的模型结构, 可以看出,conv13是骨干网络的最后一层,作者仿照VGG-SSD的结构,在Mobilenet的conv13后面添加了8个卷积层,然后总共抽取6层用作检测,貌似没有使用分辨率为38*38的层,可能是位置太靠前了吧。
模型运行
这个项目既然叫MobileNet-SSD,那首先要求能正常运行基础版本的SSD,这方面的博客教程这是不少,本人也有几篇博文涉及,可以参考。
克隆项目:
$ git clone https://github.com/chuanqi305/MobileNet-SSD.git
- 1
然后可以在自己的目录(我是用的是/home目录)下得到MobileNet-SSD文件夹,其中重要文件简介如下:
- template 存放4个网络定义的公用模板,可以由gen.py脚本修改并生成
- MobileNetSSD_deploy.prototxt 运行网络定义文件
- solver_train.prototxt 网络训练超参数定义文件
- solver_test.prototxt 网络测试超参数定义文件
- train.sh 网络训练脚本
- test.sh 网络测试脚本
- gen_model.sh 生成自定义网络脚本(调用template文件夹内容)
- gen.py 生成公用模板脚本(暂不用)
- demo.py 实际检测脚本(图片存于images文件夹)
- merge_bn.py 合并bn层脚本,用于生成最终的caffemodel
接下来下载已经训练好的caffemodel放入项目文件夹:Google Drive | 百度云
最后打开demo.py脚本,根据个人情况修改以下路径:
caffe_root = '/home/yaochuanqi/ssd/caffe/'
net_file= 'MobileNetSSD_deploy.prototxt'
caffe_model='MobileNetSSD_deploy.caffemodel'
test_dir = "images"
- 1
- 2
- 3
- 4
然后运行demo.py脚本,就能看到检测结果了,效果尚可,随便贴两张图:
模型训练
我们也可以用自己的数据集来训练这个MobileNet-SSD模型,训练步骤简要记录如下:
1.建立数据集软连接
我们需要提前建立好适用于SSD训练的数据集(VOC格式),比如博主所用的是KITTI数据集,制作方法可在往期博文中找到,最终需要生成训练验证集和测试集的lmdb文件,然后建立软连接,类似于一个快捷方式,可以简化命令和节省空间。
$ cd ~/MobileNet-SSD
$ ln ‐s /home/its/data/KITTIdevkit/KITTI/lmdb/KITTI_trainval_lmdb trainval_lmdb
$ ln ‐s /home/its/data/KITTIdevkit/KITTI/lmdb/KITTI_test_lmdb test_lmdb
- 1
- 2
- 3
执行完命令,就能在项目文件夹下看到trainval_lmdb和test_lmdb软连接。
2.创建labelmap.prototxt文件
该文件用于定义训练样本的类别,置于项目文件夹下,其内容如下:
item {
name: "none_of_the_above" label: 0 display_name: "background" }
item {
name: "Car" label: 1 display_name: "Car" }
item {
name: "Pedestrian" label: 2 display_name: "Pedestrian" }
item {
name: "Cyclist" label: 3 display_name: "Cyclist" }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
3.运行gen_model.sh脚本
由于VOC数据集是21类(加上背景),而这里只有4类,因此,我们需要重新生成训练、测试和运行网络文件,这里就要用到gen_model.sh脚本,它会调用template文件夹中的模板,按照我们指定的参数,生成所需的文。这个脚本的用法如下:
usage: ./gen_model.sh CLASSNUM
for voc the classnum is 21
- 1
- 2
只有一个类别数量的参数,因此我们执行命令如下:
./gen_model.sh 4
- 1
执行之后,得到examples文件夹,里面的3个prototxt就是从模板生成的正式网络定义,根据作者设置,其中的deploy文件是已经合并过bn层的,需要后面配套使用。
4.修改训练和测试超参数
根据实际情况,修改solver_train.prototxt和solver_test.prototxt。
其中test_iter=测试集图片数量/batchsize;初始学习率不宜太高,否则基础权重破坏比较严重;优化算法是RMSProp,可能对收敛有好处,不要改成SGD,也是为了保护权重。
5.下载预训练模型
下载地址:Google Drive | 百度云,放在项目文件夹下,这里的预训练模型是作者从Tensorflow那边转化过来的,然后经过了VOC数据集的初步调试。
6.开始训练
修改并运行train.sh脚本,中途可以不断调节参数。训练结束后,运行test.sh脚本,测试网络的精度值。
7.合并bn层
为了提高模型运行速度,作者在这里将bn层合并到了卷积层中,相当于bn的计算时间就被节省了,对检测速度可能有小幅度的帮助,打开merge_bn.py文件,然后注意修改其中的文件路径:
caffe_root = '/home/yaochuanqi/ssd/caffe/'
train_proto = 'MobileNetSSD_train.prototxt'
train_model = 'MobileNetSSD_train.caffemodel' # should be your snapshot caffemodel, e.g. mobilnetnet_iter_72000.caffemodel
deploy_proto = 'MobileNetSSD_deploy.prototxt'
save_model = 'MobileNetSSD_deploy.caffemodel'
- 1
- 2
- 3
- 4
- 5
然后运行该脚本,就可以得到最终的检测模型,那这个模型由于合并了bn层,参数格式已经变化,就不能再用于训练了。如果想继续训练,应该用合并前的。对于得到的最终模型,可用demo.py脚本查看实际检测效果,也可以部署在其他地方。
存在的问题
本人使用扩充的KITTI数据集训练Mobilenet-SSD,折腾了一周,精度才只有52%左右,而且训练速度比VGG的慢一些。我感觉不应该这么低,至少也应该有65%吧,暂时没有找到问题的根源在哪里,如果有同学也拿这个训练且效果很好,请告知,不胜感激!
更新:考虑到Mobilenet特征提取能力有限,最近试验将分辨率提升至416*416(速度降低很少),然后使用仅含4类目标(通过脚本提取)的COCO预训练模型,初始学习率为0.001,根据损失值和精度调整后续学习率,迭代50000次后,目前精度提升到62.8%。
Mobilenet使用Depthwise Layer
理论上Mobilenet的运行速度应该是VGGNet的数倍,但实际运行下来并非如此,前一章中,即使是合并bn层后的MobileNet-SSD也只比VGG-SSD快那么一点点,主要的原因是Caffe中暂时没有实现depthwise convolution,目前都是用的group。这里group相当于一个for循环,需要依次计算,如果能使用深度卷积,那就可以一次性计算完,节省不少时间。
经过大量实验,终于找到能让mobilenet加速的方法,项目地址:DepthwiseConvolution, 十分感谢该项目作者。
用上了depthwise convolution layer,对于mobilenet的提速十分明显,可以说是立竿见影。下面简单介绍使用方法.
添加新的深度卷积层
首先克隆项目:
$ git clone https://github.com/yonghenglh6/DepthwiseConvolution.git
- 1
注意到项目中的caffe文件夹,将其中的depthwise_conv_layer.hpp,depthwise_conv_layer.cpp和depthwise_conv_layer.cu这三个文件放到SSD(即caffe)的相应位置中,这里的操作是从基础卷积类中派生了深度卷积这个类,此处并不需要对caffe.proto文件进行修改。稍后,需要重新编译Caffe,这样才能识别新增的depthwise convolution layer。
修改deploy文件
接下来我们需要修改MobileNetSSD_deploy.prototxt,将其中所有名为convXX/dw(XX代指数字)的type从”Convolution”替换成”DepthwiseConvolution”,总共需要替换13处,从conv1/dw到conv13/dw,然后把“engine: CAFFE”都注释掉,这个新的网络文件可以另存为MobileNetSSD_deploy_depth.prototxt。在运行网络的时候,caffemodel模型不用动,只需要指定新的prototxt文件和含有depthwise convolution layer的Caffe即可。
效果验证
为了验证效果,我们使用demo.py脚本来测试网络的平均运行时间,运行模式设置为gpu,在demo.py文件中添加和time相关的代码:
import time
def detect(imgfile):
#
#
net.blobs['data'].data[...] = img
start=time.time() # time begin
out = net.forward()
use_time=time.time()-start # proc time
print("time="+str(use_time)+"s")
#
#
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
笔记本电脑显卡是GTX 850m,对于默认的7张检测图片,VGG-SSD,Mobilenet-SSD(group)和Mobilenet-SSD(depth)的平均检测时间为:
Model | Inference time |
---|---|
VGG-SSD | 107ms |
MobileNet-SSD(group) | 62ms |
MobileNet-SSD(depth) | 17ms |
如果我们使用Caffe自带的time工具,结果也是差不多的:
$ cd ~/caffe
$ ./build/tools/caffe time -gpu 0 -model examples/mobilenet/XXXX.prototxt
I1219 20:09:24.062338 10324 caffe.cpp:412] Average Forward pass: 109.97 ms. # VGG-SSD
I1219 20:09:47.771399 10343 caffe.cpp:412] Average Forward pass: 57.4238 ms. # Mobilenet-SSD(group)
I1219 20:10:25.145504 10385 caffe.cpp:412] Average Forward pass: 16.39 ms. # Mobilenet-SSD(depth)
- 1
- 2
- 3
- 4
- 5
- 6
可以看到,depthwise convolution layer是有效的,运行时间快了五六倍之多。然后,博主在Jetson TX1上也如法炮制,得到的检测时间如下:
I1219 22:08:15.236963 2210 caffe.cpp:412] Average Forward pass: 57.3939 ms.
- 1