上一篇中演示了从0 部署 faster-rcnn 的整个过程,并且以及跑通demo以及train训练过程。https://blog.csdn.net/qq_35306993/article/details/89885671
VOC数据说明
VOCDevkit2007 文件夹为当前项目使用到的数据
Annotations中的xml为每张图片的标记数据,分类名,检测框坐标。文件名对于图片名。
JPEGImages 为所有图片原数据
ImageSets\Main\trainval.txt 为图片名 索引文件。数据加载 图片名从这个文件中读取。
目前只关注这三个文件夹
下载Wider_Face数据
WIDER FACE 图片库,下载地址http://mmlab.ie.cuhk.edu.hk/projects/WIDERFace/
解压数据整理成以下路径形式:
数据转换文件代码在本文最后提供
命令行执行widerface_to_voc.py文件,缺包自行安装。
提示安装skimage 时:pip install scikit-image
运行脚本widerface_to_voc.py需要等待一段时间,复制图片,制作标签等。
脚本运行完毕Wider_face 文件夹下得到三个文件夹就是voc格式
将三个文件夹移动到 VOC2007文件夹下如果这个文件夹下之前有数据需要全部删除
进入到dVOC2007\ImageSets\Main下
var.txt 文件中的内容复制到train.txt中 。创建文件trainval.txt,将train.txt 和var.txt的内容先后复制到trainval.txt中
到此数据已完全符合训练数据格式。
修改data数据源快捷指向:进入项目/目录下
cd data
ln -s /home/dashuai/PycharmProjects/Wider_face_data/ VOCdevkit2007
创建文件夹:(不然test会报错找不到路径)
/home/dashuai/PycharmProjects/wider_face_rcnn/data/VOCdevkit2007/results/VOC2007/Main/ # 做相应修改
开始训练
首先,在tf-faster-rcnn/lib/datasets目录下的pascal_voc.py里第36行更改自己的类别,'background’切记不可删掉,把后面的原来的20个label换成自己的,不用更改类别数目,也没有地方可以更改。
在开始训练之前,还需要把之前训练产生的模型以及cache删除掉,分别在tf-faster-rcnn/output/vgg16/voc_2007_trainval/default路径下和tf-faster-rcnn/data/cache路径下,然后就可以开始训练了:
根目录下执行:
./experiments/scripts/train_faster_rcnn.sh 0 pascal_voc vgg16 # 训练
./experiments/scripts/test_faster_rcnn.sh 0 pascal_voc vgg16 # 利用test数据集进行模型评估,train之后会自动运行test
# 运行脚本指定参数格式
python ./tools/test_net.py --imdb voc_2007_test --model output/vgg16/voc_2007_trainval/default/vgg16_faster_rcnn_iter_300.ckpt --cfg experiments/cfgs/vgg16.yml --net vgg16 --set ANCHOR_SCALES '[8,16,32]' ANCHOR_RATIOS '[0.5,1,2]'
训练阶段NAN的解决:
https://blog.csdn.net/XZZPPP/article/details/52036794
数据清洗
如果上面方法解决不了,请继续使用下面方法,数据清洗:
在VOCdevkit2007/VOC2007/ 目录下建立脚本cl.py文件,代码附在文章最后,运行脚本,会检查Annotations文件夹下面的所有xml文件,检查边界信息,xmin<xmax 等等,影响模型训练的数据,将之标记,会生成file.txt文件。 打开file文件即可看到哪一张图片异常。下一步 将/data/VOCdevkit2007/VOC2007/ImageSets/Main/ 文件夹下的所有txt文件把file.txt中的图片删除。可自行写脚本删除,博主当时异常数据不多,手动删除的。
常用操作文件以及功能介绍:
experiments/cfgs 不同模型的配置文件 batchsize 在这里设置
experiments/logs 日志文件夹,
experiments/scripts 运行脚本sh文件,
lib/model/config 通用配置文件
模型保存和加载:同一个命令运行train 训练时候 会自动加载当前命令以及训练的模型,继续训练。无需其他操作。
修改训练步数:
/home/dashuai/PycharmProjects/wider_face_rcnn/experiments/scripts/train_faster_rcnn.sh
/home/dashuai/PycharmProjects/wider_face_rcnn/experiments/scripts/test_faster_rcnn.sh
找到相关数据的ITERS 参数。train_faster_rcnn 用于训练 test_faster_rcnn用于模型评估 ,test_faster_rcnn中的ITERS用于根据这个步数找到模型,(模型的命名根据步数)。关于评估需要的步数,一般是给的test数据集,一张图片一个步数。
运行test模型评估指定模型:
自己训练了一个模型,但是还没有训练完成,根据out文件夹中保存的模型,看步数。修改test_faster_rcnn.sh中的ITERS参数
./experiments/scripts/test_faster_rcnn.sh 0 pascal_voc vgg16
运行demo文件
自己训练的模型,运行demo输出需要配置和修改:/tools/demo.py
CLASSES 参数留下__background__ 和自己的类比如face
NETS ,修改使用的模型文件,比如vgg的res101的模型,修改根据步数迭代的模型
net.create_architecture 修改类目数量 类目+1 背景
CUDA_VISIBLE_DEVICES=0 ./tools/demo.py --net 'vgg16' --dataset 'pascal_voc' #指定数据和网络
文件代码
from xml.dom.minidom import parse
import xml.dom.minidom
import os
if os.path.exists('file'):
os.remove('file')
list_dir = os.listdir('Annotations')
for i in list_dir:
try:
path = "Annotations/"+i
DOMTree = parse(path)
collection = DOMTree.documentElement
width = int(collection.getElementsByTagName("width")[0].childNodes[0].data)
height = int(collection.getElementsByTagName("height")[0].childNodes[0].data)
boxs = collection.getElementsByTagName('bndbox')
for x in range(len(boxs)): # 检测每一个盒子
xmin = int(boxs[x].getElementsByTagName("xmin")[0].childNodes[0].data)
ymin = int(boxs[x].getElementsByTagName("ymin")[0].childNodes[0].data)
xmax = int(boxs[x].getElementsByTagName("xmax")[0].childNodes[0].data)
ymax = int(boxs[x].getElementsByTagName("ymax")[0].childNodes[0].data)
assert width >0 and height >0 # 长宽大于0
assert xmin >=0 and xmax >=0 and ymax >=0 and ymin >=0 #坐标大于0
assert xmin < xmax and ymin < ymax # 左坐标小于右坐标
assert xmax <= width and ymax <= height # 右边坐标不越界
except:
with open('file', 'a') as f:
f.write(i+'----'+ str(x)+'\r\n')
widerface_to_voc.py
# -*- coding: utf-8 -*-
"""
Created on 19-5-6
@author: 段大帅
"""
from skimage import io
import shutil
import random
import os
import string
headstr = """\
<annotation>
<folder>VOC2007</folder>
<filename>%06d.jpg</filename>
<source>
<database>My Database</database>
<annotation>PASCAL VOC2007</annotation>
<image>flickr</image>
<flickrid>NULL</flickrid>
</source>
<owner>
<flickrid>NULL</flickrid>
<name>company</name>
</owner>
<size>
<width>%d</width>
<height>%d</height>
<depth>%d</depth>
</size>
<segmented>0</segmented>
"""
objstr = """\
<object>
<name>%s</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>%d</xmin>
<ymin>%d</ymin>
<xmax>%d</xmax>
<ymax>%d</ymax>
</bndbox>
</object>
"""
tailstr = '''\
</annotation>
'''
def all_path(filename):
return os.path.join('Wider_face', filename)
def writexml(idx, head, bbxes, tail):
filename = all_path("Annotations/%06d.xml" % (idx))
f = open(filename, "w")
f.write(head)
for bbx in bbxes:
f.write(objstr % ('face', bbx[0], bbx[1], bbx[0] + bbx[2], bbx[1] + bbx[3]))
f.write(tail)
f.close()
def clear_dir():
if shutil.os.path.exists(all_path('Annotations')):
shutil.rmtree(all_path('Annotations'))
if shutil.os.path.exists(all_path('ImageSets')):
shutil.rmtree(all_path('ImageSets'))
if shutil.os.path.exists(all_path('JPEGImages')):
shutil.rmtree(all_path('JPEGImages'))
shutil.os.mkdir(all_path('Annotations'))
shutil.os.makedirs(all_path('ImageSets/Main'))
shutil.os.mkdir(all_path('JPEGImages'))
def excute_datasets(idx, datatype):
f = open(all_path('ImageSets/Main/' + datatype + '.txt'), 'a')
f_bbx = open(all_path('wider_face_split/wider_face_' + datatype + '_bbx_gt.txt'), 'r')
while True:
filename = f_bbx.readline().strip('\n')
if not filename:
break
try:
im = io.imread(all_path('WIDER_' + datatype + '/images/'+filename))
except IOError:
print('错误文件名已跳过,',filename)
continue
head = headstr % (idx, im.shape[1], im.shape[0], im.shape[2])
nums = f_bbx.readline().strip('\n')
bbxes = []
for ind in range(int(nums)):
bbx_info = f_bbx.readline().strip(' \n').split(' ')
bbx = [int(bbx_info[i]) for i in range(len(bbx_info))]
#x1, y1, w, h, blur, expression, illumination, invalid, occlusion, pose
if bbx[7]==0:
bbxes.append(bbx)
writexml(idx, head, bbxes, tailstr)
shutil.copyfile(all_path('WIDER_' + datatype + '/images/'+filename), all_path('JPEGImages/%06d.jpg' % (idx)))
f.write('%06d\n' % (idx))
idx +=1
f.close()
f_bbx.close()
return idx
# 打乱样本
def shuffle_file(filename):
f = open(filename, 'r+')
lines = f.readlines()
random.shuffle(lines)
f.seek(0)
f.truncate()
f.writelines(lines)
f.close()
if __name__ == '__main__':
clear_dir()
idx = 1
idx = excute_datasets(idx, 'train')
idx = excute_datasets(idx, 'val')