1. 程式人生 > >引數伺服器——分散式機器學習的新殺器

引數伺服器——分散式機器學習的新殺器

轉載自:資料極客
在大規模資料上跑機器學習任務是過去十多年內系統架構師面臨的主要挑戰之一,許多模型和抽象先後用於這一任務。從早期的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