2017年12月22日,袁进辉(老师木)代表OneFlow团队在全球互联网架构大会上海站做了《深度学习平台技术演进》的报告,小编对报告内容作简要梳理注解,以飨读者。
此次报告的主要观点为:(1)计算力是神经网络/深度学习复兴的最大推动力之一;(2)面对深度学习的计算力挑战,软件至少和硬件一样地关键,单靠硬件无法提供易用性和扩展性;(3)鉴于深度学习上层业务和底层硬件的独特性,传统大数据平台里的某些技术未必再对深度学习平台适用;(4)深度学习软件平台技术在快速演进中,一部分早期被采用的技术正在被新方法替代;(5)仍有很多重要问题未被现有开源深度学习平台解决;(6)深度学习软件尚处在发展早期,百花齐放,百家争鸣,但必将收敛到一种业界公认的最佳实践(best practice)。
注:深度学习在近些年带来的突破无须赘言,从图像 (ImageNet) ,语音,围棋人机大战等方面的突破都源于深度学习技术。
注:机器学习可以视为一种从训练数据中自动推导出程序的方法。以最常见的有监督学习(supervised learning)为例,可简单理解为,通过优化方法自动在高维空间找到分类超平面。
注:现实中遇到的绝大部分机器学习问题,基于原始特征(Input Space)无法找到分类超平面把训练数据里的正例和负例恰好分开。在机器学习领域,有一些通用的手段来处理线性不可分的问题,譬如可以在Input Space 寻求非线性分界面,而不再寻求线性分界面;也可以通过对特征做预处理,通过非线性映射的手段把训练数据从Input Space 映射到一个所谓的Feature Space,在原始Input Space无法线性可分的样例在Feature Space有可能线性可分。深度学习就是这种思想的一个典型应用。
注:深度学习从计算上体现为一连串的变换(transformation),常见的变换都可以表示成矩阵计算。以上图为例,输入层有6个神经元,输出层共有4个神经元,每个输出神经元都和输入层的每个神经元有一条边相连,每条边有一个对应的权重(红色神经元的输入边权重都用红色表示)。输入数据是4个样例,每个样例是一个6维的列向量,列向量的每一维对应输入层的一个神经元。输入数据经过这层神经元的作用,仍是4个样例,每个样例变成了4维的列向量。这样一层神经网络可以用右图的矩阵乘法来表示。像这种稠密矩阵运算意味着并行计算的潜力。
注:以大家耳熟能详的卷积神经网络CNN 为例,可以感觉一下目前训练深度学习模型需要多少计算力。这张表列出了常见CNN模型处理一张图片需要的内存容量和浮点计算次数,譬如VGG-16网络处理一张图片就需要16Gflops。值得注意的是,基于ImageNet数据集训练CNN,数据集一共大约120万张图片,训练算法需要对这个数据集扫描100遍(epoch),这意味着10^18次浮点计算,即1exaFlops。简单演算一下可发现,基于一个主频为2.0GHz的CPU core来训练这样的模型需要好几年的时间。下面我们看一下使用几种典型硬件来训练CNN模型需要多少时间。
注:Intel 的服务器级多核(multi core)处理器一般有20个主频为2~3GHz的core,每颗CPU 做稠密矩阵计算的吞吐率约为400Gflops, 以此推算,理想情况下,在ImageNet 训练一个CNN模型需要数天时间(考虑到访存瓶颈,cache miss等实际情况,可能需要数月时间)。
注:这是Nvidia出品的最新款GPU, 它的吞吐率比CPU搞一到两个数量级,可以把CNN训练时间从几个月缩短到几天。GPU 和 CPU 一样都是通用处理器,只是采用了众核架构(many core),一颗芯片上集成了数千个计算核心(core),尽管每个core的主频一般要比CPU core的主频低,但GPU的核心数和访存带宽都远远高于CPU,这也是GPU成为深度学习训练硬件不二之选的原因。
注:这是Google 研发的TPU, 按TPU 的吞吐率,可以在几个小时完成一个CNN模型的训练。TPU 与 GPU 的区别在于它是专用硬件,免去了通用处理器里取指,指令译码等控制电路的复杂性,可以在同样的面积内集成更多深度学习训练需要的计算器件。除了Google 这种大公司,目前国内外也有不少初创公司在研发深度学习专用的芯片。
注:当前,无论是通用处理器GPU还是专用芯片TPU 相对于CPU 都强大了许多倍,但现实应用对计算力的渴求是无止境的,从业者需要以更快的速度,以更大规模的模型处理更大规模的数据,这单靠一个硬件设备无法满足。硬件的发展要受限于制造工艺(芯片面积,功耗,时钟信号传播范围)的限制,不可能无限制的提高一颗芯片的处理能力。因此,人们常常通过高速互联技术把多个高通量的设备连接在一起,协同完成大规模任务。左图显示了一种常见的GPU 集群架构,同一个节点(服务器)内部的GPU 通过NVLink或者PCIe 通信,多个节点通过高速以太网或者Infiniband 互联。右图显示了Google 内部 TPU Cloud的硬件部署,每个服务器管理若干个TPU,多个服务器通过高速互联技术连成大规模集群。如何使得多个互联的设备在一起高效工作,这给深度学习软件开发带来了严峻挑战。
注:这张图揭示了深度学习软件平台在整个人工智能技术栈中的角色:通过易用性降低算法研发门槛,让数据科学家,算法研究人员不需要了解底层细节就可以描述和实现深度学习业务需求;通过扩展性更快的解决更大规模问题,释放硬件潜能。如果说人工智能技术是各行各业提高效率的赋能者 (enabler),深度学习软件平台就是人工智能行业的赋能者(enabler)。深度学习软件平台的关键作用就像互联网时代的浏览器,移动互联网时代的Android OS,起着承上启下的作用,向上看是作为各种应用的入口,向下看起着定义硬件功能和角色的作用。正是因为这种战略地位,成为行业巨头的兵家必争之地,Google, Microsoft, Amazon, Facebook, Baidu 都开发了自己的深度学习平台,并且都开源了,冲着建造行业生态的目标在努力。值得注意的是,深度学习软件平台具有极高的技术门槛,鲜有初创公司进入这个领域。
注:深度学习软件平台的目标是:一方面是要通过抽象以最简优雅的方式解决上层开发者灵活多变的需求,另一方面则是释放硬件潜能,提高设备的利用率。这两个目标都因为深度学习独有的特点而变得非常挑战,主要要现在:单个硬件设备速度越快,软件要高效协同多个设备就更加挑战。首先,自上而下看,深度学习模型通常采用所谓的随机梯度下降(SGD)算法,这与传统基于批量(Batch)训练机器学习算法显著不同。在随机梯度下降算法中,每处理一小片数据(即所谓mini-batch)就需要更新一次模型,在分布式环境下就意味着要在多个机器或多个设备间通过通信同步模型,因此不仅是计算密集型,而且是通信密集型任务。其次,自底向上看,深度学习训练主要使用异构环境,不仅有CPU 参与,还有所谓的协处理器 GPU 参与,密集计算都是卸载(offload)到协处理器上完成的,协处理器的吞吐率是CPU 的十倍以上,意味着同样大小的任务在协处理器上只需要1/10的时间就完成了。不过,虽然数据处理速度提升了,但数据在设备之间搬运的速度并没有相对于传统的CPU集群有质的提升,譬如机器内部通信通过PCIe完成,机器之间即使采用最快的Infiniband互联,其带宽也要比GPU cores访问片上内存(device memory)慢上一到两个数量级。一个小批次的数据在GPU 设备上处理的时间可能是数十毫秒,但这批数据从设备外部拷贝到设备上也要花上差不多量级的时间,这就意味着数据的搬运(data movement)在分布式深度学习平台中必须被当作一等公民(first class citizen)来对待。最后,GPU 高吞吐率的计算需要把计算所依赖的数据(输入数据或模型)首先搬运到显存(device memory),而显存容量一般要远小于主存(host memory),因此使用GPU 集群训练深度学习模型在处理大规模模型(譬如一个模型的容量或计算产生的中间数据超过显存容量)时,如何最好利用有限资源也带来了新的问题。
下面我们剖析一下,为什么传统大数据技术架构在深度学习平台中不再适用。
注:诸多经典的大数据处理系统都采用了这种数据流(Data flow)引擎,譬如 Hadoop, Spark。在数据流系统里,一个作业(Job)被分解成一系列互相依赖的任务(Task),这种依赖关系通常用有向无环图(Directed acyclic graph, DAG)来描述,图中每个节点表示一个任务,节点之间的边表示一个数据依赖关系(生产者和消费者关系)。在一般的大数据系统中,有一个中心调度器(Centralized scheduler)负责监控整个作业的进度以及整个系统的资源使用状况,首先选取DAG中没有输入数据依赖或者输入数据已经就绪的节点并分配给一个资源足够的工作机(Worker),当一个Worker 执行完一个任务时,会通知Scheduler ,Scheduler 会从DAG 中删除已成功执行的节点,然后再选取一个全部输入数据已经就绪的节点分配给Worker去执行。当前主流的深度学习系统也借鉴了这种数据流执行引擎的思想。页面左下角红色五角星的个数表示目前主流框架对这项特性的支持程度,全红五星角表示所有框架已支持,全绿五角星表示目前主流框架都还不支持。
注:非深度学习场景的数据流执行引擎都是基于中心调度器的,中心调度器实现难度小,也比较容易支持容错等高级特性。但在深度学习系统中,中心调度器引发的开销已经不可忽略,不可接受。分布式深度学习平台,或者显式或者隐式的,都采用了去中心化调度机制。中心调度器面临的困难包括:1,Scheduler 和 Worker 之间存在通信开销;2,深度学习Job分解后的Task 粒度非常小,无论是数据传输还是GPU上的计算,通常是数十毫秒级别,设想数十台机器或数百块GPU设备同时工作,意味着每1ms时间,整个系统就发生一个事件(Task 开始或结束),就需要调度器做一个决策,而在调度器做决策的过程时,它看到的局面可能已经发生了变化。在去中心化调度中,每台机器,每个设备上Task 都不需要了解全局信息,而只需要和自己有关的局部上下文Task通信,没有多余的通信开销,状态信息能第一时间更新,每个Task能及时响应状态的变化。
注:所谓放置(Placement)是指某个Task在哪个节点或设备(Device)上执行。在传统的大数据系统中,一般使用动态放置(Dynamic Placement)的策略,也就是某一个任务并不绑定在一个特定的机器或设备上执行,到底在哪个机器或设备上执行,要看调度器在派遣这个Task时综合考虑负载均衡(Load balance) 和局部性(Locality)等方面的因素选择一个最合适的机器或设备去执行这个Task。但在深度学习系统中,某一个Task 到底在哪个机器或设备上执行都是在运行之前手工或自动指定好的,即所谓的静态放置(Static placement),这一般出于两方面的考虑:1,深度学习是迭代式计算,一个Task要反复执行很多次,如果执行该Task的设备不固定,那么每换一次设备就需要在启动Task前做一些初始化(如资源分配)工作,Task结束时要做一些清理工作,上文提到深度学习中Task本身粒度已经小到数十毫秒级,同时在异构设备上管理资源的开销(譬如申请和释放设备内存)都比在CPU 上高的多,静态放置能显著减少这些资源管理的开销;2,深度学习同时具备计算密集和通信密集型的特点,使用流水线技术重叠通信和计算对系统扩展性非常关键,而要使用流水线进行数据的预传(Prefetch 或 Presend)就需要事先知道每个生产者和消费者Task 所处的位置,只有静态放置才能支持流水线机制。
注:与放置(Placement)密切相关的一个问题是资源(特别是内存)管理策略也可以是动态或静态,显然动态放置需要动态内存管理,按需分配和释放,但静态放置可以使用动态内存管理也可以是静态内存管理。现有开源深度学习平台既有选择动态内存管理的(Tensorflow),也有选择静态内存管理的(Caffe等)。静态内存管理有一系列的优点:1,降低资源管理开销,上文提到过在GPU上申请和释放内存的开销比较大,另外如果要支持RDMA或者主存和显存的异步内存拷贝,需要使用锁页内存(Pinned Memory),如果每次需要数据传输时都临时调用锁页操作开销也比较显著;2,使用静态内存管理,系统稳定性比较高,很多风险(譬如死锁,Out of Memory)都可以在运行前避免,而采用动态内存管理的系统为了避免这些风险在系统设计上增加很多复杂性。当然,静态内存管理有可能(不一定)降低内存资源的利用率。
注:刚才我们看到深度学习系统与传统大数据系统的一些显著区别:倾向于使用静态策略。这在熟悉传统大数据系统技术的人看来无异于“开历史倒车”:静态策略无异于牺牲了一系列的灵活性。深度学习平台还有一项“倒行逆施”的特点是:通常运行在专用集群上,而且每个作业(Job)会独占分配给它的硬件资源。这与传统大数据架构提倡“虚拟化”,“共享资源”来提高资源利用率很不一样。其实深度学习这些特点也是为了提高资源利用率,基于上文分析的种种原因,在训练深度学习模型时共享资源反而会使得系统稳定性变差,提高资源利用率也非常难。深度学习系统也倾向于使用最简单的容错机制:系统快照(Snapshot or Checkpoint),fail fast, warm start,主要原因是:1,深度学习平台使用的服务器通常成本比较高,也比较稳定,集群规模也较小,数十台服务器的场景就算比较大了;2,复杂的容错机制会增加系统复杂性,降低系统运行效率,系统设计者宁愿让系统在正常状态下跑的更快,即使出错了,能从最近一次快照热启动即可。
至此,我们了解到,深度学习系统和传统大数据系统是很不一样的,下面我们将看到深度学习平台技术尚处在非常早期的阶段,即使在最近两三年,技术选项也在快速演变。
注:数据并行是指把数据分成多份,每个设备处理一份,这样每个设备只需要处理整体数据的一小部分即可,系统运行时间从一个设备处理所有数据需要的时间降低到一个设备处理一小部分数据需要的时间,从而得到加速,这是大数据场景最常见的并行方式。以上图为例,首先把4个训练样例等分成2份,分别分配给两个设备去处理,两个设备上持有同一份模型的两个拷贝。训练深度学习模型时,一个设备上的前向和后向计算可以独立开展,但每个模型上获得模型的更新梯度后需要在设备间同步,聚合成完整数据集上的梯度之后再做模型更新。数据并行特别适合卷积神经网络这种场景,卷积神经网络的模型由很多小的卷积核构成,模型体积小,从而设备间同步模型梯度时通信量较小。模型很大的场景就不适合用数据并行了。当前,所有框架都能很好的支持这种并行模式。
注:数据并行等并行模式经常依赖于一些集合通信原语,譬如broadcast, scatter, reduce, gather 等。如何高效支持这些基本原语,在传统超算领域(MPI)已经研究的很透彻了。以数据并行模式下模型梯度同步为例,就可以通过reduce和broadcast两个原语来支持。
注:一般来说,集合通信原语扮演了一种屏障(barrier)的角色,以reduce操作为例,要实现4个节点上数值的累加,就需要等待4个节点全部完成,reduce操作才能开始,这种等待所有生产者就绪,消费者才能开始计算的模式被称为BSP (Bulk synchronizaton protocol),现在这种模式可能受所谓 straggler 的影响,即最慢的那一个生产者的影响。为了解决straggler的问题,学者们发现在传统分布式机器学习中可以对同步条件放松,不一定要严格同步,只要分布式计算节点在一定程度上保持同步就可以在更短的时间内得到差不多的收敛结果,因此发明了SSP 或者ASP的模式,在深度学习之前的场景多有应用。但是近些年,分布式深度学习平台默认都是用BSP模式,这主要是因为:1,深度学习集群规模通常较小,机器质量比较均衡,straggler 现像没有那么严重;2,实践上发现异步SGD收敛效果比同步SGD 差不少,人们宁愿用更长的时间取得更好的效果。
注:多年以来,参数服务器(Parameter server)被多个分布式机器学习系统采用,譬如Google 上一代分布式机器学习系统DistBelief, 甚至在若干知名深度学习平台中被采用,譬如PaddlePaddle, MxNet等。本质上,在数据并行场景,参数服务器是用来实现Allreduce语义的一种途径。我们能看到一种趋势,参数服务器在某些场景有可能不是最合适的选择:1,参数服务器比MPI Allreduce好在支持异步梯度更新,但如上文所述,深度学习场景同步SGD更受青睐;2,参数服务器本质是client server 架构,深度学习的网络结构可能远比client server的二分图结构复杂;3,参数服务器只是实现Allreduce的一种技术,即星状拓扑,MPI 多年以来的研究结果表明,该用什么拓扑(星状,树状,环状)来实现集合通信,取决于集群内的节点数,数据传输量,通信带宽等因素。
注:在最近一年,已经可以看到一些分布式深度学习平台摒弃了参数服务器,反而去使用MPI 的Allreduce实现,譬如Uber Horovod 通过把Tensorflow里的参数服务器机制更换成 Allreduce在数据并行场景得到一倍的效率提升,Caffe2 也通过自研的gloo通信库实现了定制的Allreduce支持,百度的DeepSpeech系统采用了环状Allreduce支持,Nvidia 提供的集合通信库NCCL 也支持多种集合通信原语。
注:除了数据并行,还有些场景模型太大以至于使用数据并行通信开销太大,或者模型超过GPU显存容量,这种情况必须对模型进行切分,每个设备上只完成一部分模型对应的计算,这称为模型并行。Alex Krizhevsky 曾提出根据并行时传输数据还是传输模型的通信量大小来自动选择数据并行或模型并行,感兴趣的读者可以去读一读 (https://arxiv.org/abs/1404.5997)。如上图所示,模型并行的一种做法时,让一个GPU 负责上面红色的两个输出神经元的计算,让另一个GPU 负责下面绿色的两个输出神经元的计算,相当于对矩阵进行分片,每个设备只完成一部分计算。可以看出,模型并行时,不需要在设备间同步模型,但需要在设备间同步数据。当前绝大部分开源框架不支持模型并行,或者支持比较弱,需要非常微妙的调整才能高效执行。模型并行是业界公认的难题。
注:除了模型并行本身比较复杂之外,模型并行模块与其它并行模式的协同也非常复杂,需要小心的管理上下游之间的数据传输(路由)。本页以相邻的两层神经网络为例,第一个层次使用数据并行,第二个层次使用模型并行,那么在前向计算时,就需要把数据并行部分的结果经过 Copy, Concat 两层路由汇总到模型并行的两个设备上去,如果前后两层在不同的机器上执行,那么还需要通过跨机通信。如果这些复杂的数据路由需要用户手工参与管理,那么一方面过于复杂(想象一下数据并行和模型并行的各种组合模式),另一方面极易出错。理想的情况,这些复杂性应该由深度学习平台来处理,但非常可惜,现有已开源的深度学习平台都不支持这一功能。
注:本页描述了前后两层分别是数据并行和模型并行的场景,后向计算时的数据路由方法。限于篇幅,这里就不再列举从数据并行到数据并行,从模型并行到数据并行,从模型并行到模型并行的数据路由方法了。
注:这是来自贾扬清的一个例子,探讨了通过流水线来提高训练效率的必要性。不使用流水线的做法较容易理解:L1, L2, L3 表示依次三层神经网络的前向计算,前向计算完成后,自后向前开始后向计算,L3b, L2b, L1b,后向计算完成后,依次通过Allreduce来实现三个层次上权重梯度的同步,R3, R2, R1,在Reduce完成后,再依次完成三个层次的权重更新 (Update), U3, U2, U1。不难发现,L3b完成后就可以启动R3, U3了,如果通过流水线把可以同时做的事情并行执行就会减小总体的执行时间。基于这个洞见,目前Caffe2, MxNet, Petuum Poseidon 都实现了这种优化,带来了显著的效率提升。但需要指出的是,除此之外,深度学习模型训练过程中还有大量的可以通过流水线来提高并行度的机会,但现有系统都需要人工去发现和手工控制流水线,如果没有显式使用这种优化,系统就不会自动支持流水线。
注:更多的利用流水线的机会出现在前后多个阶段之间的重叠执行上,以上图的神经网络为例,通过3块GPU的协作完成训练,如果没有使用流水线,硬件利用率会非常低:当GPU1在执行运算时,GPU2和GPU3因为没有输入数据只能停顿等待数据到达,当GPU1完成计算把数据从GPU1向GPU2搬运时,GPU1和GPU2,GPU3都处于停顿状态,当数据搬运完之后,GPU2才能开始计算。一般来说,一个良好的流水线机制应该能把磁盘IO, PCIe传输,网络传输,GPU计算都能同时执行,但目前已有开源框架都没有这种功能(或者非常微弱的支持)。
总结:一个理想的深度学习平台在易用性和扩展性方面都达到极致。我们即将发布的OneFlow系统冲着这个目标做了一系列独特的创新,敬请关注。
注:深度学习软件平台处在发展的早期阶段,在这个领域会出现数据库领域那么成功的软件产品吗? 我们拭目以待。
北京一流科技有限公司欢迎志同道合的同仁加入或合作,为最理想的深度学习平台早日到来而奋斗。
祝大家新年快乐!
原文发布时间为:2017-12-31
原文链接:老师木讲架构:深度学习平台技术演进