[机器学习] 常用并行计算算子原理
一、概述
? ? ? ? 在大規模機器學習中,需要應對巨大的訓練數據及計算量。當單機遇到性能瓶頸時需要通過多臺機器并行訓練來彌補計算能力與內存的不足。采用并行方式進行機器學習時,常常分為模型并行與數據并行。模型并行是將模型拆分成多個分片,由幾個計算節點分別持有,共同協作完成訓練,適用于模型規模非常大的情形。數據并行是將數據拆分為不同的部分,分別存放在不同的計算節點上,同時每個計算節點都維護一個相同的模型。數據并行方式的訓練過程中,每個計算節點只對自己存放的數據進行計算得到局部結果,然后再對局部結果進行全局歸約。每個計算節點得到全局值之后,再進行模型更新。以下分別介紹并行計算中常用的歸約算子,同時結合LightGBM的源碼分析幾個常用算子的實現方式。
二、并行計算中常用算子的主流實現方式剖析
? ? ? ? 在并行計算中,數值歸約是非常重要且使用頻率非常高的一種操作。數值歸約算法實現方式的好壞直接影響整個計算過程的速度。常用的一些歸約算子包括:Allreduce、Reduce、Allgather、Broadcast、ReduceScatter等。下面分別對這幾種算子進行介紹。
?一些定義:N---計算節點的總數;?Ri --- 第i個計算節點;?Vi --- 第i個計算節點的局部數值;?root --- 樹狀網絡的根節點;k --- <= N的最大整數; remain = N - ; reducer --- 歸約操作;
2.1 Reduce
Reduce操作的目的是對所有計算節點上的數值進行歸約操作,并將歸約后的結果保存到主節點上。
Reduce??就是將多個進程中的數據按照指定的映射函數進行運算得到最后的結果存在一個進程中
例如下面兩個圖中的歸約操作都是求和,將4個不同進程的數據歸約求和后存在了第一個進程中
? 主流實現方式有Binomial Tree Algorithm以及Rabenseifner提出的算法[2]。
2.1.1?Binomial Tree Algorithm
? ? ? ??采用二叉樹的方式,類似于Broadcast過程的逆過程。首先需要對所有的計算節點按照二叉樹建立網絡連接。然后從葉子開始,將本地數據發送到父節點,父節點在接收到數據后,按照給定的歸約函數執行一次歸約操作,再將歸約后的結果發送到其父節點,重復這一過程直到根節點。最終根節點上的值就是Reduce之后的結果。
2.2.2?Rabenseifner's?Algorithm
? ? ? 該算法適用于數據塊較大的情形,首先進行一次ReduceScatter操作,然后再進行一次Gather操作。
?
2.2?Allreduce
All-reduce?與reduce的區別就在于后者最后的結果是只保存在一個進程中,而All-reduce需要每個進程都有同樣的結果。
所以All-reduce一般包含scatter操作,所以有時候也會看到reduce-scatter這種說法,其實reduce-scatter可以看成是all reduce的一種實現方式
Allreduce操作的目的是對所有計算節點上的數值進行歸約操作,同時每一個計算節點均獲得歸約后的結果。
主流實現方式有Binomial Tree Algorithm以及Recursive Doubling。
?Binomial Tree Algorithm
? ? ? ? 如果采用二叉樹算法,可以通過2.4.1介紹的Reduce + 2.2.1介紹的Broadcast兩次操作完成。如下圖所示。
? ? ? ? 這種方式包括push up和pull down兩個過程,push up是將本地數值進行上發,同時在接收端進行歸約操作并且將得到歸約后的結果繼續上發。pull down是在root節點上得到了全局歸約值之后的一個Broadcast過程。
?Recursive Doubling
上圖中描述了使用Recursive Doubling算法進行Allreduce操作的過程。具體步驟如下:
第一步:首先對N個計算節點兩兩分組,如R0與一組。每組之間的計算節點相互發送數據,接收方將數據存放到緩存區(step1 → step2);
第二步:每一個計算節點,對緩存區數據與本地數據做一次歸約函數操作reducer(Vi, Vrecived)(step2 → step3);
第三步:再重新兩兩分組,如R0與一組。每組之間的計算節點相互發送數據,接收方將數據存放到緩存區(step3 → step4);
第四步:依此重復執行k次之后,每個節點上的結果就是最終的歸約結果;
這種方式的Allreduce要求N為2的整數倍,完成整個操作需要的的通信次數為log(N)。當N非2的整數倍時,可以采用2.6.2的方式執行一個輔助步驟再進行。
?
2015年NCCL開始實現AllReduce
openMPI的算法在2009年就都已經成熟并開源了,而英偉達在2015年下半年首次公開發布NCCL。
既然openmpi已經實現了這么多AllReduce算法,為什么英偉達還要開發NCCL?
從openMPI的源碼里我們能看到,其完全沒有考慮過深度學習的場景,基本沒有考慮過GPU系統架構。很明顯的一點,MPI中各個工作節點基本視為等同,并沒有考慮節點間latency和帶寬的不同,所以并不能充分發揮異構場景下的硬件性能。
而NCCL的優勢就在于完全貼合英偉達自己的硬件,能充分發揮性能。但是基本的算法原理其實相比openmpi里實現的ring算法是沒有變化的。
NCCL1.x只能在單機內部進行通信,NCCL2.0開始支持多節點(2017年Q2)。所以在NCCL2之前大家還會依賴MPI來進行集合通信。
?
2016年百度在深度學習中引入Ring AllReduce
openMPI代碼中2007年就有ring算法了,為什么會有Baidu在2016年提出Ring Allreduce的說法?
其實在baidu的論文題目里就說得很清楚了,他們是“Bringing HPC Techniques to Deep Learning”,ring算法是早就有了,但是應用到深度學習領域確實是他們首創的。Baidu還開源了他們基于TensorFlow修改的源碼,把TF里原來進行梯度規約的地方替換成了mpi實現的ring allreduce。
具體代碼在tensorflow/contrib/mpi_collectives/ring.h中
可以看到實現的是常規ring,而不是segmented ring。并且里面使用MPI_Sendrecv MPI_Irecv MPI_Send這些mpi通信原語來實現,和具體mpi庫無關(無論是openmpi還是MPICH2)。也沒有直接用MPI_AllReduce原語,因為按照openMPI的實現它很可能跑去用其它非ring算法了。
?
TensorFlow里的AllReduce
在tf早期版本中,分布式訓練只有PS架構。
在2017年后,開始逐步支持多種allreduce算法,其中的ring-allreduce實現正是baidu貢獻的。
NCCL2.0之后,TensorFlow/Baidu里的allreduce算法集成了NCCL來做GPU間通信,而不是依賴MPI了。
?
MPI和NCCL的關系
是不是從此我們只要NCCL,不再需要MPI了呢?NO
Nvidia的策略還是比較聰明,不和MPI競爭,只結合硬件做MPI沒做好的通信性能優化。在多機多卡分布式訓練中,MPI還是廣泛用來做節點管理。當紅炸子雞Horovod也是這么做的,NCCL只做實際的規約通信。
?
?
2.3 Gather
Gather?就是把多個進程的數據拼湊在一起
Gather操作的目的是將所有計算節點上的數據集合到主節點上。也可以采用Binomial Tree Algorithm算法。各個計算節點的網絡連接如上圖。
第一步:從葉子節點開始,將本地數據發送到其父節點;
第二步:第一步中的父節點接收到數據后,繼續將本地數據以及接收到的數據往上發,最終根節點上的數據就是所有節點數據Gather之后的結果了;
Binomial Tree Algorithm的實現方式通信次數為log(N)。
2.4?Allgather
? ? ? ? Allgather操作的目的是將計算節點的本地數據Vi同步到其他所有的計算節點,使得每一個節點都擁有一份 - 的值。
??????? 主流的實現方式有Recursive Doubling、Bruck Algorithm。
Recursive Doubling
上圖描述了Recursive Doubling算法的工作過程。
第一步:在每一個節點上開辟sizeof(V0) +?sizeof(V1) +?sizeof(V2) +?sizeof(V3)的內存,并且將本地的數據拷備到對應的起始位置;
第二步:如圖所示,step-1時,節點與其相距20的節點進行數據互發;
第三步:step-2時,節點與其相距21的節點進行數據互發,發送的數據包括節點自身的數據以及之前接收的數據;
依此方式執行k步,最終所有的計算節點都擁有一份全局的數據了。
Recursive Doubling方式要求N為2的整數倍,計算節點收集到所有數據的通信次數為log(N)。
Bruck Algorithm
上圖描述了Bruck Algorithm算法的工作過程。
第一步:如圖,R1將數據發給R0, R2將數據發給R1,......., R0將數據發給R4,發送一個數據塊;
第二步:重復k步,第k步時,Ri將數據發送給,發送個數據塊min(, N-sum_block);sum_block為計算節點上已有數據塊大小。
第三步:調整數據塊的順序,調整方法在4.1節代碼中介紹;
以上這種方式不要求N一定為2的整數倍,計算節點收集到所有數據的通信次數為ceil(log(N))。
?
2.5?Broadcast
Broadcast? 看名字就很好理解了,其實就是把同一份數據分發廣播給所有人,目的是將主節點的數值廣播到其他所有的計算節點。
主流的實現方式有Binomial Tree Algorithm、Geijin Algorithm。
Binomial Tree Algorithm
上圖描述了Binomial Tree Algorithm算法的工作過程,一般需要做broadcast操作時都有一個主節點設為R0。各個計算節點的網絡連接如上圖。
第一步:R0將本地值發送給左右孩子節點R1和R2,同時將R1和R2設置為root,再往它們的左右孩子節點發送;
第二步:第i步,Ri將本地值發送給和,同時將和設為root,再重復這一過程,直到該節點沒有孩子節點;
? ? ? ? Binomial Tree Algorithm這種廣播方式,每一次發送的數據塊為整個數據塊的大小,在數據塊較小時比較適用。但當數據塊較大時,以下介紹的Geijin Algorithm會更加適用。
Geijin Algorithm
Geijin Algorithm適用于大的數據塊。主要思想是:
第一步:將需要Broadcast的數據分成N塊,同時Scatter到各個計算節點上;
第二步:對各個計算節點分發到的子數據塊執行Allgather,即可完成操作;
Geijin Algorithm相比于Binomial Tree Algorithm增加了通信次數,但是每次收發的數據塊小了,對于大數據塊更能降低帶寬消耗。? ? ? ? ? ? ? ??
?
2.6 ReduceScatter
Scatter?不同于Broadcast, scatter可以將不同數據分發給不同的進程。
? ? ? ? ReduceScatter操作的目的也是對所有計算節點上的數值進行歸約操作,但是各個節點只保留歸約后的部分結果。以下介紹Recursive Halving的實現方式。
當workers數量為2的k次方時,以下以8個workers為例進行介紹:
? ? ? ? 最初,每一個worker都將數據分成8個數據塊,每個數據塊用Fi表示。本例中ReduceScatter的目的是使R0得到reducer(0_F0, ...., 7_F0)的結果、R1得到reducer(0_F1, ...., 7_F1)....。
上圖中描述了使用Recursive Halving算法進行ReduceScatter操作的過程。以R0為例具體步驟如下:
第一步:先計算每一個節點需要獲得數據塊哪一部分的歸約結果,并將數據塊切分成N個子塊;
第二步:第一次通信,R0將4-7子塊發送給R4,并接收R4發送過來的0-3子塊,然后在本地對自身的數據以及接收到的數據進行歸約操作;
第三步:第二次通信,R0將2-3子塊發送給R2,并接收R2發送過來的0-1子塊,然后在本地對自身的數據以及接收到的數據進行歸約操作;
第四步:依次執行,最終每個節點在對應的位置都得到了分配給其的結果,如圖中紅色位置;
這種方式的ReduceScatter要求N為2的整數倍,完成整個操作需要的的通信次數為log(N)
?
當workers數量非2的k次方時,可以采用以下方法:
第一步:小于2*remain的節點中,Ri為偶數的節點,將它需要做ReduceScatter的所有數據發送給Ri+1;
第二步:設置一個virtual_rank(VR)。VR的計算:第一步中偶數計算節點設置為-1,奇數的設置為0開始依次遞增。大于2*remain的計算節點VR=R-remain。詳細做法見4.2代碼;
第三步:virtual_rank非-1的節點按照2.6.1的Recursive Halving方法進行操作即可;
這種方法相比于2的k次方時多了第一步的所有數據的發送步驟。
?
三、總結
? ? ? ? 本文主要是通過對MPI中主流歸約算子的理解,進行歸納并且通過實例化的形式進行總結。一般并行計算中的歸約算子很多,并且每個算子都有多種實現方式,各種不同的實現適用于不同的場景,有些在小數據塊時效率高,有些在大數據塊時更有優勢,如何進行選擇需要根據業務場景甚至通過實驗對比來確定。
?
參考
Generalisation of Recursive Doubling for AllReduce
Optimization of Collective Communication Operations in MPICH
https://github.com/Microsoft/LightGBM
https://www.zhihu.com/question/37057945
https://zhuanlan.zhihu.com/p/100012827
AllReduce算法的前世今生
總結
以上是生活随笔為你收集整理的[机器学习] 常用并行计算算子原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: cebx文件怎么打开
- 下一篇: linux如何查询mongodb运行状态