用SSD框架训练自己的数据集

一份非常简单易懂的SSD tensorflow训练教程,初学者也可以看得懂。当初自学的时候走了很多弯路,所以把自己的经验和踩过的坑分享给大家,如果文中有什么写的不对的地方欢迎大家在评论区和我讨论。

https://blog.csdn.net/Echo_Harrington/article/details/81131441原作链接

零、前期准备

安装Tensorflow-GPU版本。

一、SSD框架准备

从https://github.com/balancap/SSD-Tensorflow 下载、解压。

解压checkpoints文件夹中的zip文件。

二、准备自己的数据集

在SSD-Tensorflow-master中创建文件夹: VOC2007,VOCtest

在VOC2007和VOCtest中分别创建:test

在两个test文件夹中分别中创建:Annotations,ImageSets,JPEGImages

其中VOC2007中的Annotations和JPEGImages分别装载训练的标签文件(.xml)和图片文件(.jpg或.jpeg),VOCtes中则装载相对应的测试文件。

可以通过xml_to_txt.py和txt_to_xml.py将label文件转换为txt或xml,python文件下载地址为:

链接: https://pan.baidu.com/s/1H0vOGef6oqrkvty0NHVGgg 密码: tnxm

三、修改代码

(1)SSD-Tensorflow-mastet/datasets/pascalvoc_common.py文件中,24-46行,第一类none不要动,其他的类修改为自己数据集的类。

(2)SSD-Tensorflow-master/datasets/pascalvoc_to_tfrecords.py文件中,82行,格式改为相对应的‘.jpg’或‘.jpeg’,83行‘r’改为‘rb’。

67行,修改SAMPLES_PER_FILES为几张图片转为一个tfrecord文件

(3)SSD-Tensorflow-master/nets/ssd_vgg_300.py文件中,96-97行的num_classes和no_annotation_label改为“类别数+1”

(4)SSD-Tensorflow-master/eval_ssd_network.py 文件中,66行,修改num_classes为“类别数+1”

(5) SSD-Tensorflow-master/datasets/pascalvoc_2007.py文件中,第31行和55行的none类不要动,其他类修改为自己数据集的类,其中括号内的第一个数为图片数,第二个数为目标数(也就是bonding box数,关于如何计算目标数,我写了一个python文件,在附录中),52行和76行的total是所有类的总和。

79和80行的数改为自己数据集的训练和测试集总数,86行的NUM_CLASSES改为自己的类别数(不用加一)

(6)SSD-Tensorflow-master/train_ssd_network.py文件中,修改135行的num_classes为“类别数+1

修改154行的‘None’为最大训练步数(如50000),设为None时训练会一直进行,需要手动停止。同时还可以修改此文件中的batch size,leaning rate等等……

四、生成tfrecord文件

(1)训练的tfrecord。在SSD-Tensorflow-master中创建tf_convert_data.sh文件,写入下面的指令。在终端中用bash tf_convert_data.sh 运行。

DATASET_DIR=./VOC2007/test/
OUTPUT_DIR=./tfrecords
python tf_convert_data.py \
    --dataset_name=pascalvoc \
    --dataset_dir=${DATASET_DIR} \
    --output_name=voc_2007_train \
    --output_dir=${OUTPUT_DIR}

(2)测试的tfrecord。在SSD-Tensorflow-master中创建文件夹:tfrecords_test。修改上述代码为:

DATASET_DIR=./VOCtest/test/           #路径改为测试文件路径
OUTPUT_DIR=./tfrecords_test		#修改路径
python tf_convert_data.py \
    --dataset_name=pascalvoc \
    --dataset_dir=${DATASET_DIR} \
    --output_name=voc_2007_test \	  #注意这里修改为test
    --output_dir=${OUTPUT_DIR}

五、训练

在SSD-Tensorflow-master中创建文件夹log。在SSD-Tensorflow-master中创建train.sh文件,写入下面的指令。其中的数值都可以根据自己的数据集来修改。

DATASET_DIR=./tfrecords
TRAIN_DIR=./log/
CHECKPOINT_PATH=./checkpoints/ssd_300_vgg.ckpt/ssd_300_vgg.ckpt
python train_ssd_network.py \
    --train_dir=${TRAIN_DIR} \
    --dataset_dir=${DATASET_DIR} \
    --dataset_name=pascalvoc_2007 \
    --dataset_split_name=train \
    --model_name=ssd_300_vgg \
    --checkpoint_path=${CHECKPOINT_PATH} \
    --save_summaries_secs=60 \
    --save_interval_secs=600 \
    --weight_decay=0.0005 \
    --optimizer=adam \
    --learning_rate=0.001 \
    --batch_size=6 \
    --ignore_missing_vars = True \
    --num_classes = 21 \ #修改为自己的类别数+1
    --checkpoint_exclude_scopes =ssd_300_vgg/conv6,ssd_300_vgg/conv7,ssd_300_vgg/block8,ssd_300_vgg/block9,ssd_300_vgg/block10,ssd_300_vgg/block11,ssd_300_vgg/block4_box,ssd_300_vgg/block7_box,ssd_300_vgg/block8_box,ssd_300_vgg/block9_box,ssd_300_vgg/block10_box,ssd_300_vgg/block11_box \

最重要的是加上最后一行checkpoint_exclude_scopes。有人在训练中出现如下的报错:

InvalidArgumentError: Assign requires shapes of both tensors to match. lhs shape= [8] rhs shape= [84] 
[[Node: save_1/Assign_4 = Assign[T=DT_FLOAT, _class=["loc:@ssd_300_vgg/block10_box/conv_cls/biases"], use_locking=true, validate_shape=true, _device="/job:localhost/replica:0/task:0/device:CPU:0"](ssd_300_vgg/block10_box/conv_cls/biases, save_1/RestoreV2_4)]]

其中lhs的shape是自己的类别数2*4bounding box的值,rhs shape是checkpoint中的类21*4 bounding box的值。原因就在于这个已经存在的checkpoints是用21个类别来训练的,所以我们要做的就是加上一行checkpoint_exclude_scopes,其目的是舍弃了一些层,我们可以只用这个原始结构中的weights来训练。另一个很关键的因素是要使用Tensorflow-gpu版本而不是cpu版本来训练。

六、测试

在SSD-Tensorflow-master中创建文件夹ssd_eval_log。在SSD-Tensorflow-master中创建test.sh文件,写入指令:

DATASET_DIR= ./test_tfrecords  		    #修改路径为test的tfrecord文件夹
EVAL_DIR=./ssd_eval_log/     			#修改路径
CHECKPOINT_PATH=./log/model.ckpt-0      #改成自己训练的模型
python3 ./eval_ssd_network.py \
        --eval_dir=${EVAL_DIR} \
        --dataset_dir=${DATASET_DIR} \
        --dataset_name=pascalvoc_2007 \
        --dataset_split_name=test \			#这里改为test
        --model_name=ssd_300_vgg \
        --checkpoint_path=${CHECKPOINT_PATH} \
        --batch_size=1 \
	--ignore_missing_vars = True \
        --num_classes = 21 \ 
        --checkpoint_exclude_scopes =ssd_300_vgg/conv6,ssd_300_vgg/conv7,ssd_300_vgg/block8,ssd_300_vgg/block9,ssd_300_vgg/block10,ssd_300_vgg/block11,ssd_300_vgg/block4_box,ssd_300_vgg/block7_box,ssd_300_vgg/block8_box,ssd_300_vgg/block9_box,ssd_300_vgg/block10_box,ssd_300_vgg/block11_box \

这时可能会遇到如下报错:

TypeError: Can not convert a tuple into a Tensor or Operation

这时,需要在SSD-Tensorflow-master/eval_ssd_network.py文件中加入代码:

def flatten(x):
    result = []
    for el in x:
         if isinstance(el, tuple):
               result.extend(flatten(el))
         else:
               result.append(el)
return result

并把318行和337行改为:

eval_op=flatten(list(names_to_updates.values())),

测试完后终端会显示如下信息,其中mAP(mean average precision)就是测试的准确率。

七、显示训练测试结果

在terminal中运行指令jupyter notebook notebooks/ssd_notebook.ipynb,终端会出现如下信息,把网址复制进浏览器打开,出现ssd_notebook界面,点击右上角的trusted按钮。

把ckpt_filename 改为自己训练模型的路径:

把path改为自己测试的image的路径,image_names是包含整个文件夹的图片的list,-4代表查看的是文件夹中倒数第四张图片,修改这个数值可以选择想要查看的第几张图片:

最后显示的结果如下:

八、附录

(1)计算一种类别有多少个bounding box,代码如下:

import re
import os
class1 = 'cat'		#改为自己的类
class2 = 'dog'
annotation_folder = './VOC2007/test/Annotations'		#改为自己标签文件夹的路径
list = os.listdir(annotation_folder)
total_number1 = 0
total_number2 = 0

for i in range(0, len(list)):
	path = os.path.join(annotation_folder,list[i])
	annotation_file = open(annotation_folder + '/' + os.path.basename(path)).read()
	current_number1 = len(re.findall(class1, annotation_file))
	current_number2 = len(re.findall(class2, annotation_file))
	total_number1 += current_number1
	total_number2 += current_number2

print total_number1
print total_number2

(2)用SSD-Tensorflow训练黑白图片(单通道图片)

我这次训的dataset是热成像图片,也就是黑白图片。然而ssd只能训练彩色图片(三通道图片),所以在最后Jupiter notebook上显示训练结果时会报错:

Cannot feed value of shape (240, 3) for Tensor u'Placeholder:0', which has shape ‘(?, ?, 3)’

解决的方法是,先用MATLAB把单通道图像转为三通道的图片,然后再把标签文件(.xml)中的<depth>改为3,.m文件代码如下:

path = 'C:/Users/Administrator/Desktop/train300/';  #改为自己的单通道图像路径
save_path = 'C:/Users/Administrator/Desktop/train/';  #改为保存的路径
img=dir([path,'*.jpg']);

for i=1:length(img)
    I=imread([path,img(i).name]);
    [rows,cols]=size(I);
    r=zeros(rows,cols);
    g=zeros(rows,cols);
    b=zeros(rows,cols);
    r=double(I);
    g=double(I);
    b=double(I);
    rgb=cat(3,r,g,b);
    imwrite(uint8(rgb),[save_path,img(i).name]);
end

(3)GPU内存问题

训练的时候曾经出现了如下的error:

2018-07-19 16:43:02.811877: E tensorflow/stream_executor/cuda/cuda_dnn.cc:385] could not create cudnn handle: CUDNN_STATUS_INTERNAL_ERROR
2018-07-19 16:43:02.811920: F tensorflow/core/kernels/conv_ops.cc:717] Check failed: stream->parent()->GetConvolveAlgorithms( conv_parameters.ShouldIncludeWinogradNonfusedAlgo<T>(), &algorithms)

原本以为是cuda的问题,后来发现是因为和另一个同学同时在训练,所以GPU内存满了(终端输入watch -n 0.1 nvidia-smi 可查看GPU使用信息)。在train_ssd_network.py文件的开头加上如下代码,成功解决问题。

import os
os.environ["CUDA_VISIBLE_DEVICES"] = '1' #后面的数字改为需要进行训练的显卡编号

九、参考

https://blog.csdn.net/w5688414/article/details/78529884

https://blog.csdn.net/liuyan20062010/article/details/78905517

https://www.codetd.com/article/2139154

一份非常简单易懂的SSD tensorflow训练教程,初学者也可以看得懂。当初自学的时候走了很多弯路,所以把自己的经验和踩过的坑分享给大家,如果文中有什么写的不对的地方欢迎大家在评论区和我讨论。

https://blog.csdn.net/Echo_Harrington/article/details/81131441原作链接

零、前期准备

猜你喜欢

转载自blog.csdn.net/zjc910997316/article/details/82995221