深度学习的并行化策略

Jos*_*osh 17 distributed-computing deep-learning tensorflow mxnet pytorch

哪些策略和形式的并行化是可行且可用于训练服务神经网络的?:

  • 机器内部内核(例如 GPU / TPU / CPU)
  • 网络或机架上的机器

我也在寻找有关它们如何用于 TensorFlow、PyTorch 或 MXNet 等的证据。

训练

据我所知,在大型数据集上训练大型神经网络时,至少可以有:

  1. 不同的内核 机器对图的不同部分进行操作(“拆分”)。例如,通过图本身的反向传播可以并行化,例如通过在不同的机器上托管不同的层,因为(我认为?)autodiff 图始终是DAG
  2. 不同的内核 机器不同的数据样本进行操作(“数据拆分”)。在 SGD 中,跨批次或样本的梯度计算也可以并行化(例如,可以在不同批次上独立计算梯度后组合梯度)。我相信这也称为梯度累积(?)。

什么时候每种策略更适合哪种类型的问题或神经网络?现代图书馆支持哪些模式?并且可以结合所有四种(2x2)策略吗?

最重要的是,我已经阅读了:

  • 异步训练
  • 同步训练

但我不知道到底是什么,指的,例如:Is it的计算梯度不同批次的数据或计算梯度不同的子图?或者它可能完全指的是其他东西?

服务

如果网络很大,预测/推理也可能很慢,并且模型在服务时可能不适合内存中的单个机器。是否有任何已知的多核和多节点预测解决方案可以处理此类模型?

Dan*_*iel 7

训练

一般来说,模型训练的并行化策略有两种:数据并行化和模型并行化。

1. 数据并行

该策略将训练数据分成 N 个分区,每个分区将在不同的“设备”(不同的 CPU 内核、GPU 甚至机器)上进行训练。与没有数据并行的训练相比,每个 minibatch 产生一个梯度,我们现在每个 minibatch 步骤有 N 个梯度。下一个问题是我们应该如何组合这 N 个梯度。

一种方法是平均所有 N 个梯度,然后根据平均值更新模型参数一次。这种技术称为同步分布式 SGD。通过求平均值,我们得到了更准确的梯度,但代价是等待所有设备完成自己的局部梯度计算。

另一种方法是不组合梯度——每个梯度将用于独立更新模型参数。因此,每个 minibatch 步骤将有 N 个参数更新,而之前的技术只有一个。这种技术称为异步分布式 SGD。因为它不必等待其他设备完成,所以异步方法完成小批量步骤所需的时间比同步方法少。然而,异步方法会产生更嘈杂的梯度,因此它可能需要完成更多的小批量步骤才能赶上同步方法的性能(在损失方面)。

有很多论文提出了对这两种方法的一些改进和优化,但主要思想通常与上述相同。

在文献中,对于实践中哪种技术更好,存在一些分歧。最后,大多数人现在都采用同步方法。

PyTorch 中的数据并行

要进行同步 SGD,我们可以用以下内容包装我们的模型torch.nn.parallel.DistributedDataParallel

from torch.nn.parallel import DistributedDataParallel as DDP

# `model` is the model we previously initialized
model = ...

# `rank` is a device number starting from 0
model = model.to(rank)
ddp_model = DDP(model, device_ids=[rank])
Run Code Online (Sandbox Code Playgroud)

然后我们可以类似地训练它。更详细的可以参考官方教程

为了在 PyTorch 中进行异步 SGD,我们需要更多地手动实现它,因为没有类似的包装器DistributedDataParallel

TensorFlow/Keras 中的数据并行

对于同步 SGD,我们可以使用tf.distribute.MirroredStrategy包装模型初始化:

import tensorflow as tf

strategy = tf.distribute.MirroredStrategy()
with strategy.scope():
    model = Model(...)
    model.compile(...)
Run Code Online (Sandbox Code Playgroud)

然后我们可以像往常一样训练它。更详细的可以参考KerasTensorFlow的官方指南。

对于异步 SGD,我们可以使用tf.distribute.experimental.ParameterServerStrategy类似的方法。

2. 模型并行

该策略将模型拆分为 N 个部分,每个部分将在不同的设备上进行计算。拆分模型的常用方法是基于层:不同的层集放置在不同的设备上。但是我们也可以根据模型架构更复杂地拆分它。

TensorFlow 和 PyTorch 中的模型并行

要在 TensorFlow 或 PyTorch 中实现模型并行,想法是相同的:将一些模型参数移动到不同的设备中。

在 PyTorch 中,我们可以使用torch.nn.Module.tomethod 将模块移动到不同的设备中。例如,假设我们要创建两个线性层,每个层都放置在不同的 GPU 上:

import torch.nn as nn

linear1 = nn.Linear(16, 8).to('cuda:0')
linear2 = nn.Linear(8, 4).to('cuda:1')
Run Code Online (Sandbox Code Playgroud)

在 TensorFlow 中,我们可以tf.device用来将操作放入特定设备中。要在 TensorFlow 中实现上面的 PyTorch 示例:

import tensorflow as tf
from tensorflow.keras import layers

with tf.device('/GPU:0'):
    linear1 = layers.Dense(8, input_dim=16)
with tf.device('/GPU:1'):
    linear2 = layers.Dense(4, input_dim=8)
Run Code Online (Sandbox Code Playgroud)

有关更多详细信息,您可以参考the official PyTorch tutorial;或者,如果您使用 TensorFlow,您甚至可以使用更高级的库,如mesh

3. 混合:数据和模型并行

回想一下,数据并行只拆分训练数据,而模型并行只拆分模型结构。如果我们有一个如此大的模型,即使使用了任何一种并行策略,它仍然无法放入内存中,我们总是可以同时使用这两种策略。

在实践中,大多数人更喜欢数据并行而不是模型并行,因为前者比后者与模型架构更加解耦(实际上,独立)。也就是说,通过使用数据并行,他们可以随心所欲地改变模型架构,而不必担心模型的哪一部分应该被并行化。

模型推理/服务

并行化模型服务比并行化模型训练更容易,因为模型参数已经固定并且每个请求都可以独立处理。与扩展常规 Python Web 服务类似,我们可以通过在一台机器上生成更多进程(以解决Python 的 GIL 问题),甚至生成更多机器实例来扩展模型服务。

但是,当我们使用 GPU 为模型提供服务时,我们需要做更多的工作来扩展它。由于与 CPU 相比,GPU 处理并发的方式不同,为了最大限度地提高性能,我们需要进行推理请求批处理。这个想法是当一个请求到来时,我们不是立即处理它,而是等待一些超时时间等待其他请求的到来。当超时时间到时,即使请求的数量只有一个,我们也会将它们全部批处理到 GPU 上进行处理。

为了最小化平均请求延迟,我们需要找到最佳超时持续时间。为了找到它,我们需要观察到最小化超时持续时间和最大化批量大小之间的权衡。如果超时时间太短,batch size 就会很小,因此 GPU 将得不到充分利用。但是如果超时时间太长,那么早来的请求将在处理之前等待太长时间。因此,最佳超时持续时间取决于模型复杂性(因此,推理持续时间)和每秒接收的平均请求数。

实现一个调度器来做请求批处理并不是一项简单的任务,所以与其手动完成,我们最好使用已经支持它的TensorFlow ServingPyTorch Serve


要了解有关并行和分布式学习的更多信息,您可以阅读这篇评论论文


Szy*_*zke 5

因为这个问题是相当广泛的,我会尽力摆脱一点点不同的光线和触摸不同的主题比在所示 @丹尼尔的深入回答。

训练

数据并行化与模型并行化

正如@Daniel所提到的,数据并行的使用频率更高,并且更容易正确执行。模型并行的主要警告是需要等待部分神经网络和它们之间的同步。

假设您有一个简单的前馈5层神经网络,分布在5不同的 GPU 上,每一层对应一个设备。在这种情况下,在每次前向传递期间,每个设备都必须等待来自先前层的计算。在这种简单的情况下,在设备之间复制数据和同步将花费更长的时间并且不会带来好处。

另一方面,有一些更适合模型并行化的模型,如Inception 网络,见下图:

初始块

在这里,您可以看到4来自前一层的独立路径,这些路径可以并行运行,并且只有2同步点(Filter concatenationPrevious Layer)。

问题

例如,通过图本身的反向传播可以并行化,例如通过在不同的机器上托管不同的层,因为(我认为?)autodiff 图始终是 DAG。

那并没那么简单。梯度是根据损失值(通常)计算的,你需要知道更深层的梯度来计算更浅层的梯度。如上所述,如果您有独立的路径,它会更容易并且可能会有所帮助,但在单个设备上会更容易。

我相信这也称为梯度累积(?)

不,它实际上是跨多个设备的减少。您可以在PyTorch 教程中看到其中的一些内容。梯度累积是指当您运行前向传递(在单个或多个设备上)N次数和反向传播(梯度保留在图中并在每次传递期间添加值)时,优化器仅执行一个步骤来更改神经网络的权重(并清除渐变)。在这种情况下,损失通常除以没有优化器的步骤数。这用于更可靠的梯度估计,通常当您无法使用大批量时。

跨设备减少看起来像这样:

减少

这是数据并行化的全部减少,每个设备计算发送到所有其他设备并在那里反向传播的值。

什么时候每种策略更适合哪种类型的问题或神经网络?

如上所述,数据并行几乎总是好的,如果你有足够的数据和样本大(可达8k样品或更多可一次完成,无需非常大的斗争)。

现代图书馆支持哪些模式?

tensorflow并且pytorch两者都支持,大多数现代和维护的库都以一种或另一种方式实现了这些功能

可以结合所有四种 (2x2) 策略吗

是的,您可以在机器之间和机器内部并行化模型和数据。

同步与异步

异步

@Daniel简要描述,但值得一提的是,更新并非完全独立。这没什么意义,因为我们基本上会N根据批次训练不同的模型。

相反,有一个全局参数空间,其中每个副本都应该异步共享计算更新(因此前向传递、后向、使用优化器计算更新并将此更新共享到全局参数)。

但是这种方法有一个问题:不能保证当一个工人计算前向传递时另一个工人更新了参数,所以更新是根据旧的参数集计算,这被称为stale gradients。因此,收敛可能会受到伤害。

另一种方法是N为每个工作人员计算步骤和更新,然后同步它们,尽管它不经常使用。

这部分基于很棒的博客文章,如果有兴趣,您绝对应该阅读它(更多关于陈旧性和一些解决方案)。

同步

之前大部分描述过,有不同的方法,但 PyTorch 从网络收集输出并对其进行反向传播 ( torch.nn.parallel.DistributedDataParallel)[https://pytorch.org/docs/stable/nn.html#torch.nn.parallel.DistributedDataParallel]。顺便提一句。您应该只使用这个 (no torch.nn.DataParallel),因为它克服了 Python 的 GIL 问题。

外卖

  • 数据并行化几乎总是在加速时使用,因为您“只需”在每个设备上(通过网络或在单个机器内)复制神经网络,在前向传递期间在每个设备上运行部分批处理,将它们连接成一个在一台设备上进行单批次(同步)并在所述设备上进行反向传播。
  • 有多种方法可以进行数据并行化,@Daniel已经介绍了
  • 当模型太大而无法在单机上安装时(OpenAI 的 GPT-3将是一个极端情况)或当架构适合此任务时,会进行模型并行化,但这两种情况都很少出现在 AFAIK 上。
  • 模型具有的并行路径(同步点)越多、越长,它就越适合模型并行化
  • 重要的是在相似的时间以相似的负载启动工作程序,以免在同步方法中进行同步过程或不在异步中获得过时的梯度(尽管在后一种情况下这还不够)。

服务

小型号

由于您追求的是大型模型,因此我不会深入研究较小模型的选项,只是简单提一下。

如果您想通过网络为多个用户提供服务,您需要某种方式来扩展您的架构(通常是像 GCP 或 AWS 这样的云)。您可以使用Kubernetes及其 POD 或预先分配一些服务器来处理请求,但这种方法效率低下(少量用户和运行服务器会产生毫无意义的成本,而大量用户可能会停止基础架构并占用太多资源)处理结果需要很长时间)。

另一种方法是使用基于无服务器方法的自动缩放。将根据每个请求提供资源,因此它具有很大的扩展能力+ 流量低时您无需付费。您可以看到Azure Functions正在针对 ML/DL 任务或torchlambdaPyTorch(免责声明,我是作者)针对较小模型对其进行改进。

大型号

如前所述,您可以将 Kubernetes 与您的自定义代码或即用型工具一起使用。

在第一种情况下,您可以像训练一样传播模型,但只能forward通过。这样即使是巨型模型也可以放到网络上(再次是175B参数的GPT-3),但是需要大量的工作。

第二,@Daniel提供了两种可能性。其他值得一提的可能是(阅读各自的文档,因为它们具有很多功能):

  • KubeFlow - 多个框架,基于 Kubernetes(所以自动扩展,多节点),训练,服务等等,与下面的其他东西相连,比如 MLFlow
  • AWS SageMaker - 使用 Python API 进行训练和服务,由 Amazon 支持
  • MLFlow - 多个框架,用于实验处理和服务
  • BentoML - 多个框架,训练和服务

对于 PyTorch,您可以在此处阅读更多内容,而 tensorflow 通过Tensorflow EXtended (TFX)具有许多开箱即用的服务功能。

来自 OP 评论的问题

是否有任何形式的并行性在机器内比在机器间更好

最好的并行性可能是在一台巨型计算机内,以最大限度地减少设备之间的传输。

此外,有不同的后端(至少在 PyTorch 中)可以从 ( mpi, gloo, nccl) 中进行选择,并且并非所有后端都支持在设备之间直接发送、接收、减少等数据(有些可能支持 CPU 到 CPU,其他的可能支持 GPU 到 GPU) . 如果设备之间没有直接链接,则必须先将它们复制到另一个设备,然后再复制到目标设备(例如,其他机器上的 GPU -> 主机上的 CPU -> 主机上的 GPU)。请参阅pytorch 信息

数据越多,网络越大,并行计算应该越有利可图。如果整个数据集可以放在单个设备上,则不需要并行化。此外,还应考虑互联网传输速度、网络可靠性等因素。这些成本可能会超过收益。

一般而言,如果您有大量数据(例如带有1.000.000图像的ImageNet )或大样本(例如图像2000x2000),请进行数据并行化。如果可能,在单台机器内尽量减少机器间的传输。仅当无法绕过模型时才分发模型(例如,它不适合 GPU)。不要这样做(在训练 MNIST 时几乎没有并行化的意义,因为整个数据集很容易放入 RAM 中,并且从中读取速度最快)。

为什么要构建自定义的 ML 特定硬件,例如 TPU?

CPU 不是最适合高度并行计算(例如矩阵乘法)+ CPU 可能被许多其他任务占用(例如数据加载),因此使用 GPU 是有意义的。

由于 GPU 是在考虑图形的情况下创建的(因此是代数变换),因此它可以承担一些 CPU 职责并且可以专门化(与 CPU 相比,内核更多但更简单,例如,参见V100)。

现在,TPU 是专门为张量计算(主要是深度学习)量身定制的,起源于谷歌,与 GPU 相比仍然是 WIP。这些适用于某些类型的模型(主要是卷积神经网络),在这种情况下可以带来加速。此外,应该使用该设备的最大批次(请参阅此处),最好能被 整除128。您可以将其与 NVidia 的 Tensor Cores 技术 (GPU) 进行比较,在这种技术中,批次(或层大小)可以被168float16精度和int8分别)整除以获得良好的利用率(尽管越多越好,并且取决于核心数,确切的显卡以及许多其他内容,请参阅此处的一些指南)。

另一方面,TPU 的支持仍然不是最好的,尽管有两个主要框架支持它(tensorflow官方的,而 PyTorch 带有torch_xla包)。

总的来说,GPU 是目前深度学习中一个很好的默认选择,卷积重架构的 TPU,尽管可能会让人有些头疼 tbh。此外(再次感谢@Daniel),TPU 更节能,因此在比较单浮点运算成本时应该更便宜。

  • @Josh 这些技术适用于机器内部和机器之间的并行性。然而,正如 Szymon 提到的,首先进行垂直扩展(机器内)比水平扩展(跨机器)更好,因为跨机器通信的成本很高。例如,拥有一个具有 4 个 GPU 的实例比拥有两个每个具有 2 个 GPU 的实例要好。至于为什么构建 TPU,它们在性能/瓦数和性能/价格方面优于 GPU。 (3认同)