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