百度深度学习平台PaddlePaddle框架解析

tonghe 2018-03-09

PaddlePaddle 是 2016 年 8 月底百度开源的深度学习平台,并且在短时间内迅速成为引发全球开发热度,并且成为Github Pull Request 数量增速最高的开源深度学习平台。

PaddlePaddle 的迭代速度非常快,同时也广受社区的关注。刚开源的时候,PaddlePaddle 的设计思想是基于Layer的设计。后来推出了“v2”和“Fluid”两次迭代:其中 v2增加了 operators 的概念,把 layers “打碎”成更细粒度的 operators,同时支持更复杂的网络拓扑“图”;Fluid 类似 PyTorch,但是不依赖 Python 的控制流(if-else、for等),而是提供自己的解释器甚至编译器,因此不受限于 Python 的执行速度。

我们今天就从PaddlePaddleFluid讲起,随后讲述为了能更方便利用集群而提供的在浏览器端训练的PaddlePaddleCloud,也着重讲解集群训练的原理、配置、和实验结果,也就是PaddlePaddleEDL部分。最后,讲解PaddlePaddleVisualDL,这个非常强大的训练日志解析和可视化工具。

PaddlePaddleFluid

PaddlePaddleFluid提供类似于高级语言中的控制流结构(例如while 、 if 、if-else、 for等),不仅能利用编译优化技术保证计算性能,提升使用者的开发效率。

PaddlePaddleFluid 的设计思路非常领先,不再沿用层(layer)结构和操作(operator)结构的模式。也就是说不再有“模型”的概念,也就不再有“图”(graph of operators)或者“串”(sequence of layers)。而只有“程序”的概念。同时,程序是可以导出一个图的,从程序中可以导出成 ONNX 文件格式的模型。

深度学习基础架构是最快速发展的技术之一,在四年之内,已经发明了三代技术。从下表可以看出,深度学习技术架构设计的方向正是逐渐摆脱模型的。

年份

层结构模型

操作结构模型

没有模型

2013

Caffe, Theano, Torch, PaddlePaddle

   

2015

 

TensorFlow, MxNet, Caffe2, ONNX, n-graph

 

2016

   

PyTorch, TensorFlow Eager Execution, PaddlePaddle Fluid

基于Python语言强大的生态,PyTorch 和 Eager Execution 中的控制流都是用的 Python,但面临的一个瓶颈是Python 执行速度慢且难以提速。解决 PyTorch 和 Eager Execution 程序的执行速度受限于 Python 的执行速度的问题,Fluid有一个比 PyTorch 和 Eager Execution 更激进的技术思路。在Fluid的设计上,执行会把编写的Python 程序输出成一个 protobuf message,随后调用 Fluid 解释器(而不是 Python 解释器)来解释执行这个 protobuf message。Fluid 解释器极大地加快了执行图的速度。同时,在编译执行的方式上 ,通过写一个 transpiler 把 protobuf message翻译成 C++ 程序,然后用 nvcc、icc、gcc 编译成二进制代码,可以直接运行在服务器和手机上。

PaddlePaddleCloud

PaddlePaddle 有一个 Web-based IDE,支持使用者在浏览器用 JupyterNotebook 编程来开发 AI 应用,随后可以把程序发送到云端(Kubernetes 集群上)调试或者运行,程序运行时的输出会实时地显示在浏览器里。这样使用者就不需要在个人电脑和集群等多个编程环境之间切换并且维护多个环境的版本和配置的一致性,极大地提升了工作效率。

PaddlePaddleEDL

PaddlePaddle EDL 对标的是 Google KubeFlow。PaddlePaddle EDL通过与 Kubernetes 合作来实现弹性作业调度,是全球首个支持弹性作业调度的开源 AI 云解决方案。

尽管现在很多深度学习应用用一个几台机器的小集群就可以解决,但是 随着数据量的增加和AI应用场景的不断扩大,例如Web scaled 应用(广告、搜索、推荐等),以及通过传感器采集海量数据的无人车,都需要大规模深度学习计算能力的。

这里主要为了解决深度学习面临的两大挑战。其一是需要大量的计算能力。研究室和公司经常构建由SLURM,MPI或SGE管理的GPU集群。这些集群要么运行一个提交的作业(假定它需要的比闲置的资源要少)或者将作业挂起一段难以预估的时间。但是这种方法有个缺点:在有99个可用节点和一个需要100个提交作业的任务时,作业必须等待而不能运行。

PaddlePaddle EDL弹性调度体现在可以空闲的时候一个训练作业多用一些资源,忙碌的时候少用一些,但是资源的变化并不会导致作业失败;这是优于KubeFlow的特点之一。同时,EDL也弹性调度其他作业(比如 Nginx、MySQL 等),从而极大地提升集群总体利用率。[2]这样在公有云和私有云上的推广和部署时,就很容易节省几倍的机器,为公司一年节省的计算成本可以高达百万、甚至数百万美元。

另一个挑战是,工业用户倾向于将深度学习作业作为完整数据管道的子集阶段,例如日志采集器等。这种通用集群需要基于优先级的弹性调度。比如网络开销较高的时间段内深度学习任务少运行,在网络流量较低时优先进行深度学习任务。这就需要了解全局的情况,并协调与各种工作有关的进程的数量。

PaddlePaddleEDL的测试实验

面对上述这两种挑战,PaddlePaddle作业都可以轻松应对进程数量忽高忽低的变化。这里有Fluid EDL的两种测试用例:

  1. Kubernetes集群只运行PaddlePaddle作业;
  2. 集群运行PaddlePaddle和Nginx作业。

在第一个测试中,我们开始了20个PaddlePaddle作业,间隔10秒。每个作业有60个trainers和10个参数服务进程,并将持续数小时。我们重复实验20次:关闭Fluid EDL 10次,打开Fluid EDL 10次。在下图中,实线对应于前10个实验,其余的是虚线。在图的上半部分,我们看到未处理作业的数量在没有EDL的情况下单调递增。但是,当EDL打开时,资源将平均分配给所有作业。Fluid EDL杀死了一些现有的进程,为新的其他任务腾出空间,并在晚些时候任务开始运行。在这两种情况下,集群都被平等利用(见图的下半部分)。

百度深度学习平台PaddlePaddle框架解析

在第二个测试中,每个实验都运行了400个Nginx Pods,其优先级高于6个PaddlePaddle作业。最初,每个PaddlePaddle工作有15个trainers和10个参数服务。我们每90秒杀死100个Nginx Pods,直到剩下100个,然后我们开始将Nginx工作的数量每90秒增加100个。下图的上半部分显示了这个过程。图中的中间显示,Fluid EDL通过减少Nginx Pods来自动启动一些PaddlePaddle进程,并在稍后增加Nginx Pods来杀死PaddlePaddle进程。结果,该集群维持在90%左右的利用率,如图所示。当Fluid EDL被关闭时,没有PaddlePaddle进程自动增加,并且利用率随着Nginx Pods数量的变化而波动。

百度深度学习平台PaddlePaddle框架解析

PaddlePaddleEDL的设计和实现

那上面这种深度学习服务和其它云端服务共享计算资源的过程,以及各个任务的优先级动态地调整和伸缩的过程,从而充分地利用集群CPU/GPU是如何实现的呢?

EDL和HPA

Horizontal Pod Autoscaling (HPA)是Kubernetes提供的一种弹性调度机制。它的设计出发点是通过公平分配计算资源给某一个单一的计算任务中的各个Pod来实现分布式系统资源针对单一任务的最优化利用。但我们的训练任务可能多种多样(语音、图像等)、部署时间有先有后,对资源的需求也不通,因此我们希望这种弹性调度机制能对每一种训练任务所需的系统资源有个全局的了解,然后按需分配。但目前HPA controller还没有实现。

同时,HPA的弹性调度是针对同种类型的计算任务(homogenous computing task)下的 Pods。但深度学习系统里的计算节点和参数服务器往往是在不同类型的Pods里 的。

上述特有的需求导致使用Kubernetes的时候需要有特定的弹性调度解决方案, 而不能直接采用HPA。因此更好的解决方案是PaddlePaddle EDL。

PaddlePaddleEDL 的具体设计和实现

1.让Kubernetes支持定制的弹性调度机制

Kubernetes本身就支持定制的资源管理机制。用户可以通过提交定制的resource declaration file 和controller file来实现对某种Pods的弹性调度。以下图为例,这个training_job.yaml保证了 controller会自动监管pservers,并且保证它们的数量在min-instance和max-instance之间。

百度深度学习平台PaddlePaddle框架解析

在Kubernetes集群上,这个定制的资源可以通过 kubectl create -f training_job.yaml 命令获得。接下来,我们需要有个定制的training job controller来调度这个资源。

定制的training job controller跑在一个Pod里,对集群资源有一个统一的了解,它通过Kubernetes API对集群资源起到监控和调度的作用。下图是training job controller配置文件的一个例子。

百度深度学习平台PaddlePaddle框架解析

在Kubernetes集群上,这个定制的资源管理Pod可以通过 kubectl create -f training_job_controller.yaml 命令启动。

2.控制程序的实现

上面提到的定制化资源在Kubernetes里面目前有两种实现方式。一种是 Custom Resource Definition (CRD) ,由Kubernetes 1.7版本引入;另一种是 Third Party Resource (TRP)。 PaddlePaddle项目现在用的是Kubernetes 1.6版本,所以实现的是TRP模式,今后将整合CRD模式。

当前PaddlePaddle假设只有一个单独的training job controller在运行。当前的training job controller依照下面的逻辑管理资源:

百度深度学习平台PaddlePaddle框架解析

3.弹性调度算法

PaddlePaddle根据定制资源的配置文件(training_job.yaml)来判断某个job需不需要弹性调度, 而判断的标准是trainer和pserver的min-instance =/ max-instance。

集群中GPU的调度

controller知道集群中全部GPU个数,以及当前闲置的GPU的个数,并试图把闲置的GPU全部分配给当前训练任务。PaddlePaddle给需求GPU的训练任务定义一个“满足程度”的评分(fulfillment score),此评分的范围是[0,1]。PaddlePaddle会优先分配GPU资源给满足程度评分最低的训练任务。如果有分数相同的情况,则分别优先考虑GPU需求数,CPU需求数,内存需求数。如果有某个训练任务的GPU min-instance没有满足(除非cur-instance=min-instance),那么PaddlePaddle会把一个满足程度最高分的训练任务里的GPU资源拿出来分给它。如果满足程度分数最高的训练任务cur-instance=min-instance,则整个集群不再执行新的训练任务,新来的任务需等待。

集群中CPU的调度

CPU资源的分配和GPU思路相同。controller知道集群中一共有多少个CPU,内存,它们的负载情况;同时也知道训练任务对CPU的需求。同样的,CPU资源根据满足程度评分被按需分配。

PaddlePaddle容错机制

这里讨论PaddlePaddle的容错机制。在一个分布式训练任务里,如果master进程或者所有的参数服务进程都死掉了,那么整个训练任务会被停掉,过一段时间被Kubernetes整个重启。如果具体训练进程没有都死掉,则整个训练任务继续。

PaddlePaddle用etcd来记录训练进程的状态。etcd是高可靠性的分布式key-value存储,训练进程会定时把自身状态写进etcd,而这些信息将会在必要的时候用来恢复训练进程。具体过程如下图:

百度深度学习平台PaddlePaddle框架解析

当master进程被Kubernetes启动时,它进行如下操作:

  1. 从etcd中取一个唯一的master lock,以此避免多个master实例存在
  2. 查看etcd中是否存在任务队列。如果不存在,则新建一个任务队列;否则得到这个任务队列中的信息
  3. 把自身的ip地址写进etcd中/master/addr 这个key中,便于后来的训练进程和自己通信
  4. 开端口监听训练进程的任务需求,如果收到来自训练进程的任务请求,从任务队列中取任务分配之,并且更新任务队列。

如果master进程因为任何原因死掉了,Kubernetes会将它重启,从被重启到获取etcd的信息,获取训练进程的任务,这个过程一般是几分钟。

训练进程

当训练进程被Kubernetes启动时,它进行如下操作:

  1. 查看etcd中包含参数服务前缀 /ps/ 获取当前参数服务进程的数量并等待,直到该数量达到配置文件中的要求
  2. 从etcd的/master/addr key中获取master进程地址
  3. 向master发起任务请求,根据任务开始训练程序

当训练进程死掉之后,Kubernetes会将它重启,新起来的进程会重复上述工作直到开始新的训练工作。

参数服务进程

当参数服务进程被Kubernetes启动时,它进行如下操作:

  1. 从etcd /ps_desired中读取训练任务所需求的参数服务进程个数
  2. 在etcd /ps/<index> (/ps/0, /ps/1, ...)里找一个小于所需进程数里最大的还不存在的id,并在etcd里创建这个entry,以此作为自身的id。(如下图)

百度深度学习平台PaddlePaddle框架解析

当第三个参数服务器加入时:

百度深度学习平台PaddlePaddle框架解析

3. 参数服务进程会从自身对应的etcd path中找到已有的训练结果参数并且将它读入

4. 参数服务进程开始接收来自训练进程的请求。

PaddlePaddle的可视化—PaddlePaddleVisualDL

PaddlePaddleVisualDL是PaddlePaddle自带的一个强大的可视化工具,也是一个Web应用程序套件。PaddlePaddleVisualDL目前支持4种可视化,即SCALARS、IMAGES、GRAPHS、HISTOGRAMS。这4种可视化的主要功能如下。

● SCALARS:展示训练过程中的准确率、损失值、权重/偏置的变化情况。

● IMAGES:展示训练过程中记录的图像。

● GRAPHS:展示模型的数据流图,以及训练在各个设备上消耗的内存和时间。

● HISTOGRAMS:展示训练过程中记录的数据的柱状图。

PaddlePaddleVisualDL通过运行一个本地服务器,来监听8080端口。在浏览器发出请求时,分析训练时记录的数据,绘制训练过程中的图像。而且VisualDL 兼容 ONNX, 通过与 python SDK的结合,VisualDL可以兼容包括 PaddlePaddle, pytorch, mxnet, Caffe2 在内的大部分主流DNN平台。而Tensorboard目前仅适用于Tensorflow、Pytorch、MXNet等。

百度深度学习平台PaddlePaddle框架解析

PaddlePaddleVisualDL的可视化界面如下图所示。

百度深度学习平台PaddlePaddle框架解析

VisualDL的使用

VisualDL的使用方式非常简单,只需要下面三个过程:

百度深度学习平台PaddlePaddle框架解析

VisualDL的特性

VisualDL的特性主要有下面4点:

支持Scalar打点折线图展示,方便观察训练整体趋势

百度深度学习平台PaddlePaddle框架解析

支持Image查看数据样本的质量和训练中间结果

百度深度学习平台PaddlePaddle框架解析

支持Histogram查看参数分布展示和变化趋势

百度深度学习平台PaddlePaddle框架解析

支持Graph查看深度神经网络的模型结构

相关推荐