[深度学习] 分布式Horovod介绍(四)
[深度學(xué)習(xí)] 分布式模式介紹(一)
[深度學(xué)習(xí)] 分布式Tensorflow介紹(二)
[深度學(xué)習(xí)] 分布式Pytorch 1.0介紹(三)
[深度學(xué)習(xí)] 分布式Horovod介紹(四)
?
實(shí)際應(yīng)用中,單機(jī)多卡的同步式數(shù)據(jù)并行是最常用的,在論文中最常見的訓(xùn)練方式是單機(jī)八卡. 數(shù)據(jù)再多一般就需要多機(jī)多卡.
無(wú)論是單機(jī)多卡,還是多機(jī)多卡,均是分布式訓(xùn)練,在horovod出現(xiàn)之前,使用tensorflow,一般只有官方推薦的集群訓(xùn)練方式。
可是tensorflow的集群訓(xùn)練,用起來并不輕松
Tensorflow集群的缺點(diǎn)
1. 概念多,學(xué)習(xí)曲線陡峭
tensorflow的集群采用的是parameter server架構(gòu),因此引入了比較多復(fù)雜概念,羅列如下
server client master cluster parameter server worker job task replica_device_setter master service worker service clone涉及到的函數(shù)
tf.train.Server tf.train.Supervisor tf.train.SessionManager tf.train.ClusterSpec tf.train.replica_device_setter tf.train.MonitoredTrainingSession tf.train.MonitoredSession tf.train.SingularMonitoredSession tf.train.Scaffold tf.train.SessionCreator tf.train.ChiefSessionCreator tf.train.WorkerSessionCreator反復(fù)研究過多次,還是沒有徹底弄清楚,server,client,master,master service,worker service,clone,session之間的關(guān)系。 大致是,在client中創(chuàng)建server實(shí)例,session與server一一對(duì)應(yīng),server內(nèi)含master service和worker service兩個(gè)服務(wù),master service負(fù)責(zé)與外界通訊,比如sess.run一般都是告訴server的master service要開始工作了,server的master service通知同一個(gè)server的worker service去干活,worker service調(diào)動(dòng)GPU運(yùn)算,完成后,返回結(jié)果給master service,做權(quán)值更新,如果是多機(jī)多卡的分布式,parameter server與master service之間做梯度傳遞和權(quán)值同步。 stackoverflow.com/questions/3…
2. 修改的代碼量大
如果想把單機(jī)單卡的模型,移植到多機(jī)多卡,涉及的代碼量是以天記的,慢的話甚至需要一周。
3. 需要多臺(tái)機(jī)子跑不同的腳本
tensorflow集群是采用parameter server架構(gòu)的,要想跑多機(jī)多卡的集群,每個(gè)機(jī)子都要啟動(dòng)一個(gè)client,即跑一個(gè)腳本,來啟動(dòng)訓(xùn)練,100個(gè)機(jī)子,人就要崩潰了。
4. ps和worker的比例不好選取
tensorflow集群要將服務(wù)器分為ps和worker兩種job類型,ps設(shè)置多少性能最近并沒有確定的計(jì)算公式。
5. 性能損失較大
tensorflow的集群性能并不好,當(dāng)超過一定規(guī)模時(shí),性能甚至?xí)舻嚼硐胄阅艿囊话胍韵隆?/p>
?
Horovod
由于Tensorflow集群太不友好,業(yè)內(nèi)也一直在嘗試新的集群方案。 2017年Facebook發(fā)布了《Accurate, large minibatch SGD: Training ImageNet in 1 hour 》驗(yàn)證了大數(shù)據(jù)并行的高效性,同年百度發(fā)表了《Bringing HPC techniques to deep learning 》,驗(yàn)證了全新的梯度同步和權(quán)值更新算法的可行性。受這兩篇論文的啟發(fā),Uber開發(fā)了Horovod集群方案
Horovod 是 Uber 開源的又一個(gè)深度學(xué)習(xí)工具,本文將簡(jiǎn)要介紹這一框架的特性。
隨著 Uber 在 TensorFlow 上訓(xùn)練越來越多的機(jī)器學(xué)習(xí)模型,項(xiàng)目的數(shù)據(jù)和計(jì)算能力需求正在急劇增加。在大部分情況下,模型是可以在單個(gè)或多 GPU 平臺(tái)的服務(wù)器上運(yùn)行的,但隨著數(shù)據(jù)集的增大和訓(xùn)練時(shí)間的增長(zhǎng),有些時(shí)候訓(xùn)練需要一周甚至更長(zhǎng)時(shí)間。因此,Uber 的工程師們不得不尋求分布式訓(xùn)練的方法。
Uber 開始嘗試部署標(biāo)準(zhǔn)分布式 TensorFlow 技術(shù),在試驗(yàn)了一些方法之后,開發(fā)者意識(shí)到原有方法需要進(jìn)行一些調(diào)整:首先,在遵循文檔和代碼示例之后,我們并不總是清楚哪些功能對(duì)應(yīng)著哪些模型訓(xùn)練代碼的分布式計(jì)算。標(biāo)準(zhǔn)分布式 TensorFlow 引入了很多新的概念:工作線程、參數(shù)服務(wù)器、tf.Server()、tf.ClusterSpec()、 tf.train.SyncReplicasOptimizer() 以及 tf.train.replicas_device_setter() 等等。它們?cè)谀承┣闆r下能起到優(yōu)化作用,但也讓我們難以診斷拖慢訓(xùn)練速度的 bug。
第二個(gè)問題有關(guān) Uber 規(guī)模的計(jì)算性能。在進(jìn)行了一些基準(zhǔn)測(cè)試之后,我們發(fā)現(xiàn)標(biāo)準(zhǔn)的分布式 TensorFlow 機(jī)制無(wú)法滿足需求。例如,在使用 128 個(gè) GPU 進(jìn)行訓(xùn)練時(shí),我們因?yàn)榈托蕮p失了一半的計(jì)算資源。
?
約定如下: 網(wǎng)絡(luò)帶寬記為:B(單位Mb/s), 模型總參數(shù)數(shù)據(jù)量記為:D(單位Mb), 總服務(wù)器數(shù)量記為:n, 參數(shù)服務(wù)器數(shù)量記為:n_p(其中有n= n_p+ n_w), worker服務(wù)器數(shù)量記為:n_w(其中有n= n_p+ n_w) 單服務(wù)器計(jì)算一次耗時(shí)記為:T_0
梯度同步和權(quán)值更新算法
1) parameter server架構(gòu)
tensorflow的集群架構(gòu)是parameter server架構(gòu),數(shù)據(jù)的傳導(dǎo)模型如下圖。
?
?
則可以計(jì)算出,parameter server架構(gòu)的集群方案,總耗時(shí):
?
?
可以看出T與總節(jié)點(diǎn)數(shù)n基本成線性關(guān)系,但不同的參數(shù)服務(wù)器和woker服務(wù)器分配方案,總性能也將不同。 假設(shè),e表示worker服務(wù)器占比,即e=n_w/n,則可以計(jì)算出最優(yōu)的e值為:
?
?
可以看出,最優(yōu)worker服務(wù)器占比與模型大小、網(wǎng)絡(luò)帶寬、單機(jī)運(yùn)行時(shí)間都有關(guān)系,并不是一個(gè)一眼能最優(yōu)值得超參數(shù)。
2)horovod的ring-allreduce算法
百度2017年發(fā)表的《Bringing HPC techniques to deep learning 》中,采用了全新的梯度同步和權(quán)值同步算法,叫做ring-allreduce。此種算法各個(gè)節(jié)點(diǎn)之間只與相鄰的兩個(gè)節(jié)點(diǎn)通信,并不需要參數(shù)服務(wù)器。因此,所有節(jié)點(diǎn)都參與計(jì)算也參與存儲(chǔ)。 一次權(quán)重更新,主要包含兩個(gè)過程, 1)累計(jì)梯度 將所有梯度分為n個(gè)片段,每次只與相鄰節(jié)點(diǎn)傳遞1個(gè)片段的梯度,n-1次后,每一片段的梯度都完成了所有節(jié)點(diǎn)這一片段梯度的累計(jì),但不用片段的累計(jì)值分布在不同節(jié)點(diǎn)上。如下圖的第2、第3步; 2)將累計(jì)后的梯度分發(fā)到所有節(jié)點(diǎn) 將第一步累計(jì)的梯度再次通過n-1次的相互交換后,所有節(jié)點(diǎn)的梯度完成同步。如下圖的第4、第5步。再平均后,更新權(quán)重,就完成了所有節(jié)點(diǎn)權(quán)重的更新。
?
?
可以計(jì)算出ring-allreduce算法的總耗時(shí)為:
?
可以看出,總耗時(shí)基本與總節(jié)點(diǎn)數(shù)n成線性關(guān)系(n較大時(shí),1/n基本為0)
Horovod的梯度同步和權(quán)值同步就采用了ring-allreduce算法。
概念
horovod的數(shù)據(jù)傳遞是基于MPI,因此其涉及的概念也是MPI中的概念。以4個(gè)服務(wù)器,每個(gè)服務(wù)器4個(gè)GPU為例,
- size 進(jìn)程數(shù)量,也即所有GPU數(shù)量,為16
- rank 進(jìn)程的唯一ID,0-15
- local rank 每一個(gè)server中的進(jìn)程的本地唯一ID,0-3
- allreduce 累加所有數(shù)據(jù),并同步到所有節(jié)點(diǎn)的操作,如下圖
?
?
?
- allgather 收集所有數(shù)據(jù),并同步到所有節(jié)點(diǎn)的操作,完成后每個(gè)節(jié)點(diǎn)都包含所有節(jié)點(diǎn)的數(shù)據(jù),并且這些數(shù)據(jù)單獨(dú)存在。如下圖。
?
?
?
- broadcast 將數(shù)據(jù)(需要由根節(jié)點(diǎn)確認(rèn))從一個(gè)節(jié)點(diǎn)傳播到其他所有節(jié)點(diǎn)的操作
?
?
?
大概就這么多概念,簡(jiǎn)單清晰。
將單機(jī)單卡改為多機(jī)多卡
將一個(gè)只支持單機(jī)單卡的訓(xùn)練腳本修改為支持多機(jī)多卡的訓(xùn)練腳本,以tensorflow為例,只需要做如下改動(dòng):
import tensorflow as tf import horovod.tensorflow as hvd# Initialize Horovod hvd.init()# Pin GPU to be used to process local rank (one GPU per process) config = tf.ConfigProto() config.gpu_options.visible_device_list = str(hvd.local_rank())# Build model... loss = ... opt = tf.train.AdagradOptimizer(0.01 * hvd.size())# Add Horovod Distributed Optimizer opt = hvd.DistributedOptimizer(opt)# Add hook to broadcast variables from rank 0 to all other processes during # initialization. hooks = [hvd.BroadcastGlobalVariablesHook(0)]# Make training operation train_op = opt.minimize(loss)# Save checkpoints only on worker 0 to prevent other workers from corrupting them. checkpoint_dir = '/tmp/train_logs' if hvd.rank() == 0 else None# The MonitoredTrainingSession takes care of session initialization, # restoring from a checkpoint, saving to a checkpoint, and closing when done # or an error occurs. with tf.train.MonitoredTrainingSession(checkpoint_dir=checkpoint_dir,config=config,hooks=hooks) as mon_sess:while not mon_sess.should_stop():# Perform synchronous training.mon_sess.run(train_op)可以看出,改動(dòng)不大,只需添加10行左右的代碼,主要分為6步:
1)初始化horovod
hvd.init()2)一個(gè)GPU與一個(gè)進(jìn)程綁定
config = tf.ConfigProto() config.gpu_options.visible_device_list = str(hvd.local_rank())3)根據(jù)總GPU數(shù)量放大學(xué)習(xí)率
opt = tf.train.AdagradOptimizer(0.01 * hvd.size())因?yàn)锽atchSize會(huì)根據(jù)GPU數(shù)量放大,所以學(xué)習(xí)率也應(yīng)該放大
4)使用hvd.DistributedOptimizer封裝原有的optimizer
opt = hvd.DistributedOptimizer(opt)分布式訓(xùn)練涉及到梯度同步,每一個(gè)GPU的梯度計(jì)算仍然由原有的optimizer 計(jì)算,只是梯度同步由hvd.DistributedOptimizer負(fù)責(zé)。
5)廣播初始變量值到所有進(jìn)程
hooks = [hvd.BroadcastGlobalVariablesHook(0)]主要為了確保所有進(jìn)程變量初始值相同
6)只在worker 0上保存checkpoint
checkpoint_dir = '/tmp/train_logs' if hvd.rank() == 0 else None防止checkpoint保存錯(cuò)亂
horovod只是需要改動(dòng)必要改動(dòng)的,不涉及parameter server架構(gòu)的device設(shè)置等,繁瑣的操作。
開始訓(xùn)練
在單機(jī)4卡的機(jī)上起訓(xùn)練,只需執(zhí)行以下命令:
horovodrun -np 4 -H localhost:4 python train.py在4機(jī),每機(jī)4卡的機(jī)子上起訓(xùn)練,只需在一個(gè)機(jī)子上執(zhí)行以下命令即可:
horovodrun -np 16 -H server1:4,server2:4,server3:4,server4:4 python train.py注意無(wú)論是單機(jī)多卡,還是多機(jī)多卡,都只需在一個(gè)機(jī)子上執(zhí)行一次命令即可,其他機(jī)horovod會(huì)用MPI啟動(dòng)進(jìn)程和傳遞數(shù)據(jù)。
性能對(duì)比
?
?
?
horovod隨著規(guī)模增大,性能損失遠(yuǎn)小于tensorflow,基本是線性增加的。
Horovod 問題
- checkpoint 的保存與恢復(fù)
只需要將 Worker 0 的checkpoint保存即可, 重啟恢復(fù)的時(shí)候 Worker 0 的參數(shù)會(huì)通過hvd.BroadcastGlobalVariablesHook(0) 傳播到其他 Worker
- horovod Failures due to SSH issues
執(zhí)行horovodrun命令的主機(jī)必須能夠免密通過SSH登陸到其他主機(jī),
參考 SSH login without password
- 更多問題請(qǐng)見Troubleshooting
結(jié)論
通過Tensorflow集群的人,會(huì)深刻體會(huì)到horovod有多好用,感謝百度、Facebook和Uber讓深度學(xué)習(xí)更美好。
不過,也要注意到,horovod的分布式貌似只支持同步更新式的數(shù)據(jù)并行,模型并行和異步更新式的數(shù)據(jù)并行,我沒有嘗試過,根據(jù)ring-allreduce算法可知,應(yīng)該是不支持的。
?
鏈接:是時(shí)候放棄tensorflow集群投入horovod的懷抱
?
總結(jié)
以上是生活随笔為你收集整理的[深度学习] 分布式Horovod介绍(四)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [深度学习] PyTorch-BigGr
- 下一篇: [深度学习] 分布式Tensorflow