diff --git a/chapter_distributed_training_system/collective.md b/chapter_distributed_training_system/collective.md new file mode 100644 index 0000000..bf4b819 --- /dev/null +++ b/chapter_distributed_training_system/collective.md @@ -0,0 +1,70 @@ +## 集合通讯 + +接下来,我们会讲解常见的大型深度模型训练的系统实现。 +这一类系统往往部署在商用的数据中心(Data +Centers),以及如何在数据中心中高效实现集合通讯,从而让分布式训练系统免于网络瓶颈。 + +### 在数据中心的梯度计算 + +![数据中心](../img/ch09/ch10-datacentre.pdf) +:width:`800px` +:label:`ch10-datacentre` + +:numref:`ch10-datacentre` 描述了一个典型的用于深度学习模型训练的数据中心。数据中心中的训练服务器一般会有多个设备。如需增加服务器,我们会将多个训练服务器放置在一个机柜(Rack)上,同时接入一个架顶交换机(Top +of Rack +Switch)来连接多个服务器。当一个机柜满的时候,我们可以通过在架顶交换机之间增加骨干交换机(Spine +Switch),接入新的机柜。通过这种方式,我们可以在数据中心内不断增加服务器,从而为神经网络的训练提供海量的算力和内存。目前的商用数据中心可能拥有超过一百万台服务器。 + +在数据中心中训练大型神经网络的首要挑战是:如何高效计算大量的平均梯度。假设给定一个千亿级别参数的神经网络(GPT-3模型含有1750亿参数),如果用32位浮点数来表达每一个参数,那么每一步训练中,一个数据并行模式下的模型副本(Model +replica)就需要生成700GB的本地梯度数据(即 175G $\times$ 4 bytes = +700GB)。假如我们有3个模型副本,那么至少需要传输1.4TB(即,700GB +$\times$ +$(3-1)$)的本地梯度(这是因为$N$个副本中,我们只需要传送$N-1$梯度来完成平均梯度计算)。当平均梯度计算完成后,我们需要进一步将平均梯度广播到全部的模型副本(即1.4TB的数据),更新本地参数,从而确保模型副本不会偏离(Diverge)。 + +当前的数据中心往往使用以太网(Ethernet)构建网络。主流的商用以太网链路带宽一般是10Gbps和25Gbps。利用以太网传输海量梯度会产生严重的传输延迟,从而降低模型训练的速度。新型深度学习训练集群(如英伟达的DGX系列机器)往往配置有更快的Inifiband。单个InfiniBand链路可以提供100Gbps和200Gbps的带宽。即使拥有这种高速网络,传输TB级别的本地梯度依然需要大量延迟(1TB的数据需要在200Gbps的链路上传输25秒)。 + +为了避免通过网络传输数据,现代深度学习服务器一般都会配备多个计算设备(例如说,DGX-3机器会被配备8个A100 +GPU),而在一个服务器内的多个设备可以通过高速机内网络互联(如NVLink)。这种高速机内网络可以提供高达400GB/s的带宽,从而让传输TB级别数成为可能。然而,受限于单个服务器的散热,成本和硬件故障等需求,在一个服务器内我们无法无限制的持续增加设备,大型深度学习模型的训练最终还是需要多个服务器共同完成。因此,计算平均梯度需要同时借助以太网或者是InfiniBand,以及服务器内部的NVLink等机内网络。 + +### Allreduce算法 + +为了在数据中心中高效完成梯度平均的操作,我们往往会实现 +Allreduce算法。这个算法诞生的背景是:传统计算平均梯度的方法往往是在集群中找出一个设备来收集本地梯度,计算平均梯度,然后再将平均梯度广播到全部的设备。这种做法易于实现,但是其引入了两个问题。首先,多设备共同给这个聚合设备发送数据的时候,在聚合设备上往往会产生严重的带宽不足和网络拥塞。其次,单设备需要负担大量的梯度平均的计算,而受限于单设备上的有限算力,这种平均计算会受限于算力瓶颈。 + +![Allreduce初始状态和终止状态](../img/ch09/ch10-allreduce-state.pdf) +:width:`800px` +:label:`ch10-allreduce-state` + +为了解决上述问题,人们设计了Allreduce算法。该算法的核心设计思路是:让全部的节点参与进来平均梯度的网络通信和平均计算中,从而将巨大的网络和算力开销均摊给全部节点,从而解决使用单个梯度聚合节点的问题。假设我们有$M$个设备,每个设备有一个模型副本,该模型由$N$个参数构成。那么按照Allreduce算法要求,我们需要首先将全部的参数按照设备数量切分成$M$个分区(Partition),每个分区具有$N/M$个参数。 +为了讲解Allreduce的过程,我们首先给出这个算法的初始和终止状态。如 :numref:`ch10-allreduce-state` +所示,该例子含有3个设备,每个设备有一个模型副本,这个副本有3个参数。那么按照Allreduce的分区方法,参数会被划分成3个分区(3个设备),而每一个分区有1个参数($N/M$,N代表3个参数,M代表3个设备)。在这个例子中,假定设备1拥有参数2,4,6,设备2拥有参数1,2,3,设备3拥有参数4,8,12,那么Allreduce结束后,全部的设备都拥有梯度相加后的结果7,14,21,其中分区1的结果7是由3个设备中分区1的初始结果相加而成(7 += 1 + 2 + +4)。为了计算平均梯度,每个设备只需要在最后将梯度之和除以设备数量即可(分区1的最终结果为7除以3)。 + +![Allreduce算法的过程](../img/ch09/ch10-allreduce-process.pdf) +:width:`800px` +:label:`ch10-allreduce-process` + +Allreduce算法会把梯度的加和计算拆分成$M-1$个Reduce步骤和$M-1$个Broadcast步骤(其中$M$是节点的数量)。Reduce步骤是为了计算出梯度的和(Summation),Broadcast步骤是为了把梯度之和广播给全部的节点。为了说明这些步骤的执行过程,我们利用 +:numref:`ch10-allreduce-process` 。Allreduce算法由Reduce步骤开始,在第一个Reduce步骤中,Allreduce算法会对全部节点进行配对(Pairing),让他们共同完成梯度相加的操作。在 :numref:`ch10-allreduce-process` 的第一个Reduce步骤中,设备1和设备2进行了配对共同对分区1的数据相加。其中,设备2把本地的梯度数据1发送给设备1,设备将接收到1和本地的分区1内的梯度数据:2进行相加,计算出中间(intermediate)梯度相加的结果:3。于此同时,设备1和设备3进行配对,共同完成对分区3的数据相加。而设备3和设备2进行配对,共同完成对于分区2的数据相加。 + +在上述Reduce的步骤中,梯度的计算实现了以下几个特性: + +- **网络优化:** + 全部设备都同时在接收和发送数据,利用起了每个设备的入口(Ingress)和出口(Egress)带宽。因此Allreduce过程中可利用的带宽是$M \times B$,其中$M$是节点数量, + $B$是节点带宽,从而让系统实现网络带宽上的可扩展性。 + +- **算力优化:** + 全部设备的处理器都参与了梯度相加的计算。。因此Allreduce过程中可利用的处理器是$M \times P$,其中$M$是节点数量, + $P$是处理器数量,从而让系统实现计算上的可扩展性。 + +- **负载均衡:** + 由于数据分区是平均划分的,因此每次设备分摊到的通讯和计算开销是相等的。 + +在接下来的Reduce步骤中,Allreduce算法会对不同数据分区选择另外的配对方法。例如说,在 :numref:`ch10-allreduce-process` 的第二个Reduce步骤中,Allreduce算法会将:设备1和设备3进行配对,负责分区1的数据相加。将设备1和设备2进行配对,负责分区2。将设备2和设备3进行配对,负责分区3。在一个3个节点的Allreduce集群里,在2个Reduce步骤完成后,我们就计算出了每个分区的数据相加结果(分区1的结果7此时在设备3上,分区2的结果14此时在设备1上,分区3的结果21此时在设备2上)。 + +接下来,Allreduce算法将进入Broadcast阶段。这一阶段的过程和Reduce步骤类似,核心区别是节点进行配对后,他们不再进行数据相加,而是将Reduce的计算结果进行广播。在 :numref:`ch10-allreduce-process` 中的第一个Broadcast步骤中,设备1会将分区2的结果14直接写入设备3的分区2中。设备2会讲分区3的结果21直接写入设备1中。设备3会将分区1的结果直接写入设备2中。在一个3个节点的Allreduce集群中,我们会重复2次Broadcast步骤来将每个分区的Reduce结果告知全部的节点。 + +Allreduce算法已经被常见的分布式训练框架(包括Horovod, KungFu, TensorFlow +distributed, PyTorch +distributed)等支持。当用户选择使用数据并行模式的过程,其底层会默认触发。 \ No newline at end of file diff --git a/chapter_distributed_training_system/index.md b/chapter_distributed_training_system/index.md index 909f969..54c6f4f 100644 --- a/chapter_distributed_training_system/index.md +++ b/chapter_distributed_training_system/index.md @@ -1,9 +1,4 @@ ---- -bibliography: -- references.bib ---- - -# 分布式训练系统 {#ch:distributed} +# 分布式训练 随着机器学习的进一步发展,科学家们设计出更大型,更多功能的机器学习模型(例如说,GPT-3)。这种模型含有大量参数,需要复杂的计算以及处理海量的数据。单个机器上有限的资源无法满足训练大型机器学习模型的需求。因此,我们需要设计分布式训练系统,从而将一个机器学习模型任务拆分成多个子任务,并将子任务分发给多个计算节点,解决资源瓶颈。 @@ -21,311 +16,14 @@ Libraries)实现。不同的系统实现具有各自的优势和劣势。我 - 理解常见分布式训练的实例,和采用不同实现方法的利弊。 -## 系统概述 -### 设计动机 +```toc +:maxdepth: 2 -接下来,我们详细讨论分布式训练系统的设计动机 - -![对比机器学习模型参数量增长和计算硬件的算力增长](figs/ch10/ch10-computation-increase.pdf){#fig:ch010/ch10-computation-increase} - -##### 算力不足 - -单处理器的算力不足是促使人们设计分布式训练系统的一个主要原因。一个处理器的算力可以用**每秒钟浮点数操作**(Floating -Point Operations Per Second,FLOPS)来衡量。 -如图[1.1](#fig:ch010/ch10-computation-increase){reference-type="ref" -reference="fig:ch010/ch10-computation-increase"}所示,根据摩尔定律(Moore's -Law),中央处理器的算力每18个月增长2倍。虽然计算加速卡,如GPU和Tensor -Processing -Unit(TPU),针对机器学习计算(如矩阵相乘)提供了大量的算力。这些加速卡的发展最终也受限于摩尔定律,增长速度也停留在每18个月2倍。而与此同时,机器学习模型正在快速发展。短短数年,我们从仅能识别有限物体的AlexNet模型([@krizhevsky2012alexnet]),一路发展到在复杂任务中打败人类的AlphaStar([@alphastar])。这期间,模型对于算力需求每18个月增长了35倍。解决处理器性能和算力需求之间的鸿沟 -的关键就在于利用分布式计算。通过大型数据中心和云计算设施,我们可以快速获取大量的处理器。通过分布式训练系统有效管理这些处理器,我们可以实现算力的快速增长,从而持续满足模型的需求。 - -##### 内存不足 - -在训练机器学习模型的过程中,训练系统需要在内存中存储大量数据。这些数据包括:模型参数(Parameters)以及训练和更新这些参数所产生的中间数据,如特征图(Feature -Map)和梯度(Gradients)。假设一个深度神经网络模型具有10亿的参数,所有特征图共有20亿参数,每个参数都由一个32位浮点数表达,而更新这些参数至少还需要产生与特征图和参数等量的梯度。由于一个32位浮点数需要4个字节(Byte)的内存来存储,那么训练这个10亿规模的模型就需要至少24GB($24 \times 10^9$ -Byte)的内存。现在,随着大型预训练模型的崛起,一个深度神经网络(如GPT-3)会拥有超过千亿的参数。假设我们依然使用32位浮点数来存储参数,激活值和梯度,那么训练这个模型就至少需要1.2TB的内存。而如今的训练加速卡(如NVIDIA -A100)仅能提供最高80GB的内存。单卡内存空间的增长受到硬件规格,散热和成本等诸多因素,难以进一步快速增长。因此,我们需要分布式训练系统来同时使用数百个训练加速卡,从而为千亿级别的模型提供所需的TB级别的内存。 - -### 分布式训练架构 - -\[概述本章核心系统组件,定义本章的技术用语\] -受限于单节点的有限算力,内存和存储资源,人们把关注投向了日益成熟的云计算数据中心。一个数据中心管理着数十万个计算服务器。随着数据中心的全球部署,人们可以很方便地获得数百个服务器。这些服务器可以通过分布式训练系统来协调和管理,解决训练大型机器学习模型过程遇到的算力,内存和存储不足,从而完成训练过程的加速。 - -![单节点计算和多节点分布式计算](figs/ch10/ch10-single-vs-multi.pdf){#fig:ch010/ch10-single-vs-multi} - -在设计分布式训练系统的过程中,我们需要找出有资源瓶颈的计算任务,根据计算任务的特点,将其拆分成多个子任务,然后将子任务分发给多个节点(可以是服务器,机器,或者是加速卡)并行完成。 -图 [1.2](#fig:ch010/ch10-single-vs-multi){reference-type="ref" -reference="fig:ch010/ch10-single-vs-multi"}描述了如何将单节点执行转换为分布式执行的一般过程。在机器学习系统中(如图 [1.2](#fig:ch010/ch10-single-vs-multi){reference-type="ref" -reference="fig:ch010/ch10-single-vs-multi"}所示),一个计算任务往往会有一组数据(例如训练样本)或者任务(例如算子)作为输入,利用一个计算节点(例如GPU)生成一组输出(例如梯度)。假如单节点成为瓶颈,我们可以利用分布式计算进行加速。如图 [1.2](#fig:ch010/ch10-single-vs-multi){reference-type="ref" -reference="fig:ch010/ch10-single-vs-multi"}所示,分布式执行一般具有三个步骤:第一步,我们需要将输入进行**切分**。第二步,每个输入部分会分发给不同的计算节点,实现**并行**计算。第三步,每个计算节点的输出,进一步**合并**,最终得到和单节点等价的计算结果。这种切分-并行-合并的模式,本质上实现了分而治之算法(Divide-and-Conquer -Algorithm)的设计思想:由于每个计算节点只需要负责更小的子任务,因此其可以更快速的完成计算,最终形成对整个计算过程的加速。 - -### 用户益处 - -\[总结系统对于用户的好处\] -通过使用分布式训练系统,我们往往可以获得以下几个关键好处: - -- **提升系统性能**:使用分布式训练,往往可以带来训练性能的巨大提升。一个分布式训练系统往往用以下这个指标来衡量性能:到达目标精度所需的时间(time-to-accuracy)。这个指标由两个参数决定: - 一个数据周期所需的完成时间,以及一个数据周期模型所提升的精度。通过持续增加并行处理节点,我们可以将数据周期的完成时间不断变短,最终显著减少到达目标精度所需的时间。 - -- **经济性(Economy)**:使用分布式训练,我们也可以进一步减少训练及其模型所需的成本。受限于单节点散热和半导体制程的限制,在一个节点上不断增加算力和内存的成本一般会指数增加\[引用\]。因此,高性能的深度学习计算节点往往具有极高的硬件成本。而提供同等的算力下,通过组合多个计算节点往往比使用单个节点显著的更低。举例,在亚马逊\[\...\...\]。因此通过构建分布式训练系统,我们可以提升训练集群的经济性,降低模型的训练成本。 - -- **抵御硬件故障**:分布式训练系统同时能有效提升抵御硬件故障的能力。机器学习训练集群往往由商用硬件(Commodity - Hardware)组成,这类硬件(例如说,磁盘和网卡)运行一定周期就会产生故障。而仅使用单个硬件进行训练的话,那么一个硬件的故障就会造成整个训练的任务的失败。通过将这个训练任务又多个硬件共同完成,即使一个硬件故障了,我们也可以通过将这个硬件上相应的计算子任务转移给其余硬件,继续完成训练,从而避免训练任务的失败。 - -## 分布式训练方法 - -我们会讨论分布式训练系统实现的常用并行方法。我们首先给出并行方法的设计目标以及分类。然后,我们会详细描述各个并行方法。 - -### 概述 - -![单节点训练系统](figs/ch10/ch10-single-node.pdf){#fig:ch010/ch10-single-node} - -分布式训练系统的设计目标是:将单节点训练系统转化成**等价的**并行训练系统,从而在不影响模型精度的条件下完成训练过程的加速。一个单节点训练系统往往如图[1.3](#fig:ch010/ch10-single-node){reference-type="ref" -reference="fig:ch010/ch10-single-node"}所示。一个训练过程会由多个数据小批次(mini-batch)完成。在图中,一个数据小批次被标示为**数据**。训练系统会利用数据小批次来生成梯度,提升模型精度。这个过程由一个训练**程序**实现。在实际中,这个程序往往实现了一个多层神经网络的执行过程。 -该神经网络的执行由一个计算图(Computational -Graph)表达。这个图有多个相互连接的算子(Operator),每个算子会拥有计算参数。每个算子往往会实现一个神经网络层(Neural -Network Layer),而参数则代表了这个层在训练中所更新的的权重(Weights)。 - -为了更新参数,计算图的执行会分为**前向**传播和**反向**传播两个阶段。前向传播的第一步会将数据读入第一个算子,该算子会根据当前的参数,计算出传播给下一个算子的数据。算子依次重复这个前向传播的过程(算子1 --\> 算子2 -\> -算子3),直到最后一个算子结束。最后的算子随之马上开始反向传播。反向传播中,每个算子依次计算出梯度(梯度3 --\> 梯度2 -\> -梯度1),并利用梯度更新本地的参数。反向传播最终在第一个算子结束。反向传播的结束也标志本次数据小批次的结束,系统随之读取下一个小批次,继续更新模型。 - -::: {#tab:ch010/ch10-parallel-methods} - 单数据 多数据 - -------- ------------------------ ------------------------ - 单程序 单程序单数据:单点执行 单程序多数据:数据并行 - 多程序 多程序单数据:模型并行 多程序多数据:混合并行 - - : 分布式训练方法分类 -::: - -给定一个单节点训练系统,人们会对**数据**和**程序**分区(Partition),从而完成并行加速。表[1.1](#tab:ch010/ch10-parallel-methods){reference-type="ref" -reference="tab:ch010/ch10-parallel-methods"}总结了不同的切分方法。单节点训练系统可以被归类于 -单程序单数据模式。而假如用户希望使用更多的设备来实现并行计算,他们首先可以选择对数据进行分区,并将同一个程序复制到多个设备上并行执行。这种方式是单程序多数据模式,常被称为**数据并行**(Data -Parallelism)。另一种并行方式是对程序进行分区:程序的算子会被分发给多个设备按照依次完成。这种模式是 -多程序单数据模式,常被称为**模型并行**(Model -Parallelism)。当训练超大型智能模型时,开发人们往往要同时对数据和程序进行切分,从而实现最高程度的并行。这种模式是多程序多数据模式,常被称为**混合并行**(Hybrid -Parallelism)。 - -接下来,我们详细讲解各种并行方法的执行过程。 - -### 数据并行 - -![数据并行训练系统](figs/ch10/ch10-data-parallel.pdf){#fig:ch010/ch10-data-parallel} - -数据并行往往可以解决单节点的算力不足。这种并行方式在人工智能框架中最为常见,具体实现包括:TensorFlow -DistributedStrategy [@tensorflow_distributed]),PyTorch -Distributed [@pytorch_distributed],Horovod -DistributedOptimizer [@horovod_distributed]等。在一个数据并行系统中,假设用户给定一个训练批大小$N$,并且希望使用$M$个并行设备来加速训练。那么,该训练批大小会被分为$M$个分区,每个设备会分配到$N/M$个训练样本。这些设备共享一个训练程序的副本,在不同数据分区上独立执行,计算梯度。不同的设备(假设设备编号为$i$)会根据本地的训练样本估计出梯度$G_i$。为了确保训练程序参数的一致性,本地梯度$G_i$需要聚合,计算出平均梯度$(\sum_{i=1}^{N} G_i) / N$。最终,训练程序利用平均梯度修正模型参数,完成小批量的训练。 - -图[1.4](#fig:ch010/ch10-data-parallel){reference-type="ref" -reference="fig:ch010/ch10-data-parallel"}展示了2个设备构成的数据并行例子。假设用户给定的批大小(Batch -Size)是64,那么每个设备会分配到32个训练样本,并且具有相同的神经网络参数(程序副本)。本地的训练样本会依次通过这个程序副本中的算子,完成前向传播和反向传播。在反向传播的过程中,程序副本会生成局部梯度。不同设备上对应的局部梯度(如设备1和设备2上各自的梯度1)会进行聚合,从而计算平均梯度。这个聚合的过程往往由集合通讯库(Collective -Communication)的Allreduce操作来完成。 - -### 模型并行 - -![模型并行系统:算子内并行](figs/ch10/ch10-model-parallel-intra-op.pdf){#fig:ch010/ch10-model-parallel-intra-op} - -模型并行往往用于解决单节点的内存不足问题。一个常见的内存不足场景是模型中含有大型算子,例如说深度神经网络中需要计算大量分类的全连接层(Fully -Connected -Layer)。完成这种大型算子计算所需的内存可能超过单设备的内存容量。那么我们需要对这个大型算子进行切分。假设这个算子具有$P$个参数,而我们拥有$N$个设备,那么我们可以将$P$个参数平均分配给$N$个设备(每个设备分配$P/N$个参数),从而让每个设备负责更少的计算量,能够在内存容量的限制下完成前向传播和反向传播中所需的计算。这种切分方式是模型并行的应用,被称为**算子内并行**(Intra-operator -Parallelism)。 - -图[1.5](#fig:ch010/ch10-model-parallel-intra-op){reference-type="ref" -reference="fig:ch010/ch10-model-parallel-intra-op"}给出了一个由2个设备实现的算子内并行的例子。在这个例子中,假设一个神经网络具有2个算子,算子1的计算(包含正向和反向传播)需要预留16G的内存,算子2的计算需要预留1G的内存。而本例中的设备最多可以提供10G的内存。为了完成这个神经网络的训练,我们需要对算子1实现并行。具体做法是,将算子1的参数平均分区,设备1和设备2各负责其中部分算子1的参数。由于设备1和设备2的参数不同,因此它们各自负责程序分区1和程序分区2。在训练这个神经网络的过程中,数据(小批量)会首先传给算子1。由于算子1的参数分别由2个设备负责,因此数据会被广播给这2个设备。不同设备根据本地的参数分区完成前向计算,生成的本地计算结果需要进一步合并(Combine),发送给下游的算子2。在反向传播中,算子2的数据会被广播给设备1和设备2,这些设备根据本地的算子1分区各自完成局部的反向计算。计算结果进一步合并传播回数据,最终完成反向传播。 - -另一种内存不足的场景是:模型的总内存需求超过了单设备的内存容量。在这种场景下,假如我们总共有$N$个算子和$M$个设备,我们可以将算子平摊给这$M$个设备,让每个设备仅需负责$N/M$个算子的前向和反向计算,降低设备的内存开销。这种并行方式是模型并行的另一种应用,被称为**算子间并行**(Inter-operator -Parallelism)。 - -![模型并行系统:算子间并行](figs/ch10/ch10-model-parallel-inter-op.pdf){#fig:ch010/ch10-model-parallel-inter-op} - -图[1.6](#fig:ch010/ch10-model-parallel-inter-op){reference-type="ref" -reference="fig:ch010/ch10-model-parallel-inter-op"}给出了一个由2个设备实现的算子间并行的例子。在这个例子中,假设一个神经网络具有2个算子,算子1和算子2各自需要10G的内存完成计算,则模型总共需要20G的内存。而每个设备仅能提供10G内存。在这个例子中,用户可以把算子1放置在设备1上,算子2放置在设备2上。在前向传播中,算子1的输出会被发送(Send)给下游的设备2。设备2接收(Receive)来自上游的数据,完成算子2的前向计算。在反向传播中,设备2将算子2的反向计算结果发送给设备1。设备1完成算子1的反向计算,完成本次训练。 - -### 混合并行 - -![混合并行系统](figs/ch10/ch10-hybrid-parallel.pdf){#fig:ch010/ch10-hybrid-parallel} - -在训练大型人工智能模型中,我们往往会同时面对算力不足和内存不足。因此,我们需要混合使用数据并行和模型并行,这种方法被称为混合并行。图[1.7](#fig:ch010/ch10-hybrid-parallel){reference-type="ref" -reference="fig:ch010/ch10-hybrid-parallel"}提供了一个由4个设备实现的混合并行的例子。在这个例子中,我们首先实现算子间并行来解决训练程序内存开销过大的问题:该训练程序的算子1和算子2被分摊到了设备1和设备2上。进一步,我们通过数据并行来添加3和设备4,提升系统算力。为了达到这一点,我们对训练数据进行分区(数据分区1和数据分区2),并将模型(算子1和算子2)分配复制到设备3和设备4上生成可以并行执行的程序副本。在前向计算的过程中,设备1和设备3上的算子1副本同时开始,计算结果分别发送(Send)给设备2和设备4完成算子2副本的计算。在反向计算中,设备2和设备4同时开始计算梯度,本地梯度通过Allreduce进行平均。反向计算传递到设备1和设备3上的算子1副本结束。 - -## 流水线并行 - -在数据并行和模型并行以外,流水线并行是另一种常用的并行加速方法。 -流水线并行往往被应用在大型模型并行系统中。这种系统通过算子内并行和算子间并行解决单设备内存不足的问题。 -然而,当这类系统的运行中(如图[1.5](#fig:ch010/ch10-model-parallel-intra-op){reference-type="ref" -reference="fig:ch010/ch10-model-parallel-intra-op"}和图[1.6](#fig:ch010/ch10-model-parallel-inter-op){reference-type="ref" -reference="fig:ch010/ch10-model-parallel-inter-op"}所示),计算图中的下游设备需要长期持续处于空闲状态,等待上游设备的计算完成,才可以开始计算,这极大降低了设备的平均使用率。这种现象被称为模型并行空洞(Model -Parallelism Bubble)。 - -![流水线并行系统。**注意!图的F和B任务的编号需要更新!**](figs/ch10/ch10-pipeline-parallel.pdf){#fig:ch010/ch10-pipeline-parallel -width="\\linewidth"} - -为了减少空洞,提升设备使用率,我们可以在模型并行系统中构建流水线。这种做法的核心想法是将一个数据小批量(Data -Mini-batch)划分为多个微批量(Micro-batch)。假设一个数据小批量有$D$个训练数据,这个小批量可以被划分为$M$个微批量,那么微批量的大小就是$D/M$。每个微批量相应进入训练系统,完成前向传播(Forwards -propagation)和反向传播(Backwards -propagation),计算出梯度。每个微批量对应的梯度将会缓存,等到全部微批量完成,缓存的梯度会被加和,算出平均梯度,更新模型参数。 - -图[1.8](#fig:ch010/ch10-pipeline-parallel){reference-type="ref" -reference="fig:ch010/ch10-pipeline-parallel"}进一步给出了一个流水线并行的执行例子。在本例中,模型参数需要切分给4个设备存储。为了充分利用起来这4个设备,我们将小批量切分为2个微批量。当设备1完成第一个微批量的前向传播后(表示为$F_{0,0}$)后,他会将中间结果发送给设备2,触发响应的前向传播任务(表示为$F_{1,0}$)。与此同时,设备1也可以开始第二个微批量的前向传播任务(表示为$F_{0,1}$)。前向传播会在流水线的最后一个设备--设备3--完成。系统于是开始反向传播。设备4开始第1个微批量的反向传播任务(表示为$B_{3,0}$)。该任务完成后的中间结果会被发送给设备3,触发响应的反向传播任务(表示为$B_{2,0}$)。与此同时,设备4会缓存好对应第1个微批量的梯度,接下来开始第2个微批量计算(表示为$B_{3,1}$)。当设备4完成了全部的反向传播计算后,他会将本地缓存的梯度进行相加,并且除以微批量数量,计算出平均梯度,该梯度用于更新模型参数。 - -流水线并行的关键因素是流水线泡沫(Bubble)。当设备完成前向传播后,必须等到全部反向传播开发,在此期间设备会处于空闲状态。在图[1.8](#fig:ch010/ch10-pipeline-parallel){reference-type="ref" -reference="fig:ch010/ch10-pipeline-parallel"}中,我们可以看到设备1在完成2个前向传播任务后,要等很多时间才能开始2个传向传播任务。这其中的等待时间即被称为泡沫。为了减少设备的等待时间,一种常见的做法是尽可能的增加微批量的数量,从而让反向传播尽可能早的开始。然而,使用非常小的微批量大小,可能会造成加速器无法被充分利用。因此最优的微批量大小是多种因素的折中。其中最核心的因素是流水线泡沫的大小和加速器的计算能力。 - -## 集合通讯的高效实现 - -接下来,我们会讲解常见的大型深度模型训练的系统实现。 -这一类系统往往部署在商用的数据中心(Data -Centers),以及如何在数据中心中高效实现集合通讯,从而让分布式训练系统免于网络瓶颈。 - -### 梯度计算和数据中心网络 - -图[1.9](#fig:ch010/ch10-datacentre){reference-type="ref" -reference="fig:ch010/ch10-datacentre"}描述了一个典型的用于深度学习模型训练的数据中心。数据中心中的训练服务器一般会有多个设备。如需增加服务器,我们会将多个训练服务器放置在一个机柜(Rack)上,同时接入一个架顶交换机(Top -of Rack -Switch)来连接多个服务器。当一个机柜满的时候,我们可以通过在架顶交换机之间增加骨干交换机(Spine -Switch),接入新的机柜。通过这种方式,我们可以在数据中心内不断增加服务器,从而为神经网络的训练提供海量的算力和内存。目前的商用数据中心可能拥有超过一百万台服务器。 - -![数据中心](figs/ch10/ch10-datacentre.pdf){#fig:ch010/ch10-datacentre} - -在数据中心中训练大型神经网络的首要挑战是:如何高效计算大量的平均梯度。假设给定一个千亿级别参数的神经网络(GPT-3模型含有1750亿参数),如果用32位浮点数来表达每一个参数,那么每一步训练中,一个数据并行模式下的模型副本(Model -replica)就需要生成700GB的本地梯度数据(即 175G $\times$ 4 bytes = -700GB)。假如我们有3个模型副本,那么至少需要传输1.4TB(即,700GB -$\times$ -$(3-1)$)的本地梯度(这是因为$N$个副本中,我们只需要传送$N-1$梯度来完成平均梯度计算)。当平均梯度计算完成后,我们需要进一步将平均梯度广播到全部的模型副本(即1.4TB的数据),更新本地参数,从而确保模型副本不会偏离(Diverge)。 - -当前的数据中心往往使用以太网(Ethernet)构建网络。主流的商用以太网链路带宽一般是10Gbps和25Gbps。利用以太网传输海量梯度会产生严重的传输延迟,从而降低模型训练的速度。新型深度学习训练集群(如英伟达的DGX系列机器)往往配置有更快的Inifiband。单个InfiniBand链路可以提供100Gbps和200Gbps的带宽。即使拥有这种高速网络,传输TB级别的本地梯度依然需要大量延迟(1TB的数据需要在200Gbps的链路上传输25秒)。 - -为了避免通过网络传输数据,现代深度学习服务器一般都会配备多个计算设备(例如说,DGX-3机器会被配备8个A100 -GPU),而在一个服务器内的多个设备可以通过高速机内网络互联(如NVLink)。这种高速机内网络可以提供高达400GB/s的带宽,从而让传输TB级别数成为可能。然而,受限于单个服务器的散热,成本和硬件故障等需求,在一个服务器内我们无法无限制的持续增加设备,大型深度学习模型的训练最终还是需要多个服务器共同完成。因此,计算平均梯度需要同时借助以太网或者是InfiniBand,以及服务器内部的NVLink等机内网络。 - -### 高效梯度计算:Allreduce算法 - -为了在数据中心中高效完成梯度平均的操作,我们往往会实现 -Allreduce算法。这个算法诞生的背景是:传统计算平均梯度的方法往往是在集群中找出一个设备来收集本地梯度,计算平均梯度,然后再将平均梯度广播到全部的设备。这种做法易于实现,但是其引入了两个问题。首先,多设备共同给这个聚合设备发送数据的时候,在聚合设备上往往会产生严重的带宽不足和网络拥塞。其次,单设备需要负担大量的梯度平均的计算,而受限于单设备上的有限算力,这种平均计算会受限于算力瓶颈。 - -![Allreduce初始状态和终止状态](figs/ch10/ch10-allreduce-state.pdf){#fig:ch010/ch10-allreduce-state -width="\\linewidth"} - -为了解决上述问题,人们设计了Allreduce算法。该算法的核心设计思路是:让全部的节点参与进来平均梯度的网络通信和平均计算中,从而将巨大的网络和算力开销均摊给全部节点,从而解决使用单个梯度聚合节点的问题。假设我们有$M$个设备,每个设备有一个模型副本,该模型由$N$个参数构成。那么按照Allreduce算法要求,我们需要首先将全部的参数按照设备数量切分成$M$个分区(Partition),每个分区具有$N/M$个参数。 -为了讲解Allreduce的过程,我们首先给出这个算法的初始和终止状态。如图[1.10](#fig:ch010/ch10-allreduce-state){reference-type="ref" -reference="fig:ch010/ch10-allreduce-state"} -所示,该例子含有3个设备,每个设备有一个模型副本,这个副本有3个参数。那么按照Allreduce的分区方法,参数会被划分成3个分区(3个设备),而每一个分区有1个参数($N/M$,N代表3个参数,M代表3个设备)。在这个例子中,假定设备1拥有参数2,4,6,设备2拥有参数1,2,3,设备3拥有参数4,8,12,那么Allreduce结束后,全部的设备都拥有梯度相加后的结果7,14,21,其中分区1的结果7是由3个设备中分区1的初始结果相加而成(7 -= 1 + 2 + -4)。为了计算平均梯度,每个设备只需要在最后将梯度之和除以设备数量即可(分区1的最终结果为7除以3)。 - -Allreduce算法会把梯度的加和计算拆分成$M-1$个Reduce步骤和$M-1$个Broadcast步骤(其中$M$是节点的数量)。Reduce步骤是为了计算出梯度的和(Summation),Broadcast步骤是为了把梯度之和广播给全部的节点。为了说明这些步骤的执行过程,我们利用 -图[1.11](#fig:ch010/ch10-allreduce-process){reference-type="ref" -reference="fig:ch010/ch10-allreduce-process"}。Allreduce算法由Reduce步骤开始,在第一个Reduce步骤中,Allreduce算法会对全部节点进行配对(Pairing),让他们共同完成梯度相加的操作。在图[1.11](#fig:ch010/ch10-allreduce-process){reference-type="ref" -reference="fig:ch010/ch10-allreduce-process"}的第一个Reduce步骤中,设备1和设备2进行了配对共同对分区1的数据相加。其中,设备2把本地的梯度数据1发送给设备1,设备将接收到1和本地的分区1内的梯度数据:2进行相加,计算出中间(intermediate)梯度相加的结果:3。于此同时,设备1和设备3进行配对,共同完成对分区3的数据相加。而设备3和设备2进行配对,共同完成对于分区2的数据相加。 - -在上述Reduce的步骤中,梯度的计算实现了以下几个特性: - -- **网络优化:** - 全部设备都同时在接收和发送数据,利用起了每个设备的入口(Ingress)和出口(Egress)带宽。因此Allreduce过程中可利用的带宽是$M \times B$,其中$M$是节点数量, - $B$是节点带宽,从而让系统实现网络带宽上的可扩展性。 - -- **算力优化:** - 全部设备的处理器都参与了梯度相加的计算。。因此Allreduce过程中可利用的处理器是$M \times P$,其中$M$是节点数量, - $P$是处理器数量,从而让系统实现计算上的可扩展性。 - -- **负载均衡:** - 由于数据分区是平均划分的,因此每次设备分摊到的通讯和计算开销是相等的。 - -在接下来的Reduce步骤中,Allreduce算法会对不同数据分区选择另外的配对方法。例如说,在图[1.11](#fig:ch010/ch10-allreduce-process){reference-type="ref" -reference="fig:ch010/ch10-allreduce-process"}的第二个Reduce步骤中,Allreduce算法会将:设备1和设备3进行配对,负责分区1的数据相加。将设备1和设备2进行配对,负责分区2。将设备2和设备3进行配对,负责分区3。在一个3个节点的Allreduce集群里,在2个Reduce步骤完成后,我们就计算出了每个分区的数据相加结果(分区1的结果7此时在设备3上,分区2的结果14此时在设备1上,分区3的结果21此时在设备2上)。 - -![Allreduce算法的过程](figs/ch10/ch10-allreduce-process.pdf){#fig:ch010/ch10-allreduce-process -width="\\linewidth"} - -接下来,Allreduce算法将进入Broadcast阶段。这一阶段的过程和Reduce步骤类似,核心区别是节点进行配对后,他们不再进行数据相加,而是将Reduce的计算结果进行广播。在图[1.11](#fig:ch010/ch10-allreduce-process){reference-type="ref" -reference="fig:ch010/ch10-allreduce-process"}中的第一个Broadcast步骤中,设备1会将分区2的结果14直接写入设备3的分区2中。设备2会讲分区3的结果21直接写入设备1中。设备3会将分区1的结果直接写入设备2中。在一个3个节点的Allreduce集群中,我们会重复2次Broadcast步骤来将每个分区的Reduce结果告知全部的节点。 - -Allreduce算法已经被常见的分布式训练框架(包括Horovod, KungFu, TensorFlow -distributed, PyTorch -distributed)等支持。当用户选择使用数据并行模式的过程,其底层会默认触发。 - -## 参数服务器 - -接下来,我们介绍另一种常见的分布式训练系统实现:参数服务器。TensorFlow原生提供了参数服务器的实现。而其他框架,例如PyTorch和MindSpore,则需要用户使用第三方的参数服务器实现,例如PS-Lite。 - -### 计算和存储分离 - -利用参数服务器的其中一个核心需求是实现:计算和存储的分离。在训练模型中,计算可以被理解为计算更新模型参数所需要的计算(例如说,计算本地梯度和计算平均梯度),而存储可以被理解为将模型参数存储在内存设备中(例如说,主机内存,加速卡内存和SSD设备)。传统的神经网络训练中,计算往往是核心瓶颈,因此我们只需要配置有合适数量的带有加速卡的服务器,常被称为训练服务器(Training -servers)。 - -随着机器学习的发展,新型的稀疏模型被开发出来。相比于传统的神经网络训练,稀疏模型的训练往往不需要大量昂贵的计算加速卡(GPU),而需要海量的内存来存储嵌入表(Embedding -table)。例如说,一个大型深度学习推荐系统中,它们往往使用小型的深度神经网络(如Multi-layer -Perception),训练这种神经网络只需要几个GPU即可。而另一方面,推荐系统中往往需要存储PB级别的嵌入表。嵌入表往往由推荐系统的用户特征(User -feature)和产品特征(Item -feature)构成。这些特征往往是大型向量(Vector)。现代推荐系统需要服务数亿的用户,推荐数以千万的商品。假设用户的特征是1MB,而系统需要服务10亿的用户,那么用户的嵌入表就会有1PB的大小。而这个大小远远超过了一个深度学习服务器所具有的内存。假如我们部署大量的昂贵的深度学习服务器来存储海量嵌入表,那么这些服务器上的加速卡的使用率将会极低,无法实现对于硬件的高效利用。 - -![参数服务器](figs/ch10/ch10-parameter-servers.pdf){#fig:ch010/ch10-parameter-servers -width="0.63\\linewidth"} - -为了解决上述问题,人们往往会在稀疏模型集群中混合部署:训练服务器和参数服务器,从而实现对于计算需求和内存需求分别满足。图[1.12](#fig:ch010/ch10-parameter-servers){reference-type="ref" -reference="fig:ch010/ch10-parameter-servers"}描述了带有参数服务器的机器学习集群。这个集群中含有2个训练服务器和2个参数服务器,训练服务器一般是拥有加速卡的计算优化服务器(Compute-optimised -server)。而参数服务器一般是内存优化服务器(Memory-optimised -server),其的内存大小一般远远大于计算优化服务器。在一个稀疏模型中往往拥有神经网络参数和嵌入表参数。神经网络较小,其可以存储在训练服务器内存中。而嵌入表很大,因此需要存储在额外的参数服务器中。参数服务器一般会按照键-值对(Key-value -pairs)的方式来存储参数。常用的键包括用户名(User ID),产品名(Item -ID)或者是参数名(Parameter -Key)。常用的值是以多维度向量(Multi-dimensional -tensors)表达的模型参数。假如存在多个参数服务器,参数服务器会用数据分区函数(例如,哈希函数和区域划分)将健-值映射到不同参数服务器上。 - -为了完成对于模型的训练,在每一步训练中,训练服务器会根据当前的小批量训练数据,找到本批量中需要用到的参数。例如说,本小批量数据只会训练部分用户的特征,那么这些用户的特征才会需要。根据参数服务器的数据分区函数,训练服务器可以知道参数当前在哪个参数服务器上,它们因此会用参数的键(Key)向对应的参数服务器发起拉取请求(Pull -request)。参数服务器响应,并返回对应的值(Value)。训练服务器将拉取的参数(往往是嵌入表)和本地内存中的模型参数(往往是神经网络)进行合并,从而对合并的模型进行训练,计算梯度。假如训练服务器实现了数据并行,那么训练服务器计算出的本地梯度需要利用Allreduce计算出平均梯度。对于训练服务器本地内存中的参数,训练服务器可以马上利用平均梯度进行修改。对于在参数服务器中存储的参数,训练服务器发起推送请求(Push -request)将平均梯度发送到参数服务器,参数服务器更新本地存储的参数。 - -在以上的参数服务器架构中,机器学习集群拥有者可以灵活的根据梯度计算所需要算力配置合理数量的训练服务器。他们也可以根据参数的数量配置大部分的稀疏参数(Sparse -parameters)在参数服务器中,仅留下小部分的密集参数(Dense -parameters)在训练服务器中。密集参数和稀疏参数的核心区别是:稀疏参数在每一步训练不一定都会被用到,他们需要根据当前训练小批量来决定。而密集参数每一步训练都需要用到。因此为了频繁从参数服务器中拉取,密集参数往往会存储在训练服务器中。 - -### 数据副本 - -在参数服务器的实际部署中,人们往往需要解决数据热点问题。互联网数据往往符合幂律概率(Power-law -distribution),这会导致部分稀疏参数在训练过程中被访问的次数会显著高于其他参数。例如说,热门商品的特征向量被训练服务器拉取的次数就会远远高于非热门商品。因此,存储了热门数据的参数服务器所承受的数据拉取和推送请求会远远高于其他参数服务器,因此形成数据热点,伤害了系统的可扩展性。 - -解决数据热点问题的关键是利用在没有副本的情况下,通用的做法是每隔一段时间将所有参数在外存中保存一份检查点(checkpoint)。当出现机器故障时,首先所有的训练必须停止,等待故障的机器恢复上线,然后从外存中重新加载检查点。这就会导致从上一次保存检查点到故障发生时的数据全部丢失。保存一次检查点的开销随模型大小而增加,训练大模型时通常每隔1-2小时保存一次。因此无副本的参数服务器如果发生故障,会丢失最多1-2小时的数据。 - -解决参数服务器故障和数据热点问题的常用技术是构建模型主从副本。(Master-slave -replication)。一份参数在多个机器上拥有副本,并指定其中一个副本作为主副本。训练服务器的所有更新操作都向主副本写入并同步至从副本上。如何取得共识确定哪一个副本是主副本是分布式系统领域一个经典问题,已经有了相当多的成熟的算法,例如Paxos[@lamport2001paxos],RAFT[@184040],ZooKeeper[@hunt2010zookeeper]等。此外,主副本上的更新如何复制到从副本上也同样是分布式系统领域的经典共识问题。通常系统设计者需要在可用性(Availability)和一致性(Consistency)之间做出取舍。如果参数服务器副本间采用强一致性的复制协议(例如,链式复制[@li2014scaling])则可能导致训练服务器的推送请求失败,即参数服务器不可用。反之,如果参数服务器采用弱一致性的复制协议([@yu2020weips]),则可能导致副本间存储的参数不一致。 - -### 掉队者问题 - -参数服务器的另一大核心作用是可以让用户方便解决掉队者问题。在之前的讨论中,在每一步训练结束后,训练服务器都需要计算平均梯度来对每一个模型副本进行更新,从而保证下一步训练开始前,全部模型副本的参数的一致性,这种对于参数一致性的确保一般被称为同步训练(Synchronous -training)。同步训练一般会有助于训练系统达到更好的模型精度,但是当系统规模变大,我们往往会在系统中引入掉队者(Straggler)。掉队者出现的原因很多。常见的原因包括:掉队者设备可能和其他设备不在同一个机柜中,因此掉队者的通讯带宽显著小于其他设备。另外,掉队者设备也可能和其他进程共享本地的服务器计算和通讯资源,形成资源竞争,从而降低了性能。 - -掉队者对于基于Allreduce的同步训练系统的性能有显著影响,这是因为Allreduce让全部节点参与到平均梯度的计算和通讯中,而每个节点负责等量的数据。因此任何一个掉队者的出现,都会让整个Allreduce操作延迟完成。为了解决这个问题,人们也会使用参数服务器来计算平均梯度。一种常见的设计是:训练服务器训练出本地梯度后,会把本地梯度全部推送到参数服务器。参数服务器在等到一定数据训练服务器(例如说90%的训练服务器)的本地梯度后,就开始计算平均梯度。这样可以确保平均梯度的计算不会被落后者的出现延误。计算好的平均梯度马上推送给全部训练服务器,开始下一轮训练。 -0 -解决掉队者的另外一种常见做法是利用参数服务器实现**异步训练**(Asynchronous -training)。在一个异步训练系统中,每个训练服务器在训练开始时,有相同的模型参数副本。在训练中,他们计算出本地梯度后会马上将本地梯度推送到参数服务器,参数服务器将推送的梯度立刻用于更新参数,并把更新好的参数马上推送回对应的训练服务器。在这个过程中,不同的训练服务器很可能会使用不同版本的模型参数进行本地梯度的计算,这种做法有可能会伤害模型的精度,但它同时让不同训练服务器可以按照各自的运算速度来推送和拉取参数,而无需等待同伴,因此避免了掉队者对于整个集群性能的影响。 - -## 总结 - -- 大型机器学习模型的出现带来了对于算力和内存需求的快速增长,催生了分布式训练系统的出现。 - -- 分布式训练系统的设计往往遵循"分而治之"的设计思路。 - -- 利用分布式训练系统,人们可以显著提升性能性能,经济性,并且帮助抵御硬件故障。 - -- 分布式训练系统可以通过数据并行增加设备来提升算力。 - -- 当单节点内存不足时,我们可以通过模型并行来解决单设备内存不足。模型并行有两种实现方式:算子内并行和算子间并行。 - -- 大型模型并行系统容易出现设备使用空洞,而这种空洞可以通过流水线并行解决。 - -- 分布式训练系统往往运行在商用数据中心之中,数据中心网络无法提供充足的网络带宽来传输大量训练中生成的梯度。 - -- 为了提供海量的带宽,机器学习集群拥有异构的网络:以太网,机内网络(NVLink)和InfiniBand。 - -- 为了解决单节点瓶颈,我们可以使用 - Allreduce来分摊梯度聚合过程中的计算和通讯开销。 - -- 参数服务器可以帮助机器学习集群实现计算-存储的分离,从而更好的支持大型稀疏模型。 - -- 参数服务器常用数据副本技术解决数据热点问题,同时它们也可以被用来解决同步训练系统中常见的掉队者问题。 +overview +methods +pipeline +collective +parameter_servers +summary +``` \ No newline at end of file diff --git a/chapter_distributed_training_system/methods.md b/chapter_distributed_training_system/methods.md new file mode 100644 index 0000000..0214529 --- /dev/null +++ b/chapter_distributed_training_system/methods.md @@ -0,0 +1,80 @@ +## 分布式方法 + +我们会讨论分布式训练系统实现的常用并行方法。我们首先给出并行方法的设计目标以及分类。然后,我们会详细描述各个并行方法。 + +### 概述 + +![单节点训练系统](../img/ch09/ch10-single-node.pdf) +:width:`800px` +:label:`ch10-single-node` + +分布式训练系统的设计目标是:将单节点训练系统转化成**等价的**并行训练系统,从而在不影响模型精度的条件下完成训练过程的加速。一个单节点训练系统往往如:numref:`ch10-single-node`所示。一个训练过程会由多个数据小批次(mini-batch)完成。在图中,一个数据小批次被标示为**数据**。训练系统会利用数据小批次来生成梯度,提升模型精度。这个过程由一个训练**程序**实现。在实际中,这个程序往往实现了一个多层神经网络的执行过程。 +该神经网络的执行由一个计算图(Computational +Graph)表达。这个图有多个相互连接的算子(Operator),每个算子会拥有计算参数。每个算子往往会实现一个神经网络层(Neural +Network Layer),而参数则代表了这个层在训练中所更新的的权重(Weights)。 + +为了更新参数,计算图的执行会分为**前向**传播和**反向**传播两个阶段。前向传播的第一步会将数据读入第一个算子,该算子会根据当前的参数,计算出传播给下一个算子的数据。算子依次重复这个前向传播的过程(算子1 +-\> 算子2 -\> +算子3),直到最后一个算子结束。最后的算子随之马上开始反向传播。反向传播中,每个算子依次计算出梯度(梯度3 +-\> 梯度2 -\> +梯度1),并利用梯度更新本地的参数。反向传播最终在第一个算子结束。反向传播的结束也标志本次数据小批次的结束,系统随之读取下一个小批次,继续更新模型。 + +:分布式训练方法分类 + +| | 单数据 | 多数据 | +|:---:|:---:|:---:| +| 单程序 | 单程序单数据:单点执行 | 单程序多数据:数据并行 | +| 多程序 | 多程序单数据:模型并行 | 多程序多数据:混合并行 | +:label:`ch10-parallel-methods` + +给定一个单节点训练系统,人们会对**数据**和**程序**分区(Partition),从而完成并行加速。:numref:`ch10-parallel-methods`总结了不同的切分方法。单节点训练系统可以被归类于 +单程序单数据模式。而假如用户希望使用更多的设备来实现并行计算,他们首先可以选择对数据进行分区,并将同一个程序复制到多个设备上并行执行。这种方式是单程序多数据模式,常被称为**数据并行**(Data +Parallelism)。另一种并行方式是对程序进行分区:程序的算子会被分发给多个设备按照依次完成。这种模式是 +多程序单数据模式,常被称为**模型并行**(Model +Parallelism)。当训练超大型智能模型时,开发人们往往要同时对数据和程序进行切分,从而实现最高程度的并行。这种模式是多程序多数据模式,常被称为**混合并行**(Hybrid +Parallelism)。 + +接下来,我们详细讲解各种并行方法的执行过程。 + +### 数据并行 + +![数据并行训练系统](../img/ch09/ch10-data-parallel.pdf) +:width:`800px` +:label:`ch10-data-parallel` + +数据并行往往可以解决单节点的算力不足。这种并行方式在人工智能框架中最为常见,具体实现包括:TensorFlow +DistributedStrategy,PyTorch Distributed,Horovod DistributedOptimizer等。在一个数据并行系统中,假设用户给定一个训练批大小$N$,并且希望使用$M$个并行设备来加速训练。那么,该训练批大小会被分为$M$个分区,每个设备会分配到$N/M$个训练样本。这些设备共享一个训练程序的副本,在不同数据分区上独立执行,计算梯度。不同的设备(假设设备编号为$i$)会根据本地的训练样本估计出梯度$G_i$。为了确保训练程序参数的一致性,本地梯度$G_i$需要聚合,计算出平均梯度$(\sum_{i=1}^{N} G_i) / N$。最终,训练程序利用平均梯度修正模型参数,完成小批量的训练。 + +:numref:`ch10-data-parallel`展示了2个设备构成的数据并行例子。假设用户给定的批大小(Batch +Size)是64,那么每个设备会分配到32个训练样本,并且具有相同的神经网络参数(程序副本)。本地的训练样本会依次通过这个程序副本中的算子,完成前向传播和反向传播。在反向传播的过程中,程序副本会生成局部梯度。不同设备上对应的局部梯度(如设备1和设备2上各自的梯度1)会进行聚合,从而计算平均梯度。这个聚合的过程往往由集合通讯库(Collective +Communication)的Allreduce操作来完成。 + +### 模型并行 + +![模型并行系统:算子内并行](../img/ch09/ch10-model-parallel-intra-op.pdf) +:width:`800px` +:label:`ch10-model-parallel-intra-op` + +模型并行往往用于解决单节点的内存不足问题。一个常见的内存不足场景是模型中含有大型算子,例如说深度神经网络中需要计算大量分类的全连接层(Fully +Connected +Layer)。完成这种大型算子计算所需的内存可能超过单设备的内存容量。那么我们需要对这个大型算子进行切分。假设这个算子具有$P$个参数,而我们拥有$N$个设备,那么我们可以将$P$个参数平均分配给$N$个设备(每个设备分配$P/N$个参数),从而让每个设备负责更少的计算量,能够在内存容量的限制下完成前向传播和反向传播中所需的计算。这种切分方式是模型并行的应用,被称为**算子内并行**(Intra-operator +Parallelism)。 + +:numref:`ch10-model-parallel-intra-op`给出了一个由2个设备实现的算子内并行的例子。在这个例子中,假设一个神经网络具有2个算子,算子1的计算(包含正向和反向传播)需要预留16G的内存,算子2的计算需要预留1G的内存。而本例中的设备最多可以提供10G的内存。为了完成这个神经网络的训练,我们需要对算子1实现并行。具体做法是,将算子1的参数平均分区,设备1和设备2各负责其中部分算子1的参数。由于设备1和设备2的参数不同,因此它们各自负责程序分区1和程序分区2。在训练这个神经网络的过程中,数据(小批量)会首先传给算子1。由于算子1的参数分别由2个设备负责,因此数据会被广播给这2个设备。不同设备根据本地的参数分区完成前向计算,生成的本地计算结果需要进一步合并(Combine),发送给下游的算子2。在反向传播中,算子2的数据会被广播给设备1和设备2,这些设备根据本地的算子1分区各自完成局部的反向计算。计算结果进一步合并传播回数据,最终完成反向传播。 + +另一种内存不足的场景是:模型的总内存需求超过了单设备的内存容量。在这种场景下,假如我们总共有$N$个算子和$M$个设备,我们可以将算子平摊给这$M$个设备,让每个设备仅需负责$N/M$个算子的前向和反向计算,降低设备的内存开销。这种并行方式是模型并行的另一种应用,被称为**算子间并行**(Inter-operator +Parallelism)。 + +![模型并行系统:算子间并行](../img/ch09/ch10-model-parallel-inter-op.pdf) +:width:`800px` +:label:`ch10-model-parallel-inter-op` + +:numref:`ch10-model-parallel-inter-op`给出了一个由2个设备实现的算子间并行的例子。在这个例子中,假设一个神经网络具有2个算子,算子1和算子2各自需要10G的内存完成计算,则模型总共需要20G的内存。而每个设备仅能提供10G内存。在这个例子中,用户可以把算子1放置在设备1上,算子2放置在设备2上。在前向传播中,算子1的输出会被发送(Send)给下游的设备2。设备2接收(Receive)来自上游的数据,完成算子2的前向计算。在反向传播中,设备2将算子2的反向计算结果发送给设备1。设备1完成算子1的反向计算,完成本次训练。 + +### 混合并行 + +![混合并行系统](../img/ch09/ch10-hybrid-parallel.pdf) +:width:`800px` +:label:`ch10-hybrid-parallel` + +在训练大型人工智能模型中,我们往往会同时面对算力不足和内存不足。因此,我们需要混合使用数据并行和模型并行,这种方法被称为混合并行。:numref:`ch10-hybrid-parallel`提供了一个由4个设备实现的混合并行的例子。在这个例子中,我们首先实现算子间并行来解决训练程序内存开销过大的问题:该训练程序的算子1和算子2被分摊到了设备1和设备2上。进一步,我们通过数据并行来添加3和设备4,提升系统算力。为了达到这一点,我们对训练数据进行分区(数据分区1和数据分区2),并将模型(算子1和算子2)分配复制到设备3和设备4上生成可以并行执行的程序副本。在前向计算的过程中,设备1和设备3上的算子1副本同时开始,计算结果分别发送(Send)给设备2和设备4完成算子2副本的计算。在反向计算中,设备2和设备4同时开始计算梯度,本地梯度通过Allreduce进行平均。反向计算传递到设备1和设备3上的算子1副本结束。 \ No newline at end of file diff --git a/chapter_distributed_training_system/overview.md b/chapter_distributed_training_system/overview.md new file mode 100644 index 0000000..0fa27c5 --- /dev/null +++ b/chapter_distributed_training_system/overview.md @@ -0,0 +1,50 @@ +## 系统概述 + +### 设计动机 + +接下来,我们详细讨论分布式训练系统的设计动机 + +![对比机器学习模型参数量增长和计算硬件的算力增长](../img/ch09/ch10-computation-increase.pdf) +:width:`800px` +:label:`ch10-computation-increase` + +##### 算力不足 + +单处理器的算力不足是促使人们设计分布式训练系统的一个主要原因。一个处理器的算力可以用**每秒钟浮点数操作**(Floating +Point Operations Per Second,FLOPS)来衡量。 +如:numref:`ch10-computation-increase`所示,根据摩尔定律(Moore's +Law),中央处理器的算力每18个月增长2倍。虽然计算加速卡,如GPU和Tensor +Processing +Unit(TPU),针对机器学习计算(如矩阵相乘)提供了大量的算力。这些加速卡的发展最终也受限于摩尔定律,增长速度也停留在每18个月2倍。而与此同时,机器学习模型正在快速发展。短短数年,我们从仅能识别有限物体的AlexNet模型,一路发展到在复杂任务中打败人类的AlphaStar。这期间,模型对于算力需求每18个月增长了35倍。解决处理器性能和算力需求之间的鸿沟 +的关键就在于利用分布式计算。通过大型数据中心和云计算设施,我们可以快速获取大量的处理器。通过分布式训练系统有效管理这些处理器,我们可以实现算力的快速增长,从而持续满足模型的需求。 + +##### 内存不足 + +在训练机器学习模型的过程中,训练系统需要在内存中存储大量数据。这些数据包括:模型参数(Parameters)以及训练和更新这些参数所产生的中间数据,如特征图(Feature +Map)和梯度(Gradients)。假设一个深度神经网络模型具有10亿的参数,所有特征图共有20亿参数,每个参数都由一个32位浮点数表达,而更新这些参数至少还需要产生与特征图和参数等量的梯度。由于一个32位浮点数需要4个字节(Byte)的内存来存储,那么训练这个10亿规模的模型就需要至少24GB($24 \times 10^9$ +Byte)的内存。现在,随着大型预训练模型的崛起,一个深度神经网络(如GPT-3)会拥有超过千亿的参数。假设我们依然使用32位浮点数来存储参数,激活值和梯度,那么训练这个模型就至少需要1.2TB的内存。而如今的训练加速卡(如NVIDIA +A100)仅能提供最高80GB的内存。单卡内存空间的增长受到硬件规格,散热和成本等诸多因素,难以进一步快速增长。因此,我们需要分布式训练系统来同时使用数百个训练加速卡,从而为千亿级别的模型提供所需的TB级别的内存。 + +### 分布式训练架构 + +受限于单节点的有限算力,内存和存储资源,人们把关注投向了日益成熟的云计算数据中心。一个数据中心管理着数十万个计算服务器。随着数据中心的全球部署,人们可以很方便地获得数百个服务器。这些服务器可以通过分布式训练系统来协调和管理,解决训练大型机器学习模型过程遇到的算力,内存和存储不足,从而完成训练过程的加速。 + +![单节点计算和多节点分布式计算](../img/ch09/ch10-single-vs-multi.pdf) +:width:`800px` +:label:`ch10-single-vs-multi` + +在设计分布式训练系统的过程中,我们需要找出有资源瓶颈的计算任务,根据计算任务的特点,将其拆分成多个子任务,然后将子任务分发给多个节点(可以是服务器,机器,或者是加速卡)并行完成。 +:numref:`ch10-single-vs-multi`描述了如何将单节点执行转换为分布式执行的一般过程。在机器学习系统中,一个计算任务往往会有一组数据(例如训练样本)或者任务(例如算子)作为输入,利用一个计算节点(例如GPU)生成一组输出(例如梯度)。假如单节点成为瓶颈,我们可以利用分布式计算进行加速。分布式执行一般具有三个步骤:第一步,我们需要将输入进行**切分**。第二步,每个输入部分会分发给不同的计算节点,实现**并行**计算。第三步,每个计算节点的输出,进一步**合并**,最终得到和单节点等价的计算结果。这种切分-并行-合并的模式,本质上实现了分而治之算法(Divide-and-Conquer +Algorithm)的设计思想:由于每个计算节点只需要负责更小的子任务,因此其可以更快速的完成计算,最终形成对整个计算过程的加速。 + +### 用户益处 + +通过使用分布式训练系统,我们往往可以获得以下几个关键好处: + +- **提升系统性能**:使用分布式训练,往往可以带来训练性能的巨大提升。一个分布式训练系统往往用以下这个指标来衡量性能:到达目标精度所需的时间(time-to-accuracy)。这个指标由两个参数决定: + 一个数据周期所需的完成时间,以及一个数据周期模型所提升的精度。通过持续增加并行处理节点,我们可以将数据周期的完成时间不断变短,最终显著减少到达目标精度所需的时间。 + +- **经济性(Economy)**:使用分布式训练,我们也可以进一步减少训练及其模型所需的成本。受限于单节点散热的上限,单节点的算力越高,起所需的散热硬件成本也更高。因此,在提供同等的算力的条件下,组合多个计算节点是一个更加经济高效的方式。这促使需要云服务商(如亚马逊和微软等)更加注重给用户提供成本高效的分布式机器学习系统。 + +- **抵御硬件故障**:分布式训练系统同时能有效提升抵御硬件故障的能力。机器学习训练集群往往由商用硬件(Commodity + Hardware)组成,这类硬件(例如说,磁盘和网卡)运行一定周期就会产生故障。而仅使用单个硬件进行训练的话,那么一个硬件的故障就会造成整个训练的任务的失败。通过将这个训练任务又多个硬件共同完成,即使一个硬件故障了,我们也可以通过将这个硬件上相应的计算子任务转移给其余硬件,继续完成训练,从而避免训练任务的失败。 \ No newline at end of file diff --git a/chapter_distributed_training_system/parameter_servers.md b/chapter_distributed_training_system/parameter_servers.md new file mode 100644 index 0000000..e6bc5d3 --- /dev/null +++ b/chapter_distributed_training_system/parameter_servers.md @@ -0,0 +1,54 @@ +## 参数服务器 + +接下来,我们介绍另一种常见的分布式训练系统实现:参数服务器。TensorFlow原生提供了参数服务器的实现。而其他框架,例如PyTorch和MindSpore,则需要用户使用第三方的参数服务器实现,例如PS-Lite。 + +### 计算和存储分离 + +利用参数服务器的其中一个核心需求是实现:计算和存储的分离。在训练模型中,计算可以被理解为计算更新模型参数所需要的计算(例如说,计算本地梯度和计算平均梯度),而存储可以被理解为将模型参数存储在内存设备中(例如说,主机内存,加速卡内存和SSD设备)。传统的神经网络训练中,计算往往是核心瓶颈,因此我们只需要配置有合适数量的带有加速卡的服务器,常被称为训练服务器(Training +servers)。 + +随着机器学习的发展,新型的稀疏模型被开发出来。相比于传统的神经网络训练,稀疏模型的训练往往不需要大量昂贵的计算加速卡(GPU),而需要海量的内存来存储嵌入表(Embedding +table)。例如说,一个大型深度学习推荐系统中,它们往往使用小型的深度神经网络(如Multi-layer +Perception),训练这种神经网络只需要几个GPU即可。而另一方面,推荐系统中往往需要存储PB级别的嵌入表。嵌入表往往由推荐系统的用户特征(User +feature)和产品特征(Item +feature)构成。这些特征往往是大型向量(Vector)。现代推荐系统需要服务数亿的用户,推荐数以千万的商品。假设用户的特征是1MB,而系统需要服务10亿的用户,那么用户的嵌入表就会有1PB的大小。而这个大小远远超过了一个深度学习服务器所具有的内存。假如我们部署大量的昂贵的深度学习服务器来存储海量嵌入表,那么这些服务器上的加速卡的使用率将会极低,无法实现对于硬件的高效利用。 + +![参数服务器](../img/ch09/ch10-parameter-servers.pdf) +:width:`800px` +:label:`ch10-parameter-servers` + +为了解决上述问题,人们往往会在稀疏模型集群中混合部署:训练服务器和参数服务器,从而实现对于计算需求和内存需求分别满足。:numref:`ch10-parameter-servers` 描述了带有参数服务器的机器学习集群。这个集群中含有2个训练服务器和2个参数服务器,训练服务器一般是拥有加速卡的计算优化服务器(Compute-optimised +server)。而参数服务器一般是内存优化服务器(Memory-optimised +server),其的内存大小一般远远大于计算优化服务器。在一个稀疏模型中往往拥有神经网络参数和嵌入表参数。神经网络较小,其可以存储在训练服务器内存中。而嵌入表很大,因此需要存储在额外的参数服务器中。参数服务器一般会按照键-值对(Key-value +pairs)的方式来存储参数。常用的键包括用户名(User ID),产品名(Item +ID)或者是参数名(Parameter +Key)。常用的值是以多维度向量(Multi-dimensional +tensors)表达的模型参数。假如存在多个参数服务器,参数服务器会用数据分区函数(例如,哈希函数和区域划分)将健-值映射到不同参数服务器上。 + +为了完成对于模型的训练,在每一步训练中,训练服务器会根据当前的小批量训练数据,找到本批量中需要用到的参数。例如说,本小批量数据只会训练部分用户的特征,那么这些用户的特征才会需要。根据参数服务器的数据分区函数,训练服务器可以知道参数当前在哪个参数服务器上,它们因此会用参数的键(Key)向对应的参数服务器发起拉取请求(Pull +request)。参数服务器响应,并返回对应的值(Value)。训练服务器将拉取的参数(往往是嵌入表)和本地内存中的模型参数(往往是神经网络)进行合并,从而对合并的模型进行训练,计算梯度。假如训练服务器实现了数据并行,那么训练服务器计算出的本地梯度需要利用Allreduce计算出平均梯度。对于训练服务器本地内存中的参数,训练服务器可以马上利用平均梯度进行修改。对于在参数服务器中存储的参数,训练服务器发起推送请求(Push +request)将平均梯度发送到参数服务器,参数服务器更新本地存储的参数。 + +在以上的参数服务器架构中,机器学习集群拥有者可以灵活的根据梯度计算所需要算力配置合理数量的训练服务器。他们也可以根据参数的数量配置大部分的稀疏参数(Sparse +parameters)在参数服务器中,仅留下小部分的密集参数(Dense +parameters)在训练服务器中。密集参数和稀疏参数的核心区别是:稀疏参数在每一步训练不一定都会被用到,他们需要根据当前训练小批量来决定。而密集参数每一步训练都需要用到。因此为了频繁从参数服务器中拉取,密集参数往往会存储在训练服务器中。 + +### 数据副本 + +在参数服务器的实际部署中,人们往往需要解决数据热点问题。互联网数据往往符合幂律概率(Power-law +distribution),这会导致部分稀疏参数在训练过程中被访问的次数会显著高于其他参数。例如说,热门商品的特征向量被训练服务器拉取的次数就会远远高于非热门商品。因此,存储了热门数据的参数服务器所承受的数据拉取和推送请求会远远高于其他参数服务器,因此形成数据热点,伤害了系统的可扩展性。 + +解决数据热点问题的关键是利用在没有副本的情况下,通用的做法是每隔一段时间将所有参数在外存中保存一份检查点(checkpoint)。当出现机器故障时,首先所有的训练必须停止,等待故障的机器恢复上线,然后从外存中重新加载检查点。这就会导致从上一次保存检查点到故障发生时的数据全部丢失。保存一次检查点的开销随模型大小而增加,训练大模型时通常每隔1-2小时保存一次。因此无副本的参数服务器如果发生故障,会丢失最多1-2小时的数据。 + +解决参数服务器故障和数据热点问题的常用技术是构建模型主从副本。(Master-slave +replication)。一份参数在多个机器上拥有副本,并指定其中一个副本作为主副本。训练服务器的所有更新操作都向主副本写入并同步至从副本上。如何取得共识确定哪一个副本是主副本是分布式系统领域一个经典问题,已经有了相当多的成熟的算法,例如Paxos和Raft。此外,主副本上的更新如何复制到从副本上也同样是分布式系统领域的经典共识问题。通常系统设计者需要在可用性(Availability)和一致性(Consistency)之间做出取舍。如果参数服务器副本间采用强一致性的复制协议(例如,链式副本(Chain replication)))则可能导致训练服务器的推送请求失败,即参数服务器不可用。反之,如果参数服务器采用弱一致性的复制协议,则可能导致副本间存储的参数不一致。 + +### 掉队者问题 + +参数服务器的另一大核心作用是可以让用户方便解决掉队者问题。在之前的讨论中,在每一步训练结束后,训练服务器都需要计算平均梯度来对每一个模型副本进行更新,从而保证下一步训练开始前,全部模型副本的参数的一致性,这种对于参数一致性的确保一般被称为同步训练(Synchronous +training)。同步训练一般会有助于训练系统达到更好的模型精度,但是当系统规模变大,我们往往会在系统中引入掉队者(Straggler)。掉队者出现的原因很多。常见的原因包括:掉队者设备可能和其他设备不在同一个机柜中,因此掉队者的通讯带宽显著小于其他设备。另外,掉队者设备也可能和其他进程共享本地的服务器计算和通讯资源,形成资源竞争,从而降低了性能。 + +掉队者对于基于Allreduce的同步训练系统的性能有显著影响,这是因为Allreduce让全部节点参与到平均梯度的计算和通讯中,而每个节点负责等量的数据。因此任何一个掉队者的出现,都会让整个Allreduce操作延迟完成。为了解决这个问题,人们也会使用参数服务器来计算平均梯度。一种常见的设计是:训练服务器训练出本地梯度后,会把本地梯度全部推送到参数服务器。参数服务器在等到一定数据训练服务器(例如说90%的训练服务器)的本地梯度后,就开始计算平均梯度。这样可以确保平均梯度的计算不会被落后者的出现延误。计算好的平均梯度马上推送给全部训练服务器,开始下一轮训练。 +0 +解决掉队者的另外一种常见做法是利用参数服务器实现**异步训练**(Asynchronous +training)。在一个异步训练系统中,每个训练服务器在训练开始时,有相同的模型参数副本。在训练中,他们计算出本地梯度后会马上将本地梯度推送到参数服务器,参数服务器将推送的梯度立刻用于更新参数,并把更新好的参数马上推送回对应的训练服务器。在这个过程中,不同的训练服务器很可能会使用不同版本的模型参数进行本地梯度的计算,这种做法有可能会伤害模型的精度,但它同时让不同训练服务器可以按照各自的运算速度来推送和拉取参数,而无需等待同伴,因此避免了掉队者对于整个集群性能的影响。 \ No newline at end of file diff --git a/chapter_distributed_training_system/pipeline.md b/chapter_distributed_training_system/pipeline.md new file mode 100644 index 0000000..277f77d --- /dev/null +++ b/chapter_distributed_training_system/pipeline.md @@ -0,0 +1,20 @@ +## 流水线并行 + +在数据并行和模型并行以外,流水线并行是另一种常用的并行加速方法。 +流水线并行往往被应用在大型模型并行系统中。这种系统通过算子内并行和算子间并行解决单设备内存不足的问题。 +然而,当这类系统的运行中,计算图中的下游设备需要长期持续处于空闲状态,等待上游设备的计算完成,才可以开始计算,这极大降低了设备的平均使用率。这种现象被称为模型并行空洞(Model +Parallelism Bubble)。 + +![流水线并行系统。注意!图的F和B任务的编号需要更新!](../img/ch09/ch10-pipeline-parallel.pdf) +:width:`800px` +:label:`ch10-pipeline-parallel` + +为了减少空洞,提升设备使用率,我们可以在模型并行系统中构建流水线。这种做法的核心想法是将一个数据小批量(Data +Mini-batch)划分为多个微批量(Micro-batch)。假设一个数据小批量有$D$个训练数据,这个小批量可以被划分为$M$个微批量,那么微批量的大小就是$D/M$。每个微批量相应进入训练系统,完成前向传播(Forwards +propagation)和反向传播(Backwards +propagation),计算出梯度。每个微批量对应的梯度将会缓存,等到全部微批量完成,缓存的梯度会被加和,算出平均梯度,更新模型参数。 + +:numref:`ch10-pipeline-parallel` 进一步给出了一个流水线并行的执行例子。在本例中,模型参数需要切分给4个设备存储。为了充分利用起来这4个设备,我们将小批量切分为2个微批量。当设备1完成第一个微批量的前向传播后(表示为$F_{0,0}$)后,他会将中间结果发送给设备2,触发响应的前向传播任务(表示为$F_{1,0}$)。与此同时,设备1也可以开始第二个微批量的前向传播任务(表示为$F_{0,1}$)。前向传播会在流水线的最后一个设备--设备3--完成。系统于是开始反向传播。设备4开始第1个微批量的反向传播任务(表示为$B_{3,0}$)。该任务完成后的中间结果会被发送给设备3,触发响应的反向传播任务(表示为$B_{2,0}$)。与此同时,设备4会缓存好对应第1个微批量的梯度,接下来开始第2个微批量计算(表示为$B_{3,1}$)。当设备4完成了全部的反向传播计算后,他会将本地缓存的梯度进行相加,并且除以微批量数量,计算出平均梯度,该梯度用于更新模型参数。 + +流水线并行的关键因素是流水线泡沫(Bubble)。当设备完成前向传播后,必须等到全部反向传播开发,在此期间设备会处于空闲状态。在 :numref:`ch10-pipeline-parallel` 中,我们可以看到设备1在完成2个前向传播任务后,要等很多时间才能开始2个传向传播任务。这其中的等待时间即被称为泡沫。为了减少设备的等待时间,一种常见的做法是尽可能的增加微批量的数量,从而让反向传播尽可能早的开始。然而,使用非常小的微批量大小,可能会造成加速器无法被充分利用。因此最优的微批量大小是多种因素的折中。其中最核心的因素是流水线泡沫的大小和加速器的计算能力。 + diff --git a/chapter_distributed_training_system/summary.md b/chapter_distributed_training_system/summary.md new file mode 100644 index 0000000..2ba6e1e --- /dev/null +++ b/chapter_distributed_training_system/summary.md @@ -0,0 +1,24 @@ +## 总结 + +- 大型机器学习模型的出现带来了对于算力和内存需求的快速增长,催生了分布式训练系统的出现。 + +- 分布式训练系统的设计往往遵循"分而治之"的设计思路。 + +- 利用分布式训练系统,人们可以显著提升性能性能,经济性,并且帮助抵御硬件故障。 + +- 分布式训练系统可以通过数据并行增加设备来提升算力。 + +- 当单节点内存不足时,我们可以通过模型并行来解决单设备内存不足。模型并行有两种实现方式:算子内并行和算子间并行。 + +- 大型模型并行系统容易出现设备使用空洞,而这种空洞可以通过流水线并行解决。 + +- 分布式训练系统往往运行在商用数据中心之中,数据中心网络无法提供充足的网络带宽来传输大量训练中生成的梯度。 + +- 为了提供海量的带宽,机器学习集群拥有异构的网络:以太网,机内网络(NVLink)和InfiniBand。 + +- 为了解决单节点瓶颈,我们可以使用 + Allreduce来分摊梯度聚合过程中的计算和通讯开销。 + +- 参数服务器可以帮助机器学习集群实现计算-存储的分离,从而更好的支持大型稀疏模型。 + +- 参数服务器常用数据副本技术解决数据热点问题,同时它们也可以被用来解决同步训练系统中常见的掉队者问题。 \ No newline at end of file diff --git a/chapter_introduction/requirements_for_machine_learning_systems.md b/chapter_introduction/requirements_for_machine_learning_systems.md index 9b93159..5172ba3 100644 --- a/chapter_introduction/requirements_for_machine_learning_systems.md +++ b/chapter_introduction/requirements_for_machine_learning_systems.md @@ -37,7 +37,7 @@ Pregel)等方式来达到以上设计目标。可是他们发现(如 :numref :机器学习框架和相关系统的比较 -| | 神经网络 | 自动微分 | 数据管理和处理 | 训练和部署 | 加速器 | 分布式 | +| | 神经网络 | 自动微分 | 数据管理 | 训练和部署 | 加速器 | 分布式 | |:-: |:-:| :-: |:-:|:-: |:-:|:-:| | 神经网络库 | 是 | 是 | 否 | 否 | 是 | 否 | | 大数据框架 | 否 | 否 | 是 | 否 | 否 | 是 | diff --git a/img/ch09/ch10-allreduce-process.pdf b/img/ch09/ch10-allreduce-process.pdf new file mode 100644 index 0000000..945b686 Binary files /dev/null and b/img/ch09/ch10-allreduce-process.pdf differ diff --git a/img/ch09/ch10-allreduce-state.pdf b/img/ch09/ch10-allreduce-state.pdf new file mode 100644 index 0000000..9889eab Binary files /dev/null and b/img/ch09/ch10-allreduce-state.pdf differ diff --git a/img/ch09/ch10-averaged-gradient.pdf b/img/ch09/ch10-averaged-gradient.pdf new file mode 100644 index 0000000..d5480ec Binary files /dev/null and b/img/ch09/ch10-averaged-gradient.pdf differ diff --git a/img/ch09/ch10-computation-increase.pdf b/img/ch09/ch10-computation-increase.pdf new file mode 100644 index 0000000..978739d Binary files /dev/null and b/img/ch09/ch10-computation-increase.pdf differ diff --git a/img/ch09/ch10-data-parallel.pdf b/img/ch09/ch10-data-parallel.pdf new file mode 100644 index 0000000..af60584 Binary files /dev/null and b/img/ch09/ch10-data-parallel.pdf differ diff --git a/img/ch09/ch10-datacentre.pdf b/img/ch09/ch10-datacentre.pdf new file mode 100644 index 0000000..f2f1ea1 Binary files /dev/null and b/img/ch09/ch10-datacentre.pdf differ diff --git a/img/ch09/ch10-hybrid-parallel.pdf b/img/ch09/ch10-hybrid-parallel.pdf new file mode 100644 index 0000000..0b853e6 Binary files /dev/null and b/img/ch09/ch10-hybrid-parallel.pdf differ diff --git a/img/ch09/ch10-model-parallel-inter-op.pdf b/img/ch09/ch10-model-parallel-inter-op.pdf new file mode 100644 index 0000000..ca78652 Binary files /dev/null and b/img/ch09/ch10-model-parallel-inter-op.pdf differ diff --git a/img/ch09/ch10-model-parallel-intra-op.pdf b/img/ch09/ch10-model-parallel-intra-op.pdf new file mode 100644 index 0000000..26b4d67 Binary files /dev/null and b/img/ch09/ch10-model-parallel-intra-op.pdf differ diff --git a/img/ch09/ch10-parameter-server-replication.pdf b/img/ch09/ch10-parameter-server-replication.pdf new file mode 100644 index 0000000..1e17389 Binary files /dev/null and b/img/ch09/ch10-parameter-server-replication.pdf differ diff --git a/img/ch09/ch10-parameter-servers.pdf b/img/ch09/ch10-parameter-servers.pdf new file mode 100644 index 0000000..ebeab39 Binary files /dev/null and b/img/ch09/ch10-parameter-servers.pdf differ diff --git a/img/ch09/ch10-pipeline-parallel.pdf b/img/ch09/ch10-pipeline-parallel.pdf new file mode 100644 index 0000000..43b7fcf Binary files /dev/null and b/img/ch09/ch10-pipeline-parallel.pdf differ diff --git a/img/ch09/ch10-recommendation-model.pdf b/img/ch09/ch10-recommendation-model.pdf new file mode 100644 index 0000000..34b8110 Binary files /dev/null and b/img/ch09/ch10-recommendation-model.pdf differ diff --git a/img/ch09/ch10-single-node.pdf b/img/ch09/ch10-single-node.pdf new file mode 100644 index 0000000..f495eed Binary files /dev/null and b/img/ch09/ch10-single-node.pdf differ diff --git a/img/ch09/ch10-single-vs-multi.pdf b/img/ch09/ch10-single-vs-multi.pdf new file mode 100644 index 0000000..ff42327 Binary files /dev/null and b/img/ch09/ch10-single-vs-multi.pdf differ