MXNet之ps-lite及parameter server原理

MXNet之ps-lite及parameter server原理

ps-lite框架是DMLC組自行實現的parameter server通訊框架,是DMLC其餘項目的核心,例如其深度學習框架MXNET的分佈式訓練就依賴ps-lite的實現。html

parameter server原理

在機器學習和深度學習領域,分佈式的優化已經成了一種先決條件,由於單機已經解決不了目前快速增加的數據與參數帶來的問題。現實中,訓練數據的數量可能達到1TB到1PB之間,而訓練過程當中的參數可能會達到\(10^9\)\(10^{12}\)。而每每這些模型的參數須要被全部的worker節點頻繁的訪問,這就會帶來不少問題和挑戰:node

  • 訪問這些巨量的參數,須要大量的網絡帶寬支持;
  • 不少機器學習算法都是連續型的,只有上一次迭代完成(各個worker都完成)以後,才能進行下一次迭代,這就致使了若是機器之間性能差距大(木桶理論),就會形成性能的極大損失;
  • 在分佈式中,容錯能力是很是重要的。不少狀況下,算法都是部署到雲環境中的(這種環境下,機器是不可靠的,而且job也是有可能被搶佔的)。

分佈式系統中的同步與異步機制

synchronous

圖1 在同步的機制下,系統運行的時間是由最慢的worker節點與通訊時間決定的

asynchronous

圖2 在異步的機制下,每一個worker不能等待其它workers完成再運行下一次迭代。這樣能夠提升效率,但從迭代次數的角度來看,會減慢收斂的速度。

parameter server架構

在parameter server中,每一個 server 實際上都只負責分到的部分參數(servers共同維持一個全局的共享參數),而每一個 work 也只分到部分數據和處理任務。算法

ps_struct

圖3 parameter server的架構圖,server 節點能夠跟其餘 server 節點通訊,每一個server負責本身分到的參數,server group 共同維持全部參數的更新。server manager node 負責維護一些元數據的一致性,好比各個節點的狀態,參數的分配狀況等;worker 節點之間沒有通訊,只跟本身對應的server進行通訊。每一個worker group有一個task scheduler,負責向worker分配任務,而且監控worker的運行狀況。當有新的worker加入或者退出,task scheduler 負責從新分配任務。

PS架構包括計算資源與機器學習算法兩個部分。其中計算資源分爲兩個部分,參數服務器節點和工做節點:編程

  • 參數服務器節點用來存儲參數
  • 工做節點部分用來作算法的訓練

機器學習算法也分紅兩個部分,即參數和訓練:bash

  • 參數部分即模型自己,有一致性的要求,參數服務器也能夠是一個集羣,對於大型的算法,好比DNN,CNN,參數上億的時候,天然須要一個集羣來存儲這麼多的參數,於是,參數服務器也是須要調度的。
  • 訓練部分天然是並行的,否則沒法體現分佈式機器學習的優點。由於參數服務器的存在,每一個計算節點在拿到新的batch數據以後,都要從參數服務器上取下最新的參數,而後計算梯度,再將梯度更新回參數服務器。

這種設計有兩種好處:服務器

  • 經過將機器學習系統的共同之處模塊化,算法實現代碼更加簡潔。
  • 做爲一個系統級別共享平臺優化方法,PS結構可以支持不少種算法。

從而,PS架構有五個特色:網絡

  • 高效的通訊:異步通訊不會拖慢計算
  • 彈性一致:將模型一致這個條件放寬鬆,容許在算法收斂速度和系統性能之間作平衡。
  • 擴展性強:增長節點無需重啓網絡
  • 錯誤容忍:機器錯誤恢復時間短,Vector Clock允許網絡錯誤
  • 易用性: 全局共享的參數使用向量和矩陣表示,而這些又能夠用高性能多線程庫進行優化。

Push and Pull

在parameter server中,參數都是能夠被表示成(key, value)的集合,好比一個最小化損失函數的問題,key就是feature ID,而value就是它的權值。對於稀疏參數,不存在的key,就能夠認爲是0。多線程

把參數表示成k-v, 形式更天然, 易於理,更易於編程解。workers跟servers之間經過push與pull來通訊的。worker經過push將計算好的梯度發送到server,而後經過pull從server更新參數。爲了提升計算性能和帶寬效率,parameter server容許用戶使用Range Push跟Range Pull 操做。架構

Task:Synchronous and Asynchronous

Task也分爲同步和異步,區別以下圖所示:併發

圖4 若是iter1須要在iter0 computation,push跟pull都完成後才能開始,那麼就是Synchronous,反之就是Asynchronous。Asynchronous可以提升系統的效率(由於節省了不少等待的過程),可是,它可能會下降算法的收斂速率;

因此,系統性能跟算法收斂速率之間是存在一個平衡,你須要同時考慮:

  • 算法對於參數非一致性的敏感度
  • 訓練數據特徵之間的關聯度
  • 硬盤的存儲容量

考慮到用戶使用的時候會有不一樣的狀況,parameter server 爲用戶提供了多種任務依賴方式:

consistency

圖5 三種不一樣的依賴方式
  • Sequential:這裏實際上是 synchronous task,任務之間是有順序的,只有上一個任務完成,才能開始下一個任務。
  • Eventual: 跟sequential相反,全部任務之間沒有順序,各自獨立完成本身的任務。
  • Bounded Delay: 這是sequential 跟 eventual 之間的一個均衡,能夠設置一個\(\tau\)做爲最大的延時時間。也就是說,只有大於\(\tau\)以前的任務都被完成了,才能開始一個新的任務;極端的狀況:
    • \(\tau=0\),狀況就是 Sequential;
    • \(\tau=\infty\),狀況就是 Eventual;

PS下的算法

算法1是沒有通過優化的直接算法和它的流程圖以下:

Algorithm1

圖6 算法1

Algorithm_1_1

圖7 算法1的流程

Algorithm3

圖8 優化算法1後的算法3。

算法3中的KKT Filter能夠是用戶自定義過濾:
對於機器學習優化問題好比梯度降低來講,並非每次計算的梯度對於最終優化都是有價值的,用戶能夠經過自定義的規則過濾一些沒必要要的傳送,再進一步壓縮帶寬消耗:

  1. 發送很小的梯度值是低效的:
    所以能夠自定義設置,只在梯度值較大的時候發送;
  2. 更新接近最優狀況的值是低效的:
    所以,只在非最優的狀況下發送,可經過KKT來判斷;

ps-lite實現

上面說了parameter server的原理,如今來看下這個是怎麼實現的。ps-lite是DMLC實現parameter server的一個程序,也是MXNet的核心組件之一。

ps-lite角色

ps-lite包含三種角色:Worker、Server、Scheduler。具體關係以下圖:

s_w_h

圖9 三種角色的關係圖

Worker節點負責計算參數,併發參數push到Server,同時從Serverpull參數回來。
Server節點負責管理Worker節點發送來的參數,並「合併」,以後供各個Worker使用。
Scheduler節點負責管理Worker節點和Server節點的狀態,worker與server之間的鏈接是經過Scheduler的。

重要類

class

圖10 重要類的關係圖
  • Postoffice是全局管理類,單例模式建立。主要用來配置當前node的一些信息,例如當前node是哪一種類型(server,worker,scheduler),nodeid是啥,以及worker/server 的rank 到 node id的轉換。

  • Van是負責通訊的類,是Postoffice的成員。Van中std::unordered_map<int, void*> senders_保存了node_id到鏈接的映射。Van只是定義了接口,具體實現是依賴ZMQ實現的ZMQVan,Van類負責創建起節點之間的互相鏈接(例如Worker與Scheduler之間的鏈接),而且開啓本地的receiving thread用來監聽收到的message。。

  • Customer用來通訊,跟蹤request和response。每個鏈接對應一個Customer實例,鏈接對方的id和Customer實例的id相同。

  • SimpleApp是一個基類;提供了發送接收int型的head和string型的body消息,以及註冊消息處理函數。它有2個派生類。

  • KVServer是SimpleApp的派生類,用來保存key-values數據。裏面的Process()被註冊到Customer對象中,當Customer對象的receiving thread接受到消息時,就調用Process()對數據進行處理。

  • KVWorker是SimpleApp的派生類,主要有Push()和Pull(),它們最後都會調用Send()函數,Send()對KVPairs進行切分,由於每一個Server只保留一部分參數,所以切分後的SlicedKVpairs就會被髮送給不一樣的Server。切分函數能夠由用戶自行重寫,默認爲DefaultSlicer,每一個SlicedKVPairs被包裝成Message對象,而後用van::send()發送。

  • KVPairs封裝了Key-Value結構,還包含了一個長度選項。

  • SArray是Shared array,像智能指針同樣共享數據,接口相似vector。

  • Node封裝了節點的信息,例如角色、ip、端口、是不是恢復節點。

  • Control封裝了控制信息,例如命令類型、目的節點、barrier_group的id、簽名。

  • Meta封裝了元數據,發送者、接受者、時間戳、請求仍是相應等。

  • Message是要發送的信息,除了元數據外,還包括髮送的數據。

運行腳本

爲了更好地看到ps-lite的運行原理,咱們先來看下它在本地運行的腳本:

#!/bin/bash
# set -x
if [ $# -lt 3 ]; then
    echo "usage: $0 num_servers num_workers bin [args..]"
    exit -1;
fi

export DMLC_NUM_SERVER=$1
shift
export DMLC_NUM_WORKER=$1
shift
bin=$1
shift
arg="$@"

# start the scheduler
export DMLC_PS_ROOT_URI='127.0.0.1'
export DMLC_PS_ROOT_PORT=8000
export DMLC_ROLE='scheduler'
${bin} ${arg} &


# start servers
export DMLC_ROLE='server'
for ((i=0; i<${DMLC_NUM_SERVER}; ++i)); do
    export HEAPPROFILE=./S${i}
    ${bin} ${arg} &
done

# start workers
export DMLC_ROLE='worker'
for ((i=0; i<${DMLC_NUM_WORKER}; ++i)); do
    export HEAPPROFILE=./W${i}
    ${bin} ${arg} &
done

wait

這個腳本主要作了兩件事,第一件是爲不一樣的角色設置環境變量,第二件是在本地運行多個不一樣的角色。因此說ps-lite是要多個不一樣的進程(程序)共同合做完成工做的,ps-lite採起的是用環境變量來設置角色的配置。

test_simple_app流程

test_simple_app.cc是一人很簡單的app,其它複雜的流程原理這個程序差很少,因此咱們就說說這個程序是怎麼運行的。先來看下剛開始運行程序時,worker(W)\Server(S)\Scheduler(H)之間是怎麼鏈接的,這裏沒有寫Customer處理普通訊息的流程。W\S\H表明上面腳本運行各個角色後在不一樣角色程序內的處理流程。

  • W\S\H:初始化SimpleApp --> New Customer(綁定Process函數) --> Customer起一個Receiving線程
  • W\S\H:初始化static PostOffice,全局都用同一個PostOffice --> Create(Van)用來作通訊的發/發 --> 從環境變量中讀入配置 --> 肯定不一樣的角色。
  • W\S\H:Start() --> Van::Start(), my_node_/Scheduler的初始化
  • W\S:綁定port並鏈接到同一個Scheduler
  • W\S:發送信息到指定ID
  • W\S\h:在van中起一個Reciving的線程
  • H:收到信息並回發
  • W\S: 收到信息
  • W\S\H:Finalize()

Customer處理普通訊息流程以下:

  • H:app->requst() --> 放這個請求入到tracker_中 --> send(msg) --> app->wait()[等待收回發的信息]
  • W/S:收到信息後放到recv_queue_中
  • W/S:Customer的Reciving收到信息 --> call recv_handle_ --> process(recv)[處理信息] --> response_hadle_(recv) --> ReqHandle() --> response()[回發信息]
  • H:收到回發的信息 --> 放入到recv_queue_中處理 --> 在Customer中的Reciving中處理
  • H:當tracker_.first == tracker_.second時,釋放app->wait()

參考引用:
[1] http://blog.csdn.net/stdcoutzyx/article/details/51241868
[2] http://blog.csdn.net/cyh_24/article/details/50545780
[3] https://www.zybuluo.com/Dounm/note/529299
[4] http://blog.csdn.net/KangRoger/article/details/73307685

【防止爬蟲轉載而致使的格式問題——連接】: http://www.cnblogs.com/heguanyou/p/7868596.html

相關文章
相關標籤/搜索