Uber開發了POET、Go-Explore和GTN等算法,這些算法利用大量的計算來訓練神經網絡模型。爲了使未來幾代類似算法的大規模計算成爲可能,Uber進而開發了一種新的分佈式計算庫Fiber,它可以幫助用戶輕鬆地將本地計算方法擴展到成百上千臺機器上。Fiber可以使使用Python的大規模計算項目變得快速、簡單和資源高效,從而簡化ML模型訓練過程,並獲得更優的結果。
本文最初發佈於Uber工程博客,由InfoQ中文站翻譯並分享。
項目地址: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協同。
Fiber可以幫助從事大規模分佈式計算的用戶減少從產生想法到在計算集羣上實際運行分佈式作業的時間。它還可以幫助用戶屏蔽配置和資源分配任務的繁瑣細節,並且可以縮短調試周期,簡化從本地開發到集羣開發的轉換。
架 構
Fiber讓我們可以靈活地爲經典的多處理API選擇可以在不同集羣管理系統上運行的後端。爲了實現這種集成,Fiber被分爲三個不同的層:API層、後端層和集羣層。API層爲Fiber提供了進程、隊列、池和管理器等基本構建塊。它們具有與多處理相同的語義,但是我們對它們進行擴展了,使它們可以在分佈式環境中工作。後端層處理在不同集羣管理器上創建或終止作業的任務。當用戶新增一個後端時,所有其他Fiber組件(隊列、池等)都不需要更改。最後,集羣層由不同的集羣管理器組成。儘管它們不是Fiber本身的一部分,但是它們幫助Fiber管理資源並跟蹤不同的作業,減少了Fiber所需要跟蹤的項的數量。圖2是總體架構圖:
作業支持進程
Fiber引入了一個新的概念,稱爲作業支持過程(也稱爲Fiber進程)。這些進程與Python多處理庫中的進程類似,但是更靈活:多處理庫中的進程只在本地機器上運行,但Fiber進程可以在不同的機器上遠程運行,也可以在同一機器上本地運行。當新的Fiber進程啓動時,Fiber會在當前計算機集羣上創建一個具有適當Fiber後端的新作業。
Fiber使用容器來封裝當前進程的運行環境(如上圖3所示),其中包括所有必需的文件、輸入數據和其他依賴的程序包,而且要保證每個元素都是自包含的。所有子進程都以與父進程相同的容器鏡像啓動,以確保運行環境的一致性。因爲每個進程都是一個集羣作業,所以它的生命週期與集羣上的任何作業相同。爲了方便用戶,Fiber被設計成直接與計算機集羣管理器交互。因此,不像Apache Spark或ipyparallel,Fiber不需要在多臺機器上設置,也不需要通過任何其他機制引導。它只需要作爲一個普通的Python pip包安裝在一臺機器上。
組 件
Fiber基於Fiber進程實現了大多數多處理API,包括管道、隊列、池和管理器。
Fiber中隊列和管道的行爲方式與多處理相同。不同之處在於,Fiber中的隊列和管道由運行在不同機器上的多個進程共享。兩個進程可以從同一個管道讀取和寫入數據。此外,隊列可以在不同機器上的多個進程之間共享,每個進程可以同時向同一隊列發送或從同一隊列接收信息。Fiber隊列是用高性能異步消息隊列系統Nanomsg實現的。
Fiber也支持池,如下圖5所示。它們讓用戶可以管理工作進程池。Fiber使用作業支持進程擴展池,以便每個池可以管理數千個(遠程)工作進程。用戶還可以同時創建多個池。
管理器和代理對象使Fiber能夠支持共享存儲,這在分佈式系統中至關重要。通常,這個功能由計算機集羣外部存儲系統如Cassandra和Redis提供。相反,Fiber爲應用程序提供了內置的內存存儲。該接口與多處理系統中的管理器類型接口相同。
Ring是對多處理API的擴展,可以用於分佈式計算設置。在Fiber中,Ring指的是一組共同工作的、相對平等的進程。與池不同,Ring沒有主進程和輔助進程的概念。Ring內的所有成員承擔大致相同的責任。Fiber的Ring模型拓撲(如下圖6所示)在機器學習分佈式SGD中非常常見,torch.distributed和Horovod就是例子。一般來說,在一個計算機集羣上啓動這種工作負載是非常具有挑戰性的;Fiber提供Ring特性就是爲了幫助建立這樣的拓撲。
應用程序
藉助上述靈活的組件,我們現在可以使用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最重要的應用之一是擴展計算算法(如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慢,因爲它們中間依賴於調度器。
當任務持續時間爲100毫秒或更多時,Fiber幾乎沒有表現出任何差異,當任務持續時間降至10毫秒或1毫秒時,它比其他框架更接近多處理庫。
我們以多處理作爲參考,因爲它非常輕量級,除了創建新進程和並行運行任務外沒有實現任何其他特性。此外,它還利用了僅在本地可用的通信機制(例如共享內存、Unix域套接字等)。這使得支持分佈式資源管理系統的其他框架難以超越多處理,因爲這些系統無法利用類似的機制。
與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環境的一個修改版本,這裏對修改進行了描述。
主要結果是,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庫。
查看英文原文: