作者 | Jiale Zhi,Rui Wang,Jeff Clune,Kenneth O. Stanley
譯者 | 平川
策劃 | 陳思
Uber 開發了 POET、Go-Explore 和 GTN 等算法,這些算法利用大量的計算來訓練神經網絡模型。為了使未來幾代類似算法的大規模計算成為可能,Uber 進而開發了一種新的分布式計算庫 Fiber,它可以幫助使用者輕松地将本地計算方法擴充到成百上千台機器上。Fiber 可以使使用 Python 的大規模計算項目變得快速、簡單和資源高效,進而簡化 ML 模型訓練過程,并獲得更優的結果。
本文最初釋出于 Uber 工程部落格,由 InfoQ 中文站翻譯并分享。
Fiber:Uber 的開源分布式機器學習平台,圖檔由 Flat UI Kit 提供,項目位址:https://github.com/uber/fiber
在過去的幾年中,計算機不斷增強的處理能力推動了機器學習的進步。算法越來越多地利用并行性,并依賴分布式訓練來處理大量資料。然而,随之而來的是增加資料和訓練的需求,這對管理和利用大規模計算資源的軟體提出了巨大的挑戰。
在 Uber,我們開發了 POET、Go-Explore 和 GTN 等算法,這些算法利用大量的計算來訓練神經網絡模型。為了使未來幾代類似算法的大規模計算成為可能,我們開發了一種新的分布式計算庫 Fiber,它可以幫助使用者輕松地将本地計算方法擴充到成百上千台機器上。Fiber 可以使使用 Python 的大規模計算項目變得快速、簡單和資源高效,進而簡化 ML 模型訓練過程,并獲得更優的結果。
大規模分布式計算的挑戰
在理想情況下,将運作在一台機器上的應用程式擴充為運作在一批機器上的應用程式應該很容易,隻需更改指令行參數即可。然而,在現實世界中,這并不容易。
我們每天都與許多運作大規模分布式計算任務的人一起工作,我們發現,現在很難利用分布式計算的原因有以下幾個:
- 在筆記本或桌上型電腦本地運作代碼與在生産叢集上運作代碼之間存在着巨大的差距。 你可以讓 MPI 在本地運作,但在計算機叢集上運作它是完全不同的過程。
- 不能動态擴充。如果你啟動了一個需要大量資源的作業,那麼你很可能需要等待,直到所有資源都配置設定好了才可以運作該作業。這個等待降低了擴充的效率。
- 錯誤處理缺失。在運作時,有些作業可能會失敗。你可能不得不還原部分結果或整個地放棄本次運作。
- 學習成本很高。每個系統都有不同的 API 和程式設計約定。要使用新系統啟動作業,使用者必須學習一套全新的約定。
新的 Fiber 平台專門解決了這些問題。它為更廣泛的使用者群體提供了無縫使用大規模分布式計算的可能。
Fiber 簡介
Fiber 是一個用于現代計算機叢集的基于 Python 的分布式計算庫。使用者可以利用這個系統針對整個計算機叢集進行程式設計,而不是隻針對單個桌上型電腦或筆記本電腦。它最初是為了支援像 POET 這樣的大規模并行科學計算項目而開發的,Uber 也已經用它來支援類似的項目。Fiber 的功能非常強大,這樣主要是因為:
- 易于使用。Fiber 允許使用者編寫在計算機叢集上運作的程式,而不需要深入研究計算機叢集的細節。
- 易于學習。Fiber 提供了與 Python 标準 多處理 庫相同的 API。知道如何使用多處理庫的工程師可以很容易地用 Fiber 編寫計算機叢集程式。
- 快速可靠。Fiber 的通信中樞基于 Nanomsg 建構,這是一個高性能異步消息傳遞庫,可以提供快速、可靠的通信。
- 不需要部署。Fiber 在計算機叢集上的運作方式與普通應用程式相同。它會自動為使用者處理資源配置設定和通信。
- 提供了可靠的計算。Fiber 内置的錯誤處理功能讓使用者可以專注于編寫實際的應用程式代碼,而不是處理崩潰問題。當運作一個工作程序池時,這尤其有價值。
除了這些好處之外,Fiber 還可以在特别關注性能的領域與其他專用架構搭配使用。例如,對于 随機梯度下降(SGD),Fiber 的 Ring 特性 可以幫助我們在計算機叢集上建立分布式訓練作業,并允許它與 Horovod 或 torch.distributed 協同。
圖 1:Fiber 啟動許多不同的作業支援(job-backed)程序,然後在其中運作不同的 Fiber 元件和使用者程序。Fiber Master 是管理所有其他程序的主程序。有些程序(如 Ring Node)保持成員之間的通信。
Fiber 可以幫助從事大規模分布式計算的使用者減少從産生想法到在計算叢集上實際運作分布式作業的時間。它還可以幫助使用者屏蔽配置和資源配置設定任務的繁瑣細節,并且可以縮短調試周期,簡化從本地開發到叢集開發的轉換。
架 構
Fiber 讓我們可以靈活地為經典的多處理 API 選擇可以在不同叢集管理系統上運作的後端。為了實作這種內建,Fiber 被分為三個不同的層:API 層、後端層和叢集層。API 層為 Fiber 提供了程序、隊列、池和管理器等基本建構塊。它們具有與多處理相同的語義,但是我們對它們進行擴充了,使它們可以在分布式環境中工作。後端層處理在不同叢集管理器上建立或終止作業的任務。當使用者新增一個後端時,所有其他 Fiber 元件(隊列、池等)都不需要更改。最後,叢集層由不同的叢集管理器組成。盡管它們不是 Fiber 本身的一部分,但是它們幫助 Fiber 管理資源并跟蹤不同的作業,減少了 Fiber 所需要跟蹤的項的數量。圖 2 是總體架構圖:
圖 2:Fiber 的架構包括 API 層、後端層和叢集層,這讓它可以在不同的叢集管理系統上運作。
作業支援程序
Fiber 引入了一個新的概念,稱為 作業支援過程(也稱為 Fiber 程序)。這些程序與 Python 多處理庫中的程序類似,但是更靈活:多處理庫中的程序隻在本地機器上運作,但 Fiber 程序可以在不同的機器上遠端運作,也可以在同一機器上本地運作。當新的 Fiber 程序啟動時,Fiber 會在目前計算機叢集上建立一個具有适當 Fiber 後端的新作業。
圖 3:Fiber 中的每個作業支援程序都是在計算機叢集上運作的一個容器化作業。每個作業支援程序也有自己的 CPU、GPU 和其他計算資源。在容器内運作的代碼是自包含的。
Fiber 使用容器來封裝目前程序的運作環境(如上圖 3 所示),其中包括所有必需的檔案、輸入資料和其他依賴的程式包,而且要保證每個元素都是自包含的。所有子程序都以與父程序相同的容器鏡像啟動,以確定運作環境的一緻性。因為每個程序都是一個叢集作業,是以它的生命周期與叢集上的任何作業相同。為了友善使用者,Fiber 被設計成直接與計算機叢集管理器互動。是以,不像 Apache Spark 或 ipyparallel,Fiber 不需要在多台機器上設定,也不需要通過任何其他機制引導。它隻需要作為一個普通的 Python pip 包安裝在一台機器上。
組 件
Fiber 基于 Fiber 程序實作了大多數多處理 API,包括管道、隊列、池和管理器。
Fiber 中隊列和管道的行為方式與多處理相同。不同之處在于,Fiber 中的隊列和管道由運作在不同機器上的多個程序共享。兩個程序可以從同一個管道讀取和寫入資料。此外,隊列可以在不同機器上的多個程序之間共享,每個程序可以同時向同一隊列發送或從同一隊列接收資訊。Fiber 隊列是用高性能異步消息隊列系統 Nanomsg 實作的。
圖 4:Fiber 可以在不同的 Fiber 程序之間共享隊列。在本例中,一個 Fiber 程序與隊列位于同一台機器上,另外兩個程序位于另一台機器上。一個程序寫入隊列,另外兩個程序讀取隊列。
Fiber 也支援 池,如下圖 5 所示。它們讓使用者可以管理工作程序池。Fiber 使用 作業支援程序 擴充池,以便每個池可以管理數千個(遠端)工作程序。使用者還可以同時建立多個池。
圖 5:在具有三個工作程序的池中,如本例所示,兩個工作程序位于一台機器上,另一個位于另一台機器上。它們共同處理送出到主程序中任務隊列的任務,并将結果發送到結果隊列。
管理器 和 代理對象 使 Fiber 能夠支援共享存儲,這在分布式系統中至關重要。通常,這個功能由計算機叢集外部存儲系統如 Cassandra 和 Redis 提供。相反,Fiber 為應用程式提供了内置的記憶體存儲。該接口與多處理系統中的管理器類型接口相同。
Ring 是對多處理 API 的擴充,可以用于分布式計算設定。在 Fiber 中,Ring 指的是一組共同工作的、相對平等的程序。與池不同,Ring 沒有主程序和輔助程序的概念。Ring 内的所有成員承擔大緻相同的責任。Fiber 的 Ring 模型拓撲(如下圖 6 所示)在機器學習分布式 SGD 中非常常見,torch.distributed 和 Horovod 就是例子。一般來說,在一個計算機叢集上啟動這種工作負載是非常具有挑戰性的;Fiber 提供 Ring 特性就是為了幫助建立這樣的拓撲。
圖 6:在一個有四個節點的 Fiber Ring 中,Ring 節點 0 和 Ring 節點 3 運作在同一台機器上,但在兩個不同的容器中。Ring 節點 1 和節點 2 都在單獨的機器上運作。所有這些程序共同運作同一函數的副本,并在運作期間互相通信。
應用程式
借助上述靈活的元件,我們現在可以使用 Fiber 建構應用程式了。在這一節中,我們将展示兩種使用 Fiber 幫助使用者建構分布式應用程式的方式。
賦能新應用程式
在下面的例子中,我們将展示工程師如何運用 Fiber 來實作大規模分布式計算。這個例子示範的是一個 強化學習(RL)算法。通常,分布式 RL 的通信模式涉及在機器之間發送不同類型的資料,包括動作、神經網絡參數、梯度、per-step/episode 觀察及獎勵。
Fiber 實作了管道和池來傳輸這些資料。在底層,池是普通的 Unix 套接字,為使用 Fiber 的應用程式提供接近線路速度的通信。現代計算機網絡的帶寬通常高達每秒幾百千兆。通過網絡傳輸少量資料 通常速度很快。
此外,如果有許多不同的程序向一個程序發送資料,程序間通信延遲也不會增加太多,因為資料傳輸可以并行進行。事實證明,Fiber 池可以作為許多 RL 算法的基礎,因為模拟器可以在各個池工作程序中運作,并且結果可以并行回傳。
下面的示例顯示了使用 Fiber 實作的簡化 RL 代碼:
# fiber.BaseManager is a manager that runs remotely
class RemoteEnvManager(fiber.managers.AsyncManager):
pass
class Env(gym.env):
# gym env
pass
RemoteEnvManager.register(‘Env’, Env)
def build_model():
# create a new policy model
return model
def update_model(model, observations):
# update model with observed data
return new_model
def train():
model = build_model()
manager = RemoteEnvManager()
num_envs = 10
envs = [manager.Env() for i in range(num_envs)]
handles = [envs[i].reset() for i in num_envs]
obs = [handle.get() for handle in handles]
for i in range(1000):
actions = model(obs)
handles = [env.step() for action in actions]
obs = [handle.get() for handle in handles]
model = update_model(model, obs)
複制
賦能現有的多處理應用程式
許多 Python 使用者利用了多處理。Fiber 為此類應用程式提供了更多的機會,通過這種系統,隻需更改幾行代碼,就可以在類似于 Kubernetes 的計算機叢集上的分布式設定中運作。
例如,OpenAI Baselines 是一個非常流行的 RL 庫,它有許多參考算法,比如 DQN 和 PPO。它的缺點是隻能在一台機器上工作。如果希望大規模地訓練 PPO,就必須建立自己的基于 MPI 的系統并手動設定叢集。
相比之下,有了 Fiber,事情就簡單多了。它可以無縫地擴充像 PPO 這樣的 RL 算法,進而利用分布式環境的數百個工作程序。Fiber 提供了與多處理相同的 API,OpenAI Baselines 就是使用這些 API 在本地擷取多核 CPU 的處理能力。要讓 OpenAI Baselines 使用 Fiber,隻需要修改 一行代碼:
修改完這行代碼,OpenAI Baselines 就可以在 Kubernetes 上運作了。我們在 這裡 提供了在 Kubernetes 上運作 OpenAI Baselines 的完整指南。
錯誤處理
Fiber 實作了基于池的錯誤處理。在建立新池時,還将建立關聯的任務隊列、結果隊列和挂起表。然後,使用者可以将新建立的任務添加到任務隊列中。該任務隊列由主程序和工作程序共享。每個工作程序從任務隊列中擷取一個任務,然後在該任務中運作任務函數。每當使用者從任務隊列中删除一個任務時,Fiber 就會在挂起表中添加一個條目。工作程序完成該任務後會将結果放入結果隊列中。然後,Fiber 從挂起表中删除與該任務相關的條目。
圖 7:上圖是一個包含四個工作程序的普通 Fiber 池。在下圖,Worker 3 出現故障,是以 Fiber 啟動一個新的工作程序(Worker 5),然後準備将其添加到池中。
如果池裡有一個工作程序在處理過程中失敗,如上圖 7 所示,父池作為所有工作程序的程序管理器将會檢測到該失敗。然後,如果這個失敗的程序有挂起任務,則父池會将挂起表中的挂起任務放回到任務隊列中。接下來,它啟動一個新的工作程序來替換之前失敗的程序,并将新建立的工作程序綁定到任務隊列和結果隊列。
性 能
Fiber 最重要的應用之一是擴充計算算法(如 RL) 和基于群體的方法(如 ES)。在這些應用程式中,延遲非常關鍵。RL 和基于群體的方法經常應用于需要與模拟器(如 ALE、Gym 和 Mujoco)頻繁互動以評估政策和收集經驗的設定中。等待模拟器結果所帶來的延遲嚴重影響了整體的訓練性能。
為了測試 Fiber,我們将其性能與其他架構進行了比較。我們還在架構開銷測試中增加了 Ray,以提供一些初步結果,并希望在将來添加更詳細的結果。
通常有兩種方法可以減少 RL 算法和基于群體的方法的延遲。要麼我們可以減少需要傳輸的資料量,要麼我們可以提升不同程序之間通信通道的速度。為了加快通信處理,Fiber 使用 Nanomsg 實作了管道和池。此外,使用者還可以使用 speedus 這樣的庫進一步提高性能。
架構開銷
通常,架構中的元件會影響計算資源,是以,我們測試了 Fiber 的開銷。我們比較了 Fiber、Python 多處理庫、Apache Spark、Ray 和 ipyparallel。在測試過程中,我們建立了一批工作負載,完成這些任務所需的總時間是固定的。每個任務的持續時間從 1 秒到 1 毫秒不等。
對于每個架構,我們在本地運作了 5 個工作程序,并通過調整批次的大小來確定每個架構的總耗時大約為 1 秒(即 1 毫秒的任務,我們運作了 5000 個)。我們假設,Fiber 的性能應該和多處理差不多,因為 Fiber 和多處理都不依賴于複雜的排程機制。相反,我們認為 Apache Spark、Ray 和 ipyparallel 會比 Fiber 慢,因為它們中間依賴于排程器。
圖 8:在測試 Fiber、Python 多處理庫、Apache Spark 和 ipyprallel 的架構開銷時,我們在本地運作了 5 個工作程序,并調整批次大小,使每個架構在大約 1 秒鐘内完成任務。
當任務持續時間為 100 毫秒或更多時,Fiber 幾乎沒有表現出任何差異,當任務持續時間降至 10 毫秒或 1 毫秒時,它比其他架構更接近多處理庫。
我們以多處理作為參考,因為它非常輕量級,除了建立新程序和并行運作任務外沒有實作任何其他特性。此外,它還利用了僅在本地可用的通信機制(例如共享記憶體、Unix 域套接字等)。這使得支援分布式資源管理系統的其他架構難以超越多處理,因為這些系統無法利用類似的機制。
圖 9:我們的開銷測試顯示,Fiber 的執行情況與 Python 多處理庫類似,在 1 毫秒處,ipyparallel 和 Apache Spark 處理任務的耗時更長。最佳完成時間為 1 秒。
與 Fiber 相比,ipyparallel 和 Apache Spark 在每個任務持續時間上都落後很多。當任務持續時間為 1 毫秒時,ipyparallel 花費的時間幾乎是 Fiber 的 24 倍,Apache Spark 花費的時間是後者的 38 倍。顯然,當任務持續時間較短時,ipyparallel 和 Apache Spark 都引入了相當大的開銷,而且,對于 RL 和基于群體的方法,它們不如 Fiber 合适,後者使用了模拟器,響應時間隻有幾毫秒。我們還可以看到,在運作 1 毫秒的任務時,Ray 花費的時間大約是 Fiber 的 2.5 倍。
分布式任務測試
為了探究 Fiber 的可伸縮性和效率,我們将其與 ipyparallel 進行了比較,由于之前的性能測試結果,我們沒有考慮 Apache Spark。我們也排除了 Python 多處理庫,因為它不能擴充到一台機器之外。我們運作了 50 次 進化政策疊代(ES),根據它們的耗時對比了 Fiber 和 ipyparallel 的可伸縮性和效率。
在工作負載相同的情況下,我們預計 Fiber 可以完成得更快,因為前面已測試過,它的開銷比 ipyparallel 小得多。對于 Fiber 和 ipyparallel,我們使用的群體大小為 2048,是以,無論工作程序的數量多少,總計算量都是固定的。我們還在兩者中實作了相同的 共享噪音表,每八個工作程序共享一個噪音表。這項工作的實驗域是 OpenAI Gym Bipedal Walker Hardcore 環境的一個修改版本,這裡 對修改進行了描述。
圖 10:當 ES 疊代 50 次以上時,使用不同數量的工作程序運作 ES,Fiber 的擴充性均優于 ipyparallel。每個工作程序在單個 CPU 上運作。
主要結果是,Fiber 的擴充性比 ipyparallel 更好,并且完成每次測試的速度明顯更快。随着工作程序數從 32 增加到 1024,Fiber 的運作時間逐漸縮短。相比之下,當工作程序數從從 256 增加到 512 時,ipyparallel 的運作時間逐漸變長。在使用 1024 個工作程序時,由于程序之間的通信錯誤,ipyparallel 未能完成運作。這個失敗削弱了 ipyparallel 運作大規模并行計算的能力。根據 Amdahl 定律,我們看到,當工作程序數增加到 512 以上時,Fiber 的收益會減少。在這種情況下,主程序處理資料的速度就會成為瓶頸。
總的來說,在所有工作程序數的測試中,Fiber 的性能都超過了 ipyparallel。此外,與 ipyparallel 不同的是,Fiber 在運作 1024 個工作程序時也完成了這項工作。這個結果更能顯示出 Fiber 與 ipyparallel 相比具有更好的可伸縮性。
結 論
Fiber 是一個新的 Python 分布式庫,現已 開源。我們設計它是為了讓使用者能夠在一個計算機叢集上輕松地實作大規模計算。實驗表明,Fiber 實作了我們的許多目标,包括有效地利用大量的異構計算硬體,動态地伸縮算法以提高資源使用效率,以及減少在計算機叢集上運作複雜算法所需的工程負擔。
我們希望,Fiber 将進一步加快解決工程難題的進展,使開發方法并大規模地運作以了解其好處變得更容易。
要了解更多細節,請檢視 Fiber GitHub 庫:https://github.com/uber/fiber
檢視英文原文:
https://eng.uber.com/fiberdistributed/