深度学习的并行化策略

时间:2020-05-30 16:50:45

标签: tensorflow deep-learning pytorch distributed-computing mxnet

对于神经网络培训服务,什么并行化策略和形式是可行的?

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

我还在寻找证据,证明它们也可以用于例如TensorFlow,PyTorch或MXNet。

培训

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

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

对于哪种类型的问题或神经网络,每种策略何时才更好?现代图书馆支持哪些模式?并且可以将所有四种(2x2)策略结合起来吗?

最重要的是,我读到以下内容:

  • 异步培训
  • 同步培训

但我不知道确切指的是什么,例如是不同数据批次上的梯度的计算,还是不同子图上的梯度的计算?也许它完全指的是其他东西?

服务

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

2 个答案:

答案 0 :(得分:6)

培训

通常,有两种并行化模型训练的策略:数据并行性和模型并行性。

1。数据并行性

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

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

另一种方法是不合并梯度-每个梯度将被用来独立地更新模型参数。因此,与之前的技术相比,每个小批量步骤将有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])

然后我们可以类似地训练它。有关更多详细信息,请参阅the official tutorial

要在PyTorch中执行异步SGD,我们需要implement it more manually,因为没有类似于DistributedDataParallel的包装器。

TensorFlow / Keras中的数据并行性

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

import tensorflow as tf

strategy = tf.distribute.MirroredStrategy()
with strategy.scope():
    model = Model(...)
    model.compile(...)

然后我们可以像往常一样训练它。有关更多详细信息,请参阅Keras websiteTensorFlow website上的官方指南。

对于异步SGD,我们可以类似地使用tf.distribute.experimental.ParameterServerStrategy

2。模型并行化

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

TensorFlow和PyTorch中的模型并行性

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

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

import torch.nn as nn

linear1 = nn.Linear(16, 8).to('cuda:0')
linear2 = nn.Linear(8, 4).to('cuda:1')

在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)

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

3。混合:数据和模型并行化

回想一下,数据并行性仅拆分训练数据,而模型并行性仅拆分模型结构。如果我们有一个太大的模型,即使使用任何一种并行性策略后,它仍然不能容纳在内存中,我们总是可以同时做这两个。

在实践中,大多数人更喜欢数据并行性而不是模型并行性,因为前者比后者更脱离模型架构(实际上是独立的)。也就是说,通过使用数据并行性,他们可以根据需要更改模型体系结构,而不必担心应该并行化模型的哪一部分。

模型推断/服务

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

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

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

实施调度程序来进行请求批处理不是一件容易的事,因此,最好不要使用手动支持的TensorFlow ServingPyTorch Serve


要了解有关并行学习和分布式学习的更多信息,请阅读this review paper

答案 1 :(得分:3)

由于问题涉及面很广,因此我将尝试阐明一些与本主题中显示的主题不同的主题,并涉及不同的主题 @Daniel's深入解答。

培训

数据并行化与模型并行化

@Daniel所述,数据并行性使用得更频繁,更容易正确实现。模型并行性的主要警告是需要等待神经网络的一部分以及它们之间的同步。

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

另一方面,Inception networks等模型更适合模型并行化,请参见下图:

inception block

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

问题

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

这不是那么容易。通常根据损耗值来计算梯度,并且您需要了解较深层的梯度才能计算出较浅层的梯度。如上所述,如果您具有独立的路径,则更容易并且可能会有所帮助,但是在单个设备上则更容易。

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

否,实际上是减少了多种设备的使用量。您可以在PyTorch tutorial中看到其中的一些内容。梯度累积是指您进行N次正向遍历(在单个或多个设备上)并向后传播(梯度保留在图形中,并且在每次遍历中添加值)时,优化程序仅一步步进行更改神经网络的权重(并清除梯度)。在这种情况下,通常将损失除以没有优化器的步骤数。通常,当您无法使用大批量时,此方法可用于更可靠的梯度估计。

设备间的减少如下所示:

reduction

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

每种策略何时适合哪种类型的问题或神经 网络?

如上所述,如果您有足够的数据并且样本量很大(几乎可以一次完成多达8k个样本,而无需进行非常的艰苦工作,则数据并行几乎总是可以的) )。

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

tensorflowpytorch都支持,大多数现代且经过维护的库都以一种或另一种方式实现了这些功能

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

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

同步与异步

异步

@Daniel简要描述,但是值得一提的是更新并不是完全分开的。这毫无意义,因为我们实际上是根据批次来训练N不同的模型。

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

这种方法有一个问题:不能保证当一个工作人员计算正向传递时另一个工作人员更新了参数,因此,更新是针对旧参数集计算的,这称为陈旧的渐变。因此,收敛可能会受到损害。

另一种方法是为每个工作进程计算N个步骤和更新,并在之后进行同步,尽管它并不常用。

这部分基于出色的blogpost,如果您有兴趣,肯定应该阅读它(有关陈旧性和一些解决方案的更多信息)。

同步

以前大多描述过,有不同的方法,但是PyTorch收集网络输出并在它们上反向传播(torch.nn.parallel.DistributedDataParallel)[https://pytorch.org/docs/stable/nn.html#torch.nn.parallel ..DistributedDataParallel]。顺便说一句。您应该单独使用它(不能使用torch.nn.DataParallel,因为它可以克服Python的GIL问题。

外卖

  • 为了加快速度,几乎总是使用数据并行化,因为您“仅”必须在每个设备上复制神经网络(通过网络或在单个机器上),在正向传递过程中在每个设备上批量运行一部分,并进行连接在一个设备上将它们分成一批(同步),并在该设备上反向传播。
  • @Daniel已经引入了多种数据并行化方法
  • 当模型太大而无法容纳在单台机器上时(OpenAI's GPT-3是极端情况),或者当体系结构适合于此任务时,就会进行模型并行化,但是AFAIK很少遇到这种情况。
  • li>
  • 模型具有的并行路径(同步点)越长,则越适合模型并行化
  • 重要的是要在相似的时间以相似的负载启动工作器,以免以同步方式处理同步过程,或者不以异步方式获得陈旧的梯度(尽管在后一种情况下还不够)。

服务

小型号

就像您追求大型模型一样,我不会深入探讨较小模型的选项,只是一小段内容。

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

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

大型模型

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

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

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

  • KubeFlow-基于Kubernetes的多个框架(即自动缩放,多节点),培训,服务以及其他内容,与下面的MLFlow等其他事物连接
  • AWS SageMaker-受Amazon支持的Python API培训和服务
  • MLFlow-多个框架,用于实验处理和服务
  • BentoML-多个框架,培训和服务

对于PyTorch,您可以阅读更多here,而tensorflow通过Tensorflow EXtended (TFX)开箱即用地提供了许多服务功能。

OP的评论中的问题

在机器内部,有没有更好的并行形式? 跨机器

并行性的最佳选择可能是在一台巨型计算机中,以最大程度地减少设备之间的传输。

此外,还有不同的后端(至少在PyTorch中),人们可以从中选择({{1},mpigloo),并且并非所有后端都支持直接发送,接收,还原等设备之间的数据(有些可能支持CPU到CPU,另一些可能支持GPU到GPU)。如果设备之间没有直接链接,则必须先将这些设备复制到另一台设备,然后再复制到目标设备(例如,另一台计算机上的GPU->主机上的CPU->主机上的GPU)。参见pytorch info

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

通常,如果您有大量数据(例如具有nccl个图像的ImageNet)或大样本(例如图像1.000.000),请进行数据并行化。如果可能,请在单台机器内,以最大程度地减少机器之间的转移。仅在无法解决问题时才分发模型(例如,它不适用于GPU)。否则不要这样做(训练MNIST时几乎没有一点要并行化,因为整个数据集很容易放入RAM中,并且读取速度最快)。

为什么要构建自定义的ML专用硬件(例如TPU)?

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

由于GPU是在考虑图形的基础上创建的(即代数转换),因此它可能需要承担CPU的某些职责并且可以进行专门化(与CPU相比,有更多的内核,但更简单的内核,例如,参见V100)。

现在,TPU专为张量计算量身定制(主要是深度学习),并且起源于Google(与GPU相比仍是WIP)。这些适用于某些类型的模型(主要是卷积神经网络),在这种情况下可以提高速度。另外,应该在此设备上使用最大的批次(请参见here),最好由2000x2000整除。您可以将其与NVidia的Tensor Cores技术(GPU)进行比较,后者可以用12816(分别为8精度和float16整除的批处理(或图层大小)很好),以提高利用率(尽管更好,而且取决于内核数量,确切的图形卡和许多其他内容,请参阅一些准则here)。

另一方面,尽管两个主要框架都支持TPU,但TPU仍不是最好的支持(int8,而PyTorch带有torch_xla软件包)。

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