Github 代码地址: https://github.com/HansRen1024/Tensorflow-preprocessing-training-testing
** 所有代码去 Github 上拿吧,文件名对应好。 **
** TF 版本至少 1.4.0。 **
**MonitoredTrainingSession 在网上的资料十分稀少,研究了很久官方 API 和源码 。 **
** 2018.04.13 更新 SSD 单机训练 + 测试代码: https://github.com/HansRen1024/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 ):
【Caffe】快速上手训练自己的数据 《很认真的讲讲 Caffe》
代码是准备数据集里面生成 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 里只能处理 3232 数据集,我给改了,现在这个网络可以处理正常 227227 的了。
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 频率很难平衡。
)
我不了解为什么一训练起来所有 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 (2018.04.02
step 的参数才会传给 ps 机器更新后传回 worker 机器然后继续 1001step
训练。(不知道你们能读懂不 - 0-,MonitoredTrainingSession 这种会话每次运行时其实都是分三步,begin,before run
和 after run。参数传递给 ps 机器,再返回给 worker 机器应该都是在 before 这一步进行的。)
重写 MonitoredTrainingSession 后,只在 index=0 机器上进行 validation)
每次同步模式训练完,index 0 机器会卡主一会,然后报错:
Google 搜了一下,没找到解决办法。不过不影响我们整体训练,无视之!
-
有一个疑惑,在 tensorboard 中观察发现 val_loss 是每 100 step 记录一次。但实际我是每 1000 step 才跑一次 validation。有点懵逼。
-
同步训练中,性能好的机器会先一步开始训练。如果主机没先开始训练的话,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 更新 ----- 去均值、归一化 -------
-
修改了 img2bin_list.py,最后会计算并打印 bgr 三通道均值。
-
修改了 arg_parsing.py,增加了均值参数。
-
修改了 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
-
增加 resnet-50, resnet-101, resnet-152, resnet-200 网络模型
-
解决了一运行就将 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 更新 ----- 优化了可视化效果 ------- ****
- 把训练阶段和验证阶段 summary 分别放到了不同 namespace 中, 网络结构图也做了优化。
---------2018.04.09 更新 ----- 同步异步训练 ------- ****
-
在分布式环境中,添加了一个布尔开关(--issync)来控制同步还是异步更新参数训练。
-
worker 服务器一定要按 index 从 0 开始的机器一个一个运行程序, 如果不按顺序运行程序的话,会使各个服务器 step 差距很大。
-
重写了 MonitoredTrainingSession,将 log 和 validation 分别写进两个 hook 中,并且同步模式下只有 index=0 的 worker 机器上会进行 validation。
---------2018.04.10 更新 ----- 解决 log 输出 step 不匹配问题 -------
- 分布式,同步训练环境下,通过在 hook 中 run_context.session.run (global_step) 读取全局 step 来控制 log 和 validation,解决了不同机器之间 step 不匹配问题。
---------2018.04.11 更新 ----- 解决异步训练记录最后一次 event 失败问题 -------
- 将异步训练也改成通过读取 global_step 来控制,在 ExitHook 中强制 index=0 主机在最后阶段做一次 validation,保证其他机器先执行 run_context.request_stop ()。这样解决了记录最后一次 event 失败的问题。我理想中解决这个问题的方法是 index=0 主机发现当前 global step 达到训练结束次数的时候,先等待其他机器都 request stop 后,自己才 request stop。
---------2018.04.13 更新 ----- 添加了 SSD 代码 -------
-
具体模型保存路径和数据路径要设置好
-
数据路径下图片和 xml 文件要分别保存在两个目录中