參數服務器——分佈式機器學習的新殺器

轉自:微信公衆號 數據極客
在大規模數據上跑機器學習任務是過去十多年內系統架構師面臨的主要挑戰之一,許多模型和抽象先後用於這一任務。從早期的MPI,到後來的Hadoop,乃至於目前使用較多的Spark,都曾被廣泛應用於大規模機器學習處理任務。John Langford曾經在他的博客(機器學習領域最好的博客之一)上總結和對比了主流的分佈式機器學習框架的抽象[1]:
MPI Gradient Aggregation:主要缺點是批任務求解器的速度不高,另外是MPI本身的問題無法支撐大規模數據。
MapReduce:解決了MPI無法支撐大數據的問題,但無法改進批處理求解器的訓練性能,並且還引入了新的問題,包括迭代式計算的低效,節點之間通信低效。
基於圖的抽象:由CMU推出的GraphLab是這方面的佼佼者,目前已經成立了Dato公司專門推廣基於圖的大規模機器學習系統。用圖來做抽象可以解決許多機器學習問題,但仍然有許多問題無法很好高效求解,比如深度學習中的多層結構。
Parameter Server參數服務器:跟基於圖的方法主要區別在於把模型參數存儲和更新上升爲主要組件,並且採用了異步機制提升處理能力,這是本文主要介紹的模型。
AllReduce:AllReduce本身就是MPI的原語,這其實是最顯然和直接的分佈式機器學習抽象,因爲大部分算法的結構都是分佈數據,在每個子集上面算出一些局部統計量,然後整合出全局統計量,並且再分配給各個節點去進行下一輪的迭代,這樣一個過程就是AllReduce。AllReduce跟MapReduce有類似,但後者採用的是面向通用任務處理的多階段執行任務的方式,而AllReduce則讓一個程序在必要的時候佔領一臺機器,並且在所有迭代的時候一直跑到底,來防止重新分配資源的開銷,這更加適合於機器學習的任務處理。AllReduce跟參數服務器都會是機器學習算法框架的重要抽象,DMLC組陳天奇開發的Rabit框架[2]是AllReduce模型的良好實現之一,其餘的當然可以藉助於vw這樣的系統,它們都能直接運行在Hadoop上。基於同步的AllReduce模型並不是本文討論的重點,我們只需要知道它很重要,很多算法離不了。

機器學習算法和計算機領域的其他算法相比,有自己的一些獨特特點。例如:迭代性,模型的更新並非一次完成,需要循環迭代多次;容錯性,即使在每個循環中產生一些錯誤,模型最終的收斂不受影響;參數收斂的非均勻性,模型中有些參數經過幾個循環便不再改變,其他參數需要很長時間收斂。這些特點決定了機器學習系統的設計和其他計算系統的設計有很大不同,因此理想中的分佈式機器學習任務,並不能隨着機器的增加而能力線性提升,因爲大量資源都會浪費在通訊,等待,協調,這些時間可能會佔據大部分比例。參數服務器就是被提出來專門用於大規模最優化處理的框架,它特定用於這種需求:大規模的訓練數據,比如TB甚至PB級別的;大規模的模型參數,在大規模的優化框架中,常常會有數十億乃至千億級別的參數需要估計。因此,在設計面臨這種挑戰的系統時,比如大規模深度學習系統,大規模Logistic Regression系統,大規模主題模型,大規模矩陣分解等等依賴於SGD或者L-BFGS最優化的算法,需要解決頻繁訪問修改模型參數時所需消耗的巨大帶寬,以及如何提高並行度,減少同步等待造成的延遲,還有容錯等挑戰。

參數服務器的概念最早來自Alex Smola於2010年提出的並行LDA的框架[4]。它通過採用一個分佈式的Memcached作爲存放參數的存儲,這樣就提供了有效的機制用於在分佈式系統不同的Worker節點之間同步模型參數,而每個Worker只需要保存它計算時所依賴的一小部分參數即可。當然,這裏存放參數的存儲跟做OLTP應用中的Key-Value抽象有所不同,因爲以Key-Value爲單元進行頻繁的參數數據交互會導致過高的通信開銷,因此參數服務器通常採用數學封裝來進行參數同步,比如向量,張量,矩陣的行列等。

這裏寫圖片描述

上圖的sampler是並行LDA裏的組件,可類比爲通用參數服務器框架裏的計算單元。Smola提出的模型是最早的參數服務器抽象,隨後出現了許多改進,最出名的應當是Google的跨界高人Jeff Dean 2012年進一步提出了第一代Google大腦的解決方案DistBelief[5],主要用於超大規模深度學習網絡的訓練。DistBelief將巨大的深度學習模型分佈存儲在全局的參數服務器中,計算節點通過參數服務器進行信息傳遞,很好地解決了SGD和L-BFGS算法的分佈式訓練問題。由於SGD和L-BFGS是機器學習的普遍性優化問題,因此儘管DistBelief是作爲深度學習的系統框架而提出,但DistBelief的核心結構卻可以應用到多種普通機器學習手段中。相比最早的參數服務器模型,DistBelief把該模型擴展成爲更加通用和靈活的框架,豆瓣的Paracel[3]正是參考DistBelief的直接實現,先來看看Paracel和DistBelief模型:

這裏寫圖片描述

圖中是分佈式異步SGD架構流程圖,運行時,需要把訓練數據分爲多個子集,然後在每個子集上運行模型的多個副本,模型通過集中式的參數服務器通信,參數服務器存放了模型的全部參數和狀態。異步體現在兩方面:模型的副本獨立運行;參數服務器的分片也各自獨立運行。DistBelief沒有過多談論系統實現,從Paracel裏我們可以看到具體的工程實現:總體上Paracel實現非常簡單,參數服務器直接採用內存hashtable,並封裝了對分網絡,圖,稀疏矩陣,稠密矩陣等數據格式用於參數同步。Paracel解決的另一問題是straggler問題:由於一些軟硬件的原因,節點的計算能力往往不盡相同。對於迭代問題來說,每一輪結束時算得快的節點都需等待算得慢的節點算完,再進行下一輪迭代。這種等待在節點數增多時將變得尤爲明顯,從而拖慢整體的性能。Paracel放寬了“每個迭代步都等待”這個約束:當在一輪迭代結束時,算得快的節點可以繼續下一輪迭代,但不能比最慢的節點領先參數s個迭代步。當領先超過s個迭代步,Paracel纔會強制進行等待。這樣異步的控制方式既從整體上省去了等待時間,也能間接地幫助慢的節點趕上。從優化問題的角度來看,雖然單迭代步收斂得慢了,然而每個迭代步的時間開銷變少了,總體上收斂也就變快了。這種做法又叫Staleness Synchronous Parallel (SSP),基本思想是允許各機器以不同步調對模型進行更新,但是加一個限制,使得最快的機器的進度和最慢機器的進度之差不要太大。這樣做的好處是:既減輕慢的機器拖整個系統的後腿,又能保證模型的最終收斂。

這裏寫圖片描述

SSP是相對於BSP(Bulk Synchronous Parallel)來說的,BSP是上世紀八十年代就提出的,它要求算法在每一次迭代結束後都要同步等待,因此會因爲最慢的機器拖慢整個系統。BSP在絕大部分已有的分佈式機器學習和數據挖掘框架框架中都在使用,例如Spark MLBase,Google Pregel,Apache Hama等。

這裏寫圖片描述

SSP是由CMU Eric Xing的Petuum項目組提出的[6],Paracel引入SSP使得豆瓣的參數服務器方案工程上更加成熟,在Paracel內部,SSP的等待通過調用MPI來實現。關於一致性收斂和Petuum,在下邊還會有介紹。關於參數服務器,另一個重要的方面是容錯設計。在幾十臺機器的集羣上運行,這也許並不是一個問題,但是如果在有上千臺機器的集羣上運行任務,節點發生任務失敗的概率就會大很多,如果缺乏容錯設計,就會導致任務重啓,從而浪費大量時間。不過,在Paracel的代碼裏並沒有找到相關的處理邏輯,通常容錯處理需要藉助於Checkpoint來做快照,這樣任務重啓時無需從頭進行,比如DistBelief就是這樣處理。跟豆瓣的工程師諮詢後已經確認,在開源版本的Paracel裏確實還沒有相關設計。

上面講述了不少參數服務器的背景和系統結構,那麼爲什麼參數服務器能夠具備更好的性能呢?仍以SGD爲例說明:在傳統同步SGD中,如果一臺機器失效,整個訓練過程將會延時;但是對於異步SGD來講,如果某個模型副本的一臺機器失效,其他模型副本仍然繼續處理樣本並更新參數服務器中的模型參數,因此異步SGD具備更好的魯棒性。此外,多種異步處理形式給最優化過程帶來進一步的隨機性:模型實例最可能是使用一個稍微過時的參數來計算梯度,因爲這時其他的副本可能已經更新了參數服務器上的參數。除此之外還有其他隨機的來源,因爲參數服務器組的每臺機器是行爲獨立的,所以無法保證在給定時間點上,每個節點的參數被更新的次數相同,或者以同樣的順序被更新。更進一步,因爲模型副本使用不同的線程來獲取參數和推送梯度值,故在同一時間戳上,單個副本內的參數將有額外的稍微不一致的現象。儘管對於非凸問題的這些操作的安全性缺乏理論基礎,但是在實踐中,這種放鬆一致性要求的做法是相當有效的。傳統同步SGD的最優化過程,每次迭代選取的方向是由全部訓練數據決定,或者由隨機選定的一小部分訓練集指定(mini-batch)。而異步的做法由於上述更多的隨機性則會同時在很多方向上由不同的mini-batch選定不同梯度方向,這就好比整個最優化過程是以一個區域爲單位進行的,而區域內的點代表不同SGD的過程,因此這種並行化的工作會帶來性能上的提升。

豆瓣的Paracel並不是唯一一種開源的參數服務器,這裏繼續介紹另外一個重要項目,來自Alex Smola的高徒——李沐設計的參數服務器[7]。這個項目在早期擁有一個獨立域名http://parameterserver.org,後來因爲李沐和陳天奇等國內英才成立的DMLC深度學習項目組,之前的項目也進行了重構因此轉移到[7]所在的地址,而項目的背景介紹則在[8]和[9]。從架構上來說,ps-lite跟Paracel並沒有什麼不同,作爲參數服務器,都需要一個全局分佈式的key-value用來存儲算法的模型或參數。當計算節點需要某個參數的時候,可以從參數服務器上讀取。用戶可定義不同的函數在參數服務器端對參數進行更新、過濾等操作。在大部分情況下,計算節點之間的通信都是通過參數服務器進行。圖中W代表計算節點,X代表參數服務器節點。

這裏寫圖片描述

根據作者的宣傳,ps-lite應當屬於第三代參數服務器,就是提供了更加通用的設計,在架構上包含一個Server Group和若干個Worker Group:
Server Group用來做參數服務器,每個Server Node存放一個參數分片,由Server Manager管理整個Server Group,維持整個Server Group的元數據的一致性視圖,以及參數分片情況。
每個Worker Group運行一個應用,Worker Node只跟Server Node通信用來更新參數,Worker Node之間沒有任何交互。每個Worker Group內有一個調度器,負責給Worker Nodes分配任務以及監控,如果有Worker Node掛掉或者新加入,調度器負責重新調度剩餘的任務。

這裏寫圖片描述

跟Paracel不同,ps-lite提供了多種數據一致性選擇:
Sequential:這種情況下,所有任務順序進行,因此數據嚴格一致,不會出現不同副本看到的數據有不同的情況,因此實際上跟前文介紹的BSP是等價的。
Eventual:這種情況下,所有任務並行執行,因此擁有最大的隨機性。Eventual只適用於對於數據一致沒有要求,非常健壯的算法,比如SGD。
Bounded Delay:每個任務需要設置最大超時時間,在該時間之前如果有任務未結束,那麼新任務將會等待。Bounded Delay類似於上面的SSP,只不過這是用時間而SSP則是用迭代次數。
在容錯設計方面,ps-lite通過給參數服務器引入多副本機制來提供:整個模型參數按照一致哈希分片存儲,在默認情況下,採用鏈式複製確保參數多副本,如下圖所示:

這裏寫圖片描述

鏈式複製會導致網絡帶寬佔用增加數倍,而ps-lite還提供了另外一種容錯設計:先聚合再複製。聚合的意思是在機器學習算法中,參數在很多時候是可以累加的,比如梯度。採用先聚合再複製的方式,可以降低網絡帶寬佔用。

這裏寫圖片描述

根據作者在Logistic Regression上的測試,ps-lite可以比傳統實現所佔用時間縮短1到兩個數量級。

這裏寫圖片描述

ps-lite目前在DMLC項目中處於核心基礎地位,因爲大部分的分佈式機器學習算法都會基於它來進行,包括DMLC最新推出的熱門深度學習框架MXNet。ps-lite的代碼整體非常簡單,便於修改和移植,而且DMLC項目組目前也給它增加了資源管理器的集成,使得Yarn能夠來管理參數服務器的資源分配。
下面再來介紹由CMU機器學習系領頭人Eric Xing帶領的小組推出的參數服務器Petuum。Eric Xing和Smola和李沐同來自CMU,但卻做出了兩份獨立的工作,箇中緣由不在本文八卦之行列,畢竟跟雙方都不熟悉。因此這裏只談論技術。事實上,Petuum是最早開源的參數服務器,其目的都是在DistBelief之後期望推出通用的參數服務器設計。跟Paracel和ps-lite一樣,Petuum也採用C++開發,Eric Xing據此給出的解釋是目前Petuum仍然處於原型階段,是學術產品,所以沒有考慮通用的語言如Java等。Petuum目前分成幾個子項目,分別包含了參數服務器Bosen,以及基於Bosen和Caffe的分佈式深度學習系統Poseidon,後者不是本文介紹的範圍。Bosen的系統設計建立於機器學習的特徵之上,目前包含兩個主要模塊:Key Value Store和Scheduler,一致性協議是上文介紹過的SSP。通過調節SSP的staleness參數,SSP可以轉化成數據流系統常用的BSP(Bulk Synchronous Parallel) 協議或者早期機器學習系統(如Yahoo LDA)使用的ASP(Asynchronous Parallel)。Scheduler用於模型並行,它提供的編程接口主要包含三個操作:
Schedule: 調度節點根據模型參數的相互依賴性和收斂的不均勻性,自動選擇一個待更新的參數子集。
Push: 調度節點令計算節點並行地爲選好的參數計算Update。
Pull:調度節點從計算節點收集Update,並更新參數。

這裏寫圖片描述

Petuum/Bosen的架構如上圖所示。跟其他的參數服務器並沒有大的差別,但模塊化設計更加良好,比如一致性模型,調度這些重要功能都放入單獨組件。比較遺憾的是Petuum/Bosen也沒有在容錯設計上有所考慮,這跟Eric Xing宣稱的原型系統也相吻合,因此跟Paracel類似,Bosen目前主要適用於幾十臺機器的集羣,在更大集羣上處理有風險。

從代碼上來看,Bosen的結構相比Paracel和ps-lite都要複雜不少,主要原因是Bosen在所有的組件,包括存儲,調度,還有Worker上面都是多線程實現,調度器的設計更爲複雜,因爲對機器學習模型參數的更新進行細粒度的調度,能根據參數的優先級自動調整更新次序,並根據參數的相關性防止不安全的並行。我們來進一步分析功能:

這裏寫圖片描述

如上圖所示,在Bosen中,SSP跟參數服務器封裝在一起,稱爲SSPTable,供多個Worker節點以類似分佈式共享內存的方式訪問,讓Worker節點操作SSPTable跟操作本地內存一樣。Worker通過SSPTable更新參數服務器的同時,SSPTable同時更新SSP一致性控制器內部的設置。SSPTable是參數服務器節點運行的主體結構,Bosen在每個參數服務器節點上可以啓動多個SSPTable,這是它顯著區別於ps-lite和Paracel的地方之一。之所以這樣設計,是因爲Petuum項目需要根據不同的算法來配置並行任務。例如,Petuum的示例程序中,運行深度學習DNN算法,就配置了神經網絡層數兩倍的SSPTable數量,而其他一些算法比如隨機森林,矩陣分解,只配置了2個SSPTable。究竟應當如何配置並沒有從Petuum的文檔和代碼中找到說明,因爲本文只是介紹性文字,故而這裏不去深究,在使用中需要注意這一點。SSPTable內部運行若干Background線程,來運行SSP控制邏輯,線程的數目跟SSPTable數保持一致。Bosen實現了多種SSP模型,默認的SSP實現需要根據客戶端(運行在參數服務器節點)彙報的時鐘計數來決策,時鐘在這裏代表機器學習算法的計算單元,比如一次迭代。其他還有SSP Push和SSP Aggregation,前者只是在SSP基礎之上提供額外的接口用於異步推送SSPTable的指定行數據,後者則修改了SSP的實現原語,如何使用這兩個模型目前還沒找到正式說明,使用SSP應當基本可以滿足全部需求。

接下來看看Bosen的調度器設計,這是Petuum項目區別於其他參數服務器的主要組件之一。它的主要思想是在並行的同時避免模型出錯,這個概念叫做Structure-Aware Parallelization(SAP)[11],在Petuum項目裏叫做Strads。

這裏寫圖片描述

Strads系統由若干Master節點,若干Worker節點和一個Cordinator節點構成。調度流程如下:Master執行Schedule接口,作用是選出可以並行的參數子集,這個過程可能需要Strads從參數服務器讀取參數數據。用來更新這些參數的任務被Cordinator通過Push接口下發到Worker節點進行計算,參數服務器通過Pull接口從Worker節點接收參數然後更新存儲。爲了更有效地執行任務,Strads把調度流程流水線化,Master無需等待Worker的結果就提前把任務準備好,Cordinator依次把待執行的任務下發到Worker。在決定如何並行從而避免模型出錯這方面,並不存在一個通用的做法,因爲這跟不同的模型有很大關係,因此Strads把這個工作留給不同算法來進行,例如對主題模型,Lasso,矩陣分解,都提供了相應的實現。

Eric Xing把他眼中的若干分佈式機器學習模型的適用場景做了個概要圖,可以看到,Petuum/Bosen的定位在於在較小集羣上運行,但同時需要大量參數(百億,千億級)的場景,而DistBelief和ps-lite這些工作是運行在大規模集羣上的參數服務器方案。至於爲何是這樣的結論(更大量參數),從架構上還沒有得出很明顯的結論,只能說Petuum項目相比其他參數服務器,對於更廣泛的機器學習算法上考慮更多,然而由於缺乏容錯機制,所以最好還是運行在獨立中小集羣之上(幾十臺服務器規模)。

這裏寫圖片描述

上面介紹了3種最知名的開源參數服務器,隨着這種模型爲更多人所接受,一些公司也推出了相關的框架和產品,例如微軟研究院[12]和英特爾[13]。下面分別簡要介紹一下:

微軟研究院的參數服務器是跟它在15年底開源的機器學習工具包DMTK一起發佈的,DMTK最知名的項目就是老師木主導的LightLDA,這是一個超大規模主題模型(百萬主題級別),在最初的版本中,LightLDA正是基於Petuum來開發的。DMTK內部的參數服務器項目叫做Multiverso,在架構上比較簡單,由於是來自研究院的項目,因此對大集羣,容錯等方面考慮得並不多。一致性模型方面,Multiverso包含BSP,SSP,以及ASP(Asynchronous Parallel),ASP就是指全異步,所有的任務相互沒有等待。在一致性模型的選擇上,Multiverso跟ps-lite類似。

英特爾的DistML是對Spark的通用機器學習庫MLLib的一個補充,它跟Spark和Hadoop的關係如下圖所展示。這是本文談論到目前爲止第一個JVM上的實現。DistML利用Spark RDD任務來運行Worker,在Spark之外新增了兩個組件,一個是Databus用於參數傳送,另一個就是參數服務器本身,基於Actor實現。儘管Spark RDD任務具備容錯功能,但參數服務器本身並沒有類似ps-lite那樣的多副本機制。由於Spark RDD的限制,DistML的參數更新只能在一次迭代完成後進行,因此這並不是嚴格意義上的參數服務器方案,所以也就談不上採用何種一致性模型,但DistML第一次在Spark集羣上增加了類似參數服務器的模型抽象,在網上看到有人用它嘗試過4億參數和200億維度的Logistic Regression,從這個角度來說,DistML也是滿足了許多依賴Spark進行算法開發的人的需求。

這裏寫圖片描述

除了以上這些參數服務器,Spark社區本身也有支持參數服務器的計劃[14],從issue的建議來看,Spark計劃採用可選的BSP和SSP作爲一致性模型,容錯設計採用Checkpoint定期刷盤。由於是真正的參數服務器模型,跟Spark RDD本質上的BSP有衝突,因此這需要對Spark核心的修改,目前的實現在[15],也許在Spark上使用參數服務器應當不會很久了。
這裏寫圖片描述
在商業化方面,國內的百度,阿里,今日頭條是已知大規模採用參數服務器的公司。阿里的材料可以從QCon 2015上海的演講中獲得:
這裏寫圖片描述
從上圖顯示,阿里的方案採用HBase作爲參數服務器,Worker則採用內部的數據流式計算引擎,並且只實現了DistBelief裏的異步SGD—Downpour SGD,因此,這個架構主要是服務在線實時計算,而不是通用的參數服務器。另據稱今日頭條的參數服務器集羣已達到4000臺規模,百度也是拿C++自行研發專用集羣,這些公司並沒有採用開源方案,主要原因應當還是在於超大規模集羣上的容錯處理,因此,這兩家公司目前在大規模集羣應用參數服務器的經驗屬於領先(阿里的上述方案目前只用於特定應用的在線實時計算,在容錯架構上不需更多複雜的設計)。

這篇文章是本人在2014年就有想法撰寫的,一直拖拉到了2016年初才完成初步輪廓。短短的一年多內,參數服務器發生了巨大的變化:DMLC項目組的橫空出世,Petuum的重構,Paracel的開源,以及其他衆多公司的工作,並且這些工作全部是國人或者國人主導的項目。究其原因,是因爲在最熱門的機器學習算法中,包括深度學習,推薦引擎,計算廣告等領域,參數服務器相對於其他抽象都具有無可比擬的優勢,再加上其架構實現簡單,因此出現這麼多的可選就並不是一件意外的事情了。在爲自己的項目引入參數服務器的同時,需要深刻了解每種方案背後的應用場景。在當前來說,所有的開源方案,都還不具備在大規模商業集羣上使用的能力,因此這也是爲何會有公司自己攢相關輪子的緣故。如果你想打造自己的輪子,DMLC的ps-lite是一個好的起點;如果你對機器學習算法很精通並且願意探索更優秀的性能,你值得在Petuum基礎上進一步研究;如果你只想等着在已有中等規模集羣上使用,那麼可以等待Spark社區。可以預計,在接下來的一兩年中,相關的工作會更快地推進,尤其是在可運維性和容錯方面有更加周到的考慮,這也是基礎架構領域值得大幹特幹的若干領域之一。

[1] http://hunch.net/?p=151364
[2] https://github.com/dmlc/rabit
[3] http://paracel.io/
[4] Alex Smola, An Architecture for Parallel Topic Models. VLDB, 2010
[5] Jeff Dean. Large scale distributed deep networks. In NIPS, 2012
[6] Solving the stragglerproblem with bounded staleness. In HotOS (2013).
[7] https://github.com/dmlc/ps-lite
[8] Mu Li, Dave Andersen, Alex Smola, Scaling Distributed Machine Learning with the Parameter Server, In OSDI, 2014
[9] Mu Li, Zhou Li , Alex Smola, Parameter server for distributed machine learning, In NIPS, 2013
[10] Eric P Xing, Petuum: A New Platform for Distributed Machine Learning on Big Data, In SIGKDD 2015
[11] Seunghak Lee, Jin Kyu Kim, Xun Zheng, Qirong Ho, Garth A. Gibson, and Eric P. Xing, On Model Parallelization and Scheduling Strategies for Distributed Machine Learning, In NIPS 2014
[12] https://github.com/Microsoft/DMTK
[13] https://github.com/intel-machine-learning/DistML
[14] https://issues.apache.org/jira/browse/SPARK-6932
[15] https://github.com/chouqin/spark/tree/ps-on-spark-1.3

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章