安装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