企业如何选择合适的技术方案?点击看专家聊数字化转型落地过程中的困难和解决办法 了解详情
写点什么

百度飞桨 (PaddlePaddle) 分布式训练在 Volcano 系统上的实践

  • 2019 年 11 月 11 日
  • 本文字数:11067 字

    阅读完需:约 36 分钟

百度飞桨(PaddlePaddle)分布式训练在Volcano系统上的实践

飞桨(PaddlePaddle)是百度于 2016 年 9 月开源的深度学习框架,旨在提供一款安全高效、灵活易用、可扩展的深度学习平台。


2018 年 10 月,飞桨团队发布 Paddle Fluid 1.0 版本,对神经网络描述、大规模分布式训练、高性能推理引擎等核心能力进行了全面升级。以工业界应用必需的分布式训练能力为例,在最新的 Paddle Fluid 1.5.2 版本中,飞桨支持数据并行、模型并行、流水线并行等多种并行模式,参数服务器架构和点对点同步训练架构全面支持在 CPU、GPU 等硬件资源设备上的大规模训练。本文将介绍飞桨分布式训练在 Kubernetes 社区中的 Volcano 系统上进行实践的案例。


Kubernetes 是当今最火的容器化应用自动部署、伸缩和资源管理的开源系统。随着 Kubernetes 的崛起,越来越多的公司愿意将自己的业务应用部署在 Kubernetes 上。除了典型的 Web 服务、数据库等服务会基于 Kubernetes 进行部署以外,深度学习框架的分布式训练也不例外。


然而,在 Kubernetes 系统中提交深度学习训练任务的功能并不像传统的高性能计算 MPI 平台那样直观。在 2017 年,Kubernetes 社区就有文章 Run Deep Learning with PaddlePaddle on Kubernetes 分析了运行 PaddlePaddle 对底层资源的诉求,基于 PaddlePaddle 对计算容错性、弹性伸缩、资源隔离的要求,提出在 Kubernetes 平台上运行 PaddlePaddle 是最佳实践。


自 Paddle Fluid 1.0 发布以来,飞桨在平台部署和任务调度管理上已经取得了长足的进步。借助 Kubernetes 平台,飞桨可以实现 CPU/GPU 等硬件资源的合理调度分配、训练任务的弹性扩缩容,并能显著提升计算资源的利用率。但是,在并行创建和调度任务、训练任务的生命周期管理、计算资源亲和性调度、调度策略优化等方面还有提升空间。为了提升飞桨框架的计算效率,飞桨团队和 Volcano 团队联合发布 PaddlePaddle on Volcano 方案。


Volcano 是一款构建于 Kubernetes 之上的增强型高性能计算任务批量处理系统。


作为一个面向高性能计算场景的平台,它弥补了 kubernetes 在机器学习、深度学习、HPC、大数据计算等场景下的基本能力缺失,其中包括 gang-schedule 的调度能力、计算任务队列管理、GPU 亲和性调度。另外,Volcano 在原有 Kubernetes 能力基础上对计算任务的批量创建及生命周期管理、Fair-share 调度等方面做了增强。


Volcano 平台可以满足飞桨对资源创建,资源调度的基本要求。Volcano 的批量创建批量调度计算任务为飞桨作业提供计算任务的自动化生命周期管理,gang-scheduler 调度策略可以满足 PServer 和 Trainer “all or nothing”的业务调度约束,Queue 和 priority 逻辑可以管理集群下计算作业的执行顺序,Fair-share 和 GPU 亲和度调度使计算任务调度更贴合 PServer 和 Trainer 对节点资源和网络拓扑结构的要求而提升任务计算效率。


Volcano 借助 Kubernetes 创建 CRD 能力,在 Kubernetes 中引入“apiVersion”为“batch.volcano.sh/v1alpha1”,“kind”为“Job”的资源对象,用于描述计算任务。通过配置和创建 Volcano job 可以使用 Volcano 平台创建、管理和调度计算任务。使用 volcano 平台,需要先在 Kubernetes 集群下安装 Volcano,安装 Volcano 的方法可参考 Volcano 官网。


选择一个飞桨框架任务分别使用 Kubernetes 原生资源和 Volcano job 执行计算任务并对比分析,以下对比将着重体现两者在使用方法、任务管理、调度策略方面进行比较。选择飞桨官网分布式训练 CTR(Click-Through-Rate) demo 进行对比测试。CTR demo 将运行两个 PServer 任务和两个 Trainer 任务。


首先使用飞桨官网推荐模式执行分布式计算任务,先创建一个副本数为 2 的 Kubernetes ReplicaSet 对象,用于运行 PServer 业务,然后创建一个并行度为 2 的 Kubernetes Job 对象,用于运行 Trainer 任务。


创建 PServer 任务


root@volcano-paddlepaddle:~# kubectl apply -f pserver.yamlreplicaset.extensions/fluid-ctr-pserver create
复制代码


查看 pserver ReplicaSet 组件


root@volcano-paddlepaddle:~# kubectl get rs  NAME DESIRED CURRENT READY AGE  fluid-ctr-pserver 2 2 2 5
复制代码


查看 pserver pods


root@volcano-paddlepaddle:~# kubectl get pods | grep fluid    fluid-ctr-pserver-b9w99 1/1 Running 0 9m18s  fluid-ctr-pserver-pb9vd 1/1 Running 0 9m18
复制代码


查看 pserver 日志,PServer 已经开始工作,并对外提供服务


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 2  label selector: paddle-job-pserver=fluid-ctr, desired: 2  current 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 training  2019-09-03 06:43:10,715 - INFO - run pserver  get\_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:  
复制代码


创建 trainer 任务


root@volcano-paddlepaddle:~# kubectl apply -f trainer.yamljob.batch/fluid-ctr-trainer create
复制代码


查看 trainer pods


root@volcano-paddlepaddle:~# kubectl get pod | grep fluidfluid-ctr-pserver-b9w99 1/1 Running 0 87mfluid-ctr-pserver-pb9vd 1/1 Running 0 87m  fluid-ctr-trainer-lg9n5 1/1 Running 0 12s  fluid-ctr-trainer-tvr99 1/1 Running 0 12  
复制代码


查看 trainer 任务日志,看到任务已经开始执行


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 2  label selector: paddle-job-pserver=fluid-ctr, desired: 2  \+ '\[' TRAINER == TRAINER '\]'  \+ stdbuf -oL python /root/k8s\_tools.py wait\_pods\_running paddle-job=fluid-ctr 2  label 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 training  2019-09-03 08:10:20,951 - INFO - download the training materials   % Total % Received % Xferd Average Speed Time Time Time Current   Dload Upload Total Spent Left Speed  100 433M 100 433M 0 0 70.9M 0 0:00:06 0:00:06 --:--:-- 97.0M  2019-09-03 08:11:04,522 - INFO - run trainer  2019-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 copies  I0903 08:11:04.875757  25 rpc\_client.h:101\] init rpc client with trainer\_id 0  2019-09-03 08:11:38,625 - INFO - TRAIN --> pass: 0 batch: 0 loss: 0.697331115723 auc: 0.500826068453, batch\_auc: 0.500826068453  2019-09-03 08:11:38,967 - INFO - TRAIN --> pass: 0 batch: 1 loss: 0.652093688965 auc: 0.48451329672, batch\_auc: 0.48451329672  2019-09-03 08:11:39,242 - INFO - TRAIN --> pass: 0 batch: 2 loss: 0.629092956543 auc: 0.485173881519, batch\_auc: 0.485173881519  2019-09-03 08:11:39,577 - INFO - TRAIN --> pass: 0 batch: 3 loss: 0.603850708008 auc: 0.482131778494, batch\_auc: 0.482131778494  2019-09-03 08:11:39,874 - INFO - TRAIN --> pass: 0 batch: 4 loss: 0.591485412598 auc: 0.479737304993, batch\_auc: 0.479737304993  2019-09-03 08:11:40,133 - INFO - TRAIN --> pass: 0 batch: 5 loss: 0.58376159668 auc: 0.478554220739, batch\_auc: 0.478554220739  2019-09-03 08:11:40,385 - INFO - TRAIN --> pass: 0 batch: 6 loss: 0.561969116211 auc: 0.481465857424, batch\_auc: 0.481465857424  2019-09-03 08:11:40,637 - INFO - TRAIN --> pass: 0 batch: 7 loss: 0.557065185547 auc: 0.486014931119, batch\_auc: 0.486014931119  2019-09-03 08:11:40,890 - INFO - TRAIN --> pass: 0 batch: 8 loss: 0.562498413086 auc: 0.489651573333, batch\_auc: 0.489651573333  2019-09-03 08:11:41,158 - INFO - TRAIN --> pass: 0 batch: 9 loss: 0.566428283691 auc: 0.489853260221, batch\_auc: 0.49137884426  2019-09-03 08:11:41,452 - INFO - TRAIN --> pass: 0 batch: 10 loss: 0.564840087891 auc: 0.492880386228, batch\_auc: 0.494013763938  2019-09-03 08:11:41,742 - INFO - TRAIN --> pass: 0 batch: 11 loss: 0.564809204102 auc: 0.493201528907, batch\_auc: 0.498872381582  2019-09-03 08:11:42,056 - INFO - TRAIN --> pass: 0 batch: 12 loss: 0.584479736328 auc: 0.494151972036, batch\_auc: 0.503926628391  2019-09-03 08:11:42,329 - INFO - TRAIN --> pass: 0 batch: 13 loss: 0.615677246094 auc: 0.49252557362, batch\_auc: 0.5028352489  
复制代码


等待 trainer 任务执行完成,查看 pserver 和 trainer pods 状态,trainer 已经执行完成,pserver 仍然于运行中


root@volcano-paddlepaddle:~# kubectl get pods | grep fluidfluid-ctr-pserver-b9w99 1/1 Running 0 177m  fluid-ctr-pserver-pb9vd 1/1 Running 0 177m  fluid-ctr-trainer-lg9n5 0/1 Completed 0 90m  fluid-ctr-trainer-tvr99 0/1 Completed 0 90  
复制代码


将上述计算任务迁移到 volcano 平台上进行测试。


Volcano 支持 Multi-pod jobs,拓展“tasks”字段,tasks 下可以定义多个 pod 描述,其中“replicas” 字段描述 task 将要生成的 pod 数量,“name”描述 task 名称,pod 名称将根据 task 名称生成。Template 字段与 kubernetes “podTemplate”一致。ctr 的 demo 中含有两个 task: “pserver”和“trainer”,每个 task 的 replicas 都是 2,将会创建两个 PServer 任务,两个 Trainer 任务。


使用 Volcano 调度器,在 job 的配置中需要指定“schedulerName”为“volcano”,如果 schedulerName 没有指定为“volcano”,job 下的任务调度将会使用 kubernetes 的默认调度器“default”调度器。


Volcano 通过指定“minAvailable”字段保证计算任务的 gang-scheduler 调度策略。“minAvailable”数值指明在对当前计算任务下的 pods 进行调度时,需保证多少计算任务都能够调度才会执行调度任务,“minAvailable”的数值需要小于或等于计算任务下的所有任务数量的总和。对于 PaddlePaddle 框架计算任务,只有当所有的 PServer 和 Trainer 任务都处于运行中,才能开始计算任务。因此对于飞桨计算任务,“minAvailable”的数值需要与计算任务下的所有计算任务总和相等。


对于使用飞桨分布式训练的应用,在计算过程中,如果 PServer 任务或者 Trainer 任务被驱逐或失败,PServer 和 Trainer 形成的计算集群将会失效,所有的 PServer 任务和 Trainer 任务都需要重启,以形成新的集群开始新的计算。Volcano 可以通过设置“policies”实现上述目的。设置“PodEvicted”事件对应“RestartJob”动作,设置“PodFailed”事件对应“RestartJob”动作,在设置了这两个“policies”之后,当计算任务被驱逐或者失败,所有的计算任务将会重启。


下面是使用 Volcano 平台执行 CTR 任务的配置 ctr-volcano.yaml,配置文件可从 Volcano 代码库获取


Volcano 代码仓库地址:


https://github.com/volcano-sh/volcano/blob/master/example/integrations/paddlepaddle/ctr-paddlepaddle-on-volcano.yamlapiVersion: batch.volcano.sh/v1alpha1  kind: Job  metadata:   name: ctr-volcano  spec:   minAvailable: 4  schedulerName: volcano   policies:  \- event: PodEvicted   action: RestartJob  \- event: PodFailed   action: RestartJob   tasks:  \- replicas: 2   name: pserver   template:   metadata:   labels:  paddle-job-pserver: fluid-ctr   spec:   imagePullSecrets:  \- name: default-secret   volumes:  \- hostPath:   path: /home/work/   type: ""   name: seqdata   containers:  \- image: volcanosh/edlctr:v1   command:  \- paddle\_k8s  \- start\_fluid   imagePullPolicy: IfNotPresent   name: pserver   volumeMounts:  \- mountPath: /mnt/seqdata   name: seqdata   resources:   limits:   cpu: 10   memory: 30Gi   ephemeral-storage: 10Gi   requests:   cpu: 1   memory: 100M   ephemeral-storage: 1Gi   env:  \- name: GLOG\_v   value: "0"  \- name: GLOG\_logtostderr   value: "1"  \- name: TOPOLOGY   value: ""  \- name: TRAINER\_PACKAGE   value: /workspace  \- name: NAMESPACE   valueFrom:   fieldRef:   apiVersion: v1   fieldPath: metadata.namespace  \- name: POD\_IP   valueFrom:   fieldRef:   apiVersion: v1   fieldPath: status.podIP  \- name: POD\_NAME   valueFrom:   fieldRef:   apiVersion: v1   fieldPath: metadata.name  \- name: PADDLE\_CURRENT\_IP   valueFrom:   fieldRef:   apiVersion: v1   fieldPath: status.podIP  \- name: PADDLE\_JOB\_NAME   value: fluid-ctr  \- name: PADDLE\_IS\_LOCAL   value: "0"  \- name: PADDLE\_TRAINERS\_NUM   value: "2"  \- name: PADDLE\_PSERVERS\_NUM   value: "2"  \- name: FLAGS\_rpc\_deadline  " value: "  \- name: ENTRY   value: cd /workspace/ctr && python train.py --is\_local 0 --cloud\_train 1  \- name: PADDLE\_PORT   value: "30236"  \- name: LD\_LIBRARY\_PATH   value: /usr/local/lib:/usr/local/nvidia/lib64:/usr/local/rdma/lib64:/usr/lib64/mlnx\_ofed/valgrind  \- name: PADDLE\_TRAINING\_ROLE   value: PSERVER  \- name: TRAINING\_ROLE   value: PSERVER   restartPolicy: OnFailure   \- replicas: 2   policies:  \- event: TaskCompleted   action: CompleteJob   name: trainer   template:   metadata:   labels:   paddle-job: fluid-ctr   spec:   imagePullSecrets:  \- name: default-secret   volumes:  \- hostPath:   path: /home/work/   type: ""   name: seqdata   containers:  \- image: volcanosh/edlctr:v1   command:  \- paddle\_k8s  \- start\_fluid   imagePullPolicy: IfNotPresent   name: trainer   volumeMounts:  \- mountPath: /mnt/seqdata   name: seqdata   resources:   limits:   cpu: 10   memory: 30Gi   ephemeral-storage: 10Gi   requests:   cpu: 1   memory: 100M   ephemeral-storage: 10Gi   env:  \- name: GLOG\_v   value: "0"  \- name: GLOG\_logtostderr   value: "1"  \- name: TOPOLOGY  \- name: TRAINER\_PACKAGE   value: /workspace  \- name: CPU\_NUM   value: "2"  \- name: NAMESPACE   valueFrom:   fieldRef:   apiVersion: v1   fieldPath: metadata.namespace  \- name: POD\_IP   valueFrom:   fieldRef:   apiVersion: v1   fieldPath: status.podIP  \- name: POD\_NAME   valueFrom:   fieldRef:   apiVersion: v1   fieldPath: metadata.name  \- name: PADDLE\_CURRENT\_IP   valueFrom:   fieldRef:   apiVersion: v1   fieldPath: status.podIP  \- name: PADDLE\_JOB\_NAME   value: fluid-ctr  \- name: PADDLE\_IS\_LOCAL   value: "0"  \- name: FLAGS\_rpc\_deadline  " value: "  \- name: PADDLE\_PORT   value: "30236"  \- name: PADDLE\_PSERVERS\_NUM   value: "2"  \- name: PADDLE\_TRAINERS\_NUM   value: "2"  \- name: PADDLE\_TRAINING\_ROLE   value: TRAINER  \- name: TRAINING\_ROLE   value: TRAINER  \- name: LD\_LIBRARY\_PATH   value: /usr/local/lib:/usr/local/nvidia/lib64:/usr/local/rdma/lib64:/usr/lib64/mlnx\_ofed/valgrind  \- name: ENTRY   value: cd /workspace/ctr && python train.py --is\_local 0 --cloud\_train 1   restartPolicy: OnFailure
复制代码


在集群终端中执行以下指令在 default namespace 下创建 volcano job


root@volcano-paddlepaddle:~# kubectl apply -f ctr-volcano.yamljob.batch.volcano.sh/ctr-volcano create
复制代码


检查 pods 的状态,无论是 pserver 任务还是 trainer 任务都被下发到集群中,并开始运行。如果当前集群下的空闲资源,不能满足 pserver 任务和 trainer 任务的资源述求,任何任务都不会被创建。


root@volcano-paddlepaddle:~# kubectl get pods | grep ctr-volcanoctr-volcano-pserver-0 1/1 Running 0 16sctr-volcano-pserver-1 1/1 Running 0 16s  ctr-volcano-trainer-0 1/1 Running 0 16s  ctr-volcano-trainer-1 1/1 Running 0 16  
复制代码


选择一个 PServer 任务查看日志,看到 PServer 在监听端口,并对外提供服务


root@volcano-paddlepaddle:~# kubectl logs ctr-volcano-pserver-0\+ case "$1" in\+ start\_fluid\_process  \+ pserver\_label=paddle-job-pserver=fluid-ctr  \+ trainer\_label=paddle-job=fluid-ctr  \+ hostname=ctr-volcano-pserver-0  \+ task\_index=  + '\[' PSERVER == TRAINER '\]'  \+ '\[' PSERVER == PSERVER '\]'  \+ stdbuf -oL python /root/k8s\_tools.py wait\_pods\_running paddle-job-pserver=fluid-ctr 2  label selector: paddle-job-pserver=fluid-ctr, desired: 2  current 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=172.20.0.148:30236,172.20.1.134:30237  \+ PADDLE\_PSERVERS=172.20.0.148:30236,172.20.1.134:30237  ++ python /root/k8s\_tools.py fetch\_ips paddle-job=fluid-ctr  \+ export PADDLE\_TRAINER\_IPS=172.20.0.147,172.20.1.133  \+ PADDLE\_TRAINER\_IPS=172.20.0.147,172.20.1.133  \+ '\[' 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 09:57:55,619 - INFO - run dist training  2019-09-03 09:57:55,708 - INFO - run pserver  get\_pserver\_program() is deprecated, call get\_pserver\_programs() to get pserver main and startup in a single call.  I0903 09:57:55.860916 41 grpc\_server.cc:435\] Server listening on 172.20.0.148:30236 selected port:  
复制代码


选择一个 Trainer 任务查看日志,看到计算任务已经开始执行


root@volcano-paddlepaddle:~# kubectl logs ctr-volcano-trainer-0\+ case "$1" in \+ start\_fluid\_process  \+ pserver\_label=paddle-job-pserver=fluid-ctr  \+ trainer\_label=paddle-job=fluid-ctr  \+ hostname=ctr-volcano-trainer-0  \+ task\_index=  \+ '\[' TRAINER == TRAINER '\]'  \+ stdbuf -oL python /root/k8s\_tools.py wait\_pods\_running paddle-job-pserver=fluid-ctr 2  label selector: paddle-job-pserver=fluid-ctr, desired: 2  current cnt: 0 sleep for 5 seconds...  \+ '\[' TRAINER == TRAINER '\]'  \+ stdbuf -oL python /root/k8s\_tools.py wait\_pods\_running paddle-job=fluid-ctr 2  label selector: paddle-job=fluid-ctr, desired: 2  ++ python /root/k8s\_tools.py fetch\_endpoints paddle-job-pserver=fluid-ctr 30236  \+ export PADDLE\_PSERVERS=172.20.0.148:30236,172.20.1.134:30237  \+ PADDLE\_PSERVERS=172.20.0.148:30236,172.20.1.134:30237  ++ python /root/k8s\_tools.py fetch\_ips paddle-job=fluid-ctr  \+ export PADDLE\_TRAINER\_IPS=172.20.0.147,172.20.1.133  \+ PADDLE\_TRAINER\_IPS=172.20.0.147,172.20.1.133  \+ '\[' 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 09:57:56,712 - INFO - run dist training  2019-09-03 09:57:56,773 - INFO - download the training materials   % Total % Received % Xferd Average Speed Time Time Time Current   Dload Upload Total Spent Left Speed  100 433M 100 433M 0 0 96.2M 0 0:00:04 0:00:04 --:--:-- 96.2M  2019-09-03 09:58:27,648 - INFO - run trainer  2019-09-03 09:58:27,732 - WARNING -  I0903 09:58:27.734141 25 parallel\_executor.cc:329\] The number of CPUPlace, which is used in ParallelExecutor, is 2. And the Program will be copied 2 copies  I0903 09:58:27.937546  25 rpc\_client.h:101\] init rpc client with trainer\_id 0  2019-09-03 09:58:37,957 - INFO - TRAIN --> pass: 0 batch: 0 loss: 0.670620727539 auc: 0.510430537062, batch\_auc: 0.510764985415  2019-09-03 09:58:38,264 - INFO - TRAIN --> pass: 0 batch: 1 loss: 0.641319274902 auc: 0.503955813399, batch\_auc: 0.503955813399  2019-09-03 09:58:38,585 - INFO - TRAIN --> pass: 0 batch: 2 loss: 0.617138793945 auc: 0.50334993182, batch\_auc: 0.50334993182  2019-09-03 09:58:38,873 - INFO - TRAIN --> pass: 0 batch: 3 loss: 0.598490356445 auc: 0.507263818365, batch\_auc: 0.507263818365  2019-09-03 09:58:39,182 - INFO - TRAIN --> pass: 0 batch: 4 loss: 0.573976501465 auc: 0.510442316749, batch\_auc: 0.51044231674  
复制代码


等待大概 70 分钟,查看计算任务日志,发现任务已经安全退出


root@volcano-paddlepaddle:~# kubectl get pod | grep ctr-volcanoctr-volcano-trainer-0 0/1 Completed 0 77mctr-volcano-trainer-1 0/1 Completed 0 77  
复制代码


与此同时,在训练结束之后,我们可能需要训练出来的模型用于别处。在 yaml 文件当中,我们规定了该任务 volcanosh/edlctr:v1 镜像,该镜像的工作目录在/workspace/ctr 下,在 train.py 当中有定义,会在每 1000 个 batch 或是每一轮 pass(跑完一遍训练集)的时候,调用 save_inference_model 接口来保存模型。保存的模型在/workspace/ctr/models 文件夹下。那么如何在任务结束后获取模型呢?我们建议以下几种方式。


  1. 在 yaml 文件当中 trainer 部分的 spec 当中定义 volume,通过 docker 的 volume 映射容器路径和宿主机路径的机制,将/workspace/ctr/models 文件夹映射到宿主机的文件夹中。接下来通过 kubectl describe pod ctr-volcano-trainer-0,可以得知我们的模型所在的节点,接下来 ssh 登陆到对应的节点上,到宿主机被映射到路径下,就可以获取到训练出来到模型了。

  2. 如果需要更加灵活的,自动化的模型配送流程,可以在 K8S 集群上建立 File Server 和分布式文件系统,例如 GlusterFS。将 ctr-volcano-trainer-0 容器内部的/workspace/ctr/models 文件夹映射到 GlusterFS 的 PVC(Persistent Volume Claim)上。通过 ftp 的 wget/curl 操作命令就可以实现模型的获取和配送。


综上,使用 Volcano 平台执行 PaddlePaddle 框架计算任务,可以实现计算任务的批量创建,任务的自动化管理,实现计算任务的自我管理。相较于普通的 Replicaset+Job 的模式,使用 Volcano 平台可以提升并行计算的管理效率。


作者介绍:


董大祥,@guru4elephant, PaddlePaddle Architect, Principal Architect, Baidu


王嘉炜,@wangjiawei04, PaddlePaddle Engineer, Senior Engineer, Baidu


于佃海,@raindrops2sea, PaddlePaddle Architect, Distinguished Architect, Baidu


张经辉, @sivanzcw, Volcano Contributor, Cloud Native software engineer, Huawei


马达, @k82cn, Kubernetes Maintainer, SIG-Scheduling Co-Leader, Volcano Lead, Huawei


参考文献


  1. PaddlePaddle官网

  2. Paddle on Spark

  3. Run Deep Learning with PaddlePaddle on Kubernetes

  4. Volcano官网

  5. Volcao社区

  6. 百度CTR Demo

  7. CTR-volcano 配置文件


2019 年 11 月 11 日 10:301542

评论

发布
暂无评论
发现更多内容
百度飞桨(PaddlePaddle)分布式训练在Volcano系统上的实践_AI_华为云原生团队_InfoQ精选文章