利用自己的数据重训ssd-mobileNetV1并在NCNN上部署

        随着深度学习的发展,目标检测算法已逐渐趋于成熟。目前基于深度学习的目标检测算法可以被分为两个大方向,一方面是以fast-RCNN为代表的含有Region Proposal Stage的模型,例如faster-RCNN和R-FCN;另一方面是以SSD为代表的Single Stage模型,例如SSD, yolo等。尽管Region Proposal已经逐步地被优化并且加入到了深度网络中,但是其速率仍很难达到嵌入式设备的要求。相对的Single Stage模型以卷积的形式替代了Region Proposal,不过这也带来了准确率降低的影响。

        NCNN为腾讯优图开发的面相嵌入式设备的开源深度学习正向计算框架。NCNN中自带的一个ssd-mobileNet的example。与原始ssd不同的是,ssd-mobileNet使用了mobileNet V1替换掉了VGG作为其前部的特征提取网络。mobileNets V1采用了Depthwise Sepreable 卷积替代了普通的卷积层,从而实现了网络的轻量化和运算加速。根据NCNN中ssd-mobileNet代码的注释提示,我们找到了训了ssd-mobileNet的Caffe项目。

        本文将讲解如何使用自己的数据利用chuanqi305的ssd-mobileNet训练自己的目标检测模型,并转换到NCNN框架下,实现正向检测。

1. 下载安装

        首先下载SSD-caffe框架,当前很多目标检测算法都是基于这个框架实现的。下载,make通过之后进入SSD_CAFFE_RROT/examples路径下下载ssd-mobileNet,由于一些你懂得原因,作者已经很nice地把比较大的caffemodel放到了github项目中,因此直接

        $ cd examples/

        $ git clone https://github.com/chuanqi305/MobileNet-SSD.git

        由于作者提供的文件只有python和shell脚本,因此不需要再make。

2. 构建数据集

        构建数据集的方法和SSD是一样的,这里不再赘述,具体使用的就是ssd中的create_list.sh和create_data.sh,数据集的格式就用PASCAL VOC那种就可以。

        根据作者的Readme,作者已经提前在代码中设定好数据集的位置,即ssd-mobileNet项目的根目录,直接就叫trainval_lmdb和test_lmdb 可以使用linux命令将这两个文件链接到项目根目录下

        $ ln -s PATH_TO_YOUR_TRAIN_LMDB trainval_lmdb

        $ ln -s PATH_TO_YOUR_TEST_LMDB test_lmdb

将数据集对应的labelmap.prototxt也拷贝到项目根目录下,labelmap.prototxt的内容如下所示,注意第0类为背景

item {
  name: "none_of_the_above"
  label: 0
  display_name: "background"
}
item {
  name: "dog"
  label: 1
  display_name: "dog"
}
item {
  name: "cat"
  label: 2
  display_name: "cat"
}

3. 生成网络结构定义文件

        作者的Readme里写的很简单,通过gen_model.sh生成example/目录 其中包含train、test、deploy三个网络结构定义文件。

        $ ./gen_model.sh 3 

        其中3和labelmap.prototxt对应,包含了背景类。

       作者提供的gen_model.sh内容如下,可见作者是通过这个脚本,得出类别数并计算网络中所需类别数的倍数,并且替换掉原来template/目录下prototxt中的类别数token,最终将新生成的prototxt文件存入example/目录下

#!/bin/sh
if test -z $1 ;then
	echo usage: $0 CLASSNUM
        echo "        for voc the classnum is 21"
	exit 1
fi
echo $1 |grep '^[0-9]*$' >/dev/null 2>&1
if [ $? != 0 ];then
	echo usage: $0 CLASSNUM
        echo "        for voc the classnum is 21"
	exit 1
fi
cls_num=$1
cls_num3=$(expr $1 \* 3)
cls_num6=$(expr $1 \* 6)
trainfile=example/MobileNetSSD_train.prototxt
testfile=example/MobileNetSSD_test.prototxt
deploybnfile=example/MobileNetSSD_deploy_bn.prototxt
deployfile=example/MobileNetSSD_deploy.prototxt

mkdir -p example

cp template/MobileNetSSD_train_template.prototxt $trainfile
sed -i "s/cls6x/${cls_num6}/g" $trainfile
sed -i "s/cls3x/${cls_num3}/g" $trainfile
sed -i "s/cls1x/${cls_num}/g" $trainfile

cp template/MobileNetSSD_test_template.prototxt $testfile
sed -i "s/cls6x/${cls_num6}/g" $testfile
sed -i "s/cls3x/${cls_num3}/g" $testfile
sed -i "s/cls1x/${cls_num}/g" $testfile

#cp template/MobileNetSSD_deploy_bn_template.prototxt $deploybnfile
#sed -i "s/cls6x/${cls_num6}/g" $deploybnfile
#sed -i "s/cls3x/${cls_num3}/g" $deploybnfile
#sed -i "s/cls1x/${cls_num}/g" $deploybnfile

cp template/MobileNetSSD_deploy_template.prototxt $deployfile
sed -i "s/cls6x/${cls_num6}/g" $deployfile
sed -i "s/cls3x/${cls_num3}/g" $deployfile
sed -i "s/cls1x/${cls_num}/g" $deployfile

4. 训练模型

        作者将训练用的命令写到了train.sh脚本中,但是大家caffe的安装和训练方式可能不同,因此可以根据自己的需要对脚本进行修改,同时也可以修改solver

#!/bin/sh
if ! test -f example/MobileNetSSD_train.prototxt ;then
	echo "error: example/MobileNetSSD_train.prototxt does not exist."
	echo "please use the gen_model.sh to generate your own model."
        exit 1
fi
mkdir -p snapshot
../../build/tools/caffe train -solver="solver_train.prototxt" \
-weights="mobilenet_iter_73000.caffemodel" \
-gpu 0 

        solver_train.prototxt,如果网络结构文件直接在example/下需要修改前面两行中的路径。其他参数按需调优,默认情况下效果还可以。按照Readme训练3万次左右  loss会降低到 1.5-2.5差不多就可以用了

train_net: "example/MobileNetSSD_train.prototxt"
test_net: "example/MobileNetSSD_test.prototxt"
test_iter: 673
test_interval: 10000
base_lr: 0.0005
display: 10
max_iter: 120000
lr_policy: "multistep"
gamma: 0.5
weight_decay: 0.00005
snapshot: 1000
snapshot_prefix: "snapshot/mobilenet"
solver_mode: GPU
debug_info: false
snapshot_after_train: true
test_initialization: false
average_loss: 10
stepvalue: 20000
stepvalue: 40000
iter_size: 1
type: "RMSProp"
eval_type: "detection"
ap_version: "11point"

5. 测试模型

          测试用的命令记录在了test.sh脚本中,其本质是用网络计算test_lmdb的mbox_loss。

#!/bin/sh
#latest=snapshot/mobilenet_iter_73000.caffemodel
latest=$(ls -t snapshot/*.caffemodel | head -n 1)
if test -z $latest; then
	exit 1
fi
../../build/tools/caffe train -solver="solver_test.prototxt" \
--weights=$latest \
-gpu 1

6. 合并batch_norm层

        作者在项目根目录中提供了一个merge_bn.py,通过这个python脚本合并batch_norm层生成无bn层的deploy文件。具体不详述。

7. 将旧版本caffemodel和prototxt转换到新版本上

        随着caffe的不断维护升级,caffe也有新旧版本之分,而SSD-caffe框架就属于旧版本caffe,目前直接git的caffe则是新版本的。新旧版本的转换可以通过caffe自带的tool执行。注意作者在项目的根目录提供了deploy.prototxt但是我们应该使用

        $ ./tools/upgrade_net_proto_text path/to/your/old_deploy.prototxt path/to/your/new_deploy.caffemodel

        $ ./tools/upgrade_net_proto_binary path/to/your/old_deploy.caffemodel path/to/your/new_deploy.caffemodel

8. 将caffemodel和prototxt转换为param和bin

        ncnn框架中网络定义文件为*.param文件,权值文件为*.bin文件,可以通过ncnn中自带的工具进行转换

       $ cd NCNN_ROOT/

       $  ./build/tools/caffe/caffe2ncnn Path/to/your/deploy.prototxt Path/to/your/deploy.caffemodel 

Path/to/your/deploy.param Path/to/your/deploy.bin

猜你喜欢

转载自blog.csdn.net/qq_16951525/article/details/80253322