date: 2018/08/21


RK3399上Tengine平台搭建 | Hey~YaHei!》一文介绍了RK3399和Tengine并且尝试跑通了MobilNet-SSD网络,而随后又分别用《MobileNets v1模型解析 | Hey~YaHei!》、《SSD框架解析 | Hey~YaHei!》《MobileNet-SSD网络解析 | Hey~YaHei!》三篇文章分别介绍了MobileNet v1、SSD和MobileNet-SSD。

接下来,本文将尝试训练自己的MobileNet-SSD并且部署在Tengine平台上。


安装配置cuda、caffe


cuda的安装网上有非常多的教程,比如《Ubuntu16.04+cuda9.0安装教程 | 贝多芬的悲桑, cnblogs》和《安装cuda-8.0 | 代码小哥, csdn》,过程也很简单,在官网下载你需要的版本对应的.run文件,直接运行按提示安装即可。


caffe由于要使用SSD框架,所以要编译安装caffe的ssd分支——


下载caffe-ssd源码


git clone https://github.com/weiliu89/caffe.git
cd caffe
git checkout ssd


编译caffe-ssd


编译过程可以参照 Caffe | Installation 来进行;


export CAFFE_ROOT=/your/caffe/root/path
# 进入caffe源码的根目录
cd $CAFFE_ROOT
# 从模板拷贝一份编译的配置文件
cp Makefile.config.example Makefile.config
# 按需要修改编译配置
# vim Makefile.config
# 开始编译(参数j表示编译使用的线程数量,一般数值越大越快,取决于你cpu支持的线程数)
make -j8
# 修改环境变量
echo "export PYTHONPATH=$CAFFE_ROOT/python:$PYTHONPATH" >> ~/.bashrc
source ~/.bashrc
# 编译python包
make py
# 编译测试程序
make test -j8
# 测试
make runtest -j8


关于配置文件,一般直接用默认配置就行,


[4] # cuDNN acceleration switch (uncomment to build with cuDNN).
[5] # USE_CUDNN := 1
[7] # CPU-only switch (uncomment to build without GPU support).
[8] # CPU_ONLY := 1
[27] # CUDA directory contains bin/ and lib/ directories that we need.
[28] CUDA_DIR := /usr/local/cuda
[29] # On Ubuntu 14.04, if cuda tools are installed via
[30] # "sudo apt-get install nvidia-cuda-toolkit" then use this instead:
[31] # CUDA_DIR := /usr
[64] # NOTE: this is required only if you will compile the python interface.
[65] # We need to be able to find Python.h and numpy/arrayobject.h.
[66] PYTHON_INCLUDE := /usr/include/python2.7 \
[67]         /usr/lib/python2.7/dist-packages/numpy/core/include
[68] # Anaconda Python distribution is quite popular. Include path:
[69] # Verify anaconda location, sometimes it's in root.
[70] # ANACONDA_HOME := $(HOME)/anaconda2
[71] # PYTHON_INCLUDE := $(ANACONDA_HOME)/include \
[72]         $(ANACONDA_HOME)/include/python2.7 \
[73]         $(ANACONDA_HOME)/lib/python2.7/site-packages/numpy/core/include \
[74] 
[75] # Uncomment to use Python 3 (default is Python 2)
[76] # PYTHON_LIBRARIES := boost_python3 python3.5m
[77] # PYTHON_INCLUDE := /usr/include/python3.5m \
[78] #                 /usr/lib/python3.5/dist-packages/numpy/core/include


编译过程中如果出现 google::protobuf 或者 google::protoc 相关的报错,你可能需要到 google/protobuf | github 下载合适版本的protobuf到本地编译并且配置环境变量(可以用protoc --version指令查看当前使用的protobuf版本)


开始训练MobileNet-SSD


首先,先跑通默认的 MobileNet-SSD——


准备数据集


MobileNet-SSD默认使用Pascal VOC的2007和2012数据集,

下载以下数据集,并解压到同一个目录下:



解压后目录如下图所示:

dataset_tree.png


然后用caffe-ssd提供的 VOC数据集处理工具 对数据集进行处理——


  1. 按实际情况修改并执行脚本$CAFFE_ROOT/data/VOC0712/create_list.sh
    第3行root_dir变量修改为你的VOC数据集目录,比如按照我的目录树,则设置为$HOME/data/VOCdevkit
[1] #!/bin/bash
[2] 
[3] root_dir=$HOME/data/VOCdevkit/
[4] sub_dir=ImageSets/Main
[5] bash_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
  1. 按实际情况修改并执行脚本$CAFFE_ROOT/data/VOC0712/create_data.sh
    第7行data_root_dir变量修改为你的VOC数据集目录,比如按照我的目录树,则设置为$HOME/data/VOCdevkit
[1] cur_dir=$(cd $( dirname ${BASH_SOURCE[0]} ) && pwd )
[2] root_dir=$cur_dir/../..
[3] 
[4] cd $root_dir
[5] 
[6] redo=1
[7] data_root_dir="$HOME/data/VOCdevkit"
[8] dataset_name="VOC0712"
[9] mapfile="$root_dir/data/$dataset_name/labelmap_voc.prototxt"


执行完毕后将会自动在$data_root_dir目录下生成VOC0712子目录,里边包含了从数据集VOC2007和VOC2012提取的图片和标记信息,并构建caffe能够高效读取的lmdb文件。

VOC0712子目录结构如下图所示:

lmdb_tree.png


训练


下载MobileNet-SSD源码:


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


创建数据集软链接:


export TRAINVAL_LMDB=$HOME/data/VOCdevkit/VOC0712/lmdb/VOC0712_trainval_lmdb/
export TEST_LMDB=$HOME/data/VOCdevkit/VOC0712/lmdb/VOC0712_test_lmdb/

cd $CAFFE_ROOT/examples/MobileNet-SSD
ln -s $TRAINVAL_LMDB ./trainval_lmdb
ln -s $TEST_LMDB ./test_lmdb


把VOC的标签映射文件复制过来:


cp $CAFFE_ROOT/data/VOC0712/labelmap_voc.prototxt $CAFFE_ROOT/examples/MobileNet-SSD/labelmap.prototxt


生成模型文件:


./gen_model.sh 21


这里21指的是VOC的21个类别(含负样本),生成的模型文件默认放置在example目录下;


如果需要修改训练参数和测试参数,可以分别修改目录下的solver_train.protxtsovler_test.protxt文件,

默认使用example目录下的训练模型和测试模型;


如果需要指定GPU和初始化权重,可以修改目录下的train.shtest.sh文件,以train.sh为例:


#!/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


weights参数指定初始化的权重文件,这里用了chuanqi305预训练迭代了73000次的模型;

gpu参数指定使用的gpu,多个gpu可以用逗号隔开;

除此之外,如果需要继续之前中断的训练,还可以指定snapshot参数,

比如我想从最近的快照继续训练,可以这样修改train.sh——


#!/bin/sh
latest=$(ls -t snapshot/*.caffemodel | head -n 1)
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" \
-snapshot=$latest \
-gpu 0


部署


合并BN层:

训练后会在snapshot目录下产生一个相应的caffemodel文件;

按实际情况修改merge_bn.py文件并执行:


[ 1] import numpy as np  
[ 2] import sys,os  
[ 3] caffe_root = '/your/caffe/root/path/'
[ 4] sys.path.insert(0, caffe_root + 'python')  
[ 5] import caffe  
[ 6] 
[ 7] train_proto = 'example/MobileNetSSD_train.prototxt'       # 训练时所用的模型文件
[ 8] train_model = 'mobilenet_iter_73000.caffemodel'           # 训练后产生的caffemodel文件
[ 9] 
[10] deploy_proto = 'example/MobileNetSSD_deploy.prototxt'     # 部署时所要用的模型文件(去掉BN层)
[11] save_model = 'MobileNetSSD_deploy.caffemodel'             # 最终生成的caffemodel文件(合并BN层参数)


生成的合并BN层后的caffemodel就在MobileNet-SSD项目的根目录下;

编辑example/MobileNetSSD_deploy.prototxt修改输入层,即把


input: "data"
input_shape {
    dim: 1
    dim: 3
    dim: 300
    dim: 300
}


改为


layer {
    name: "input"
    type: "Input"
    top: "data"
    input_param {
        shape {
            dim: 1
            dim: 3
            dim: 300
            dim: 300
        }
    }
}


example/MobileNetSSD_deploy.prototxtMobileNetSSD_deploy.caffemodel 拷贝到Tengine平台的models目录下,此时运行mobilenet_ssd/MSSD用的就是新训练好的模型啦!


本文简单介绍了如何用chuanqi305的MobileNet-SSD训练出自己的网络。

下一篇文章《基于MobileNet-SSD的目标检测Demo(一) | Hey~YaHei!》将继续尝试根据实际情况删减多余类别进行训练。还可以注意到,