業界 | 詳解Horovod:Uber開源的TensorFlow分佈式深度學習框架

 2017-10-18 13:55:00.0

原標題:業界 | 詳解Horovod:Uber開源的TensorFlow分佈式深度學習框架

選自Uber

作者:Alex Sergeev、Mike Del Balso

參與:李澤南、路雪

Horovod 是 Uber 開源的又一個深度學習工具,它的發展吸取了 Facebook「一小時訓練 ImageNet 論文」與百度 Ring Allreduce 的優點,可爲用戶實現分佈式訓練提供幫助。本文將簡要介紹這一框架的特性。

近年來,深度學習引領了圖像處理、語音識別和預測等方面的巨大進步。在 Uber,我們將深度學習應用到了公司業務中,從自動駕駛搜索路線到防禦欺詐,深度學習讓我們的數據科學家和工程師們能夠爲用戶提供更好的體驗。

TensorFlow 已經成爲了 Uber 首選的深度學習庫。因爲這個框架是目前使用最爲廣泛的開源深度學習框架,對於新的開發者而言非常友好。它結合了高性能與低級模型細節調試能力——例如,我們可以使用 Keras 這種高級 API,同時使用自己定製的 Nvidia CUDA 工具。此外,TensorFlow 還爲各種深度學習用例提供了端到端支持,從進行實驗性探索到將生產級模型部署到雲服務器、移動端 APP、甚至自動駕駛汽車上。

上個月 Uber 工程部門推出了 Michelangelo——一個內部機器學習服務平臺,可以讓機器學習輕鬆部署到大規模系統中。在本文中 Uber 介紹了 Michelangelo 深度學習工具包的重要開源組件 Horovod,它可以讓分佈式 TensorFlow 深度學習項目更加輕鬆地實現。

面向分佈式

隨着 Uber 在 TensorFlow 上訓練越來越多的機器學習模型,項目的數據和計算能力需求正在急劇增加。在大部分情況下,模型是可以在單個或多 GPU 平臺的服務器上運行的,但隨着數據集的增大和訓練時間的增長,有些時候訓練需要一週甚至更長時間。因此,Uber 的工程師們不得不尋求分佈式訓練的方法。

Uber 開始嘗試部署標準分佈式 TensorFlow 技術,在試驗了一些方法之後,開發者意識到原有方法需要進行一些調整:首先,在遵循文檔和代碼示例之後,我們並不總是清楚哪些功能對應着哪些模型訓練代碼的分佈式計算。標準分佈式 TensorFlow 引入了很多新的概念:工作線程、參數服務器、tf.Server()、tf.ClusterSpec()、 tf.train.SyncReplicasOptimizer() 以及 tf.train.replicas_device_setter() 等等。它們在某些情況下能起到優化作用,但也讓我們難以診斷拖慢訓練速度的 bug。

第二個問題有關 Uber 規模的計算性能。在進行了一些基準測試之後,我們發現標準的分佈式 TensorFlow 機制無法滿足需求。例如,在使用 128 個 GPU 進行訓練時,我們因爲低效率損失了一半的計算資源。

圖 1. 標準 TensorFlow 基準套件,使用英偉達 Pascal GPU(從 1 塊到 128 塊)運行 Inception V3 和 ResNet-101 模型,與理想狀態下的分佈式計算(單 GPU 算力簡單疊加)每秒處理的圖像數量對比。從中我們發現標準方法很難釋放出硬件的全部潛能。

當我們使用標準 TensorFlow 基準測試套件在 128 塊英偉達 Pascal GPU 上進行測試時(如圖 1 所示),無論是 Inception V3 還是 ResNet-101 都浪費了將近一半 GPU 算力。

充分利用 GPU 資源是目前大規模訓練的一大課題,此前 Facebook 的一小時訓練 ImageNet 論文《Accurate, Large Minibatch SGD: Training ImageNet in 1 Hour》介紹了使用 256 塊 GPU 進行 ResNet-50 網絡「數據並行」訓練的方法,引起人們的廣泛關注,這也證明了大規模分佈式訓練可以顯著提高生產力。

利用不同類型的算法

圖 2.「數據並行」方法在分佈式訓練上包含在多節點上並行分割數據和訓練。在同步情況下,不同批次數據的梯度將在不同節點上分別進行計算,但在節點之間進行互相平均,以對每個節點中的模型副本應用一致化更新。

在 Facebook 的研究之後,Uber 的研究人員開始尋找更好的分佈式 TensorFlow 模型訓練方法。由於我們的模型小到可以在單個 GPU 或多 GPU 的單服務器上運行,我們開始嘗試使用 Facebook 的數據並行方法。

在概念上,數據並行的分佈式訓練方法非常直接:

1. 運行訓練腳本的多個副本,每個副本:

a)讀取數據塊

b)將其輸入模型

c)計算模型更新(梯度)

2. 計算這些副本梯度的均值

3. 更新模型

4. 重複 1a 步驟

標準分佈式 TensorFlow 包使用參數服務器的方法來平均梯度。在這種方法之下,每個進程都有一到兩個角色:工作線程或參數服務器。工作線程處理訓練數據,計算梯度,並把它們傳遞到參數服務器上進行平均。

圖 3. 分佈式訓練中的參數服務器可以按照不同比例的參數服務器和工作線程進行配置,每一個都有着不同的配置數據。

儘管這種方法可以提升表現,但我們仍然面臨兩大挑戰:

確定工作線程與參數服務器的正確比例:一旦使用參數服務器,它就可能變成網絡或計算的瓶頸。如果使用多個參數服務器,通信模式就會變成「all-to-all」的狀態,網絡可能會很快飽和。

應對不斷增加的 TensorFlow 程序複雜性:在測試中我們發現,每個使用分佈式 TensorFlow 的案例都需要指定初始工作線程和參數服務器,傳遞服務發現信息,如所有工作線程和參數服務器的主機和端口,並使用合適的 tf.ClusterSpec() 構建 tf.Server(),進而調整訓練程序。此外,用戶必須保證所有的操作都正確地使用 tf.train.device_replica_setter(),並使用 towers 讓代碼符合服務器中多 GPU 的設置。這通常導致陡峭的學習曲線和大量的代碼重構,壓縮了實際建模的時間。

在 2017 年上半年,百度發表了研究《Bringing HPC Techniques to Deep Learning》(參見百度將 HPC 引入深度學習:高效實現模型的大規模擴展),提出使用不同的算法來平均梯度,並讓這些梯度在所有節點之間交流,這被稱爲 ring-allreduce,他們使用 TensorFlow 也實現了這種算法(https://github.com/baidu-research/tensorflow-allreduce)。該算法的思路基於 2009 年 Patarasuk 與 Xin Yuan 的論文《Bandwidth Optimal All-reduce Algorithms for Clusters of Workstations》。

圖 4. ring-allreduce 算法允許工作線程節點平均梯度並將其分散到所有節點——無需參數服務器。

在 ring-allreduce 算法中,每個 N 節點與其他兩個節點進行 2*(N-1) 次通信。在這個通信過程中,一個節點發送並接收數據緩衝區傳來的塊。在第一個 N-1 迭代中,接收的值被添加到節點緩衝區中的值。在第二次 N-1 迭代中,接收的值代替節點緩衝區中保存的值。百度的文章證明了這種算法是帶寬上最優的,這意味着如果緩衝區足夠大,它將最大化地利用可用的網絡。

除了網絡最優化,allreduce 方法也易於理解和應用。用戶可以利用消息傳遞接口(Message Passing Interface,MPI)實現,如 Open MPI,來啓動 TensorFlow 程序的所有副本。MPI 明確地建立了在分佈式條件下工作線程互相通信的範式。用戶需要使用 allreduce() 來調整自己的程序以平均梯度。

Horovod 簡介

意識到 ring-allreduce 方法能夠改善易用性和性能,這激勵我們繼續研究適合我們的實現,以滿足 UberTensorFlow 的需求。我們採用了百度的 TensorFlow ring-allreduce 算法實現,並在此基礎上進行構建。流程如下:

1. 我們將代碼轉換成獨立的 Python 包 Horovod,它的名字來自於俄國傳統民間舞蹈,舞者手牽手圍成一個圈跳舞,與分佈式 TensorFlow 流程使用 Horovod 互相通信的場景很像。Uber 的不同團隊可能使用不同版本的 TensorFlow。我們希望所有團隊無須更新到 TensorFlow 最新版,就可以利用 ring-allreduce 算法,使用補丁,甚至構建框架。擁有獨立的 Python 包使安裝 Horovod 的時間從一個小時縮減至幾分鐘,時間長短取決於硬件條件。

2. 我們用 NCCL 替換百度的 ring-allreduce 實現。NCCL 是英偉達的集合通信庫,提供高度優化的 ring-allreduce 版本。NCCL 2 允許在多個機器之間運行 ring-allreduc,這使得我們利用其多種性能提升優化。

3. 我們支持模型適應單個服務器和多個 GPU,原始版本只支持單個 GPU 模型。

4. 最後,我們根據大量初始用戶的反饋對 API 進行了多處改進。特別是,我們實現了廣播操作,使模型在所有工作線程中實現一致性初始化。新的 API 允許我們將用戶在單個 GPU 項目中的運算量減少到 4。

接下來,我們將討論如何在團隊中使用 Horovod 進行機器學習。

使用 Horovod 分配訓練任務

分佈式 TensorFlow 的參數服務器模型(parameter server paradigm)通常需要對大量樣板代碼進行認真的實現。但是 Horovod 僅需要幾行。下面是一個分佈式 TensorFlow 項目使用 Horovod 的示例:

  1. tensorflow as tf

  2. importhorovod.tensorflow as hvd

  3. #InitializeHorovod

  4. hvd.init()

  5. #PinGPU to be used to process local rank (one GPU per process)

  6. config =tf.ConfigProto()

  7. config.gpu_options.visible_device_list =str(hvd.local_rank())

  8. #Buildmodel

  9. loss =

  10. opt =tf.train.AdagradOptimizer(0.01)

  11. #AddHorovodDistributedOptimizer

  12. opt =hvd.DistributedOptimizer(opt)

  13. #Addhook to broadcast variables from rank 0to all other processes during

  14. #initialization.

  15. hooks =[hvd.BroadcastGlobalVariablesHook(0)]

  16. #Maketraining operation

  17. train_op =opt.minimize(loss)

  18. #TheMonitoredTrainingSessiontakes care of session initialization,

  19. #restoring from a checkpoint,saving to a checkpoint,and closing when done

  20. #or an error occurs.

  21. withtf.train.MonitoredTrainingSession(checkpoint_dir=「/tmp/train_logs」,

  22. config=config,

  23. hooks=hooks)as mon_sess:

  24. whilenot mon_sess.should_stop():

  25. #Performsynchronous training.

  26. mon_sess.run(train_op)

在該示例中,粗體文字指進行單個 GPU 分佈式項目時必須做的改變:

  1. hvd.init() 初始化 Horovod。

  2. config.gpu_options.visible_device_list = str(hvd.local_rank()) 向每個 TensorFlow 流程分配一個 GPU。

  3. opt=hvd.DistributedOptimizer(opt) 使用 Horovod 優化器包裹每一個常規 TensorFlow 優化器,Horovod 優化器使用 ring-allreduce 平均梯度。

  4. hvd.BroadcastGlobalVariablesHook(0) 將變量從第一個流程向其他流程傳播,以實現一致性初始化。如果該項目無法使用 MonitoredTrainingSession,則用戶可以運行 hvd.broadcast_global_variables(0)。

之後,用戶可以使用 mpirun 命令使該項目的多個拷貝在多個服務器中運行:

  1. $ mpirun -np 16-x LD_LIBRARY_PATH -H

  2. server1:4,server2:4,server3:4,server4:4python train.py

mpirun 命令向四個節點分佈 train.py,然後在每個節點的四個 GPU 上運行 train.py。

Horovod 還通過同樣的步驟分佈 Keras 項目。(TensorFlow 和 Keras 的腳本示例地址:https://github.com/uber/horovod/blob/master/examples/)

Horovod 的易用性、調試效率和速度使之成爲對單 GPU 或單服務器項目感興趣的工程師和數據科學家的好搭檔。下面,我們將介紹 Horovod Timeline,它在分佈式訓練工作中提供對工作線程節點狀態的高度理解。

Horovod Timeline

我們在允許用戶使用 Horovod 時,就意識到需要向用戶提供一種能夠輕鬆識別代碼中 bug 的方式,這也是處理複雜分佈式系統時常常面臨的問題。尤其是,由於用戶需要收集和交叉引用不同服務器上的文件,用戶很難使用原始的 TensorFlow timeline 或 CUDA 分析器。

我們希望用 Horovod 創造一種方式,提供節點之間操作 timeline 的高度理解。因此,我們構建了 Horovod Timeline。用戶可以使用 Horovod Timeline 清晰看到每個節點在訓練過程的每個時間步的狀態。這有助於識別 bug,解決性能問題。用戶可通過設置單個環境變量啓用 timeline,通過 chrome://tracing 在瀏覽器中查看分析結果。

圖 5:Horovod Timeline 在 Chrome 的事件追蹤性能分析工具(trace event profiling tool)中描述分佈式訓練過程中的高級別 timeline。

Tensor Fusion

我們分析了多個模型的 timeline 之後,發現具有大量張量的模型,如 ResNet-101,有很多小的 allreduce 操作。之前我們注意到,ring-allreduce 在張量足夠多的情況下可以最大化利用網絡,但工作效率和速度都不如張量少的情況。於是問題來了:如果在張量上執行 ring-allreduce 之前,先融合多個小張量,會發生什麼呢?

答案就是:Tensor Fusion,一種在執行 Horovod 的 ring-allreduce 之前先融合張量的算法。我們使用該方法進行實驗,發現在未優化的傳輸控制協議(TCP)網絡上運行的多層模型性能提升了 65%。我們簡要介紹了 Tensor Fusion 的使用方法:

1. 確定要減少哪些向量。首先選擇幾個在緩衝區(buffer)中適用且具備同樣的數據類型的張量。

2. 爲未分配的張量分配融合緩衝區(fusion buffer)。默認的融合緩衝區大小是 64 MB。

3. 將所選張量的數據複製到融合緩衝區。

4. 在融合緩衝區上執行 allreduce 操作。

5. 將融合緩衝區中的數據複製到輸出張量中。

6. 重複直到該循環中沒有需要減少的張量。

我們使用 Horovod、Tensor Fusion 和在 Michelangelo 平臺上構建的其他特徵,提高模型在我們的機器學習系統中的效率、速度和易用性。下一部分,我們將分享現實世界的基準,來展示 Horovod 的性能。

Horovod 基準

圖 6:Inception V3 和 ResNet-101 TensorFlow 模型在 25GbE TCP 上使用不同數量的 NVIDIA Pascal GPU 時,使用標準分佈式 TensorFlow 和 Horovod 運行分佈式訓練工作每秒處理的圖像數量對比。

我們重新運行調整後適合 Horovod 的官方 TensorFlow 基準,並與常規的分佈式 TensorFlow 的性能進行對比。如圖 6 所示,Horovod 的能力有大幅改進,我們不再浪費一半的 GPU 資源。事實上,使用 Inception V3 和 ResNet-101 模型進行縮放可以達到 88% 的計算效率。也就是說,訓練速度是標準分佈式 TensorFlow 的兩倍。

圖 7:Horovod 在 25GbE TCP 和 25GbE RDMA 網絡上每秒處理的圖像對比。它們在不同數量的 NVIDIA Pascal GPU 上爲 Inception V3、ResNet-101 和 VGG-16 運行分佈式訓練工作。

由於 MPI 和 NCCL 都支持遠程直接內存訪問(RDMA)網絡,我們使用 RDMA 網卡運行額外的基準測試,來確定它們提升的效率是否能夠超過 TCP 網絡。

我們發現 RDMA 沒有明顯提升 Inception V3 和 ResNet-101 模型上的性能,僅比 TCP 網絡提高了三四個百分點。但是,RDMA 幫助 Horovod 在兩個模型上實現了超過 90% 的縮放效率(scaling efficiency)。

與此同時,VGG-16 模型在使用 RDMA 網絡時速度提升了 30%。這可以用 VGG-16 的大量模型參數來解釋,全連接層和少量層的結合引起大量模型參數。這些特徵改變了從 GPU 計算到通信的關鍵路徑,造成了網絡瓶頸。

這些基準說明 Horovod 在 TCP 和 RDMA 網絡上的縮放效果很好,儘管使用 RDMA 網絡的用戶能夠在使用大量模型參數的模型如 VGG-16 時才能獲取最優性能和顯著效率提升。

我們使用 Horovod 探索深度學習中的性能優化還只是開始。未來,我們將持續利用開源社區使用我們的機器學習系統和框架實現性能提升。

下一步

今年早些時候,Uber 開源了 Horovod,讓這一可擴展機器學習模型走向整個社區。目前 Horovod 還在發展之中,我們正在向以下幾個方向繼續推進:

1. 讓 MPI 更易安裝:雖然在工作站上安裝 MPI 比較容易,但是在集羣上安裝 MPI 仍然需要一些努力;例如,有很多工作負載管理器,我們需要根據不同的硬件進行相應的調整。我們正在開發爲集羣運行 Horovod 的參考設計,爲此,我們希望與 MPI 社區和網絡硬件供應商合作,開發安裝 MPI 和相關驅動程序的說明。

2. 收集和分享調整分佈式深度學習模型參數的心得:Facebook 的「一小時訓練 ImageNet 論文」描述了與在單 GPU 上訓練模型相比,分佈式訓練任務需要超參數調整以達到甚至超越前者的準確性。Facebook 證明了在 256 塊 GPU 上訓練 TensorFlow 模型的可行性。

3. 加入超大模型示例:Horovod 目前支持適用於單 GPU,同時也支持多 GPU 服務器的模型。我們希望在更多形式的硬件上應用更大的模型。

我們希望 Horovod 的簡潔性可以使大家採用分佈式訓練,更好地利用計算資源用於深度學習。

原文地址:https://eng.uber.com/horovod/

本文爲機器之心編譯,轉載請聯繫本公衆號獲得授權。

責任編輯:

文章來源:機器之心