Chapter 10 distributed training (#26)

* WIP: distributed training,

* Finish chapter 9.

* Checkpoint
This commit is contained in:
Luo Mai
2022-03-03 08:09:13 +00:00
committed by GitHub
parent 86661feadd
commit 7671dca95a
23 changed files with 309 additions and 313 deletions

View File

@@ -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.4TB700GB
$\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步骤是为了计算出梯度的和SummationBroadcast步骤是为了把梯度之和广播给全部的节点。为了说明这些步骤的执行过程我们利用
: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等支持。当用户选择使用数据并行模式的过程其底层会默认触发。

View File

@@ -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 SecondFLOPS来衡量。
如图[1.1](#fig:ch010/ch10-computation-increase){reference-type="ref"
reference="fig:ch010/ch10-computation-increase"}所示根据摩尔定律Moore's
Law中央处理器的算力每18个月增长2倍。虽然计算加速卡如GPU和Tensor
Processing
UnitTPU针对机器学习计算如矩阵相乘提供了大量的算力。这些加速卡的发展最终也受限于摩尔定律增长速度也停留在每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.4TB700GB
$\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步骤是为了计算出梯度的和SummationBroadcast步骤是为了把梯度之和广播给全部的节点。为了说明这些步骤的执行过程我们利用
图[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
```

View File

@@ -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
DistributedStrategyPyTorch DistributedHorovod 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副本结束。

View File

@@ -0,0 +1,50 @@
## 系统概述
### 设计动机
接下来,我们详细讨论分布式训练系统的设计动机
![对比机器学习模型参数量增长和计算硬件的算力增长](../img/ch09/ch10-computation-increase.pdf)
:width:`800px`
:label:`ch10-computation-increase`
##### 算力不足
单处理器的算力不足是促使人们设计分布式训练系统的一个主要原因。一个处理器的算力可以用**每秒钟浮点数操作**Floating
Point Operations Per SecondFLOPS来衡量。
如:numref:`ch10-computation-increase`所示根据摩尔定律Moore's
Law中央处理器的算力每18个月增长2倍。虽然计算加速卡如GPU和Tensor
Processing
UnitTPU针对机器学习计算如矩阵相乘提供了大量的算力。这些加速卡的发展最终也受限于摩尔定律增长速度也停留在每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组成这类硬件例如说磁盘和网卡运行一定周期就会产生故障。而仅使用单个硬件进行训练的话那么一个硬件的故障就会造成整个训练的任务的失败。通过将这个训练任务又多个硬件共同完成即使一个硬件故障了我们也可以通过将这个硬件上相应的计算子任务转移给其余硬件继续完成训练从而避免训练任务的失败。

View File

@@ -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)。在一个异步训练系统中,每个训练服务器在训练开始时,有相同的模型参数副本。在训练中,他们计算出本地梯度后会马上将本地梯度推送到参数服务器,参数服务器将推送的梯度立刻用于更新参数,并把更新好的参数马上推送回对应的训练服务器。在这个过程中,不同的训练服务器很可能会使用不同版本的模型参数进行本地梯度的计算,这种做法有可能会伤害模型的精度,但它同时让不同训练服务器可以按照各自的运算速度来推送和拉取参数,而无需等待同伴,因此避免了掉队者对于整个集群性能的影响。

View File

@@ -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个传向传播任务。这其中的等待时间即被称为泡沫。为了减少设备的等待时间一种常见的做法是尽可能的增加微批量的数量从而让反向传播尽可能早的开始。然而使用非常小的微批量大小可能会造成加速器无法被充分利用。因此最优的微批量大小是多种因素的折中。其中最核心的因素是流水线泡沫的大小和加速器的计算能力。

View File

@@ -0,0 +1,24 @@
## 总结
- 大型机器学习模型的出现带来了对于算力和内存需求的快速增长,催生了分布式训练系统的出现。
- 分布式训练系统的设计往往遵循"分而治之"的设计思路。
- 利用分布式训练系统,人们可以显著提升性能性能,经济性,并且帮助抵御硬件故障。
- 分布式训练系统可以通过数据并行增加设备来提升算力。
- 当单节点内存不足时,我们可以通过模型并行来解决单设备内存不足。模型并行有两种实现方式:算子内并行和算子间并行。
- 大型模型并行系统容易出现设备使用空洞,而这种空洞可以通过流水线并行解决。
- 分布式训练系统往往运行在商用数据中心之中,数据中心网络无法提供充足的网络带宽来传输大量训练中生成的梯度。
- 为了提供海量的带宽机器学习集群拥有异构的网络以太网机内网络NVLink和InfiniBand。
- 为了解决单节点瓶颈,我们可以使用
Allreduce来分摊梯度聚合过程中的计算和通讯开销。
- 参数服务器可以帮助机器学习集群实现计算-存储的分离,从而更好的支持大型稀疏模型。
- 参数服务器常用数据副本技术解决数据热点问题,同时它们也可以被用来解决同步训练系统中常见的掉队者问题。

View File

@@ -37,7 +37,7 @@ Pregel等方式来达到以上设计目标。可是他们发现如 :numref
:机器学习框架和相关系统的比较
| | 神经网络 | 自动微分 | 数据管理和处理 | 训练和部署 | 加速器 | 分布式 |
| | 神经网络 | 自动微分 | 数据管理 | 训练和部署 | 加速器 | 分布式 |
|:-: |:-:| :-: |:-:|:-: |:-:|:-:|
| 神经网络库 | 是 | 是 | 否 | 否 | 是 | 否 |
| 大数据框架 | 否 | 否 | 是 | 否 | 否 | 是 |

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.