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 這一步的時候,性能不同的機器會不同步進行 validation。
順序是:當到達 validation 步驟時,性能好的機器先進行 validation,等完事後,性能差的機器才會進行 validation。
這會增加整體訓練的耗時,具體原因我觀察是:如果設置 validation 步驟為 1000 的話,當 sess run 1001 步時,1000 步的參數才會傳給 ps 機器更新後傳回 worker 機器然後繼續 1001 步 (2018.04.02
訓練。(不知道你們能讀懂不 - 0-,MonitoredTrainingSession 這種會話每次運行時其實都是分三步,begin,before run
和 after run。參數傳遞給 ps 機器,再返回給 worker 機器應該都是在 before 這一步進行的。)
重寫 MonitoredTrainingSession 後,只在 index=0 機器上進行 validation)
每次同步模式訓練完,index 0 機器會卡住一會,然後報錯:
Google 搜了一下,沒找到解決辦法。不過不影響我們整體訓練,無視之!
-
有一個疑惑,在 tensorboard 中觀察發現 val_loss 是每 100 步記錄一次。但實際我是每 1000 步才跑一次 validation。有點懵逼。
-
同步訓練中,性能好的機器會先一步開始訓練。如果主機沒先開始訓練的話,tensorboard 中初始步驟將不是從 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 文件要分別保存在兩個目錄中