Distributed Computing
劃分
-
數據并行(DataParallelism)
將數據分為n份,發送到n個GPU上,每個GPU上都存在一個完整的大模型
缺點: 模型太大
-
Pipeline Parallelism(串行的)
將模型做split,每個GPU負責一部分,GPU1運行完后將結果發送給GPU2
缺點:
- 不好分割
- 分割成差不多的計算量是個大的問題
- 速度受限于最慢的GPU模塊
- 可能會出現許多空閑狀態的GPU
-
Tensor Parallelism
更細化的,在tensor維度上
會造成額外的通信
Data Parallelism
parameter server
-
Parameter server
分為兩個部分
- Parameter Server : recieive gradients from workers and send back the aggregated results
- workers: compute gradients using splitted dataset and send to parameter server
這種方式不太適合大模型
-
步驟
-
replicate models to workers
-
split data to workers
-
compute gradient
-
Aggregate and synchronize gradient
-
Gradient update and update model parameters
-
-
All in one picture
-
Parameter server:代碼
parameter server 通信-Communication:reduce and broadcast
-
one-to-many communication: a type of operations performed across all workers
- Reduce : 類似聚合,但是在聚合過程中進行平均或求和
- Broadcast: 向所有的workers發送相同的復制
-
Parameter server 的bottle neck(瓶頸)
parameter server主要起的作用就是同步信息的作用,不希望有類似server的節點:All-Reduce
-
Naive All reduce implementation
需要循環,每次傳輸所有的數據
-
Better All reduce implementation
每個節點只和旁邊的節點做交互,也需要循環三次,但每次只傳輸旁邊的一部分
-
更聰明的方式: Recursive Halving reduce(遞歸減半規約)
同上面的類似,也是臨近的workers交換,對于8個worker來說,做了3次的iteration,然后交換間隔是20,21,232^0,2^1,2^320,21,23,這樣可以將時間復雜度從O(N)降到O(log?N)O(N)降到O(\log N)O(N)降到O(logN)
Zero-1/2/3 and FADP
-
如果我們訓練一個非常大的大模型,那么即使是最好的GPU也沒法完全將模型權重完全加載到內存中,然而,訓練需要存儲梯度和優化器
在fp32精度下,如果模型的weight占2bytes,那么其gradients大概也占2bytes左右,如果優化器使用Adam,其optimizer states因為要存儲parameters, momentum 和variance,所以大概需要6倍(這個倍數取決于配置,再怎么配置一般也都是weight的三到四倍),即使是使用A100或者H100顯卡(80G)來訓練,最多也只能訓練5.0B的模型
-
第一種方式
ZERO-1
沒個GPU存放完整的額weight和gradients,分割optimizer states 到N個不同的GPU卡上,假設N=64,則這時候用80G的顯卡,大概能訓練19B參數量的模型
-
第二種方式
ZERO-2
相比zero-1,除了optimizer states,我們還將gradients也分布在不同的GPU上,假設N=64, 則這時候用80G的顯卡,大概能訓36B參數量的模型
-
第三種方式
ZERO-3
將optimizer states,gradients and weights都分布在不同的GPU上,假設N=64, 則這時候用80G的顯卡,大概能訓320B參數量的模型
-
在pytorch中,ZERO-3等價于
FSDP
(FullyShardedDataParallel),即所有的參數都做parallelism難點在于GPU之間的通信,如何將GPU前后向傳播聯合起來計算
Pipeline Parallelism
-
與數據并行不同,Pipeline直接對模型進行分割
Naive Implementation
-
下圖表示的是4層網絡在訓練的時候,使用F代表Forward,B代表Backward,下面圖中的(b)Training timeline,其橫軸為時間軸,假設這4層網絡分別存放在4個GPU上
所以計算的順序為GPU0->GPU1->GPU2->GPU3->GPU3->GPU2->GPU1->GPU0,那么這四個GPU沒個都使用了兩個時間單元,占有率都是 28=0.25\frac{2}{8}=0.2582?=0.25,這意味著其他75%的時間都是空閑的,而且這25%還是在假設沒個pipeline的執行時間是一樣的情況下,否則這個占有率還可能更低,這個是pipeline并行的一大問題,沒有辦法很好的利用到GPU的資源
同一時間點只有一個設備在計算,其他的都在等待。
Micro-batch
-
讓它多跑一跑不斷地將計算給到流水線,如下圖,將batch為16的分為4個batch為4的(Micro-batch技術),下圖下面的部分,這時候T=14, 那么每個GPU的使用率就是4?84?14=47\frac{4*8}{4*14}=\frac{4}{7}4?144?8?=74?,這樣空閑的時間實際上就下降了很多,當然如果再將任務拆解的更小,還可以提升使用
注意,紅色為空閑時間
-
如何提高Pipeline Paralisem的效率?盡量將任務拆解的更小,然后做micro-batch
Tensor Parallelism
-
在pipeline Parallelism中再做tensor Parallelism,還可以提高pipeline Parallelism的效率
-
tensor并行的核心關鍵點:如何把運算拆解
注意,這里后續還需要進行一個類似reduce的操作
MLP
-
MLP和Self-Attention的tensor并行
-
partition in First FFN Layer,注意這里用兩個GPU設備來舉例
-
partition in Second FFN Layer,注意這里用兩個GPU設備來舉例
self-attention
-
假設這里是用三個GPU來舉例 ,每個GPU分別來存儲QKV,先在各GPU上分別計算QKV
softmax計算
計算Z
所以tensor parallelism核心是怎么將這些操作設計出來
不同并行方法的總結
-
總結
Data Oarallelism
- 分割數據
- copy數據到N的設備上
- 高利用率,高內存開銷,設備間低通信
- 優化:ZeRO 1/2/3,FSDP
Pipeline Parallelism
- 按層分割模型
- 低利用率,低內存開銷,適中的通信要求
Tensor Parallelism
- 按tensor維度分割模型
- 高利用率,低內存開銷,高通信要求(有許多all-reduce操作)
3D并行
-
將上面的三種并行方法都混在一起
下面的相同的顏色表示同一個server里面的GPU(Model Parallel是Tensor Parallelism)
需要注意的是:
為什么同一個server中用 ModelParallel(Tensor Parallelism)?
因為tensor并行是高通信的,GPU之間需要經常交互,同一個server中交互更快
-
如何設計并行?
當模型太大,無法加載到一個GPU上:使用pipeline parallelism來拆分模型
當layer太大,無法加載到一個GOU上:使用tensor parallelism來拆分layer
帶寬:bandwith
-
通信的時間可能比計算的時間更長,所以我們需要降低通信的開銷
在同一個數據中心,數據通信網絡延遲可能是1毫秒到10毫秒,無線wifi連接數據通信延遲是100ms,地球間的通信網絡延遲大概是500毫秒到1秒,但是在同一個機架內(同一個GPU集群上)那么延遲1納秒,非常小
-
減小傳輸的數據大小
在worker之間,或者在GPU之間,減小傳輸的數據(gradient,parameters)大小
- 梯度剪枝
- 量化(會損失精度和信息)
-
壓縮通信:梯度剪枝
注意梯度剪枝是一種基于梯度信息的剪枝方法。它通過分析梯度的大小來決定哪些神經元或連接是重要的,哪些可以被移除,區別于梯度裁剪