分布式训练使用手册-paddle 数据并行
分布式訓練使用手冊?
分布式訓練基本思想?
分布式深度學習訓練通常分為兩種并行化方法:數據并行,模型并行,參考下圖:
在模型并行方式下,模型的層和參數將被分布在多個節點上,模型在一個mini-batch的前向和反向訓練中,將經過多次跨節點之間的通信。每個節點只保存整個模型的一部分;在數據并行方式下,每個節點保存有完整的模型的層和參數,每個節點獨自完成前向和反向計算,然后完成梯度的聚合并同步的更新所有節點上的參數。Fluid目前版本僅提供數據并行方式,另外諸如模型并行的特例實現(超大稀疏模型訓練)功能將在后續的文檔中予以說明。
在數據并行模式的訓練中,Fluid使用了兩種通信模式,用于應對不同訓練任務對分布式訓練的要求,分別為RPC通信和Collective 通信。其中RPC通信方式使用 gRPC ,Collective通信方式使用 NCCL2 。
RPC通信和Collective通信的橫向對比如下:
| Feature | Collective | RPC |
|---|---|---|
| Ring-Based通信 | Yes | No |
| 異步訓練 | Yes | Yes |
| 分布式模型 | No | Yes |
| 容錯訓練 | No | Yes |
| 性能 | Faster | Fast |
RPC通信方式的結構:
使用RPC通信方式的數據并行分布式訓練,會啟動多個pserver進程和多個trainer進程,每個pserver進程會保存一部分模型參數,并負責接收從trainer發送的梯度并更新這些模型參數;每個trainer進程會保存一份完整的模型,并使用一部分數據進行訓練,然后向pserver發送梯度,最后從pserver拉取更新后的參數。
pserver進程可以在和trainer完全不同的計算節點上,也可以和trainer公用節點。一個分布式任務所需要的pserver進程個數通常需要根據實際情況調整,以達到最佳的性能,然而通常來說pserver的進程不會比trainer更多。
注: 在使用GPU訓練時,pserver可以選擇使用GPU或只使用CPU,如果pserver也使用GPU,則會增加一次從CPU拷貝接收到的梯度數據到GPU的開銷,在某些情況下會導致整體訓練性能降低。
注: 在使用GPU訓練時,如果每個trainer節點有多個GPU卡,則會先在每個trainer節點的多個卡之間執行NCCL2通信方式的梯度聚合,然后再通過pserver聚合多個節點的梯度。
NCCL2通信方式的結構:
使用NCCL2(Collective通信方式)進行分布式訓練,是不需要啟動pserver進程的,每個trainer進程都保存一份完整的模型參數,在完成計算梯度之后通過trainer之間的相互通信,Reduce梯度數據到所有節點的所有設備然后每個節點在各自完成參數更新。
使用parameter server方式的訓練?
使用 transpiler API可以把單機可以執行的程序快速轉變成可以分布式執行的程序。在不同的服務器節點 上,通過傳給 transpiler 對應的參數,以獲取當前節點需要執行的 Program 。
需要配置參數包括?
| 參數 | 說明 |
|---|---|
| role | 必選區分作為pserver啟動還是trainer啟動,不傳給transpile,也可以用其他的變量名或環境變量 |
| trainer_id | 必選如果是trainer進程,用于指定當前trainer在任務中的唯一id,從0開始,在一個任務中需保證不重復 |
| pservers | 必選當前任務所有pserver的ip:port列表字符串,形式比如:127.0.0.1:6170,127.0.0.1:6171 |
| trainers | 必選trainer節點的個數 |
| sync_mode | 可選True為同步模式,False為異步模式 |
| startup_program | 可選如果startup_program不是默認的fluid.default_startup_program(),需要傳入此參數 |
| current_endpoint | 可選只有NCCL2模式需要傳這個參數 |
一個例子,假設有兩個節點,分別是 192.168.1.1 和 192.168.1.2 ,使用端口6170,啟動4個trainer, 則代碼可以寫成:
role = "PSERVER"
trainer_id = 0 # get actual trainer id from cluster
pserver_endpoints = "192.168.1.1:6170,192.168.1.2:6170"
current_endpoint = "192.168.1.1:6170" # get actual current endpoint
trainers = 4
t = fluid.DistributeTranspiler()
t.transpile(trainer_id, pservers=pserver_endpoints, trainers=trainers)
if role == "PSERVER":pserver_prog = t.get_pserver_program(current_endpoint)pserver_startup = t.get_startup_program(current_endpoint,pserver_prog)exe.run(pserver_startup)exe.run(pserver_prog)
elif role == "TRAINER":train_loop(t.get_trainer_program())
選擇同步或異步訓練?
Fluid分布式任務可以支持同步訓練或異步訓練,在同步訓練方式下,所有的trainer節點,會在每個mini-batch 同步地合并所有節點的梯度數據并發送給parameter server完成更新,在異步訓練方式下,每個trainer沒有相互同步等待的過程,可以獨立地更新parameter server的參數。通常情況下,使用異步訓練方式,可以在trainer節點更多的時候比同步訓練方式有更高的總體吞吐量。
在調用 transpile 函數時,默認會生成同步訓練的分布式程序,通過指定 sync_mode=False 參數即可生成異步訓練的程序:
t.transpile(trainer_id, pservers=pserver_endpoints, trainers=trainers, sync_mode=False)
選擇是否使用分布式embedding表進行訓練?
embedding被廣泛應用在各種網絡結構中,尤其是文本處理相關的模型。在某些場景,例如推薦系統或者搜索引擎中, embedding的feature id可能會非常多,當feature id達到一定數量時,embedding參數會變得很大,一方面可能 單機內存無法存放導致無法訓練,另一方面普通的訓練模式每一輪迭代都需要同步完整的參數,參數太大會讓通信變得 非常慢,進而影響訓練速度。
Fluid支持千億量級超大規模稀疏特征embedding的訓練,embedding參數只會保存在parameter server上,通過 參數prefetch和梯度稀疏更新的方法,大大減少通信量,提高通信速度。
該功能只對分布式訓練有效,單機無法使用。 需要配合稀疏更新一起使用。
使用方法,在配置embedding的時候,加上參數 is_distributed=True 以及 is_sparse=True 即可。 參數 dict_size 定義數據中總的id的數量,id可以是int64范圍內的任意值,只要總id個數小于等于dict_size就可以支持。 所以配置之前需要預估一下數據中總的feature id的數量。
emb = fluid.layers.embedding(is_distributed=True,input=input,size=[dict_size, embedding_width],is_sparse=True)
選擇參數分布方法?
參數 split_method 可以指定參數在parameter server上的分布方式。
Fluid默認使用 RoundRobin 方式將參數分布在多個parameter server上。此方式在默認未關閉參數切分的情況下,參數會較平均的分布在所有的 parameter server上。如果需要使用其他,可以傳入其他的方法,目前可選的方法有: RoundRobin 和 HashName 。也可以使用自定義的分布方式,只需要參考 這里 編寫自定義的分布函數。
關閉切分參數?
參數 slice_var_up 指定是否將較大(大于8192個元素)的參數切分到多個parameter server以均衡計算負載,默認為開啟。
當模型中的可訓練參數體積比較均勻或者使用自定義的參數分布方法是參數均勻分布在多個parameter server上, 可以選擇關閉切分參數,這樣可以降低切分和重組帶來的計算和拷貝開銷:
t.transpile(trainer_id, pservers=pserver_endpoints, trainers=trainers, slice_var_up=False)
開啟內存優化?
在parameter server分布式訓練模式下,要開啟內存優化 memory_optimize 和單機相比,需要注意按照下面的規則配置:
- 在pserver端,不要執行
memory_optimize - 在trainer端,先執行
fluid.memory_optimize再執行t.transpile() - 在trainer端,調用
memory_optimize需要增加skip_grads=True確保發送的梯度不會被重命名:fluid.memory_optimize(input_program, skip_grads=True)
示例:
if role == "TRAINER":fluid.memory_optimize(fluid.default_main_program(), skip_grads=True)
t = fluid.DistributeTranspiler()
t.transpile(trainer_id, pservers=pserver_endpoints, trainers=trainers)
if role == "PSERVER":# start pserver here
elif role == "TRAINER":# start trainer here
使用NCCL2通信方式的訓練?
NCCL2模式的分布式訓練,由于沒有parameter server角色,是trainer之間互相通信,使用時注意:
- 配置
fluid.DistributeTranspilerConfig中mode="nccl2"。 - 調用
transpile時,trainers傳入所有trainer節點的endpoint,并且傳入參數current_endpoint。 在此步驟中,會在startup program中增加gen_nccl_id_op用于在多機程序初始化時同步NCCLID信息。 - 初始化
ParallelExecutor時傳入num_trainers和trainer_id。 在此步驟中,ParallelExecutor會使用多機方式初始化NCCL2并可以開始在多個節點對每個參數對應的梯度執行跨節點的allreduce操作,執行多機同步訓練
一個例子:
trainer_id = 0 # get actual trainer id here
trainers = "192.168.1.1:6170,192.168.1.2:6170"
current_endpoint = "192.168.1.1:6170"
config = fluid.DistributeTranspilerConfig()
config.mode = "nccl2"
t = fluid.DistributeTranspiler(config=config)
t.transpile(trainer_id, trainers=trainers, current_endpoint=current_endpoint)
exe = fluid.ParallelExecutor(use_cuda,loss_name=loss_name, num_trainers=len(trainers.split(",")), trainer_id=trainer_id)
...
NCCL2模式必要參數說明?
| 參數 | 說明 |
|---|---|
| trainer_id | (int) 任務中每個trainer節點的唯一ID,從0開始,不能有重復 |
| trainers | (int) 任務中所有trainer節點的endpoint,用于在NCCL2初始化時,廣播NCCL ID |
| current_endpoint | (string) 當前節點的endpoint |
目前使用NCCL2進行分布式訓練僅支持同步訓練方式。使用NCCL2方式的分布式訓練,更適合模型體積較大,并需要使用同步訓練和GPU訓練,如果硬件設備支持RDMA和GPU Direct,可以達到很高的分布式訓練性能。
啟動多進程模式 NCCL2 分布式訓練作業?
通常情況下使用多進程模式啟動 NCCL2 分布式訓練作業可以獲得更好多訓練性能,Paddle 提供了 paddle.distributed.launch 模塊可以方便地啟動多進程作業,啟動后每個訓練進程將會使用一塊獨立的 GPU 設備。 使用時需要注意:
- 設置節點數:通過環境變量
PADDLE_NUM_TRAINERS設置作業的節點數,此環境變量也會被設置在每個訓練進程中。 - 設置每個節點的設備數:通過啟動參數
--gpus可以設置每個節點的 GPU 設備數量,每個進程的序號將會被自動設置在環境變量PADDLE_TRAINER_ID中。 - 數據切分: 多進程模式是每個設備一個進程,一般來說需要每個進程處理一部分訓練數據,并且保證所有進程能夠處理完整的數據集。
- 入口文件:入口文件為實際啟動的訓練腳本。
- 日志:每個訓練進程的日志默認會保存在
./mylog目錄下,您也可以通過參數--log_dir進行指定。
啟動樣例:
> PADDLE_NUM_TRAINERS=<TRAINER_COUNT> python -m paddle.distributed.launch --gpus <NUM_GPUS_ON_HOSTS> <ENTRYPOINT_SCRIPT> --arg1 --arg2 ...
NCCL2分布式訓練注意事項?
注意: 使用NCCL2模式分布式訓練時,需要確保每個節點訓練等量的數據,防止在最后一輪訓練中任務不退出。通常有兩種方式:
- 隨機采樣一些數據,補全分配到較少數據的節點上。(推薦使用這種方法,以訓練完整的數據集)。
- 在python代碼中,每個節點每個pass只訓練固定的batch數,如果這個節點數據較多,則不訓練這些多出來的數據。
說明: 使用NCCL2模式分布式訓練時,如果只希望使用一個節點上的部分卡,可以通過配置環境變量:export CUDA_VISIBLE_DEVICES=0,1,2,3 指定。
注意: 如果系統中有多個網絡設備,需要手動指定NCCL2使用的設備,假設需要使用 eth2 為通信設備,需要設定如下環境變量:
export NCCL_SOCKET_IFNAME=eth2
另外NCCL2提供了其他的開關環境變量,比如指定是否開啟GPU Direct,是否使用RDMA等,詳情可以參考 ncclknobs 。
總結
以上是生活随笔為你收集整理的分布式训练使用手册-paddle 数据并行的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 深度学习的分布式训练--数据并行和模型并
- 下一篇: 未授予用户在此计算机上的请求登陆类型处理