• 为了保证你在浏览本网站时有着更好的体验,建议使用类似Chrome、Firefox之类的浏览器~~
    • 如果你喜欢本站的内容何不Ctrl+D收藏一下呢,与大家一起分享各种编程知识~
    • 本网站研究机器学习、计算机视觉、模式识别~当然不局限于此,生命在于折腾,何不年轻时多折腾一下

Ring Allreduce并行计算优化

Tensorflow admin 来源:简书 3个月前 (06-28) 253次浏览 0个评论 扫描二维码

之前使用的 horovod 就是使用 ring allreduce 的方法实现深度学习并行计算优化,这篇文章主要来源于转载,部分加上自己的注释。

当将神经网络的训练并行化到许多 GPU 上时,你必须选择如何将不同的操作分配到你可用的不同 GPU 上。在这里,我们关注一种称为数据并行随机梯度下降( SGD )的技术。与标准 SGD 一样,梯度下降是通过数据子集(小批次)完成的,需要多次迭代才能在整个数据集上进行。然而,在数据并行训练中,每个 GPU 都有整个神经网络模型的完整副本,对于每次迭代,只分配了小批次中样本的子集。对于每次迭代,每个 GPU 在其数据上运行网络的前向传播,随后进行误差反向传播,以计算损耗相对于网络参数的梯度。最后,GPU 相互通信以平均由不同 GPU 计算的梯度,将平均梯度应用于权重以获得新权重。GPU 都在锁定步骤的迭代中前进,一旦 GPU 完成了迭代,它必须等待所有其他 GPU 完成它们的迭代,这样权重才能被正确更新。这相当于在单个 GPU 上执行 SGD,但是我们通过在多个 GPU 之间分发数据并并行执行计算来获得加速。

当你只有两个 GPU 和以兆字节数据衡量的参数时,这些 GPU 的通信方式可能并不重要。然而,当你的模型有数十亿个参数时,梯度可能需要几十亿字节的空间(因为每个参数都有一个梯度值),并且你正在协调几十个 GPU,通信机制变得至关重要。

例如,考虑最直接的通信机制。每一个 GPU 都计算其子集的小批次上的梯度。然后,每个 GPU 将其梯度发送到单个 GPU,该 GPU 取所有梯度的平均值,并将平均值发送回所有其他 GPU。

在直接从单个 GPU 发送和接收数据的机制中,单个 GPU 必须从所有 GPU 接收所有参数,并将所有参数发送到所有 GPU。系统中的 gpu 越多,通信成本就越大。

让我们评估一下这种通信策略如何在真实模型上运行,例如以百度深度语音 2 为模型的语音识别网络,具有三亿个可训练参数。 每个参数四个字节的三亿个参数大约是 1.2 千兆字节的数据。 假设您系统上的网络硬件可以支持每秒 1 千兆字节的带宽; 在这种情况下,如上所述将系统并行化到两个 GPU 上将使每次迭代减慢 1.2 秒。 将您的训练并行化到 10 个 GPU 将使每次迭代减慢 10.8 秒; 随着 GPU 数量的增长,每次迭代所需的时间呈线性增长。 即使每次迭代花费几秒钟,通信成本的这种线性增长也会使得进一步的并行化变得不切实际并且会降低训练效率。

需要发送的数据越多,发送时间就越长;每个通信通道都有一个最大的吞吐量(带宽)。例如,一个好的 internet 连接可以提供每秒 15 兆字节的带宽,而千兆以太网连接可以提供每秒 125 兆字节的带宽。HPC 集群上的专用网络硬件(如 Infiniband)可以在节点之间提供每秒数 gb 的带宽。

另一种选择是放弃训练算法的同步性,并通过梯度下降的迭代消除所有 GPU 在锁定步骤中前进的限制。然而,虽然这可以使模型更容易并行化,但是消除这种约束的算法(异步 SGD 的变体)可能很难调试,对于某些模型来说,可能会收敛到子结果,所以我们不考虑这些问题。

实际上这里会涉及到同步更新与异步更新梯度的问题。同步更新设计各个 worker 节点之间通信传递参数统一更新,这个面临大量参数时网路 IO 带来的时间消耗,异步并行化很好,但是参数更新不到位会出现不更新或者参数过时的问题。

相反,我们可以通过使用高性能计算领域的分布式缩减算法并利用带宽优化环来解决通信问题。

The Ring Allreduce

上述简单通信策略的主要问题是,通信成本随系统中 gpu 的数量线性增长。相比之下,环 allreduce 算法的通信成本是恒定的,与系统中 gpu 的数量无关,完全由系统中 gpu 之间最慢的连接决定;事实上,如果您只考虑带宽作为通信成本的一个因素(并忽略延迟),那么环 allreduce 是一种最优通信算法(当您的模型很大,并且您需要发送大量数据的次数很少时,这是一个很好的通信成本估算。)。

环中的 gpu 都被安排在一个逻辑环中。每个 GPU 应该有一个左邻和一个右邻;它只会向它的右邻居发送数据,并从它的左邻居接收数据。

该算法分两个步骤进行:首先是 scatter-reduce,然后是 allgather。在 scatter-reduce 步骤中,GPU 将交换数据,使每个 GPU 可得到最终结果的一个块。在 allgather 步骤中,gpu 将交换这些块,以便所有 gpu 得到完整的最终结果。

The Scatter-Reduce

为简单起见,让我们假设目标是对一个浮点数的大数组求和; 系统中有 N 个 GPU,每个 GPU 都有一个相同大小的数组,并且在 allreduce 的末尾,每个 GPU 都应该有一个相同大小的数组,其中包含原始数组中数字的总和。

首先,gpu 将数组划分为 N 个更小的块(其中 N 是环中的 gpu 数)。

接下来,GPU 将进行 N-1 次 Scatter-Reduce 迭代;在每次迭代中,GPU 将向其右邻居发送一个块,并从其左邻居接收一个块并累积到该块中。每个 GPU 发送和接收的块在每次迭代中都是不同的;第 n 个 GPU 从发送块 N 和接收块 N – 1 开始,然后从那里向后进行,每次迭代都发送它在前一次迭代中接收到的块。

例如,在第一次迭代中,上图中的五个 GPU 将发送和接收以下区块:

Data transfers in the first iteration of scatter-reduce

在第一次发送和接收完成之后,每个 GPU 将拥有一个块,该块由两个不同 GPU 上相同块的和组成。例如,第二个 GPU 上的第一个块将是该块中来自第二个 GPU 和第一个 GPU 的值的和。

Itermediate sums after the first iteration of scatter-reduce is complete

在下一次迭代中,该过程继续进行,到最后,每个 GPU 将有一个块,该块包含所有 GPU 中该块中所有值的总和。下图展示了所有数据传输和中间结果,从第一次迭代开始,一直持续到 Scatter-Reduce 完成。

The Allgather

在 scatter-reduce 步骤完成之后,每个 GPU 都有一个值数组,其中一些值(每个 GPU 一个块)是最终的值,其中包括来自所有 GPU 的贡献。为了完成 allreduce, gpu 必须交换这些块,以便所有 gpu 都具有所有必需的值。

环的收集过程与 scatter-reduce 是相同的(发送和接收的 N-1 次迭代),只是 gpu 接收的值没有累加,而是简单地覆盖块。第 n 个 GPU 首先发送第 n+1 个块并接收第 n 个块,然后在以后的迭代中总是发送它刚刚接收到的块。

例如,在我们的 5 – gpu 设置的第一次迭代中,gpu 将发送和接收以下块

第一次迭代完成后,每个 GPU 将拥有最终数组的两个块。

在下一个迭代中,该过程将继续,到最后,每个 GPU 将拥有整个数组的完整累积值。下面的图像演示了所有数据传输和中间结果,从第一次迭代开始,一直到 allgather 完成。

Allreduce Communication Cost

回想一下,对于介绍中描述的简单通信算法,通信成本随着 GPU 的数量线性增长。 allreduce 运行良好的主要原因是不再是这种情况。

在我们描述的系统中,N 个 GPU 中的每一个都将发送和接收 N-1 次 scatter-reduce,N-1 次 allgather。每次,GPU 都会发送 K / N 值,其中 K 是数组中不同 GPU 上相加的值总数。因此,传输到每个 GPU 和从每个 GPU 传输的数据总量为

重要的是,这与 GPU 的数量无关。

由于所有传输都是在离散迭代中同步进行的,因此所有传输的速度受到环中相邻 GPU 之间最慢(最低带宽)连接的限制。给定每个 GPU 的邻居的正确选择,该算法是带宽最优的,并且是执行全面操作的最快算法(假设延迟成本与带宽相比可以忽略不计)。一般来说,如果一个节点上的所有 GPU 在环中彼此相邻,则该算法的功能最佳;这最小化了网络争用的量,否则这可能会显著降低 GPU-GPU 连接的有效带宽。

Applying the Allreduce to Deep Learning

Ring allreduce 是高性能计算领域中著名的算法,但在深度学习中很少使用。在我们的实验室中,我们已经成功地将这个工具作为所有数据并行训练的基础,使我们能够有效地将训练扩展到几十个 gpu。

为了最小化通信开销,我们可以利用神经网络的结构。在每次迭代中,每个 GPU 运行正向传播来计算误差,然后运行反向传播来计算神经网络的每个参数的梯度。反向传播计算梯度,从输出层开始,向输入层移动,这意味着输出层参数的梯度在早期层的梯度之前很明显是可用的。因为全部运算可以一次对网络的一部分参数进行运算,所以我们可以在其他梯度仍在计算的时候开始对输出层参数进行全部运算。这样做将通信与反向传播步骤中的其余计算重叠,从而减少了每个 GPU 等待通信完成的总时间。

例如,考虑一个类似于 2 的语言模型,但有大约 3 亿个可学习的参数(因此总梯度大小为 1.2 千兆字节)。 使用 allreduce,每个 GPU 必须发送和接收大约 2.4 千兆字节的数据。 使用支持 CUDA 的 MPI 实现(例如 OpenMPI),我们可以使用 GPUDirect RDMA 在 GPU 之间传输数据,带宽大约为每秒 10 千兆字节; 但是,我们集群中节点之间的连接速度较慢,Infiniband 提供的带宽大约为每秒 6 千兆字节。 由于限制因素是 Infiniband 连接,因此单次迭代需要大约

由于更深层次的网络首先有可用的梯度,我们可以在完成整个反向传播传递之前开始进行数据传输,因此真正的开销可能小于 400 毫秒;根据所优化的神经网络的性质,通信和计算之间的重叠可能有所不同。

我们实现了上述语言模型,并测试了每次迭代所花费的时间,因为我们从单个 GPU(没有通信开销)扩展到 40 个 GPU。 这 40 个 GPU 排列成 5 个节点,每个节点有 8 个 GPU,由 Infiniband 连接。 我们运行语言模型 300 次迭代,批量大小为 32,并计算每秒处理的样本数。

正如您所看到的,整个系统的吞吐量随着 GPU 的数量线性扩展;超过一定的意见后,添加更多的 GPU 不会导致每次迭代的显著减速。在 40 个 GPU 上运行模型每次迭代大约需要 650 – 700 毫秒,而在单个 GPU 上大约需要 370 毫秒。根据我们的估计,通信将花费 400 毫秒,通过将反向传播与数据传输重叠,我们在每次迭代中节省了额外的 70 – 120 毫秒。

作者:初七 123
链接:https://www.jianshu.com/p/8c0e7edbefb9
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。


Deeplearn, 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明Ring Allreduce 并行计算优化
喜欢 (0)
admin
关于作者:
互联网行业码农一枚/业余铲屎官/数码影音爱好者/二次元

您必须 登录 才能发表评论!