2018 年 10 月,飛槳團隊發(fā)布 Paddle Fluid 1.0 版本,對神經(jīng)網(wǎng)絡描述、大規(guī)模分布式訓練、高性能推理引擎等核心能力進行了全面升級。以工業(yè)界應用必需的分布式訓練能力為例,在最新的 Paddle Fluid 1.5.2 版本中,飛槳支持數(shù)據(jù)并行、模型并行、流水線并行等多種并行模式,參數(shù)服務器架構和點對點同步訓練架構全面支持在 CPU、GPU 等硬件資源設備上的大規(guī)模訓練。本文將介紹飛槳分布式訓練在 Kubernetes 社區(qū)中的 Volcano 系統(tǒng)上進行實踐的案例。
Kubernetes 是當今最火的容器化應用自動部署、伸縮和資源管理的開源系統(tǒng)。隨著 Kubernetes 的崛起,越來越多的公司愿意將自己的業(yè)務應用部署在 Kubernetes 上。除了典型的 Web 服務、數(shù)據(jù)庫等服務會基于 Kubernetes 進行部署以外,深度學習框架的分布式訓練也不例外。
然而,在 Kubernetes 系統(tǒng)中提交深度學習訓練任務的功能并不像傳統(tǒng)的高性能計算 MPI 平臺那樣直觀。在 2017 年,Kubernetes 社區(qū)就有文章 Run Deep Learning with PaddlePaddle on Kubernetes 分析了運行 PaddlePaddle 對底層資源的訴求,基于 PaddlePaddle 對計算容錯性、彈性伸縮、資源隔離的要求,提出在 Kubernetes 平臺上運行 PaddlePaddle 是最佳實踐。
自 Paddle Fluid 1.0 發(fā)布以來,飛槳在平臺部署和任務調(diào)度管理上已經(jīng)取得了長足的進步。借助 Kubernetes 平臺,飛槳可以實現(xiàn) CPU/GPU 等硬件資源的合理調(diào)度分配、訓練任務的彈性擴縮容,并能顯著提升計算資源的利用率。但是,在并行創(chuàng)建和調(diào)度任務、訓練任務的生命周期管理、計算資源親和性調(diào)度、調(diào)度策略優(yōu)化等方面還有提升空間。為了提升飛槳框架的計算效率,飛槳團隊和 Volcano 團隊聯(lián)合發(fā)布 PaddlePaddle on Volcano 方案。
Volcano 是一款構建于 Kubernetes 之上的增強型高性能計算任務批量處理系統(tǒng)。
作為一個面向高性能計算場景的平臺,它彌補了 kubernetes 在機器學習、深度學習、HPC、大數(shù)據(jù)計算等場景下的基本能力缺失,其中包括 GANg-schedule 的調(diào)度能力、計算任務隊列管理、GPU 親和性調(diào)度。另外,Volcano 在原有 Kubernetes 能力基礎上對計算任務的批量創(chuàng)建及生命周期管理、FAIr-share 調(diào)度等方面做了增強。
Volcano 平臺可以滿足飛槳對資源創(chuàng)建,資源調(diào)度的基本要求。Volcano 的批量創(chuàng)建批量調(diào)度計算任務為飛槳作業(yè)提供計算任務的自動化生命周期管理,gang-scheduler 調(diào)度策略可以滿足 PServer 和 Trainer “all or nothing”的業(yè)務調(diào)度約束,Queue 和 priority 邏輯可以管理集群下計算作業(yè)的執(zhí)行順序,F(xiàn)air-share 和 GPU 親和度調(diào)度使計算任務調(diào)度更貼合 PServer 和 Trainer 對節(jié)點資源和網(wǎng)絡拓撲結構的要求而提升任務計算效率。
Volcano 借助 Kubernetes 創(chuàng)建 CRD 能力,在 Kubernetes 中引入“apiVersion”為“batch.volcano.sh/v1alpha1”,“kind”為“Job”的資源對象,用于描述計算任務。通過配置和創(chuàng)建 Volcano job 可以使用 Volcano 平臺創(chuàng)建、管理和調(diào)度計算任務。使用 volcano 平臺,需要先在 Kubernetes 集群下安裝 Volcano,安裝 Volcano 的方法可參考 Volcano 官網(wǎng)。
選擇一個飛槳框架任務分別使用 Kubernetes 原生資源和 Volcano job 執(zhí)行計算任務并對比分析,以下對比將著重體現(xiàn)兩者在使用方法、任務管理、調(diào)度策略方面進行比較。選擇飛槳官網(wǎng)分布式訓練 CTR(Click-Through-Rate) demo 進行對比測試。CTR demo 將運行兩個 PServer 任務和兩個 Trainer 任務。
首先使用飛槳官網(wǎng)推薦模式執(zhí)行分布式計算任務,先創(chuàng)建一個副本數(shù)為 2 的 Kubernetes ReplicaSet 對象,用于運行 PServer 業(yè)務,然后創(chuàng)建一個并行度為 2 的 Kubernetes Job 對象,用于運行 Trainer 任務。
創(chuàng)建 PServer 任務
root@volcano-paddlepaddle:~# kubectl apply -f pserver.yamlreplicaset.extensions/fluid-ctr-pserver create
查看 pserver ReplicaSet 組件
root-paddlepaddle:~# kubectl get rsNAME DESIRED CURRENT READY AGEfluid-ctr-pserver 2 2 2 5
查看 pserver pods
root-paddlepaddle:~# kubectl get pods | grep fluidfluid-ctr-pserver-b9w99 1/1 Running 0 9m18sfluid-ctr-pserver-pb9vd 1/1 Running 0 9m18
查看 pserver 日志,PServer 已經(jīng)開始工作,并對外提供服務
root@volcano-paddlepaddle:~# kubectl logs fluid-ctr-pserver-b9w99+ case "$1"in+ start_fluid_process+ pserver_label=paddle-job-pserver=fluid-ctr+ trainer_label=paddle-job=fluid-ct+ hostname=c-rlnrdybm-muamumvq-1+ task_index=+ '[' PSERVER == TRAINER ']+ '[' PSERVER == PSERVER ']'+ stdbuf -oL python /root/k8s_tools.py wait_pods_running paddle-job-pserver=fluid-ctr 2label selector: paddle-job-pserver=fluid-ctr, desired: 2current cnt: 0 sleep for 5 seconds...+ '[' PSERVER == TRAINER ']'+ '[' PSERVER == WORKER ']++ python /root/k8s_tools.py fetch_endpoints paddle-job-pserver=fluid-ctr 30236+ export PADDLE_PSERVERS=192.168.48.24:30236,192.168.48.25:30237+ PADDLE_PSERVERS=192.168.48.24:30236,192.168.48.25:30237++ python /root/k8s_tools.py fetch_ips paddle-job=fluid-ctr+ export PADDLE_TRAINER_IPS=+ PADDLE_TRAINER_IPS=+ '[' PSERVER == TRAINER ']'+ '[' PSERVER == WORKER ']'++ python /root/k8s_tools.py fetch_id paddle-job-pserver=fluid-ctr+ task_index=0+ export PADDLE_TRAINER_ID=0+ PADDLE_TRAINER_ID=0+ export PADDLE_PSERVER_ID=0+ PADDLE_PSERVER_ID=0+ stdbuf -oL sh -c 'cd /workspace/ctr && python train.py --is_local 0 --cloud_train 1'2019-09-03 06:43:10,661 - INFO - run dist training2019-09-03 06:43:10,715 - INFO - run pserverget_pserver_program() is deprecated, call get_pserver_programs() to get pserver main and startup in a single call.I0903?06:43:10.826609????41?grpc_server.cc:435]?Server?listening?on?192.168.48.24:30236?selected?port:
創(chuàng)建?trainer 任務
root@volcano-paddlepaddle:~# kubectl apply -f trainer.yamljob.batch/fluid-ctr-trainer create
查看 trainer pods
root-paddlepaddle:~# kubectl get pod | grep fluidfluid-ctr-pserver-b9w99 1/1 Running 0 87mfluid-ctr-pserver-pb9vd 1/1 Running 0 87mfluid-ctr-trainer-lg9n5 1/1 Running 0 12sfluid-ctr-trainer-tVR99 1/1 Running 0 12
查看 trainer 任務日志,看到任務已經(jīng)開始執(zhí)行
root@volcano-paddlepaddle:~# kubectl logs fluid-ctr-trainer-lg9n5+ case "$1" in+ start_fluid_process+ pserver_labe=paddle-job-pserver=fluid-ctr+ trainer_label=paddle-job=fluid-ctr+ hostname=c-rlnrdybm-muamumvq-2+ task_index=+ '[' TRAINER == TRAINER ']'+ stdbuf -oL python /root/k8s_tools.py wait_pods_running paddle-job-pserver=fluid-ctr 2label selector: paddle-job-pserver=fluid-ctr, desired: 2+ '[' TRAINER == TRAINER ']'+ stdbuf -oL python /root/k8s_tools.py wait_pods_running paddle-job=fluid-ctr 2label selector: paddle-job=fluid-ctr, desired: 2++ python /root/k8s_tools.py fetch_endpoints paddle-job-pserver=fluid-ctr 30236+ export PADDLE_PSERVERS=192.168.48.24:30236,192.168.48.25:30237+ PADDLE_PSERVERS=192.168.48.24:30236,192.168.48.25:30237++ python /root/k8s_tools.py fetch_ips paddle-job=fluid-ctr+ export PADDLE_TRAINER_IPS=192.168.48.24,192.168.48.25+ PADDLE_TRAINER_IPS=192.168.48.24,192.168.48.25+ '[' TRAINER == TRAINER ']'+ check_failed_cnt 1+ max_failed=1++ python /root/k8s_tools.py count_pods_by_phase paddle-job=fluid-ctr Failed+ failed_count=0+ '[' 0-gt 1 ']'++ python /root/k8s_tools.py fetch_id paddle-job=fluid-ctr+ task_index=0+ export PADDLE_TRAINER_ID=0+ PADDLE_TRAINER_ID=0+ export PADDLE_PSERVER_ID=0+ PADDLE_PSERVER_ID=0+ stdbuf -oL sh -c 'cd /workspace/ctr && python train.py --is_local 0 --cloud_train 1'2019-09-03 08:10:20,888 - INFO - run dist training2019-09-03 08:10:20,951 - INFO - download the training materials% Total % Received % Xferd Average Speed Time Time Time CurrentDload Upload Total Spent Left Speed100 433M 100 433M 0 0 70.9M 0 0:00:06 0:00:06 --:--:-- 97.0M2019-09-03 08:11:04,522 - INFO - run trainer2019-09-03 08:11:04,591 - WARNING -I0903 08:11:04.594007 25 parallel_executor.cc:329] The number of CPUPlace, which is used in ParallelExecutor, is 2. And the Program will be copied 2 copiesI0903 08:11:04.875757 25 rpc_client.h:101] init rpc client with trainer_id 02019-09-03 08:11:38,625 - INFO - TRAIN --> pass: 0 batch: 0 loss: 0.697331115723 auc: 0.500826068453, batch_auc: 0.5008260684532019-09-03 08:11:38,967 - INFO - TRAIN --> pass: 0 batch: 1 loss: 0.652093688965 auc: 0.48451329672, batch_auc: 0.484513296722019-09-03 08:11:39,242 - INFO - TRAIN --> pass: 0 batch: 2 loss: 0.629092956543 auc: 0.485173881519, batch_auc: 0.4851738815192019-09-03 08:11:39,577 - INFO - TRAIN --> pass: 0 batch: 3 loss: 0.603850708008 auc: 0.482131778494, batch_auc: 0.4821317784942019-09-03 08:11:39,874 - INFO - TRAIN --> pass: 0 batch: 4 loss: 0.591485412598 auc: 0.479737304993, batch_auc: 0.4797373049932019-09-03 08:11:40,133 - INFO - TRAIN --> pass: 0 batch: 5 loss: 0.58376159668 auc: 0.478554220739, batch_auc: 0.4785542207392019-09-03 08:11:40,385 - INFO - TRAIN --> pass: 0 batch: 6 loss: 0.561969116211 auc: 0.481465857424, batch_auc: 0.4814658574242019-09-03 08:11:40,637 - INFO - TRAIN --> pass: 0 batch: 7 loss: 0.557065185547 auc: 0.486014931119, batch_auc: 0.4860149311192019-09-03 08:11:40,890 - INFO - TRAIN --> pass: 0 batch: 8 loss: 0.562498413086 auc: 0.489651573333, batch_auc: 0.4896515733332019-09-03 08:11:41,158 - INFO - TRAIN --> pass: 0 batch: 9 loss: 0.566428283691 auc: 0.489853260221, batch_auc: 0.491378844262019-09-03 08:11:41,452 - INFO - TRAIN --> pass: 0 batch: 10 loss: 0.564840087891 auc: 0.492880386228, batch_auc: 0.4940137639382019-09-03 08:11:41,742 - INFO - TRAIN --> pass: 0 batch: 11 loss: 0.564809204102 auc: 0.493201528907, batch_auc: 0.4988723815822019-09-03 08:11:42,056 - INFO - TRAIN --> pass: 0 batch: 12 loss: 0.584479736328 auc: 0.494151972036, batch_auc: 0.5039266283912019-09-03 08:11:42,329 - INFO - TRAIN --> pass: 0 batch: 13 loss: 0.615677246094 auc: 0.49252557362, batch_auc: 0.5028352489
等待 trainer 任務執(zhí)行完成,查看 pserver 和 trainer pods 狀態(tài),trainer 已經(jīng)執(zhí)行完成,pserver 仍然于運行中
root-paddlepaddle:~# kubectl get pods | grep fluidfluid-ctr-pserver-b9w99 1/1 Running 0 177mfluid-ctr-pserver-pb9vd 1/1 Running 0 177mfluid-ctr-trainer-lg9n5 0/1 Completed 0 90mfluid-ctr-trainer-tvr99 0/1 Completed 0 90
將上述計算任務遷移到 volcano 平臺上進行測試。
Volcano 支持 Multi-pod jobs,拓展“tasks”字段,tasks 下可以定義多個 pod 描述,其中“replicas” 字段描述 task 將要生成的 pod 數(shù)量,“name”描述 task 名稱,pod 名稱將根據(jù) task 名稱生成。Template 字段與 kubernetes “podTemplate”一致。ctr 的 demo 中含有兩個 task:“pserver”和“trainer”,每個 task 的 replicas 都是 2,將會創(chuàng)建兩個 PServer 任務,兩個 Trainer 任務。
使用 Volcano 調(diào)度器,在 job 的配置中需要指定“schedulerName”為“volcano”,如果 schedulerName 沒有指定為“volcano”,job 下的任務調(diào)度將會使用 kubernetes 的默認調(diào)度器“default”調(diào)度器。
Volcano 通過指定“minAvailable”字段保證計算任務的 gang-scheduler 調(diào)度策略?!癿inAvailable”數(shù)值指明在對當前計算任務下的 pods 進行調(diào)度時,需保證多少計算任務都能夠調(diào)度才會執(zhí)行調(diào)度任務,“minAvailable”的數(shù)值需要小于或等于計算任務下的所有任務數(shù)量的總和。對于 PaddlePaddle 框架計算任務,只有當所有的 PServer 和 Trainer 任務都處于運行中,才能開始計算任務。因此對于飛槳計算任務,“minAvailable”的數(shù)值需要與計算任務下的所有計算任務總和相等。
對于使用飛槳分布式訓練的應用,在計算過程中,如果 PServer 任務或者 Trainer 任務被驅逐或失敗,PServer 和 Trainer 形成的計算集群將會失效,所有的 PServer 任務和 Trainer 任務都需要重啟,以形成新的集群開始新的計算。Volcano 可以通過設置“policies”實現(xiàn)上述目的。設置“PodEvicted”事件對應“RestartJob”動作,設置“PodFailed”事件對應“RestartJob”動作,在設置了這兩個“policies”之后,當計算任務被驅逐或者失敗,所有的計算任務將會重啟。



?津公網(wǎng)安備12011002023007號