到目前为止,我们讨论了如何在CPU和GPU上高效地训练模型。在13.3节中,我们甚至展示了深度学习框架如何允许人们在它们之间自动并行计算和通信。我们还在6.7节中展示了如何使用nvidia-smi命令列出计算机上所有可用的GPU。我们没有讨论的是如何真正并行化深度学习训练。相反,我们暗示传递一个会以某种方式将数据拆分到多个设备并使其工作。本节填写详细信息并展示如何从头开始并行训练网络。有关如何利用高级API中的功能的详细信息归入第13.6节.我们假设您熟悉minibatch随机梯度下降算法,例如12.5节中描述的算法。
让我们从一个简单的计算机视觉问题和一个稍微陈旧的网络开始,例如,具有多层卷积、池化,最后可能还有一些完全连接的层。也就是说,让我们从一个看起来与LeNet(LeCun等人,1998年)或AlexNet(Krizhevsky等人,2012年)非常相似的网络开始。给定多个GPU(如果是桌面服务器则为2个,在AWSg4dn.12xlarge实例上为4个,在p3.16xlarge上为8个,或在p2.16xlarge上为16个),我们希望以实现良好加速的方式对训练进行分区同时受益于简单且可重现的设计选择。毕竟,多个GPU会增加内存和计算能力。简而言之,给定我们要分类的小批量训练数据,我们有以下选择。
首先,我们可以跨多个GPU划分网络。也就是说,每个GPU将流入特定层的数据作为输入,跨多个后续层处理数据,然后将数据发送到下一个GPU。与单个GPU可以处理的数据相比,这使我们能够使用更大的网络处理数据。此外,可以很好地控制每个GPU的内存占用量(它只占网络总占用量的一小部分)。
然而,层(以及GPU)之间的接口需要紧密同步。这可能很棘手,特别是如果层与层之间的计算工作负载没有正确匹配。对于大量GPU,问题会更加严重。层与层之间的接口也需要大量的数据传输,例如激活和梯度。这可能会超出GPU总线的带宽。此外,计算密集型但顺序的操作对于分区来说并不重要。参见例如Mirhoseini等人。(2017年)在这方面尽最大努力。这仍然是一个难题,尚不清楚是否有可能在非平凡问题上实现良好的(线性)缩放。我们不推荐它,除非有出色的框架或操作系统支持将多个GPU链接在一起。
其次,我们可以分层拆分工作。例如,与其在单个GPU上计算64个通道,不如将问题拆分到4个GPU,每个GPU生成16个通道的数据。同样,对于全连接层,我们可以拆分输出单元的数量。图13.5.1(取自Krizhevsky等人(2012年))说明了这种设计,其中这种策略用于处理内存占用非常小(当时为2GB)的GPU。如果通道(或单元)的数量不太小,这就可以在计算方面实现良好的缩放。此外,由于可用内存线性扩展,多个GPU可以处理越来越大的网络。
图13.5.1由于GPU内存有限,原始AlexNet设计中的模型并行性。
然而,我们需要大量的同步或屏障操作,因为每一层都依赖于所有其他层的结果。此外,需要传输的数据量可能比跨GPU分布层时更大。因此,由于带宽成本和复杂性,我们不推荐这种方法。
最后,我们可以跨多个GPU对数据进行分区。这样,所有GPU都执行相同类型的工作,尽管观察结果不同。在每个小批量训练数据之后,梯度在GPU之间聚合。这是最简单的方法,适用于任何情况。我们只需要在每个小批量之后进行同步。也就是说,非常希望在其他仍在计算的同时开始交换梯度参数。此外,更大数量的GPU会导致更大的小批量大小,从而提高训练效率。然而,添加更多GPU并不能让我们训练更大的模型。
图13.5.2多GPU上的并行化。从左到右:原始问题、网络分区、分层分区、数据并行。
图13.5.2描绘了多GPU上不同并行化方式的比较。总的来说,数据并行是最方便的方法,前提是我们可以访问具有足够大内存的GPU。另请参阅(Lietal.,2014)以了解分布式训练分区的详细描述。在深度学习的早期,GPU内存曾经是一个问题。到目前为止,除了最不寻常的情况外,所有问题都已解决。下面我们重点介绍数据并行性。
假设有k机器上的GPU。给定要训练的模型,每个GPU将独立维护一组完整的模型参数,尽管GPU之间的参数值是相同且同步的。例如,图13.5.3说明了在以下情况下使用数据并行性进行训练k=2.
图13.5.3在两个GPU上使用数据并行计算小批量随机梯度下降。
一般来说,训练过程如下:
请注意,在实践中我们增加了小批量大小k-训练时折叠kGPU这样每个GPU都有相同数量的工作要做,就好像我们只在单个GPU上训练一样。在16-GPU服务器上,这会大大增加小批量大小,我们可能不得不相应地增加学习率。另请注意,第8.5节中的批量归一化需要进行调整,例如,通过为每个GPU保留一个单独的批量归一化系数。下面我们将使用玩具网络来说明多GPU训练。
%matplotlibinlineimporttorchfromtorchimportnnfromtorch.nnimportfunctionalasFfromd2limporttorchasd2l%matplotlibinlinefrommxnetimportautograd,gluon,np,npxfromd2limportmxnetasd2lnpx.set_np()13.5.3。玩具网络我们使用7.6节中介绍的LeNet(稍作修改)。我们从头开始定义它以详细说明参数交换和同步。