Spark GraphX在淘寶的實踐

原文鏈接:http://rec-sys.net/forum.php?mod=viewthread&tid=398

由於Spark GraphX性能良好,又有豐富的功能和運算符,能在海量數據上自如運行復雜的圖算法,淘寶嘗試將它作爲分佈式圖計算平臺,進行各種算法嘗試和生產應用。本文結合GraphX的原理和特點,分享其在淘寶的應用實踐。

 

早在0.5版本,Spark就帶了一個小型的Bagel模塊,提供了類似Pregel的功能。當然,這個版本還非常原始,性能和功能都比較弱,屬於實驗型產品。到0.8版本時,鑑於業界對分佈式圖計算的需求日益見漲,Spark開始獨立一個分支Graphx-Branch,作爲獨立的圖計算模塊,借鑑GraphLab,開始設計開發GraphX。在0.9版本中,這個模塊被正式集成到主幹,雖然是Alpha版本,但已可以試用,小麪包圈Bagel告別舞臺。1.0版本,GraphX正式投入生產使用。

值得注意的是,GraphX目前依然處於快速發展中,從0.8的分支到0.9和1.0,每個版本代碼都有不少的改進和重構。根據觀察,在沒有改任何代碼邏輯和運行環境,只是升級版本、切換接口和重新編譯的情況下,每個版本有10%~20%的性能提升。雖然和GraphLab的性能還有一定差距,但憑藉Spark整體上的一體化流水線處理,社區熱烈的活躍度及快速改進速度,GraphX具有強大的競爭力。

分佈式圖計算

在正式介紹GraphX之前,先看看通用的分佈式圖計算框架。簡單來說,分佈式圖計算框架的目的,是將對於巨型圖的各種操作包裝爲簡單的接口,讓分佈式存儲、並行計算等複雜問題對上層透明,從而使複雜網絡和圖算法的工程師,更加聚焦在圖相關的模型設計和使用上,而不用關心底層的分佈式細節。爲了實現該目的,需要解決兩個通用問題:圖存儲模式和圖計算模式。

圖存儲模式

巨型圖的存儲總體上有邊分割和點分割兩種存儲方式。2013年,GraphLab2.0將其存儲方式由邊分割變爲點分割,在性能上取得重大提升,目前基本上被業界廣泛接受並使用。

邊分割:每個頂點都存儲一次,但有的邊會被打斷分到兩臺機器上。這樣做的好處是節省存儲空間;壞處是對圖進行基於邊的計算時,對於一條兩個頂點被分到不同機器上的邊來說,要跨機器通信傳輸數據,內網通信流量大。

點分割:每條邊只存儲一次,都只會出現在一臺機器上。鄰居多的點會被複制到多臺機器上,增加了存儲開銷,同時會引發數據同步問題。好處是可以大幅減少內網通信量。

雖然兩種方法互有利弊,但現在是點分割佔上風,各種分佈式圖計算框架都將自己底層的存儲形式變成了點分割。主要原因有以下兩個。

  • 磁盤價格下降,存儲空間不再是問題,而內網的通信資源沒有突破性進展,集羣計算時內網帶寬是寶貴的,時間比磁盤更珍貴。這點就類似於常見的空間換時間的策略。

  • 在當前的應用場景中,絕大多數網絡都是“無尺度網絡”,遵循冪律分佈,不同點的鄰居數量相差非常懸殊。而邊分割會使那些多鄰居的點所相連的邊大多數被分到不同的機器上,這樣的數據分佈會使得內網帶寬更加捉襟見肘,於是邊分割存儲方式被漸漸拋棄了。



    圖計算模型

    目前的圖計算框架基本上都遵循BSP(Bulk Synchronous Parallell)計算模式。在BSP中,一次計算過程由一系列全局超步組成,每一個超步由併發計算、通信和柵欄同步三個步驟組成。同步完成,標誌着這個超步的完成及下一個超步的開始。BSP模式很簡潔。基於BSP模式,目前有兩種比較成熟的圖計算模型。

  • Pregel模型——像頂點一樣思考

     

    2010年,Google的新的三架馬車Caffeine、Pregel、Dremel發佈。隨着Pregel一起,BSP模型廣爲人知。Pregel借鑑MapReduce的思想,提出了“像頂點一樣思考”(Think Like A Vertex)的圖計算模式,讓用戶無需考慮並行分佈式計算的細節,只需要實現一個頂點更新函數,讓框架在遍歷頂點時進行調用即可。

    常見的代碼模板如下:

     

     

    這個模型雖然簡潔,但很容易發現它的缺陷。對於鄰居數很多的頂點,它需要處理的消息非常龐大,而且在這個模式下,它們是無法被併發處理的。所以對於符合冪律分佈的自然圖,這種計算模型下很容易發生假死或者崩潰。

  • GAS模型——鄰居更新模型

     

    相比Pregel模型的消息通信範式,GraphLab的GAS模型更偏向共享內存風格。它允許用戶的自定義函數訪問當前頂點的整個鄰域,可抽象成Gather、Apply和Scatter三個階段,簡稱爲GAS。相對應,用戶需要實現三個獨立的函數gather、apply和scatter。常見的代碼模板如下所示:

     

    由於gather/scatter函數是以單條邊爲操作粒度,所以對於一個頂點的衆多鄰邊,可以分別由相應的worker獨立調用gather/scatter函數。這一設計主要是爲了適應點分割的圖存儲模式,從而避免Pregel模型會遇到的問題。

    GraphX的框架

    在設計GraphX時,點分割和GAS都已成熟,並在設計和編碼中針對它們進行了優化,在功能和性能之間尋找最佳的平衡點。如同Spark本身,每個子模塊都有一個核心抽象。GraphX的核心抽象是Resilient Distributed Property Graph,一種點和邊都帶屬性的有向多重圖。它擴展了Spark RDD的抽象,有Table和Graph兩種視圖,而只需要一份物理存儲。兩種視圖都有自己獨有的操作符,從而獲得了靈活操作和執行效率。

    如同Spark,GraphX的代碼非常簡潔。GraphX的核心代碼只有3千多行,而在此之上實現的Pregel模型,只要短短的20多行。GraphX的代碼結構整體如圖1所示,其中大部分的實現,都是圍繞Partition的優化進行的。這在某種程度上說明了點分割的存儲和相應的計算優化,的確是圖計算框架的重點和難點。

    圖1  GraphX的代碼結構

    GraphX的底層設計有以下幾個關鍵點。

     
    • 對Graph視圖的所有操作,最終都會轉換成其關聯的Table視圖的RDD操作來完成。這樣對一個圖的計算,最終在邏輯上,等價於一系列RDD的轉換過程。因此,Graph最終具備了RDD的3個關鍵特性:Immutable、Distributed和Fault-Tolerant。其中最關鍵的是Immutable(不變性)。邏輯上,所有圖的轉換和操作都產生了一個新圖;物理上,GraphX會有一定程度的不變頂點和邊的複用優化,對用戶透明。

    • 兩種視圖底層共用的物理數據,由RDD[Vertex-Partition]和RDD[EdgePartition]這兩個RDD組成。點和邊實際都不是以表Collection[tuple]的形式存儲的,而是由VertexPartition/EdgePartition在內部存儲一個帶索引結構的分片數據塊,以加速不同視圖下的遍歷速度。不變的索引結構在RDD轉換過程中是共用的,降低了計算和存儲開銷。

    • 圖的分佈式存儲採用點分割模式,而且使用partitionBy方法,由用戶指定不同的劃分策略(PartitionStrategy)。劃分策略會將邊分配到各個EdgePartition,頂點Master分配到各個VertexPartition,EdgePartition也會緩存本地邊關聯點的Ghost副本。劃分策略的不同會影響到所需要緩存的Ghost副本數量,以及每個EdgePartition分配的邊的均衡程度,需要根據圖的結構特徵選取最佳策略。目前有EdgePartition2d、EdgePartition1d、RandomVertexCut和CanonicalRandomVertexCut這四種策略。在淘寶大部分場景下,EdgePartition2d效果最好。


    GraphX的圖運算符

    如同Spark一樣,GraphX的Graph類提供了豐富的圖運算符,大致結構如圖2所示。可以在官方GraphX Programming Guide中找到每個函數的詳細說明,本文僅講述幾個需要注意的方法。

    圖2  GraphX的圖運算符

    圖的cache

    每個圖是由3個RDD組成,所以會佔用更多的內存。相應圖的cache、unpersist和checkpoint,更需要注意使用技巧。出於最大限度複用邊的理念,GraphX的默認接口只提供了unpersistVertices方法。如果要釋放邊,調用g.edges.unpersist()方法才行,這給用戶帶來了一定的不便,但爲GraphX的優化提供了便利和空間。參考GraphX的Pregel代碼,對一個大圖,目前最佳的實踐是:

    大體之意是根據GraphX中Graph的不變性,對g做操作並賦回給g之後,g已不是原來的g了,而且會在下一輪迭代使用,所以必須cache。另外,必須先用prevG保留住對原來圖的引用,並在新圖產生後,快速將舊圖徹底釋放掉。否則,十幾輪迭代後,會有內存泄漏問題,很快耗光作業緩存空間。

    mrTriplets——鄰邊聚合

    mrTriplets(mapReduceTriplets)是GraphX中最核心的一個接口。Pregel也基於它而來,所以對它的優化能很大程度上影響整個GraphX的性能。mrTriplets運算符的簡化定義是:

    它的計算過程爲:map,應用於每一個Triplet上,生成一個或者多個消息,消息以Triplet關聯的兩個頂點中的任意一個或兩個爲目標頂點;reduce,應用於每一個Vertex上,將發送給每一個頂點的消息合併起來。

    mrTriplets最後返回的是一個VertexRDD[A],包含每一個頂點聚合之後的消息(類型爲A),沒有接收到消息的頂點不會包含在返回的VertexRDD中。

    在最近的版本中,GraphX針對它進行了一些優化,對於Pregel以及所有上層算法工具包的性能都有重大影響。主要包括以下幾點。

  • Caching for Iterative mrTriplets & Incremental Updates for Iterative mrTriplets:在很多圖分析算法中,不同點的收斂速度變化很大。在迭代後期,只有很少的點會有更新。因此,對於沒有更新的點,下一次mrTriplets計算時EdgeRDD無需更新相應點值的本地緩存,大幅降低了通信開銷。

  • Indexing Active Edges:沒有更新的頂點在下一輪迭代時不需要向鄰居重新發送消息。因此,mrTriplets遍歷邊時,如果一條邊的鄰居點值在上一輪迭代時沒有更新,則直接跳過,避免了大量無用的計算和通信。

  • Join Elimination:Triplet是由一條邊和其兩個鄰居點組成的三元組,操作Triplet的map函數常常只需訪問其兩個鄰居點值中的一個。例如,在PageRank計算中,一個點值的更新只與其源頂點的值有關,而與其所指向的目的頂點的值無關。那麼在mrTriplets計算中,就不需要VertexRDD和EdgeRDD的3-way join,而只需要2-way join。

     

    所有這些優化使GraphX的性能逐漸逼近GraphLab。雖然還有一定差距,但一體化的流水線服務和豐富的編程接口,可以彌補性能的微小差距。

    進化的Pregel模型

    GraphX中的Pregel接口,並不嚴格遵循Pregel模型,它是一個參考GAS改進的Pregel模型。定義如下:

    這種基於mrTrilets方法的Pregel模型,與標準Pregel的最大區別是,它的第2段參數體接收的是3個函數參數,而不接收messageList。它不會在單個頂點上進行消息遍歷,而是將頂點的多個Ghost副本收到的消息聚合後,發送給Master副本,再使用vprog函數來更新點值。消息的接收和發送都被自動並行化處理,無需擔心超級節點的問題。

    常見的代碼模板如下所示:

    可以看到,GraphX設計這個模型的用意。它綜合了Pregel和GAS兩者的優點,即接口相對簡單,又保證性能,可以應對點分割的圖存儲模式,勝任符合冪律分佈的自然圖的大型計算。另外,值得注意的是,官方的Pregel版本是最簡單的一個版本。對於複雜的業務場景,根據這個版本擴展一個定製的Pregel是很常見的做法。

    圖算法工具包

    GraphX也提供了一套圖算法工具包,方便用戶對圖進行分析。目前最新版本已支持PageRank、數三角形、最大連通圖和最短路徑等6種經典的圖算法。這些算法的代碼實現,目的和重點在於通用性。如果要獲得最佳性能,可以參考其實現進行修改和擴展滿足業務需求。另外,研讀這些代碼,也是理解GraphX編程最佳實踐的好方法。

    GraphX在淘寶圖譜體檢平臺

    基本上,所有的關係都可以從圖的角度來看待和處理,但到底一個關係的價值多大?健康與否?適合用於什麼場景?很多時候是靠運營人員和產品經理憑直覺來判斷和評估的。如何將各種圖的指標精細化和規範化,對於產品和運營的構思進行數據上的預研指導,提供科學決策的依據,是圖譜體檢平臺設計的初衷和出發點。

    基於這樣的出發點,藉助GraphX豐富的接口和工具包,針對淘寶內部林林總總的圖業務需求,我們開發一個圖譜體檢平臺。目前主要進行下列指標的檢查。

    度分佈:這是一個圖最基礎和重要的指標。度分佈檢測的目的,主要是瞭解圖中“超級節點”的個數和規模,以及所有節點度的分佈曲線。超級節點的存在對各種傳播算法都會有重大的影響(不論是正面助力還是反面阻力),因此要預先對這些數據量有個預估。藉助GraphX最基本的圖信息接口degrees: VertexRDD[Int](包括inDegrees和outDegrees),這個指標可以輕鬆計算出來,並進行各種各樣的統計。

    二跳鄰居數:對大部分社交關係來說,只獲得一跳的度分佈遠遠不夠,另一個重要的指標是二跳鄰居數。例如,無祕App中好友的好友的祕密,傳播範圍更廣,信息量更豐富。因此,二跳鄰居數的統計是圖譜體檢中很重要的一個指標。對於二跳鄰居的計算,GraphX沒有給出現成的接口,需要自己設計和開發。目前使用的方法是:第一次遍歷,所有點向鄰居點傳播一個帶自身ID,生命值爲2的消息;第二次遍歷,所有點將收到的消息向鄰居點再轉發一次,生命值爲1;最終統計所有點上,接收到的生命值爲1的ID,並進行分組彙總,得到所有點的二跳鄰居。

    值得注意的是,進行這個計算之前,需要藉助度分佈將圖中的超級節點去掉,不納入二跳鄰居數的計算。否則,這些超級節點會在第一輪傳播後收到過多的消息而爆掉,同時它們參與計算,會影響與它們有一跳鄰居關係的頂點,導致不能得到真正有效的二跳鄰居數。

    連通圖:檢測連通圖的目的是弄清一個圖有幾個連通部分及每個連通部分有多少頂點。這樣可以將一個大圖分割爲多個小圖,並去掉零碎的連通部分,從而可以在多個小子圖上進行更加精細的操作。目前,GraphX提供了ConnectedComponents和StronglyConnected-Components算法,使用它們可以快速計算出相應的連通圖。連通圖可以進一步演化變成社區發現算法,而該算法優劣的評判標準之一,是計算模塊的Q值,來查看所謂的modularity情況。

    更多的指標,例如Triangle Count和K-Core,無論是藉助GraphX已有的函數,還是自己從頭開發,都陸續在進行中。目前這個圖譜體檢平臺已初具規模,通過平臺的建立和推廣,圖相關的產品和業務逐漸走上“無數據,不討論,用指標來預估效果”的數據化運營之路,有效提高溝通效率,爲各種圖相關的業務開發走上科學化和系統化之路做好準備。

    多圖合併工具

    在圖譜體檢平臺的基礎上,我們可以瞭解到各種關係的特點。每種關係都有自己的強項和弱項,例如有些關係圖譜連通性好些,而有些關係圖譜的社交性好些,因此往往要使用關係A來豐富關係B。爲此,藉助GraphX,我們在圖譜體檢平臺上開發了一個多圖合併工具,提供類似圖並集的概念,可快速對指定的兩個不同的關係圖譜進行合併,產生一個新的關係圖譜。

    以用基於A關係的圖來擴充基於B關係的圖,生成擴充圖C爲例,融合算法的基本思路如圖3:若圖B中某邊的兩個頂點都在圖A中,則將該邊加入C圖(如BD邊);若圖B中某邊的一個頂點在圖A中,另一個頂點不在,則將該邊和另一頂點都加上(如CE邊和E點);若圖A中某邊的兩個頂點都不在圖B中,則捨棄這條邊和頂點(如EF邊)。

    圖3  圖合併算法

    使用GraphX的outerJoinVertices等圖運算符,能很簡單地完成上述操作。另外,在考慮圖合併時,也可爲不同圖的邊加上不同的權重,綜合考慮點之間不同關係的重要性。新產生的圖,會再進行一輪圖譜體檢。通過前後三個圖各個體檢指標的對比,有助於對業務上線後的效果做預估和判斷。如果不符合期望,可嘗試重新選擇擴充方案。

    能量傳播模型

    加權網絡上的能量傳播是經典的圖模型之一,可用於用戶信譽度預測。模型的思路是:物以類聚,人以羣分。常和信譽度高的用戶進行交易的,信譽度自然較高;常和信譽度差的用戶有業務來往的,信譽度自然較低。模型不復雜,但淘寶全網有上億的用戶點和幾十億關係邊,要對如此規模的巨型圖進行能量傳播,並對邊的權重進行精細的調節,對圖計算框架的性能和功能都是巨大的考驗。藉助GraphX,我們在這兩點之間取得了平衡,成功實現了該模型。

    流程如圖4所示,先生成以用戶爲點、買賣關係爲邊的巨型圖initGraph,對選出的種子用戶,分別賦予相同的初始正負能量值(trustRank & badRank),然後進行兩輪隨機遊走,一輪好種子傳播正能量(tr),一輪壞種子傳播負能量(br),然後正負能量相減得到finalRank,根據finalRank判斷用戶的好壞。邊的初始傳播強度是0.85,這時AUC很低,需要再給每條邊,帶上一個由多個特徵(交易次數、金額……)組成的組合權重。每個特徵,都有不同的獨立權重和偏移量。通過使用partialDerivativeAUC方法,在訓練集上計算AUC,然後對AUC求偏導,得到每個關係維度的獨立權重和偏移量,生成新的權重調節器(WeightAdjustor),對圖上所有邊上的權重更新,再進行新一輪大迭代,這樣一直到AUC穩定時,終止計算。

     

    圖4  能量傳播和訓練模型

    在接近全量的數據上進行3輪大迭代,每輪2+6次Pregel,每次Pregel大約30次小迭代後,最終的AUC從0.6提升到0.9,達到了不錯的用戶預測準確率。訓練時長在6個小時左右,無論在性能還是準確率上,都超越業務方的期望。

    未來圖計算的前景

    經過半年多的嘗試,之前一些想做但因爲沒有足夠的計算能力而不能實現的圖模型,現已不是問題。我們將會進一步將越來越多的圖模型,在GraphX上實現。這些模型應用於用戶網絡的社區發現、用戶影響力、能量傳播、標籤傳播等,可以提升用戶黏性和活躍度;而應用到推薦領域的標籤推理、人羣劃分、年齡段預測、商品交易時序跳轉,則可以提升推薦的豐富度和準確性。

    本文作者:黃明,花名明風,淘寶網數據挖掘和計算團隊負責人,在分佈式計算和機器學習算法領域有所涉獵,Spark早期研究和佈道者之一。新浪微博:@明風Andy

     

    吳煒,曾在盛大、MediaV任職,長期從事數據挖掘方面工作。目前供職於淘寶技術部數據挖掘與計算組。新浪微博:@吳煒_數據挖掘機器學習

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