sona:Spark on Angel大規模分佈式機器學習平臺介紹

Angel是一個基於參數服務器(Parameter Server)開發的高性能分佈式機器學習平臺,它基於騰訊內部的海量數據進行了反覆的調優。
Angel的核心設計理念圍繞模型,將高維度的大模型切分到多個參數服務器節點,並通過高效的模型更新接口和運算函數,以及靈活的同步協議,輕鬆實現各種高效的機器學習算法。
Angel基於Java和scala開發,能在Yarn上直接調度運行,並基於PS Service,支持Spark on Angel,集成了部分圖計算和深度學習算法。
Angel-PS實現了基於參數服務器的矩陣計算,將分佈在多臺PS Server上的參數矩陣抽象爲PSModel,只需要完成PSModel的定義、實現其計算過程,就可以實現一個運行在參數服務器上的簡單算法。
簡單的angel-ps架構如下圖所示
  • PS是存儲矩陣參數的多臺機器,向計算節點提供矩陣參數的拉取、更新服務
  • 每個worker是一個邏輯計算節點,一個worker可以運行一或多個task
機器學習的算法,一般以迭代的方式訓練,每次迭代worker從PS拉取最新的參數,計算一個更新值,推送給PS
Angel整體架構
angel的架構分爲三大模塊:
1、 Parameter Server層:提供通用的參數服務器服務,負責模型的分佈存儲,通訊同步和協調計算,並通過PSAgent提供PS Service
2、worker層:基於Angel自身模型設計的分佈式運行節點,自動讀取並劃分數據,局部訓練出模型增量,通過PS Client和PS Server通信,完成模型訓練和預測。一個worker包含一個或多個Task,Task是Angel計算單元,這樣設計的原因是可以讓Task共享Worker的許多公共資源。
3、Model層,這是一層虛擬抽象層,並非真實存在的物理層。關於Model的Push和Pull,各種異步控制,模型分區路由,自定義函數。。。是聯通Worker和PSServer的橋樑。
除了這三個模塊,還有2個很重要的類,
1、Client:Angel任務運行的發起者
  • 啓動和停止PSServer
  • 啓動和停止Angel的Worker
  • 加載和存儲模型
  • 啓動具體計算過程
  • 獲取任務運行狀態
2、Master:Angel任務運行的守護者
  • 原始計算數據以及參數矩陣的分片和分發
  • 向Gaia申請Worker和ParameterServer所需的計算資源
  • 協調,管理和監控Worker以及PSServer
 
通過上述設計,Angel實現了可擴展架構
  • PSServer層:通過PS-Service,提供靈活的多框架PS支持
  • Model層:提供PS必備的功能,並支持對性能進行針對性優化
  • Worker層:能基於Angel自主API,進行算法開發和創新的需求
 
Angel代碼結構
1、Angel-Core:核心層
Angel的核心層,包含如下組件:
  • PSServer
  • PSAgent
  • Worker
  • AngelClient
  • 網絡:RPC & RDMA
  • 存儲:Memory & Disk
2、Angel-ML:機器學習層
Angel是面向機器學習的,所以機器學習相關的元素也加入到core層,只是在一個單獨的目錄下
  • Matrix
  • Vector
  • Feature
  • Optimizer
  • Objective
  • Metric
  • psFunc
要注意的是,在Core包中的ML層,大都是底層的基礎接口,而相關的擴展和實現,還是在具體的算法層
3、Angel-client:接口層
基於Angel本身的接口,開發了多個算法:
  • SVM 
  • LR (各種優化方法)
  • KMeans
  • GBDT
  • LDA
  • MF(矩陣分解)
  • FM(因式分解機)
3.0版本又增加了很多算法,後續熟練後再補充
Angel的算法開發思路比較直接,都是圍繞着PS上的模型進行,不停更新。算法的實現技巧也比較靈活,追求最優的性能
整體上,通過這4個層級的代碼,用戶可以全方位的積木式進行代碼的設計,開發出高效的機器學習代碼
Angel在設計上,考慮到現有的機器學習框架衆多的問題,提供了2種模式。Angel支持兩種運行模式:ANGEL_PS_WORKER和ANGEL_PS_SERVICE。
通過這2種方式結合Angel本身的worker模式,追求最大化的性能優勢,主打速度。而PS-Service模式,主打對接,可以接入Spark,TensorFlow,Torch等平臺,主打生態,從而實現最大程度的靈活性
  • Angel_PS_Worker:啓動master,PS和Worker,Angel獨立完成模型的訓練
  • Angel_PS_service:PS Service模式,在這種模式下,Angel只啓動Master和PS,具體的計算交給其他計算平臺(如Spark,TensorFlow)負責,Angel只負責提供Parameter Server的功能
同步協議:
  • Angel支持多種同步協議:除了通用的BSP(Bulk Synchronous Parallel)外,爲了解決task之間互相等待的問題,Angel還支持SSP(Stale Synchronous Parallel)和ASP(Asynchronous Parallel)
易用性:
  • 訓練數據和模型自動切割:Angel根據配置的worker和task數量,自動對訓練數據進行切分,同樣,也會根據模型大小和PS實例數量,對模型實現自動分區。
  • 易用的編程接口:MLModel、PSModel、AngelClient
擴展性:
  • psFunc:爲了滿足各類算法對參數服務器的特殊需求,Angel將參數獲取和更新過程進行了抽象,提供了psf函數功能。用戶只需要繼承Angel提供的psf函數接口,並實現自己的參數獲取/更新邏輯,就可以在不修改Angel自身代碼的情況下定製自己想要的參數服務器的接口。
  • 自定義數據格式:Angel支持Hadoop的InputFormat接口,可以方便的實現自定義文件格式。
  • 自定義模型切分方式:默認情況下,Angel將模型(矩陣)切分成大小相等的矩形區域;用戶也可以自定義分區類來實現自己的切分方式
穩定性:
Angel保證在機器學習過程中,單個worker和PS掛掉,都不會影響整個作業的進度,而且能最快的找到備用的機器,快速啓動,加載模型或者數據,繼續訓練過程。
  • PS容錯:PS容錯採用了checkpoint模式,也就是每隔一段時間將PS承載的參數分區寫到hdfs上。如果一個PS實例掛掉,Master會新啓動一個PS實例,新啓動的實例會加載掛掉PS實例寫的最近的一個checkpoint,然後重新開始服務。這種方案的優點是簡單,藉助了hdfs多副本容災,缺點是不可避免的會丟失少量參數更新
  • worker容錯:一個worker實例掛掉後,Master會重新啓動一個Worker實例,新啓動的Worker實例從Master處獲取當前迭代輪數等狀態信息,從PS處獲取最新模型參數,然後重新開始被斷掉的迭代。
  • Master容錯:Master定期將任務狀態寫入hdfs,藉助於Yarn提供的App Master 重試機制,當Angel的Master掛掉後,Yarn會重新拉起一個Angel的master,新的Master加載狀態信息,然後重新啓動Worker和PS,從斷點出發重新開始計算。
  • 慢worker檢測:Master將會收集一些Worker計算性能的一些指標,如果檢測到有一些worker計算明顯慢於平均計算速度,Master會將這些worker重新調度到其他的機器上,避免這些worker拖慢整個任務的計算進度。
 
Angel擁有兩種不同的運行模式:ANGEL_PS_WORKER和ANGEL_PS
在angel_ps_worker模式下,angel可以獨立完成模型的訓練和預測等計算任務;在angel_ps模式下,angel啓動ps服務,爲其他的計算平臺提供參數的存儲和交換服務。目前基於angel_ps運行模式的是spark on angel,而且比較好的解決了Spark ML的單點瓶頸,能夠支持很大規模的模型訓練
Spark on Angel
 
Angel從1.0開始,就加入了PS-Service的特性,不僅僅可以作爲一個完整的PS框架運行,也可以作爲一個PS-Service,爲不具備參數服務器能力的分佈式框架,引入PS能力。Spark就是第一個嘗試。spark on angel可以很方便的利用spark在數據分析方面的優勢,在一個平臺中完成數據預處理和模型訓練兩階段的計算任務。
sona架構設計
  • spark RDD是不可變區,Angel PS是可變區
  • Spark通過PSAgent與Angel進行協作和通訊
核心實現:
  • PSContext:利用spark的Context和angel的配置,創建AngelContext,在Driver端負責全局的初始化和啓動工作
  • PSModel:PSModel是PS server上PSVector/PSMatrix的總稱,包含着PSClient對象,PSModel是PSVector和PSMatrix的父類
  • PSVector:PSVector的申請通過PSVector.dense(dim:Int, capacity: Int=50, rowType:RowType.T_DENSE_DOUBLE)申請PSVector,會創建一個維度爲dim,容量爲capacity,類型爲Double的VectorPool,同一個VectorPool內的兩個PSVector可以做運算,通過PSVector.duplicate(psVector)申請一個與psVector在同一個VectorPool的PSVector。
  • PSMatrix:PSMatrix的創建和銷燬,通過PSMatrix.dense(rows: Int, cols:Int)創建,當PSMatrix不再使用後,需要手動調用destory銷燬該Matrix
使用Spark on angel的簡單代碼如下:
PSContext.getOrCreate(spark.sparkContext)
val psVector = PSVector.dense(dim, capacity)
rdd.map{
    case (label, feature) => psVector.increment(feature)
}
println(“feature sum=”, psVector.pull.mkString(“ "))
 
啓動流程:
spark on angel本質上是一個spark任務,spark啓動後,driver通過Angel PS的接口啓動Angel PS,必要時將部分數據封裝成PSVector丟給PS node管理。因此,整個Spark on Angel的執行過程與Spark差不多,driver負責啓動、管理PS node,executor在需要的時候向PS node發起對PSVector操作的請求。
Spark driver的執行流程:
  • 啓動SparkSession
  • 啓動PSCont
  • 申請PSVector/PSMatrix
  • 執行算法邏輯
  • 終止PSContext和SparkSession
 
Spark executor的執行流程:
  • 啓動PSContext
  • 執行driver分配的task
 
模型存儲格式
Angel的模型是以矩陣爲單位來保存的,每一個矩陣在模型保存路徑下對應一個以矩陣名命名的文件夾,裏面包含矩陣的元數據文件和數據文件。一個矩陣只有一個元數據文件,但是一般有多個數據文件,因爲angel的大部分算法模型都是從PS導出的
 
元數據文件
元數據採用json格式保存,矩陣元數據主要由矩陣特徵,分區索引和行相關索引組成:分別由MatrixFilesMeta,MatrixPartitionMeta和RowPartitionMeta類來描述
MatrixFilesMeta
用途:矩陣相關信息  具體字段:
  • matrixId:矩陣ID
  • rowType:矩陣類型,可參考RowType類
  • row:矩陣行數
  • blockRow:矩陣分區塊行數
  • col:矩陣列數
  • blockCol:矩陣分區塊列數
  • matrixName:矩陣名字
  • formatClassName:矩陣存儲格式
  • options:其他矩陣參數
  • partMetas:矩陣分區索引
MatrixPartitionMeta
用途:分區元數據    具體字段:
  • startRow:分區起始行行號
  • endRow:分區結束行行號
  • startCol:分區起始列列號
  • endCol:分區結束列列號
  • nnz:分區非零元素個數
  • fileName:分區數據所在文件名
  • offset:分區數據在文件中的位置
  • length:分區數據長度(字節數)
  • saveRowNum: 分區中保存的行數
  • saveColNum:分區中保存的列數(只在列主序格式中有用)
  • saveColElemNum:每一列保存的元素個數(只在列主序格式中有用)
  • rowMetas:分區行索引
RowPartitionMeta
用途:分區的某一行分片對應的元數據  具體字段:
  • rowId:行號
  • offset:行數據在文件中的位置
  • elementNum:該行包含的元素個數
  • saveType:該行保存的文件格式
模型數據文件格式
Angel採用了用戶自定義的模型格式,即,可以根據實際需求定製模型輸出格式。一般情況下,使用angel的默認模型輸出格式即可。由於angel默認的輸出格式比較簡單,大部分並不需要依賴元數據就可以直接解析。
默認格式說明
angel提供了8種默認的模型輸出格式:ValueBinaryRowFormat, ColIdValueBinaryRowFormat,  RowIdColIdValueBinaryRowFormat, ValueTextRowFormat, ColIdValueTextRowFormat, RowIdColIdValueTextRowFormat, BinaryColumnFormat和TextColumnFormat。。
下面分別介紹這8種格式:
  • ValueBinaryRowFormat:二進制格式,只包含模型的值,只適合單行稠密的模型,數據文件中沒有列號(特徵索引),需要從模型元數據中獲取索引範圍,  格式:| value | value | 。。。|
  • ColIdValueBinaryRowFormat:二進制格式,包含特徵索引和對應的值。適合單行模型例如LR等, 格式: | index | value | index | value| 。。。|
  • RowIdColIDValueBinaryRowFormat:二進制格式,包含模型行號,特徵索引和對應的值,可以表示多行模型,格式: | rowid | index | value | rowid | index | value | … |
  • ValueTextRowFormat:文本格式,只包含模型的值,每個值是一個單獨的行,與ValueBinaryRowFormat類似,只適合單行稠密的模型,數據文件中沒有列號(特徵索引),需要從模型元數據中獲取索引範圍,格式:value\n   value\n   value\n    。。。。
  • ColIdValueTextRowFormat:文本格式,包含特徵索引和對應的值,每一行是一個特徵id和值的對,特徵id和值之間的分隔符默認是逗號。適合單行模型,如LR等, 格式: index,value\n    index,value\n    index,value\n …..
  • RowIdColIdValueTextRowFormat: 文本格式,包含行號,特徵索引和對應的值,每一行是一個行號,特徵id和值的三元組。行號,特徵id和值之間以逗號分隔,可以表示多行模型   格式:rowid,index,value\n    rowid,index,value\n     rowid,index,value\n …..
  • BinaryColumnFormat:二進制格式,以列主序輸出一個矩陣,目前只用於embedding相關的輸出(例如DNN,FM等算法中的embedding層)。模型格式爲:| index | row1 value | row2 value | ….. | index | row1 value | row2 value | …. |
  • TextColumnFormat: 文本格式,這種格式以列主序輸出矩陣,目前只用於embedding相關的輸出(例如DNN,FM等算法中的embedding層),每一行是一列數據,默認逗號分隔,格式:index,row1 value, row2 value, …. \n index, row1 value, row2 value, …. \n index, row1 value, row2 value,...
具體算法輸出格式:
angel的算法目前基本都是基於新的計算圖框架實現的,計算圖中的每一層都可以單獨設置模型格式。在默認的情況下,SimpleInputLayer使用的是ColIdValueTextRowFormat,Embedding層使用的是TextColumnFormat,FCLayer使用的是RowIdColIdValueTextRowFormat
  • LR,線性迴歸,SVM:默認的模型保存格式爲ColIdValueTextRowFormat
  • GBDT:RowIdColIdValueTextRowFormat
  • FM:線性部分使用的是ColIdValueTextRowFormat,Embedding層使用的TextColumnFormat
  • DeepFM,DNN,Wide&Deep,PNN,NFM等:線性部分使用的是ColIdValueTextRowFormat,Embedding層使用的是TextColumnFormat,全連接部分使用的是RowIdColIdValueTextRowFormat
如果不想使用默認格式,可以通過參數配置模型輸出格式:
  • ml.simpleinputlayer.matrix.output.format:SimpleInputLayer使用的輸出格式
  • ml.embedding.matrix.output.format:Embedding使用的輸出格式
  • ml.fclayer.matrix.output.format:FClayer使用的輸出格式
 
如果angel提供的8種格式仍然無法滿足要求,可以擴展RowFormat類或者ColumnFormat類來自定義需要的格式。
具體實現可參考已有的8種類型。實現完成後,編譯打包並通過angel提供的參數加入angel的依賴路徑,同時通過上面提到的四個參數進行配置使用自定義的輸出格式。
 
模型分區(model Partitioner)
爲了支持超大模型,無論是寬模型還是深模型,angel都需要將模型切分爲多個部分,存儲在不同的PSServer節點上,並提供方便的訪問服務,這是參數服務器的本質。
好的模型劃分要能保證PS負載均衡,降低PS單點性能瓶頸,關聯的數據在同一個PS上,但實際應用中各個算法以及一個算法的不同實現對劃分方式的要求都不一樣。angel既提供了默認劃分算法滿足一般劃分需求,也提供了自定義劃分功能。
整體設計
angel的模型分區設計如下:
 
要注意的點:
  • 對於用戶來說,入口類是PSModel,儘量通過它操作
  • 默認的模型分區算法類RangePartitioner,但是它可以被替換
  • 分區(Partition)是最小的單位,每個分區對應着PSServer上具體的Model Shard
 
PSServer會在Load Model階段,根據傳入的Partitioner進行Model Shard的初始化,因此,Model Partitioner的設計,和運行時的真實模型數據,會影響PS Server上的模型分佈和行爲。
默認的模型分區(RangePartitioner)
RangePartitioner使用模型的index下標範圍來劃分模型:即將模型的index範圍切分成一個個不重合的區間,這樣做的好處是在劃分應用層請求時可以快速的計算出需要的模型部分落在哪個區間上。在angel中,模型使用Matrix來表示。當應用程序沒有指定Matrix劃分參數或者方法時,angel將使用默認的劃分算法。默認的劃分算法遵循以下幾個原則:
  • 儘量將一個模型平均分配到所有PS節點上
  • 對於非常小的模型,將它們儘量放在一個PS節點上
  • 對於多行的模型,儘量將同一行放在一個PS節點上
自定義的模型分區
爲了實現更加複雜的模型分區方式,適用於複雜的算法。angel允許用戶自定義矩陣分區的大小,並且有兩種方法。
1.簡單方法:在定義PSModel時,傳入blockRow和blockCol
val sketch = PSModel[TDoubleVector](modelName, sampleNum, featNum, blockRow, blockCol)
 
通過這種方式,用戶可以輕鬆控制矩陣分區的大小,滿足需求,但不夠靈活
2.高階方法,設置自定義的Partitioner
應對定製化需求,如矩陣各部分訪問頻率不一樣,不同的分區有不同的大小,將某些存在關聯的分區放到同一個PS上等
angel抽象了一個分區接口Partitioner,PSPartitioner只是其中一個默認實現,用戶可以通過實現Partitioner接口來實現自定義的模型分區方式,並注入到PSModel中,改變模型的分區行爲。
 
異步控制(Sync Controller)
在分佈式計算系統中,由於多個計算節點計算進度不可能完全一致,會導致在彙總結果時需要等待那些計算速度較慢的節點,即慢節點會拖慢整個計算任務的進度,浪費計算資源。
考慮到機器學習的特殊性,系統其實可以適當放寬同步限制,沒有必要每一輪都等待所有的計算節點完成計算,部分跑得快的Worker,其實完全可以先把訓練好的增量push上去,然後進行下一輪的訓練。這樣可以減少等待時間,讓整個計算任務更快。
angel提供了三個級別的異步控制協議:BSP(Bulk Synchronous Parallel),SSP(Stalness Synchronous Parallel)和ASP(Asynchronous Parallel),它們的同步限制依次放寬。爲了追求更快的計算速度,算法可以選擇更寬鬆的同步協議。
 
 
協議介紹
1.BSP:默認的同步協議。也是一般的分佈式計算採用的同步協議,每一輪迭代中都需要等待所有的task計算完成。
  • 優點:適用範圍廣;每一輪迭代收斂質量高
  • 缺點:但是每一輪迭代都需要等待最慢的task,整體任務計算時間長。
  • 使用方式:默認的同步協議
 
2.SSP:允許一定程度的task進度不一致,但這個不一致有一個上限,稱爲staleness值,即最快的task最多領先最慢的task staleness輪迭代。
  • 優點:一定程度減少了task之間的等待時間,計算速度較快
  • 缺點:每一輪迭代的收斂質量不如BSP,達到同樣的收斂效果可能需要更多輪的迭代,適用性也不如BSP,部分算法不適用
  • 使用方式:配置參數angel.staleness=N, 其中N爲正整數
 
3.ASP:task之間完全不用相互等待,先完成的task,繼續下一輪的訓練
  • 優點:消除了等待慢task的時間,計算速度快
  • 缺點:適用性差,在一些情況下並不能保證收斂性
  • 使用方式:配置參數angel.staleness=-1
只要設置不同的staleness,就能以不同的異步模型運行,但是同步限制放寬後可能導致收斂質量下降甚至任務不收斂的情況,需要在實際算法中,需要指標的變化情況,調整同步協議以及相關的參數,以達到收斂性和計算速度的平衡。
 
實現原理—向量時鐘
angle中,通過向量時鐘實現異步模型控制
步驟如下:
  1. 在server端爲每個分區維護一個向量時鐘,記錄每個worker在該分區的時鐘信息
  2. 在worker端維護一個後臺同步線程,用於同步所有分區的時鐘信息
  3. task在對PSModel進行get或其他讀取操作時,根據本地時鐘信息和staleness進行判斷,選擇是否進行等待操作
  4. 每次迭代完,算法調用PSModel的Clock方法,更新向量時鐘
默認調用:
psModel.increment(update)
….
psModel.clock().get()
ctx.incIteration()
通過這樣的方式,angel實現了靈活多變的異步控制模式,爲用戶的算法,提供了最大化的便利,也解決了大規模機器學習中,由於個別機器故障,引起嚴重的性能問題。
 
定製函數—psFunc
作爲標準的參數服務器,正常都會提供基本的參數獲取(pull)和更新(push)功能。但有些場景需要的不止這些接口。
angel引入和實現psFunc的概念,對遠程模型的獲取和更新的流程進行了封裝和抽象。它也是一種用戶自定義函數(UDF),但都和PS操作密切相關,被稱爲psFunc,簡稱psf。架構如下:
 
隨着psFunc的引入,模型的計算,也會發生在PSServer端,PSServer也有一定的模型計算職責,而不是單純的模型存儲功能。合理的設計psFunc,可以大大加速算法的運行。
隨着psFunc的引入和強化,在很多複雜的算法實現中,降低了worker要把模型完整的拖回來進行整體計算的可能性,從而間接地實現了模型並行。
分類
angel的psFunc分成兩大類,一類是獲取型,一類是更新型
  • GetFunc(獲取類函數)
  • UpdateFunc(更新類函數)
PSF從數據交互角度可以分成Worker-to-PSServer和PSServer-to-PSServer兩種。
  • worker-to-PSServer是指worker的數據和PSServer的數據做運算。如Push、Pull,將worker本地的Vector PUSH給PS,或者將PS上的Row Pull到worker上。
  • PSServer-to-PSServer是指PSServer上的Matrix內部row直接發生的運算。如Add,copy等。Add是將matrix兩行數據相加,將結果保存在另外一行;Copy是將某行的內容copy到另外一行。
 
GetFunc(獲取型函數)
原理
  1. 請求劃分
參數服務器的接口,操作的是整個模型參數。而模型參數是被劃分成多個分區存儲在不同的PS實例中的,因此在請求階段,就要進行劃分了。
  • PS client(參數服務器客戶端)進行請求劃分,生成一個請求列表,其中每個請求都和一個模型參數分區對應
  1. 請求發送
  • angel將請求列表中的所有請求,發送給模型參數分區所在的PS實例
  • PS實例以模型參數分區爲單位執行參數獲取和更新操作,並返回相應的結果
  1. 結果合併
  • 合併所有的模型分區級別結果,得到最終的結果並返回
定義:
接口:GetResult get(GetFunc get) throws AngelException;
 
參數:
get型psFunc的參數類型是一個GetFunc對象,該對象封裝了get psf方法的參數和執行流程:
 
GetFunc對象的參數類型爲GetParam
  • GetParam實現了ParamSplit接口,ParamSplit接口定義了一個split方法,該方法的含義是將一個針對整個矩陣的全局的參數劃分成一個矩陣分區參數列表
  • GetParam類型提供了一個默認的split接口實現,即針對該矩陣的每一個分區都生成一個矩陣分區的參數
  • get psf的矩陣分區參數是一個PartitionGetParam類型
 
執行流程
get型psFunc的執行流程分爲兩步,分別由接口partitionGet和merge方法表示
  • partitionGet方法定義了從一個矩陣分區獲取所需結果的具體流程,它的返回結果類型爲PartitionGetResult
  • merge方法定義瞭如何將各個矩陣分區的結果合併得到完整結果的過程,完整的結果類型爲GetResult
這2步分別需要Worker端和PSServer端完成:
  • 參數劃分和merge方法在worker端執行
  • partitionGet是在PS端執行
具體的流程如下圖所示,左子圖表示worker端處理流程,右子圖表示PS端處理流程:
 
由於getFunc的接口太底層,建議普通用戶從AggrFunc開始繼承編寫psFunc,這樣需要cover的細節比較少
將代碼編譯後打成jar包,在提交任務時通過參數—angel.lib.jars上傳該jar包,然後就可以在應用程序中調用了。
 
UpdateFunc(更新型函數)
原理
  1. PS client(參數服務器客戶端)進行請求劃分,生成一個請求列表,其中每個請求都和一個模型參數分區對應
  2. 將請求列表中的所有請求發送給模型參數分區所在的PS實例。PS實例以模型參數分區爲單位執行參數獲取和更新操作,並返回相應的結果。
  3. 等待所有請求完成後返回
執行流程
與get psf不同,update psf的執行流程只有一步:
  • 即以矩陣分區爲單位分別進行update操作,這個過程由partitionUpdate方法執行
update psf沒有具體的返回值,只返回給應用程序一個Future,應用程序可以選擇是否等待操作完成。
update型psFunc執行流程需要PS Client和PS共同完成。
  • UpdateParam劃分是在Worker執行
  • partitionUpdate方法在PSServer端執行
流程如下,左子圖表示worker處理流程,右子圖表示PSServer處理流程:
 
核心接口類
如上圖所示,angel的核心接口類,在train的過程中,按照調用的流程,分爲:
1、MLRunner(啓動器)
  • MLRunner根據Conf,從工廠類,創建AngelClient,按照標準的Train流程開始依次調用AngelClient的各個接口
  • 所有的算法的啓動入口類,定義了啓動angel任務的標準流程,封裝了對AngelClient的使用
  • 在angel中,啓動類需要繼承MLRunner,並實現train和predict兩個方法
  • 一般情況,應用程序直接調用它的默認實現,不必重寫
 
2、AngelClient
  • 啓動PSServer
  • 在PSServer上進行初始化,加載空白的模型,啓動worker運行指定任務
  • 訓練完成後,將模型從多個PSServer,保存到HDFS
 
3、TrainTask / Predict task(任務類)
  • 被Angel Client調用後,開始train過程
  • 作用類似mapper和reducer,將任務封裝好,傳給遠程的worker去分佈式啓動和執行
  • 這兩個task都是BaseTask的子類,angel會負責透明的數據切分和讀取,並feed給這些task類,用戶需要定義2個公共操作:
  • 解析數據(parse):將單行數據解析爲算法所需的數據結構(如用於LR/SVM算法的LabeledData),必須實現
  • 預處理(preProcess):讀取原始數據集,按行調用parse方法,將數據進行簡單預處理,包括切分訓練集和檢驗集
  • task是angel的元計算類,所有的機器學習算法都要通過繼承它實現訓練或者預測過程,它運行於worker之內,task可以共享一個worker的某些資源
  • task中,完成對數據的讀取和訓練兩個動作,一個Task只負責它自己讀取到的數據的訓練
  • 中間結果不落地,不對外界開放,不同的task計算時不會互相傳輸數據,它們都只和PSServer打交道
一個task的執行流程如圖:
task基本流程主要有2步:
  • 訓練數據讀取:原始的數據存在分佈式文件系統之上,且格式一般不能直接被機器學習算法使用。所以angel抽象出了訓練數據準備這一過程:在這個過程中,task將分佈式系統上的數據拉取到本地,然後解析並轉換成所需的數據結構,放入DataBlock中。這一步包括preProcess和parse
  • 計算(訓練or預測):對於一般的模型訓練,這一步會進行多輪的迭代計算,最後輸出一個模型;對於預測,數據只會被計算一次,輸出預測結果。這一步一般叫run
爲了讓應用程序定製所需的計算流程,angel抽象出了BaseTaskInterface接口,並在其基礎上提供了BaseTask、TrainTask和PredictTask等基類。應用程序可以根據自己的需求擴展這些基類。
Task在計算過程中,需要用到一些系統配置信息和控制迭代進度等,這些是通過TaskContext來提供的。
 
4、DataBlock
  • 數據塊管理和存儲的基類,提供基本的數據存取接口,適合一次寫入,多次讀取的場景
  • 可以看做是一個可以動態增長的數組,新加入的對象只能放置在數組的末尾,它在內部維護了讀寫索引信息
  • TrainTask調用parse和preProcess方法,將數據從HDFS中讀取,並組裝成多個LabeledData組成的DataBlock
  • TrainTask調用Train方法,創建MLLearner對象,並將DataBlock傳給MLLearner
 
5、MLLearner
  • 模型訓練的核心類,理論上,angel所有模型訓練核心邏輯,都應該寫在這個類中。通過這個類實現和調用,它是train的核心類
  • MLLearner調用自己的Learn方法,不斷讀取DataBlock,計算出模型的更新,並通過MLModel中的PSModel,和PSServer進行不停的Push和Pull,最終得到一個完整的MLModel
 
6、MLModel(模型集)
  • 根據算法的需要,創建並容納多個PSModel,MLModel表示一個機器學習算法中,需要用到的完整模型集合,是angel所有模型的基類
  • 一個複雜的機器學習算法,往往需要多個遠程模型PSModel協作,需要一個類,來對這些模型進行統一操作
  • 作爲一個容器類,管理具體算法中的所有PSModel,作爲一個整體模型被加載,訓練和保存
  • 整體性的操作,包括predict,setSavePath,。。。都是統一通過MLMole進行,但算法對遠程模型的具體操作,都是通過PSModel來進行的
 
7、PSModel(遠程模型)
  • 封裝了AngelClient中和PSServer的所有通信接口,方便MLLearner調用,也是angel的核心抽象
  • 封裝了遠程參數服務器的Context和client細節,提供了常用的遠程矩陣(Matrix)和向量(Vector)的獲取和更新接口,使得算法工程師可以如同操作本地對象一樣的操作參數服務器上的分佈式矩陣和向量,是一個可以進行反覆迭代更新的可變模型對象
  • 是一個遠程模型的概念,對於client來說是一個類似模型代理的類,PSModel是分佈式可變的,這種可變是線程安全的。
  • 通過它可以在每個worker上像操作本地模型一樣操作模型,但實際上是一個均勻切分在遠程多個PSServer上的分佈式模型切片,所有操作透明併發
  • PSModel中包含了MatrixContext,MatrixClient,TaskContext這3個核心類,可以對遠程的參數服務器進行任意的操作。
 
Angel中的計算圖
計算圖是主流深度學習框架普遍採用的,如TensorFlow,Caffe和Mxnet等。與TensorFlow相比,angel的計算圖更輕量,主要表現在:
  • 粗粒度:angel的計算圖中的節點是層(layer),而不是TensorFlow中的操作(operator)
  • 特徵交叉:對於推薦系統相關的算法,特徵embedding後往往要通過一些交叉處理後再輸入DNN,angel直接提供了這種特徵交叉層
  • 自動生成網絡:angel可以讀取json文件生成深度網絡,借鑑Caffe,不編寫代碼就可以生成網絡,減輕工作量
angel目前不支持CNN,RNN等,只關注推薦領域的常用算法
計算圖的基本結構
層的基本結構
  • status:angel計算圖中的節點是有狀態的,用一個狀態機處理
  • input:用以記錄本節點/層的輸入,用一個ListBuffer表示,一個層可以有多個輸入層,可以多次調用addInput(layer:Layer)加入
  • outputDim:在Angel中最多只能有一個輸出,outputDim用於指定輸出的維度
  • consumer:層雖然只有一個輸出,但輸出節點可以被多次消費,因此用ListBuffer表示,在構件圖時調用input層的addConsumer(layer : Layer)告訴輸出層哪些層消費了它
AngelGraph的基本結構
通過input/consumer構建起了一個複雜的圖,雖然可以從圖中的任意節點對圖進行遍歷,但爲了方便,在AngelGraph中還是存儲verge節點,便於對圖的操作
verge有兩大類
  • inputLayer:這類節點的輸入是數據,AngelGraph中存儲這類節點是方便反向計算,只要依次調用inputLayer的calBackward。在InputLayer的基類中都會調用AngelGraph的addInput方法將自己加入AngelGraph
  • lossLayer:目前Angel不支持多任務學習,所以只有一個lossLayer,這類節點主要方便前向計算,只要調用它的predict或calOutput即可。由於lossLayer是linearlayer的子類,所以用戶自定義lossLayer可手動調用setOutput(layer: LossLayer),但用戶新增lossLayer的機會不多,更多的是增加lossfunc
有了inputLayers,lossLayer後,從AngelGraph中遍歷圖十分方便,正向計算只需要調用lossLayer的predict方法,反向計算只要調用inputLayer的calBackward。但是梯度計算,參數更新不方便,爲了方便參數更新,AngelGraph中增加了一個trainableLayer的變量,用以保存帶參數的層。
數據入口PlaceHolder
Angel中的PlaceHolder在構建AngelGraph時傳給Graph,而Graph又作爲隱式參數傳給layer,所以在所有的layer中都可以訪問PlaceHolder(即數據)
目前angel中只允許有一個PlaceHolder,以後會支持多種數據輸入
PlaceHolder只存放一個mini-batch的數據,
通過feeddata,將Array[LabeledData]類型的數據給PlaceHolder後,便可以從其中獲得:
  • 特徵,特徵維度,標籤,batchsize,特徵索引
Angel中計算圖的運行原理
angel的運行狀態機有如下幾個狀態:
  • Null:初始狀態,每次feedData後都會將Graph置於這一狀態
  • Forward:這一狀態表示前向計算已完成
  • Backward:表示後向計算已完成,但還沒計算參數的梯度
  • Gradient:表示梯度已經計算完成,並且梯度已經推送到PS上了
  • Update:表示模型更新已經完成
狀態機的引入主要是爲了保證計算的順序進行,減少重複計算,例如有多個層消費同一層的輸出,在計算時,可以根據狀態進行判斷,只要計算一次。
 
Angel中Graph的訓練過程
步驟如下:
  • feedData:這個過程會將Graph的狀態設爲Null
  • 拉取參數:會根據數據,只拉取當前mini-batch計算所需要的參數,所以angel可以訓練非常高維的模型
  • 前向計算:從LossLayer開始,級聯地調用它的inputLayer的calOutput方法,依次計算output,計算完後將它的狀態設爲forward。對於狀態已是forward的情況,則直接返回上一次計算的結果,避免重複計算
  • 後向計算:依次調用Graph的inputLayer,級聯調用第一層的CalGradOutput方法,完成後向計算。計算完成後將它的狀態設爲backward。對於狀態已經backward的情況,則直接返回上一次計算的結果,避免重複計算
  • 梯度計算與更新:計算backward只計算了網絡節點的梯度,並沒有計算參數的梯度。這一步計算參數的梯度,只需調用trainable的pushGradient即可。這個方法會先計算梯度,再講梯度推送到PS上,最後將狀態設爲gradient
  • 梯度更新:梯度更新在PS上進行,只要發送一個梯度更新的PSF即可,因此只需一個worker發送(Spark on Angel中是通過Driver發送)。不同的優化器的更新方式不一樣,在angel中,優化器的核心本質是一個PSF。參數更新前要做一次同步,保證所有的梯度都推送完成,參數更新完成也要做一次同步,保證所有worker拉取的參數是最新的,參數更新完成後狀態被設成update
 
Angel中的層
angel中的層按照拓撲結構可分爲三類:
  • verge:邊緣節點,只有輸入或輸出的層,如輸入層與損失層,輸入層主要由SimpleInputLayer,embedding,損失層主要是SimpleLossLayer,SoftmaxLossLayer
  • linear:有且僅有一個輸入與一個輸出的層,主要由全連接層(FCLayer),各種特徵交叉層
  • join:有兩個或多個輸入,一個輸出的層,主要有ConcatLayer,SumPooling,MulPooling,DotPooling
輸入層
angel的輸入層有兩類:
  • SimpleInputLayer
  • Embedding
線性層
線性層指有且只有一個輸入一個輸出的層。主要包括全連接層FCLayer和各種特徵交叉層
Join層
join層是指有多個輸入一個輸出的層,主要有:
  • ConcatLayer:將多個輸入層拼接起來,輸入一個Dense矩陣
  • SumPooling:將輸入元素對應相加後輸出
  • MulPooling:將輸入元素對應相乘後輸出
  • DotPooling:先將對應元素相乘,然後按行相加,輸出n行一列的矩陣
損失層
在網絡的最上層,只有輸入層,沒有輸出層,用於計算損失。SimpleLossLayer
 
雖然不同的層有不同的參數,但它們有一些共性:
  • 每個layer都有一個名稱(name)和一個類型(type),name是layer的唯一標識不能重複,type是layer的類型,實際就是Layer對應的類名
  • 除了輸入層(DenseInputLayer,SparseInputLayer,Embedding)外,linear或loss層要指定“inputLayer”參數,值是輸入層的name,對於join layer有多個輸入,用“inputlayers”一個列表指定,值是輸入層的name
  • 除Loss層外,其他層都有輸出,但angel中不用顯式指出,因爲指定了輸入關係就同時指定了輸出關係,但要顯式指定輸出維度outputdim
  • 對於trainable層,由於它有參數,所以可以指定優化器,以“optimizer”爲key,值與“default_optimizer”一樣
  • 對於某些層,如DenseInputLayer,SparseInputLayer,FCLayer還可以有激活函數,以“transfunc”爲key,值與“default_transfunc”一樣
  • 對於loss層,需要“lossfunc”指定損失函數
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章