参考https://blog.csdn.net/zhang_shuai12/article/details/52346878;
https://blog.csdn.net/jiangyanting2011/article/details/78873199
前提
首先把Caffe的环境搭建好,要求能正常运行官方的例子:
# $CaffeRoot路径下,首先下载官方数据集范例
cd $HOME/data
wget http://host.robots.ox.ac.uk/pascal/VOC/voc2012/VOCtrainval_11-May-2012.tar
wget http://host.robots.ox.ac.uk/pascal/VOC/voc2007/VOCtrainval_06-Nov-2007.tar
wget http://host.robots.ox.ac.uk/pascal/VOC/voc2007/VOCtest_06-Nov-2007.tar
# Extract the data.就能获得.jpg文件和.xml的标记文件。
tar -xvf VOCtrainval_11-May-2012.tar
tar -xvf VOCtrainval_06-Nov-2007.tar
tar -xvf VOCtest_06-Nov-2007.tar
....
# 就能生成LMDB文件
cd $CAFFE_ROOT
./data/VOC0712/create_list.sh
./data/VOC0712/create_data.sh
# 接下来就能进行训练了
python examples/ssd/ssd_pascal.py
也就是说主要的流程为
原始图片jpg(JPEGImages) 》 标记文件xml(Annotations) 》 文件清单txt(ImageSets/Main) 》 Caffe可读取文件LMDB 》训练生成weights模型caffemodel
接下来按各步骤依次分析。
标记
标记也就是在图片上提供2个点坐标确定一个矩形框及矩形框内的类别,图片内可有多个矩形框(也就是可有多个类)。你可以自己写代码来标记,opencv也不难,但一般使用现成标记工具:
- LabelImg
- BBox-Label-Tool
操作上LabelImg更方便,且能同时标记多个类,故以LabelImg为例进行讲解(安装方式参考官网)。
先建立VOC结构文件夹
先参照VOC2007/ VOC2012建立文件夹结构,在$HOME/data/VOCdevkit/
路径下建立你自己的项目文件夹,假设命名为indoor
,这样路径就为$HOME/data/VOCdevkit/indoor/
。再在indoor文件夹下建立如下文件夹结构:
indoor
├── Annotations
├── ImageSets
│ └── Main
└── JPEGImages
数据集原始jpg图片放在JPEGImages文件夹下。
设置标定路径
进入LabelImg
的源文件下输入python labelImg.py
,就能打开界面,分别点击左侧按钮设置
- Open Dir,设置 数据集原始jpg图片路径,一般选JPEGImages
- Change Save Dir,设置 生成的标定xml文件路径,一般选Annotations
标定
非常简单,就是鼠标点击即可完成。注意有快捷键可加快工作效率:
Ctrl + u | Load all of the images from a directory |
---|---|
Ctrl + r | Change the default annotation target dir |
Ctrl + s | Save |
Ctrl + d | Copy the current label and rect box |
Space | Flag the current image as verified |
w | Create a rect box |
d | Next image |
a | Previous image |
del | Delete the selected rect box |
Ctrl++ | Zoom in |
Ctrl– | Zoom out |
↑→↓← | Keyboard arrows to move selected rect box |
标定完后就会在Annotations路径下看到图片同名的xml文件了。
生成清单
参考:https://blog.csdn.net/u014696921/article/details/53353896
以上的2个标记工具都没有生成清单的功能,得你自己编程实现。
在your doc
路径下新建genMainList.py文件, 后文假设your doc
就是indoor
。
[your doc]
├── genMainList.py
├── Annotations
├── ImageSets
│ └── Main
└── JPEGImages
改写的相关代码为
import os
import sys
import random
CURDIR = os.path.dirname(os.path.realpath(__file__))
dirImg=os.listdir("{}/JPEGImages".format(CURDIR))
print(dirImg)
try:
start = 0
end = len(dirImg)
allNum = end-start
test = int(allNum * 0.25)
except:
print 'Please input test proportion'
print './genMainList.py [test proportion]'
os._exit(0)
b_list = range(start,end)
blist_webId = random.sample(b_list, test )
blist_webId = sorted(blist_webId)
allFile = []
testFile = open('ImageSets/Main/test.txt', 'w')
trainFile = open('ImageSets/Main/trainval.txt', 'w')
for i in b_list:
allFile.append(i)
# generate test.txt
for test in blist_webId:
allFile.remove(test)
filename=str(dirImg[test])
filenameIndex=filename.rfind('.')
filename=filename[:filenameIndex]
testFile.write(filename + '\n')
# generate trainval.txt
for train in allFile:
filename=str(dirImg[train])
filenameIndex=filename.rfind('.')
filename=filename[:filenameIndex]
trainFile.write(filename + '\n')
testFile.close()
trainFile.close()
然后在 该路径下的控制台输入python genMainList.py
,就能在ImageSets/Main
自动生成训练用的清单文件trainval.txt
和test.txt
。
生成caffe可食用文件LMDB
为啥LMDB?没办法,人家二进制文件,读取速度快啊。
修改自己脚本
进入Caffe的源文件夹,也是参照VOC建立文件夹结构,假设你的项目名称为indoor
。
caffe-ssd
├── data
│ ├── ...
│ ├── indoor...............你需要新建的文件夹
│ └── VOC0712
├── examples
│ ├── ...
│ ├── indoor...............自动生成的lmdb文件链接
│ ├── ssd
│ │ │── ...
│ │ └── ssd_pascal.py
│ └── VOC0712
├── models
│ ├── ...
... └── VGGNet
└── VOC0712...........最后存放训练好的caffemodel模型
在源文件夹$CAFFE_ROOT/data下新建indoor文件夹,然后把同一路径下的VOC0712文件夹中的
- create_list.sh…………………..读取上步生成的清单
- create_data.sh…………………..生成lmdb文件
- labelmap_voc.prototxt…………….定义标定种类的文件
3个文件拷贝到你新建的indoor文件夹中,然后依次修改它们。
修改create_list.sh
只用改一处,第13行把
for name in VOC2007 VOC2012
改为
for name in indoor
其实你搜索关键词VOC
就能找到了。
修改create_data.sh
只用改一处,第8行把
dataset_name="VOC0712"
改为
dataset_name="indoor"
同样,搜索关键词VOC
就能找到了。
修改labelmap_voc.prototxt
这个文件夹里面定义你标定的种类。
其格式为
item {
name: "none_of_the_above"
label: 0
display_name: "background"
}
item {
name: "face"
label: 1
display_name: "face"
}
注意其中的label: 0
是指背景,不用改。也就是你的种类从label:1
开始,假设我这里的种类是face
。
那么总共的种类数量就是2了(后续改examples/ssd/ssd_pascal_indoor.py
用得着)。
开始生成
进入Caffe源码根目录直接运行你定义的.sh文件
cd $CAFFE_ROOT
./data/indoor/create_list.sh
./data/indoor/create_data.sh
这样就会在$HOME/data/VOCdevkit/indoor/lmdb
(你原来定义的源图像数据文件夹路径下)生成对应的**_lmdb
的二进制文件,同时也会在$CAFFE_ROOT/examples/indoor/
下建立相对应的链接。
训练生成caffemodel
Caffe的神经网络训练过程中会用到新的2类文件:
- 神经网络结构是通过.prototxt定义
- 预训练好的模型.caffemodel,其实就是网络结构上各weights/bias的值。
下载预训练好的caffemodel(可选)
参考:https://blog.csdn.net/Jesse_Mx/article/details/74011886
SSD的网络结构是基于VGGNet的,相当于在VGGNet的基础上再新建一些层。重头开始训练未免太费时间,这时可以 迁移学习,从网上直接下载已训练好的VGGNet网络 (在外网上,内网上可用此链接),然后将其解压放至models/VGGNet/VGG_ILSVRC_16_layers_fc_reduced.caffemodel
。相应的路径对应于ssd_pascal_indoor.py
中的pretrain_model(261行)
部分。
这样,接下来只需训练SSD新增的那几层就行了。
修改脚本
在$CAFFE_ROOT/examples/ssd/
文件夹下复制ssd_pascal.py
并重命名为ssd_pascal_indoor.py
。
caffe-ssd
├── data
├── examples
│ ├── ...
│ ├── indoor
│ ├── ssd
│ │ │── ...
│ │ │── ssd_pascal.py
│ │ └── ssd_pascal_indoor.py
... ...
然后开始修改ssd_pascal_indoor.py
:
...
# The database file for training data. Created by data/indoor/create_data.sh
train_data = "/home/[yourname]/data/VOCdevkit/indoor/lmdb/indoor_trainval_lmdb" #81行
# The database file for testing data. Created by data/indoor/create_data.sh
test_data = "/home/[yourname]/data/VOCdevkit/indoor/lmdb/indoor_test_lmdb" #84行
...
# A learning rate for batch_size = 1, num_gpus = 1. change into 0.001
base_lr = 0.00004 #232行,修改学习率
...
# The name of the model. Modify it if you want.
model_name = "VGG_indoor_{}".format(job_name) #237行,将VOC0712改为indoor
# Directory which stores the model .prototxt file
save_dir = "models/VGGNet/indoor/{}".format(job_name) #240行,同上
# Directory which stores the snapshot of models
snapshot_dir = "models/VGGNet/indoor/{}".format(job_name) #242行,同上
# Directory which stores the job script and log file.
job_dir = "jobs/VGGNet/indoor/{}".format(job_name) #244行,同上
# Directory which stores the detection results.
output_result_dir = "{}/data/VOCdevkit/results/indoor/{}/Main".format(os.environ['HOME'], job_name) #246行,同上
...
# Stores the test image names and sizes. Created by data/indoor/create_list.sh
name_size_file = "data/indoor/test_name_size.txt" #259行
...
# Stores LabelMapItem.
label_map_file = "data/indoor/labelmap_voc.prototxt" #263行
...
# MultiBoxLoss parameters.
num_classes = 2 #266行,这里改成你需要训练的识别类别数量(包括背景类)
...
# Defining which GPUs to use.
gpus = "0" #332行,GPU数量改成适合你电脑的
...
# Divide the mini-batch to different GPUs.
batch_size = 32 #338-339行,根据你电脑的GPU性能合理设置batch尺寸
accum_batch_size = 32
...
# Evaluate on whole test set.
num_test_image = 32 #360-361行,设置测试数据batch尺寸
test_batch_size = 8
...
prior_variance=prior_variance, kernel_size=3, pad=1, lr_mult=lr_mult,conf_postfix='_indoor') #445行,加上conf_postfix
...
prior_variance=prior_variance, kernel_size=3, pad=1, lr_mult=lr_mult,conf_postfix='_indoor') #474行,加上conf_postfix
...
训练
回到$CAFFE_ROOT
下,运行python examples/ssd/ssd_pascal_indoor.py
。
它将会在$CAFFE_ROOT/models/VGGNet/indoor/SSD_300x300
(上面237-263行设置,就与传统的/models/VOC0712/文件夹分开了)生成prototxt/ caffemodel/ solverstate三类文件。
部署使用
如果要部署使用的话,神经网络结构可用deploy.prototxt
,生成的模型可用VGG_VOC0712_SSD_300x300_iter_[训练迭代次数].caffemodel
,一般地取[迭代次数]最大的caffemodel。
这样就得到了自己图像训练集训练的最终结果:
- deploy.prototxt
- VGG_VOC0712_SSD_300x300_iter_[训练迭代次数].caffemodel
注意在部署使用用过程中,deploy.prototxt,还需要更改一个小细节:
...
save_output_param {
# output_directory: "/home/tage/data/VOCdevkit/results/indoor/SSD_300x300/Main"
# output_name_prefix: "comp4_det_test_"
# output_format: "VOC"
# label_map_file: "data/indoor/labelmap_voc.prototxt"
# name_size_file: "data/indoor/test_name_size.txt"
# num_test_image: 32
}
...
将最后(1616行左右)的save_output_param
内的参数都注释了,这些参数只与训练有关。