TensorFlow中如何在多系统和网络拓扑中构建高性能模型

发布时间:2021-12-23 16:31:16 作者:柒染
来源:亿速云 阅读:136

本篇文章为大家展示了TensorFlow中如何在多系统和网络拓扑中构建高性能模型,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。

输入管道

性能指南阐述了如何诊断输入管道可能存在的问题及其***解决方法。在使用大量输入和每秒更高的采样处理中我们发现 tf.FIFOQueue 和  tf.train.queue_runner 无法使用当前多个 GPU 生成饱和,例如在使用 AlexNet 训练 ImageNet 时。这是因为使用了  Python 线程作为底层实现,而 Python 线程的开销太大了。

我们在脚本中采用的另一种方法是通过 Tensorflow 中的本机并行构建输入管道。我们的方法主要由如下 3 个阶段组成:

通过利用 data_flow_ops.StagingArea,每个阶段的主要部分与其他阶段并行执行。StagingArea  是一个像队列(queue)一样且类似于 tf.FIFOQueue 的运算符。不同之处在于 StagingArea 提供了更简单的功能且可在 CPU 和 GPU  中与其他阶段并行执行。将输入管道拆分为 3 个独立并行操作的阶段,并且这是可扩展的,充分利用大型的多核环境。本节的余下部分将详细介绍每个阶段以及  data_flow_ops.StagingArea 的使用细节。

并行 I/O 读取

data_flow_ops.RecordInput 用于磁盘的并行读取。给定一个代表 TFRecords 的输入文件列表,RecordInput  可使用后台线程连续读取记录。这些记录被放置在大型的内部池中,当这个池加载量达到其容量的一半时,会有相应的张量输出。这个操作有其内部线程,线程由占用最少的 CPU  资源的 I/O 时间主导,这就允许它可与模型的其余部分并行运行。

并行图像处理

从 RecordInput 读取图像后,它们作为张量被传递至图像处理管道。为了更方便解释图像处理管道,假设输入管道的目标是 8 个批量大小为 256(每个  GPU 32 个)GPU。256 个图像记录的读取和处理是独立并行的。从图中 256 个 RecordInput  读操作开始,每个读取操作后都有一个与之相匹配的图像预处理操作,这些操作是彼此独立和并行执行的。这些图像预处理操作包括诸如图像解码、失真和调整大小。

当图像通过预处理器后,它们被联接成 8 个大小为 32 的张量。为了达到这一目的,使用了 tf.parallel_stack,而不是 tf.concat  ,目的作为单一操作被实现,且在将它们联结在一起之前需要所有输入准备就绪。tf.parallel_stack  将未初始化的张量作为输出,并且在有张量输入时,每个输入的张量被写入输出张量的指定部分。

当所有的张量完成输入时,输出张量在图中传递。这有效隐藏了由于产生所有输入张量的长尾(long tail)而导致的内存延迟。

并行从 CPU 到 GPU 的数据传输

继续假设目标是批量大小为 256(每个 GPU 32 个)8 个 GPU,一旦输入图像被处理完并被 CPU 联接后,我们将得到 8 个批量大小为 32  的张量。Tensorflow 可以使一个设备的张量直接用在任何其他设备上。为使张量在任何设备中可用,Tensorflow  插入了隐式副本。在张量被实际使用之前,会在设备之间调度副本运行。一旦副本无法按时完成运行,需要这些张量的计算将会停止并且导致性能下降。

在此实现中,data_flow_ops.StagingArea 用于明确排定并行副本。最终的结果是当 GPU 上的计算开始时,所有张量已可用。

软件管道

由于所有的阶段都可以在不同的处理器下运行,在它们之间使用 data_flow_ops.StagingArea 可使其并行运行。StagingArea  是一个与 tf.FIFOQueue 相似且像队列(queue)一样的运算符,tf.FIFOQueue 提供更简单的功能可在 CPU 和 GPU  中被执行。

在模型开始运行所有的阶段之前,输入管道阶段将被预热,以将其间的分段缓存区置于一组数据之间。在每个运行阶段中,开始时从分段缓冲区中读取一组数据,并在***将该组数据推送。

例如有 A、B、C 三个阶段,这之间就有两个分段区域 S1 和 S2。在预热时,我们运行:

Warm up: Step 1: A0 Step 2: A1  B0  Actual execution: Step 3: A2  B1  C0 Step 4: A3  B2  C1 Step 5: A4  B3  C2

预热结束之后,S1 和 S2 各有一组数据。对于实际执行的每个步骤,会计算一组来自分段区域的数据,同时分段区域会添加一组新数据。

此方案的好处是:

构建高性能模型的***实践

以下收集的是一些额外的***实践,可以改善模型性能,增加模型灵活性。

使用 NHWC 和 NCHW 建模

CNN 使用的绝大多数 Tensorflow 操作都支持 NHWC 和 NCHW 数据格式。在 GPU 中,NCHW 更快;但是在 CPU 中,NHWC  只是偶尔更快。

构建一个支持日期格式的模型可增加其灵活性,能够在任何平台上良好运行。基准脚本是为了支持 NCHW 和 NHWC 而编写的。使用 GPU  训练模型时会经常用到 NCHW。NHWC 在 CPU 中有时速度更快。在 GPU 中可以使用 NCHW 对一个灵活的模型进行训练,在 CPU 中使用 NHWC  进行推理,并从训练中获得合适的权重参数。

使用融合的批处理归一化

Tensorflow 中默认的批处理归一化被实现为复合操作,这是很通用的做法,但是其性能不好。融合的批处理归一化是一种替代选择,其在 GPU  中能取得更好的性能。如下是用 tf.contrib.layers.batch_norm 实现融合批处理归一化的一个实例:

bn = tf.contrib.layers.batch_norm(           input_layer, fused=True, data_format='NCHW'           scope=scope)

变量分布和梯度聚合

训练期间,训练的变量值通过聚合的梯度和增量进行更新。在基准脚本中,展示了通过使用灵活和通用的 Tensorflow  原语,我们可以构建各种各样的高性能分布和聚合方案。

在基准脚本中包括 3 个变量分布和聚合的例子:

以下是有关每种方法的其他细节。

参数服务器变量

在 Tensorflow 模型中管理变量的最常见方式是参数服务器模式。

在分布式系统中,每个工作器(worker)进程运行相同的模型,参数服务器处理其自有的变量主副本。当一个工作器需要一个来自参数服务器的变量时,它可从其中直接引用。Tensorflow  在运行时会将隐式副本添加到图形中,这使得在需要它的计算设备上变量值可用。当在工作器上计算梯度时,这个梯度会被传输到拥有特定变量的参数服务器中,而相应的优化器被用于更新变量。

以下是一些提高吞吐量的技术:

为了协调工作器,常常采用异步更新模式,其中每个工作器更新变量的主副本,而不与其他工作器同步。在我们的模型中,我们展示了在工作器中引入同步机制是非常容易的,所以在下一步开始之前所有的工作器必须完成更新。

这个参数服务器方法同样可以应用在本地训练中,在这种情况下,它们不是在参数服务器之间传播变量的主副本,而是在 CPU 上或分布在可用的 GPU 上。

由于该设置的简单性,这种架构在社区中获得广泛的推广。

通过传递参数 variable_update=parameter_server,也可以在脚本中使用此模式。

TensorFlow中如何在多系统和网络拓扑中构建高性能模型

变量复制

在这种设计中,服务器中的每个 GPU 都有自己的变量副本。通过将完全聚合的梯度应用于变量的每个 GPU 副本,使得这些值在 GPU 之间保持同步。

因为变量和数据在训练的初始阶段就准备好了,所以训练的前向计算可以立即开始。聚合各个设备的梯度以得到一个完全聚合的梯度,并将该梯度应用到每个本地副本中。

服务器间的梯度聚合可通过不同的方法实现:

分布式训练中的变量复制

上述变量复制的方法可扩展到分布式训练中。一种类似的方法是:完全地聚合集群中的梯度,并将它们应用于每个本地副本。这种方法在未来版本的脚本中可能会出现,但是当前的脚本采用不同的方法。描述如下。

在这一模式中,除了变量的每一个 GPU 副本之外,主副本被存储在参数服务器之中。借助这一复制模式,可使用变量的本地副本立刻开始训练。

随着权重的梯度可用,它们会被送回至参数服务器,并所有的本地副本都会被更新:

尽管这些听起来与参数服务器的标准用法很相似,但是其性能在很多案例中表现更佳。这很大程度因为计算没有任何延迟,早期梯度的大部分复制延迟可被稍后的计算层隐藏。

通过传递参数 variable_update=distributed_replicated 可以在脚本中使用该模式。

TensorFlow中如何在多系统和网络拓扑中构建高性能模型

NCCL

为了在同一台主机的不同 GPU 上传播变量和聚合梯度,我们可以使用 Tensorflow 默认的隐式复制机制。

然而,我们也可以选择 NCCL(tf.contrib.nccl)。NCCL 是英伟达的一个库,可以跨不同的 GPU 实现数据的高效传输和聚合。它在每个  GPU 上分配一个协作内核,这个内核知道如何***地利用底层硬件拓扑结构,并使用单个 SM 的 GPU。

通过实验证明,尽管 NCCL 通常会加速数据的聚合,但并不一定会加速训练。我们的假设是:隐式副本基本是不耗时的,因为它们本在 GPU  上复制引擎,只要它的延迟可以被主计算本身隐藏起来,那么。虽然 NCCL 可以更快地传输数据,但是它需要一个 SM,并且给底层的 L2  缓存增加了更多的压力。我们的研究结果表明,在 8 个 GPU 的条件下,NCCL 表现出了更优异的性能;但是如果 GPU  更少的情况下,隐式副本通常会有更好的表现。

分段变量

我们进一步介绍一种分段变量模式,我们使用分段区域来进行变量读取和更新。与输入管道中的软件流水线类似,这可以隐藏数据拷贝的延迟。如果计算所花的时间比复制和聚合的时间更长,那么可以认为复制本身是不耗时的。

这种方法的缺点是所有的权重都来自之前的训练步骤,所以这是一个不同于 SGD 的算法,但是通过调整学习率和其他超参数,还是有可能提高收敛性。

脚本的执行

这一节将列出执行主脚本的核心命令行参数和一些基本示例(tf_cnn_benchmarks.py)

注意:tf_cnn_benchmarks.py 使用的配置文件 force_gpu_compatible 是在 Tensorflow 1.1  版本之后引入的,直到 1.2 版本发布才建议从源头建立。

主要的命令行参数

单个实例

# VGG16 training ImageNet with 8 GPUs using arguments that optimize for # Google Compute Engine. python tf_cnn_benchmarks.py --local_parameter_device=cpu --num_gpus=8 \ --batch_size=32 --model=vgg16 --data_dir=/home/ubuntu/imagenet/train \ --variable_update=parameter_server --nodistortions  # VGG16 training synthetic ImageNet data with 8 GPUs using arguments that # optimize for the NVIDIA DGX-1. python tf_cnn_benchmarks.py --local_parameter_device=gpu --num_gpus=8 \ --batch_size=64 --model=vgg16 --variable_update=replicated --use_nccl=True  # VGG16 training ImageNet data with 8 GPUs using arguments that optimize for # Amazon EC2. python tf_cnn_benchmarks.py --local_parameter_device=gpu --num_gpus=8 \ --batch_size=64 --model=vgg16 --variable_update=parameter_server  # ResNet-50 training ImageNet data with 8 GPUs using arguments that optimize for # Amazon EC2. python tf_cnn_benchmarks.py --local_parameter_device=gpu --num_gpus=8 \ --batch_size=64 --model=resnet50 --variable_update=replicated --use_nccl=False

分布式命令行参数

1)ps_hosts:在<host>:port 的格式中(比如 10.0.0.2:50000),逗号分隔的主机列表用做参数服务器。

2)worker_hosts:(比如 10.0.0.2:50001),逗号分隔的主机列表用作工作器,在<host>:port  的格式中。

3)task_index:正在启动的 ps_host 或 worker_hosts 列表中的主机索引。

4)job_name:工作的类别,例如 ps 或者 worker。

分布式实例

如下是在两个主机(host_0 (10.0.0.1) 和 host_1 (10.0.0.2))上训练 ResNet-50  的实例,这个例子使用的是合成数据,如果要使用真实数据请传递 data_dir 参数。# Run the following commands on host_0 (10.0.0.1): 

python tf_cnn_benchmarks.py --local_parameter_device=gpu --num_gpus=8 \ --batch_size=64 --model=resnet50 --variable_update=distributed_replicated \ --job_name=worker --ps_hosts=10.0.0.1:50000,10.0.0.2:50000 \ --worker_hosts=10.0.0.1:50001,10.0.0.2:50001 --task_index=0  python tf_cnn_benchmarks.py --local_parameter_device=gpu --num_gpus=8 \ --batch_size=64 --model=resnet50 --variable_update=distributed_replicated \ --job_name=ps --ps_hosts=10.0.0.1:50000,10.0.0.2:50000 \ --worker_hosts=10.0.0.1:50001,10.0.0.2:50001 --task_index=0  # Run the following commands on host_1 (10.0.0.2): python tf_cnn_benchmarks.py --local_parameter_device=gpu --num_gpus=8 \ --batch_size=64 --model=resnet50 --variable_update=distributed_replicated \ --job_name=worker --ps_hosts=10.0.0.1:50000,10.0.0.2:50000 \ --worker_hosts=10.0.0.1:50001,10.0.0.2:50001 --task_index=1  python tf_cnn_benchmarks.py --local_parameter_device=gpu --num_gpus=8 \ --batch_size=64 --model=resnet50 --variable_update=distributed_replicated \ --job_name=ps --ps_hosts=10.0.0.1:50000,10.0.0.2:50000 \ --worker_hosts=10.0.0.1:50001,10.0.0.2:50001 --task_index=1

上述内容就是TensorFlow中如何在多系统和网络拓扑中构建高性能模型,你们学到知识或技能了吗?如果还想学到更多技能或者丰富自己的知识储备,欢迎关注亿速云行业资讯频道。

推荐阅读:
  1. tensorflow 20如何搭网络,导出模型和运行模型
  2. tensorflow中ckpt模型和pb模型如何获取节点名称

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

tensorflow

上一篇:如何使用TensorFlow Hub进行神经风格迁移

下一篇:mysql中出现1053错误怎么办

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》