【Tensorflow】【Python】训练自己的数据集——数据读取、处理、训练、测试、可视化、Debug(单机单卡、单机多卡、多机多卡)

Github代码地址:https://github.com/HandsomeHans/Tensorflow-preprocessing-training-testing

所有代码去Github上拿吧,文件名对应好。

TF版本至少1.4.0。

MonitoredTrainingSession在网上的资料十分稀少,研究了很久官方API和源码

2018.04.13更新 SSD单机训练+测试代码:https://github.com/HandsomeHans/Tensorflow-SSD

2018.03.16更新 增加可视化代码

2018.03.19更新 增加debug调试代码

2018.03.20更新 增加去均值,归一化代码

2018.03.21更新 增加mobilenet和mobilenet v2网络模型

2018.03.22更新 增加resnet-50, resnet-101, resnet-152, resnet-200网络模型

                           解决了一运行就将GPU内存占满的问题(只在单机好用,分布式环境不行)

2018.03.23更新 选择GPU

2018.03.26更新 添加了validation阶段,可以在tensorboard上同时监控train和val阶段的loss

2018.03.29更新 增加了finetune功能

2018.04.02更新 优化了可视化效果,重新设置了计算step的方法。

2018.04.09更新 在分布式环境下增加了同步异步训练开关,重写了MonitoredTrainingSession

2018.04.10更新 用global step替换了自定义的i或者step,解决了log输出不匹配问题

2018.04.11更新 解决了异步训练记录最后一次event失败的问题

前言

新业务要做分布式集群,以前一直用的caffe,现在得用Tensorflow。

网上几乎全是照搬官方demo,没有详细介绍从数据读取处理到训练到测试的文章。我来写一个。

一、准备数据集

以前一直用的caffe,数据集都分类放到不同目录里面的。我这里放出两个脚本,各自功能看解释。

脚本1(img2bin.py)不建议用这个,没后续更新:

classes是images目录下各类数据集目录名

img=img.resize((227,227)) 要根据网络结构选择resize尺寸

这个脚本和images目录在同一级,images目录下有三个目录“0”,“1”,“2”,分别放了三个类别的所有图片数据集。最后生成名字为“train.tfrecords”的二进制文件。

脚本2(list2bin.py):

最后会打印一组bgr三通道均值,记录训练集的均值,放到arg_parsing.py中,用于去均值操作。

一定要转换成RGB三通道,否则后面数据读取会出问题。

以前用caffe 的时候,习惯先生成一个txt文档(格式如下),然后根据文档生成lmdb数据集。


脚本2就是根据这个txt文档生成tfrecords二进制数据集的。

生成txt文档的脚本的博客地址(就是Github中的img2list.sh):http://blog.csdn.net/renhanchi/article/details/72457630

代码是准备数据集里面生成train.txt和val.txt的.sh脚本

我写第二个脚本就是习惯了训练阶段根据val的输出判断训练过程是否正确。不过后来因为一些原因,在训练阶段做不了val。需要继续研究-0-。


二、配置准备阶段

拿到上面生成的.tfrecords二进制数据集,就可以开始布置环境进行训练了。

代码比较多,解释我都放到代码里面吧,这样看起来方便点。说实话很多tf的api我也没搞懂,现在只是跑起来了。后面还要花大量时间去学习研究。

文档1(arg_parsing.py):

这个文档主要用来设置运行输入参数的,很好理解。可以在代码中修改大写字符后面的默认参数,也可以在命令行指定参数。

文档2(dataset.py):

读取数据,去均值,归一化,整合成一batch输入网络

文档3(main.py):

在命令行要执行的文档,内容很简单。

if tf.gfile.Exists(FLAGS.model_dir): #注释的三句话是清空models目录,如果没这个目录就创建。为了防止误操作,我注释了这三句话

文档4(net/squeezenet.py):

网络结构文档,单独写出来,以后换网络结构就方便了。

我的是squeezenet网络,官方demo里只能处理32*32数据集,我给改了,现在这个网络可以处理正常227*227的了。

network.py 看看就好,后续被我抛弃停止更新了。

文档5(test.py):

用于test的文档。

文档6(train.py)

训练文档,里面有三种训练方法。写的最累的文档了,糟心~~~。

train():#单机单卡+单机多卡通用训练方法。
train_dis_():#可以用的分布式多机多卡。

关于train.py我要多说几句。

1.关于session,普遍都用tf.Session()。我这里用的tf.train.MonitoredTrainingSession(),是看的官方api推荐和cifar-10 demo里这么用的。这个接口中hook这一块很方便,可以灵活定义和使用一些功能。在官网API文档中,几乎所有hook都可以用在这里。比如debug,summary等等,值得研究。(2018.03.23 通过这个MTS管理器很难加入validation阶段,如果是通过共享参数来validation,问题主要是global_step冲突。如果通过读取本地ckpt做,保存ckpt的频率和validation频率很难平衡。

2.我不了解为什么一训练起来所有GPU的内存就会马上被占满,设置batch_ size为1还是32还是64结果都一样。所以一运行val就会报GPU内存不足,过一会机器就卡死了 -0-。这个问题以后慢慢解决吧~

3.第三个train_dis()方法,可以控制同步还是异步,不过有点问题,着急跑通流程就没管了,以后有时间再看。

4.第二个train_dis_()是可以使用的分布式多机多卡方法,我判断应该是异步的。

5.可以通过CUDA_VISIBLE_DEVICES=0,1 这句话来控制使用某个GPU。或者在main.py中加入下面一句话设置。


三、布置训练阶段

第一种情况(单机单卡,单机多卡):

设置好main.py中调用的train的方法和路径,直接运行:

python main.py

默认就是训练模式


第二种情况(多机多卡):

只要在命令行指定了--job_name,自动就会运行分布式训练。

先将所有文档和数据集分别分发到各个服务器当中去。

比如我现在只有两台服务器10.100.1.151和10.100.1.120

我现在是要把151服务器同时作为ps和worker

把120服务器作为worker

需要先在arg_parsing.py中设置好ps_hosts和worker_hosts,用逗号间隔开,不要有空格。

然后需要现在151服务器运行:

CUDA_VISIBLE_DEVICES='' python src/main.py --job_name=ps --task_index=0

这里说一下CUDA_VISIBLE_DEVICES=''表示不使用GPU,因为作为参数服务器,可以用CPU处理的。

接下来,继续在151服务器运行:

CUDA_VISIBLE_DEVICES=0 python src/main.py --job_name=worker --task_index=0

最后在120服务器运行:

CUDA_VISIBLE_DEVICES=0 python src/main.py --job_name=worker --task_index=1

可以通过CUDA_VISIBLE_DEVICES这句话来设置需要启用的GPU,序列用逗号隔开。

四、测试

特别提一下多机多卡只会在index为0的主机上保存ckpt。

准备好测试集的.tfrecords,配置好路径,在命令行运行:

python main.py --mode=testing

必须指定测试模式。


后记

命令行参数可以研究一下,虽然很多,但都很简单。

后续我会持续更新。。。

1.同步异步(2018.04.09 搞定)

2.去均值,归一化(2018.03.20搞定)

3.可视化(2018.03.16搞定)

4.debug(2018.03.19搞定)

5.validation (2018.03.26搞定)

6.关于显存一直被占满的问题(2018.03.22 搞定)

7.准备更多网络模型文档 (2018.03.19 mobilenet,2018.03.22 resnet)

8.选GPU(2018.03.19 搞定)

9.优化可视化的网络结构图(2018.04.02 搞定)

10.Finetune(2018.03.29 搞定)

11.新问题,网上没找到解决办法。异步分布式训练完成后,最后一次记录event会失败。是因为当index=0主机run_context.request_stop()时,其他机器可能在validation阶段导致的。(2018.04.11 搞定)


12.新问题,分布式同步训练环境下,经过validation阶段后global step和i出现不对应问题。具体看图。

index=0机器:

【Tensorflow】【Python】训练自己的数据集——数据读取、处理、训练、测试、可视化、Debug(单机单卡、单机多卡、多机多卡)_第1张图片

index=1机器(990step卡住,等待0机器validation结束。):


我想到的解决办法是把log和validation写成hook加到MonitoredTrainingSession中去,不过尝试后发现问题并没有得到解决。(2018.04.10 解决)

这个也和下面问题有关系:

到validation这一step的时候,性能不同的机器会不同步进行validation。

顺序是:当到达validation step时,性能好的机器先进行validation,等完事后,性能差的机器才会进行validation。

这会增加整体训练的耗时,具体原因我观察是:如果设置validation step为1000的话,当sess run 1001 step 时,1000 step的参数才会传给ps机器更新后传回worker机器然后继续1001step 训练。(不知道你们能读懂不-0-,MonitoredTrainingSession这种会话每次运行时其实都是分三步,begin,before run 和after run。参数传递给ps机器,再返回给worker机器应该都是在before这一步进行的。)(2018.04.02 重写MonitoredTrainingSession后,只在index=0机器上进行validation)

每次同步模式训练完,index 0机器会卡主一会,然后报错:

【Tensorflow】【Python】训练自己的数据集——数据读取、处理、训练、测试、可视化、Debug(单机单卡、单机多卡、多机多卡)_第2张图片

Google搜了一下,没找到解决办法。不过不影响我们整体训练,无视之!

13. 有一个疑惑,在tensorboard中观察发现val_loss是每100 step记录一次。但实际我是每1000 step才跑一次validation。有点懵逼。

14. 同步训练中,性能好的机器会先一步开始训练。如果主机没先开始训练的话,tensorboard中初始step将不是从1开始的。

---------2018.03.16更新-----可视化-------

重写了一个网络结构文档(squeezenet.py),优化了参数计算过程,添加了summary代码进去,可以在tensorboard上查看很多内容了。

相应更新了其他文档。

如果想用回老的网络结构文档(network.py),需要在train.py文档中,改动inference那一句代码就好了。

把index=0服务器上models目录传回自己的主机,在命令行运行:

tensorboard --logdir=models/

在浏览器地址栏输入:

localhost:6006
就可以查看可视化内容了。也可以在服务器上运行tensorboard命令,然后在自己主机浏览器地址栏输入IP:端口号,这样查看可视化内容。

---------2018.03.19更新-----DEBUG-------

在train代码中添加了debug代码,只有一行。在tf.train.MonitoredTrainingSession中hook列表中,在arg_parsing.py中设置是否开启debug模式

在API中还发现了一种在tensorboard上调试的接口tfdbg.TensorBoardDebugHook(),不过我的主机tensorflow版本1.2还没这个接口,就没测试。看个人习惯了,喜欢在命令行调试还是在tensorboard上调试。

同时修改了参数代码,每次运行必须指定训练模式。


---------2018.03.20更新-----去均值、归一化-------
1. 修改了img2bin_list.py,最后会计算并打印bgr三通道均值。

2. 修改了arg_parsing.py,增加了均值参数。

3. 修改了dataset.py,在数据读取的时候增加了去均值和归一化操作。

---------2018.03.21更新-----增加网络模型-------

1.修改了arg_parsing.py, test.py, train.py

2.增加mobilenet和mobilenet v2网络模型

---------2018.03.22更新-----增加网络模型,解决GPU内存问题------- 

1.修改了arg_parsing.py, test.py, train.py

2.增加resnet-50, resnet-101, resnet-152, resnet-200网络模型

3.解决了一运行就将GPU内存占满的问题,但是只在单机上好用,在分布式环境不行。

---------2018.03.26更新-----在训练阶段中增加了验证阶段------- 

对train.py改动比较大,对所有网络结构.py也有改动,要求tensorflow版本至少1.4.0。

原因是tf.variable_scope()中没有reuse=tf.AUTO_REUSE 。

当然如果你能自己一个一个设置reuse,理论上也可以用低版本tensorflow的。

比较麻烦而已,需要判断是train还是val,然后分别设置reuse为False还是True

---------2018.03.29更新-----Finetune------- 

可以进行finetune了

我是在自己之前训练出的模型的基础上finetune的,

还没去找开源的训练好的模型。2018.04.02  在Github上没找到合适的预训练模型,并且即使找到了也有可能面临一些问题。)

在命令行指定模型保存路径就可以自动finetune了

--finetune=path/

---------2018.04.02更新-----优化了可视化效果------- 

1. 把训练阶段和验证阶段summary分别放到了不同namespace中,网络结构图也做了优化。

---------2018.04.09更新-----同步异步训练------- 

1. 在分布式环境中,添加了一个布尔开关(--issync)来控制同步还是异步更新参数训练。

2. worker服务器一定要按index从0开始的机器一个一个运行程序,如果不按顺序运行程序的话,会使各个服务器step差距很大。

3. 重写了MonitoredTrainingSession,将log和validation分别写进两个hook中,并且同步模式下只有index=0的worker机器上会进行validation。

---------2018.04.10更新-----解决log输出step不匹配问题-------

1.  分布式,同步训练环境下,通过在hook中run_context.session.run(global_step)读取全局step来控制log和validation,解决了不同机器之间step不匹配问题。

---------2018.04.11更新-----解决异步训练记录最后一次event失败问题-------

1. 将异步训练也改成通过读取global_step来控制,在ExitHook中强制index=0主机在最后阶段做一次validation,保证其他机器先执行run_context.request_stop()。这样解决了记录最后一次event失败的问题。我理想中解决这个问题的方法是index=0主机发现当前global step达到训练结束次数的时候,先等待其他机器都request stop后,自己才request stop。

---------2018.04.13更新-----添加了SSD代码-------

1. 具体模型保存路径和数据路径要设置好

2. 数据路径下图片和xml文件要分别保存在两个目录中

你可能感兴趣的:(Python,Tensorflow)