SSD-tensorflow使用文档(二)——从数据处理到iOS移植

一、数据转换 1. VOC数据转换成tfrecord格式 1.1. 数据说明

  • tensorflow专用的数据格式为tfrecord.
  • /home/doctorimage/kindlehe/common/dataset是所有数据的存放目录,目录结构如下
coco :  flower : 其中flower_photos包含5个文件夹,分别对应五种花的类型 Oxford-IIT_Pet:宠物的数据集 VOC2007: voc2007数据集 VOCdevkit: voc2012数据集 VOC0712: VOC2007与voc2012合并之后,转成tfrecord数据集后,所在的存放路径

1.2. voc2007与voc2012单独转为tfrecord格式 在目录/home/doctorimage/kindlehe/project/SSD/SSD-Tensorflow-master/shell下运行bash tf_convert_data.sh会执行以下指令生成文件名为voc_2007_trainval_000.tfrecord的tfrecord格式的数据:
DATASET_DIR=../../../../common/dataset/VOC2007/VOCtrainval_06-Nov-2007/VOCdevkit/VOC2007/ OUTPUT_DIR=../../../../common/dataset/VOC2007/VOCtrainval_06-Nov-2007/VOCdevkit/VOC2007_tfrecord/ python ../tf_convert_data.py \#所执行的脚本文件 --dataset_name=pascalvoc \#数据名字,这里不能修改,因为在tf_convert_data.py里被写死了,修改之后会报错 --dataset_dir=${DATASET_DIR} \#输入数据的存放目录 --output_name=voc_2007_trainval \#输出数据名的前缀 --output_dir=${OUTPUT_DIR}#输出数据的存放目录

对于voc2012只需要将输入数据和输出数据的目录更改为voc2012的目录即可,如下所示:
DATASET_DIR=#/home/doctorimage/kindlehe/common/dataset/VOCdevkit/VOC2012/ OUTPUT_DIR=#/home/doctorimage/kindlehe/common/dataset/VOCdevkit/VOC2012_tf python ../tf_convert_data.py \#所执行的脚本文件 --dataset_name=pascalvoc \#数据名字,这里不能修改,因为在tf_convert_data.py里被写死了,修改之后会报错 --dataset_dir=${DATASET_DIR} \#输入数据的存放目录 --output_name=voc_2012_trainval \#这里改成2012 --output_dir=${OUTPUT_DIR}#输出数据的存放目录

转换成功会出现如下提示:
>> Converting image 17125/17125 Finished converting the Pascal VOC dataset!

如何根据不同的GPU内存,==设置每个数据文件包含的图片个数==呢?
pascalvoc_to_tfrecords.py中有一个参数
SAMPLES_PER_FILES = 5011 #控制转出的TFrecord文件的个数:比如一共17125张图,要求每个文件保存5011个图,那么最终输出17125/5011=4个文件/

1.3. voc2007与voc2012合并之后转为tfrecord格式 ssd作者说自己用了voc2007中trainval和voc2012的数据一起训练的,那么如何将这两个数据一次性制作成tfrecord格式的数据呢?
当你生成完voc2007和voc2012后会生成以下文件:
#/home/doctorimage/kindlehe/common/dataset/VOC2007/VOCtrainval_06-Nov-2007/VOCdevkit/VOC2007_tfrecord/目录下生成1个文件: voc_2007_trainval_000.tfrecord #/home/doctorimage/kindlehe/common/dataset/VOCdevkit/VOC2012/目录下生成4个文件 voc_2012_trainval_000.tfrecord voc_2012_trainval_001.tfrecord voc_2012_trainval_002.tfrecord voc_2012_trainval_003.tfrecord

不要怀疑你接下来看到的.没错
合并数据最难的方法是写自动化程序,一键搞定,但是没这个必要,因为有一个最简单的办法,就是把两个文件夹中的数据拷贝到同一个文件夹下,路径是:
/home/doctorimage/kindlehe/common/dataset/VOC0712/
但是,拷贝进去的5个文件,需要把voc_2007_trainval_000.tfrecord改成voc_2012_trainval_004.tfrecord,这时你就明白黄色部分为什么要着重突出了,因为如果单个文件包含的个数太少,生成的文件数量就会太多,改文件夹名字的时候就比较麻烦,当然也可以写自动化脚本批量修改也不是什么难事。==最重要在于增加单个文件包含的图片个数,可以减少内存的IO操作。==
2. 用自己的数据制作成tfrecord格式 2.1 分类任务数据集制作
数据存放目录为/home/doctorimage/kindlehe/project/CSDN/下面的flower_photos文件夹,可以按照这个文件夹的目录结构放入自己的数据
进入目录/home/doctorimage/kindlehe/project/SSD/SSD-Tensorflow-master/slim
,运行bash convert_tfrecord.sh会执行以下命令:
python download_and_convert_data.py --dataset_name=flowers --dataset_dir=/home/doctorimage/kindlehe/project/CSDN/

download_and_convert_data.py最终会调用download_and_convert_flowers去完成文件转换功能,这个文件里面有几个地方要注意一下
L43、L49 _NUM_VALIDATION = 350 #表示验证集的个数,验证集一般占所有图片的10% _NUM_SHARDS = 5 #表示训练集或者测试集生成的tfrecord文件的个数,官方建议单个Tfrecords文件放1023张,生成文件的个数就可以根据总图片的数量计算得到L83: flower_root= os.path.join(dataset_dir,‘flower_photos’)# 如果是制作自己的数据,这一行的flower_photos改成自己的文件名即可L190: dataset_utils.download_and_uncompress_tarball(_DATA_URL, dataset_dir)#如果下载好了,一定要注释掉这一行,否则会坑到自己L210: _clean_up_temporary_files(dataset_dir) #下载压缩包,解压成图片,再转成tfrecords,中间这些图片被认为是临时图片,系统会自动清除

2.1 检测任务数据集制作
主要是要将数据制作成VOC的格式,可以参考自动化工具制作PASCAL VOC 数据集
二、训练及测试 1. 训练 主要的训练过程在《SSD-tensorflow使用文档(一)》中已经详细讲解了,这里补充一些使用细节:
1.1 pyCharm环境配置及调试
要使用pyCharm进行调试,首先要将cuda的环境配置好,每个机器的环境都不一样,可以在linux终端输入vim ./bashrc查看相应的路径。
SSD-tensorflow使用文档(二)——从数据处理到iOS移植
文章图片

SSD-tensorflow使用文档(二)——从数据处理到iOS移植
文章图片

配置好就可以点击run -> debug ‘train_ssd_networ’进行单步调试了
也可以按照《SSD-tensorflow使用文档(一)》直接在终端中,进入ssd根目录下的shell目录,运行bash train_ssd_network.sh
1.2 训练细节
对于train_ssd_network.sh中的命令,通常存在以下几种训练方法:
方案1: 从vgg开始训练其中某些层的参数# 通过加载预训练好的vgg16模型,对“voc07trainval+voc2012”进行训练 # 通过checkpoint_exclude_scopes指定哪些层的参数不需要从vgg16模型里面加载进来 # 通过trainable_scopes指定哪些层的参数是需要训练的,未指定的参数保持不变 DATASET_DIR=/home/doctorimage/kindlehe/common/dataset/VOC0712/ TRAIN_DIR=.././log_files/log_finetune/train_voc0712_20170816_1654_VGG16/ CHECKPOINT_PATH=../checkpoints/vgg_16.ckptpython3 ../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} \#所加载模型的路径 --checkpoint_model_scope=vgg_16 \#所加载模型里面的作用域名 --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 \ --trainable_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 \ --save_summaries_secs=60 \#每60s保存一下日志 --save_interval_secs=600 \#每600s保存一下模型 --weight_decay=0.0005 \#正则化的权值衰减的系数 --optimizer=adam \#选取的最优化函数 --learning_rate=0.001 \#学习率 --learning_rate_decay_factor=0.94 \ #学习率的衰减因子 --batch_size=24 \ --gpu_memory_fraction=0.9#指定占用gpu内存的百分比

方案2: 从自己预训练好的模型开始训练(依然可以指定要训练哪些层)(当你的模型通过vgg训练的模型收敛到大概o.5mAP的时候,可以进行这一步的fine-tune)# 通过加载预训练好的vgg16模型,对“voc07trainval+voc2012”进行训练 # 通过checkpoint_exclude_scopes指定哪些层的参数不需要从vgg16模型里面加载进来 # 通过trainable_scopes指定哪些层的参数是需要训练的,未指定的参数保持不变 DATASET_DIR=/home/doctorimage/kindlehe/common/dataset/VOC0712/ TRAIN_DIR=.././log_files/log_finetune/train_voc0712_20170816_1654_VGG16/ CHECKPOINT_PATH=./log_files/log_finetune/train_voc0712_20170712_1741_VGG16/model.ckpt-253287python3 ../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} \#所加载模型的路径 --checkpoint_model_scope=vgg_16 \#所加载模型里面的作用域名 --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 \ --trainable_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 \ --save_summaries_secs=60 \#每60s保存一下日志 --save_interval_secs=600 \#每600s保存一下模型 --weight_decay=0.0005 \#正则化的权值衰减的系数 --optimizer=adam \#选取的最优化函数 --learning_rate=0.001 \#学习率 --learning_rate_decay_factor=0.94 \ #学习率的衰减因子 --batch_size=24 \ --gpu_memory_fraction=0.9#指定占用gpu内存的百分比

# 方案3:从头开始训练自己的模型 # 注释掉CHECKPOINT_PATH,不提供初始化模型,让模型自己随机初始化权重,从头训练 # 删除checkpoint_exclude_scopes和trainable_scopes,因为是从头开始训练 # CHECKPOINT_PATH=./log_files/log_finetune/train_voc0712_20170712_1741_VGG16/model.ckpt-253287python3 ../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} \ #所加载模型的路径,这里注释掉 --checkpoint_model_scope=vgg_16 \#所加载模型里面的作用域名 --save_summaries_secs=60 \#每60s保存一下日志 --save_interval_secs=600 \#每600s保存一下模型 --weight_decay=0.0005 \#正则化的权值衰减的系数 --optimizer=adam \#选取的最优化函数 --learning_rate=0.00001 \#学习率 --learning_rate_decay_factor=0.94 \ #学习率的衰减因子 --batch_size=32

出现如下错误,一般是保存模型的文件夹下之前已经有了模型,只需要把之前的模型删除,或者将模型保存在一个新的文件夹下就可以了
It was originally created here: ['File "../train_ssd_network.py", line 420, in \n tf.app.run()', 'File "/usr/local/lib/python3.5/dist-packages/tensorflow/python /platform/app.py", line 48, in run\n_sys.exit(main(_sys.argv[:1] + flags_passthrough))', 'File "../train_ssd_network.py", line 416, in main\n sync_optimizer=None)', 'File "/usr/local/lib/python3.5/dist-packages/tensorflow /contrib/slim/python/slim/learning.py", line 655, in train\nready_op = tf_variables.report_uninitialized_variables()', 'File "/usr/local/lib/python3.5 /dist-packages/tensorflow/python/util/tf_should_use.py", line 170, in wrapped\nreturn _add_should_use_warning(fn(*args, **kwargs))', 'File "/usr/local/lib/python3.5/dist-packages/tensorflow/python/util /tf_should_use.py", line 139, in _add_should_use_warning\n wrapped = TFShouldUseWarningWrapper(x)', 'File "/usr/local/lib/python3.5/dist-packages/tensorflow/python/ /tf_should_use.py", line 96, in __init__\n stack = [s.strip() for s in traceback.format_stack()]']

在TRAIN_DIR路径下会产生四中文件:
1. checkpoint :文本文件,包含所有model.ckpt-xxxx,相当于是不同时间节点生成的所有ckpt文件的一个索引。
2. model.ckpt-2124.data-000000-of-000001:模型文件,保存模型的权重
3. model.ckpt-2124.meta: 图文件,保存模型的网络图
4. model.ckpt-2124.index : 这个没搞太清楚
5. graph.pbtxt: 用protobuf格式保存的模型的图
2. 测试
EVAL_DIR=../log_files/log_eval/train_voc0712_20170712_1741_VGG16/ CHECKPOINT_PATH=.././log_files/log_finetune/train_voc0712_20170712_1741_VGG16/model.ckpt-197442 python3 ../eval_ssd_network.py \ --eval_dir=${EVAL_DIR} \ --dataset_dir=${DATASET_DIR} \ --dataset_name=pascalvoc_2007 \ --dataset_split_name=test \ --model_name=ssd_300_vgg \ --checkpoint_path=${CHECKPOINT_PATH} \ --batch_size=64\ --gpu_memory_fraction=0.8

3. 查看络结构张量 打开/home/doctorimage/kindlehe/project/SSD/SSD-Tensorflow-master/shell,运行bash inspect_checkpoint.sh,会执行:
#!/usr/bin/env bashpython inspect_checkpoint.py \ --file_name=log_files/log_finetune/train_voc0712_20170703_1104_pb/model.ckpt-3069

输出结果如下:
eta1_power (DT_FLOAT) [] beta2_power (DT_FLOAT) [] global_step (DT_INT64) [] ssd_300_vgg/block10/conv1x1/biases (DT_FLOAT) [128] ssd_300_vgg/block10/conv1x1/biases/Adam (DT_FLOAT) [128] ssd_300_vgg/block10/conv1x1/biases/Adam_1 (DT_FLOAT) [128] ssd_300_vgg/block10/conv1x1/weights (DT_FLOAT) [1,1,256,128] ssd_300_vgg/block10/conv1x1/weights/Adam (DT_FLOAT) [1,1,256,128] ssd_300_vgg/block10/conv1x1/weights/Adam_1 (DT_FLOAT) [1,1,256,128] ......(此处省略1万字) ssd_300_vgg/conv7/biases (DT_FLOAT) [1024] ssd_300_vgg/conv7/biases/Adam (DT_FLOAT) [1024] ssd_300_vgg/conv7/biases/Adam_1 (DT_FLOAT) [1024] ssd_300_vgg/conv7/weights (DT_FLOAT) [1,1,1024,1024] ssd_300_vgg/conv7/weights/Adam (DT_FLOAT) [1,1,1024,1024] ssd_300_vgg/conv7/weights/Adam_1 (DT_FLOAT) [1,1,1024,1024]

三、模型导出 1. 生成ssd模型专用的pb文件 这里的pb文件不同于之前的model.ckpt-2124.meta文件,这个pb文件是一个graph, 它保存了ssd模型图的结构,不包含全权重信息,因此相比 .ckpt 模型文件,它的文件体积小。
打开/home/doctorimage/kindlehe/project/SSD/SSD-Tensorflow-master/shell,运行bash export.sh,会执行:
# 代码参考slim库做出修改:/home/doctorimage/kindlehe/project/models/slim/export_inference_graph.pypython -u ../export_inference_graph.py \ --model_name=ssd_300_vgg \ --output_file=../log_files/log_finetune/train_voc0712_20170703_1104_pb/train_voc0712_20170703_1104.pb \ --dataset_name=pascalvoc_2007 \ --dataset_dir=/home/doctorimage/kindlehe/common/dataset/VOC0712/#指定用那些数据训练的模型,因为分类数和labels要从这里面取

如何修改自己的模型并生成对应的pb文件呢?
a. --model_name=ssd_300_vgg意思是export_inference_graph.py最终会进入/home/doctorimage/kindlehe/project/SSD/SSD-Tensorflow-master/nets目录,根据ssd_vgg_300.py里面定义好的模型,去生成对应的====pb文件====。所以想修改网络结构,只需要在ssd_vgg_300.py这里修改,然后进行在训练的时候把model_name改成你自己设置的名字即可。运行export.sh脚本即可生成修改的模型所对应的pb文件。
【SSD-tensorflow使用文档(二)——从数据处理到iOS移植】b. --dataset_dir这个参数用来指定章节“VOC数据转换成tfrecord格式”中生成的tfrecord文件及标签所在的路径,因为在生成pb文件的时候,会根据数据确定”分类类别的数目”并提取”label”.
c. 注意,这里生成的pb文件如果未设置tf.train.write_graph(,,as_text=False)当中as_text=true,那么会以二进制格式保存,在接下来的feeeze_graph.py固化操作中,应该加上一个参数--input_binary=true
2. 模型固化 2.1 模型固化小工具freeze_graph.py
实际上使用训练得到模型时,需要把权重固定,不然每次测试一张图片就相当于继续训练模型,权重也要重新变化一次,这会导致测试一张图片需要很久的时间。在固定权重的基础上,还需要集成图的定义。这个过程就是 freeze graph 。
这里freeze_graph.py的作用就是将上一个步骤生成的pb图文件和训练得到的ckpt参数文件固化到一个文件中,将变量参数类型替换成常量参数类型,模型大小从 降到
打开/home/doctorimage/kindlehe/project/SSD/SSD-Tensorflow-master/shell,运行bash export.sh,会执行:
#!/usr/bin/env bashpython ../freeze_graph.py \ --input_binary=true\ --input_graph=../log_files/log_finetune/train_voc0712_20170703_1104_pb/train_voc0712_20170703_1104.pb \ --input_checkpoint=../log_files/log_finetune/train_voc0712_20170703_1104_pb/model.ckpt-3069 \ --output_graph= ../log_files/log_finetune/train_voc0712_20170703_1104_pb/frozen_train_voc0712_20170703_1104.pb \ --output_node_names=output/output

参数说明 --input_binary=true :参见freeze_graph.py的bug清单 --input_graph: 模型的图的定义文件 train_voc0712_20170703_1104.pb(不包含权重); --input_checkpoint: 模型的参数文件 model.ckpt-3069; --output_graph: 绑定后包含参数的图模型文件 frozen_train_voc0712_20170703_1104.pb; -- output_node_names: 输出待计算的tensor名字【重要】;

《freeze_graph.py的bug清单》
发现tensorflow不同版本下运行freeze_graph.py 脚本时可能遇到的Bug挺多的,列举一下:
# Bug1: google.protobuf.text_format.ParseError: 2:1 : Message type "tensorflow.GraphDef" has no field named "J". # 原因: tf.train.write_graph(,,as_text=False) 之前写出的模型文件是Binary时, # 读入文件格式应该对应之前设置参数 python freeze_graph.py [***] --input_binary=true, # 如果as_text=True则可以忽略,因为默认值 --input_binary=false。 # 参考: https://github.com/tensorflow/tensorflow/issues/5780# Bug2: Input checkpoint '...' doesn't exist! # 原因: 可能是命令行用了 --input_checkpoint=data.ckpt , # 运行 freeze_graph.py 脚本,要在路径参数前加上 "./" 貌似才能正确识别路径。 # 如文件的路径--input_checkpoint=data.ckpt变为 --input_checkpoint=./data.ckpt # 参考: http://www.it1me.seriousdigitalmedia.com/it-answers?id=42439233&ttl=How+to+use+freeze_graph.py+tool+in+TensorFlow+v1# Bug3: google.protobuf.text_format.ParseError: 2:1 : Expected identifier or number. # 原因: --input_checkpoint 需要找到 .ckpt.data-000*** 和 .ckpt.meta等多个文件, # 因为在 --input_checkpoint 参数只需要添加 ckpt的前缀, 如: nn_model.ckpt,而不是完整的路径nn_model.ckpt.data-000*** # .meta.index .datacheckpoint 4个文件# Bug4: # you need to use a different restore operator? # tensorflow.python.framework.errors_impl.DataLossError: Unable to open table file ./pos.ckpt.data-00000-of-00001: Data loss: not an sstable (bad magic number): perhaps your file is in a different file format and you need to use a different restore operator? # Saver 保存的文件用格式V2,解决方法更新tensorflow....# 欢迎补充

2.2 查看output_node_names小工具
==如何确定一个模型的output_node_names呢?==
如果不指定,会自动选择节点名
训练模型的时候,会生成一个graph.pbtxt文件,里面包含了所有的节点名,阅读ssd_300_vgg.py文件里的网络结构,找到希望输出的节点即可。对于ssd,我们希望输出预测框的位置,节点为:
打开/home/doctorimage/kindlehe/project/SSD/SSD-Tensorflow-master/shell,运行bash print_node_name.sh,根据上一个步骤生成的train_voc0712_20170703_1104.pb文件,输出ssd网络结构实际用到的所有节点,保存到save_dir目录下
#!/usr/bin/env bashpython ../print_node_name.py\ --graph_file=../log_files/log_finetune/train_voc0712_20170703_1104_pb/train_voc0712_20170703_1104.pb \ --save_dir=../log_files/log_finetune/train_voc0712_20170703_1104_pb/

3. 固化、简化、量化模型 参考《kindle教你手把手跑通ios-tensorflow版SSD模型(三)—— 模型裁剪》
四、iOS怒编译tensorflow 参考《kindle教你手把手跑通ios-tensorflow版SSD模型(一)—— 编译ios-tensorflow》
五、iOS怒配置Xcode路径 参考《kindle教你手把手跑通ios-tensorflow版SSD模型(二)—— 配置Xcode》

    推荐阅读