【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_():#可以用的分布式多机多卡。
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机器:
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机器会卡主一会,然后报错:
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上调试。
同时修改了参数代码,每次运行必须指定训练模式。
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文件要分别保存在两个目录中