训练MobileNet-SSD
《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把仓库克隆到本地并切换到ssd分支
git clone https://github.com/weiliu89/caffe.git cd caffe git checkout ssd
- github服务器在海外,网络不是很稳定,你可以试着挂vpn下载源码。
如果你的linux没有配置vpn,但windows或mac有,那也可以直接在 weiliu89/caffe at ssd | github 上下载zip压缩包,再拷贝到linux用unzip
指令解压; - 如果你没有vpn,也可以到我的网盘上下载:blog-share/mobilenet_ssd/caffe-ssd.zip | 百度网盘
编译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
关于配置文件,一般直接用默认配置就行,
- 如果你想用cudnn加速而不是caffe自己提供的加速库(caffe不建议用cudnn),给第5行解除注释。
[4] # cuDNN acceleration switch (uncomment to build with cuDNN). [5] # USE_CUDNN := 1
- 如果你不想用gpu,给第8行解除注释,
[7] # CPU-only switch (uncomment to build without GPU support). [8] # CPU_ONLY := 1
- 如果你的cuda没有安装在默认位置,你可能需要在第28行修改变量
CUDA_DIR
[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
- 如果你用的是anaconda,或者你要用python3接口,你可能需要修改
[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数据集,
下载以下数据集,并解压到同一个目录下:
- VOC2007 - training/validation data
- VOC2007 - test data
- VOC2012 - training/validation data
- VOC2012 - test data
解压后目录如下图所示:
然后用caffe-ssd提供的 VOC数据集处理工具 对数据集进行处理——
- 按实际情况修改并执行脚本
$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)"
- 按实际情况修改并执行脚本
$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
子目录结构如下图所示:
训练
下载MobileNet-SSD源码:
- 直接用git克隆仓库到examples目录下
cd $CAFFE_ROOT/examples git clone https://github.com/chuanqi305/MobileNet-SSD.git
- 或者到我的网盘上下载并解压到caffe的examples目录下:blog-share/mobilenet_ssd/MobileNet-SSD.zip | 百度网盘
创建数据集软链接:
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.protxt
和sovler_test.protxt
文件,
默认使用example
目录下的训练模型和测试模型;
如果需要指定GPU和初始化权重,可以修改目录下的train.sh
或test.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.prototxt
和 MobileNetSSD_deploy.caffemodel
拷贝到Tengine平台的models
目录下,此时运行mobilenet_ssd/MSSD
用的就是新训练好的模型啦!
本文简单介绍了如何用chuanqi305的MobileNet-SSD训练出自己的网络。
下一篇文章《基于MobileNet-SSD的目标检测Demo(一) | Hey~YaHei!》将继续尝试根据实际情况删减多余类别进行训练。还可以注意到,
- chuanqi305的MobileNet-SSD模型除了基础网络部分之外依旧保守的使用了Standard Conv,可以尝试将这一部分也改造为Depthwise Conv;
- 同时,MobileNet-SSD使用带group的caffe原生Conv来进行Depthwise Conv操作,这是非常低效率的,下篇文章还将进一步比较Depthwise Conv和带group的原生Conv的效率。