写的比较好,我就保存一下:http://blog.csdn.net/Jesse_Mx/article/details/78680055

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

然后可以在自己的目录(我是用的是/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"

然后运行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

执行完命令,就能在项目文件夹下看到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"
  }

3.运行gen_model.sh脚本 
由于VOC数据集是21类(加上背景),而这里只有4类,因此,我们需要重新生成训练、测试和运行网络文件,这里就要用到gen_model.sh脚本,它会调用template文件夹中的模板,按照我们指定的参数,生成所需的文。这个脚本的用法如下:

usage: ./gen_model.sh CLASSNUM
        for voc the classnum is 21

只有一个类别数量的参数,因此我们执行命令如下:

./gen_model.sh 4

执行之后,得到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'

然后运行该脚本,就可以得到最终的检测模型,那这个模型由于合并了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

注意到项目中的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") 
    #
    #

笔记本电脑显卡是GTX 850m,对于默认的7张检测图片,VGG-SSD,Mobilenet-SSD(group)和Mobilenet-SSD(depth)的平均检测时间为:

ModelInference time
VGG-SSD107ms
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)

可以看到,depthwise convolution layer是有效的,运行时间快了五六倍之多。然后,博主在Jetson TX1上也如法炮制,得到的检测时间如下:

I1219 22:08:15.236963  2210 caffe.cpp:412] Average Forward pass: 57.3939 ms.

意味着TX1上Mobilenet-SSD能达到17帧左右,这离真正的real-time又近了一步

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐