页面树结构

2017-11-09 ApacheCN 开源组织,第二期邀请成员活动,一起走的更远 : http://www.apachecn.org/member/209.html


MachineLearning 优酷地址 : http://i.youku.com/apachecn

转至元数据结尾
转至元数据起始

本文档和随附的 脚本 详细介绍了如何构建针对各种系统类型和网络拓扑的高度可扩展的模型。本文档中的技术使用一些低级TensorFlow Python基元。将来,许多技术将被并入高级API。

输入管道

“ 性能指南”介绍了如何识别可能的输入管道问题和最佳实践。我们发现,使用tf.FIFOQueue 和tf.train.queue_runner使用具有每秒更高的样品,如用训练ImageNet大输入和处理的情况下不能饱和多个电流代GPU AlexNet。这是由于使用Python线程作为其底层实现。Python线程的开销太大了。

我们在脚本中实现的另一种方法 是使用TensorFlow中的本机并行构建输入管道。我们的实施由3个阶段组成:

  • I / O读取:从磁盘中选择和读取映像文件。
  • 图像处理:将图像记录解码为图像,预处理和组织成小批量。
  • CPU到GPU数据传输:将图像从CPU传输到GPU。

每个阶段的主导部分与其他阶段的并行执行data_flow_ops.StagingAreaStagingArea是类似于队列的操作符tf.FIFOQueue。不同之处在于StagingArea提供更简单的功能,并且可以在CPU和GPU上与其他阶段并行执行。将输入管道分解成并行运行的3个阶段是可扩展的,并充分利用大型多核环境。本节的其余部分将详细介绍使用细节data_flow_ops.StagingArea

并行I / O读取

data_flow_ops.RecordInput用于并行读取磁盘。给定表示TFRecords的输入文件的列表,RecordInput使用后台线程连续读取记录。这些记录被放置在自己的大型内部池中,并且当它们已经加载了其容量的至少一半时,它产生输出张量。

这个操作有自己的内部线程,主要由I / O时间占用最少的CPU,这允许它与模型的其余部分平行运行。

并行化图像处理

读取图像后,RecordInput它们作为张量传递到图像处理流水线。为了使图像处理流水线更容易解释,假设输入流水线的目标是8个GPU,批量大小为256(每GPU 32个)。

并行读取和处理256个记录。这从图中的256个独立的RecordInput读操作开始。每个读操作后跟一组相同的用于图像预处理的操作,它们被认为是独立的并行执行。图像预处理操作包括诸如图像解码,失真和调整大小的操作。

一旦图像通过预处理,它们就被连接成8个批量32张张量。而不是tf.concat用于这个目的,它被实现为一个单一的操作,等待所有的输入准备好,然后将它们连接在一起tf.parallel_stacktf.parallel_stack 分配未初始化的张量作为输出,并且一旦输入可用,则每个输入张量被写入输出张量的指定部分。

当所有输入张量完成时,输出张量在图中传递。这有效地隐藏了产生所有输入张量的长尾的所有内存延迟。

并行化CPU到GPU数据传输

继续假设目标是8个GPU,批量大小为256(每GPU 32个)。一旦输入图像被CPU处理并连接在一起,我们就有8个张量,每个标签的批量大小为32。

TensorFlow可以使一个设备的张量直接在任何其他设备上使用。TensorFlow插入隐式副本,使张量在使用它们的任何设备上可用。在实际使用张量之前,运行时会在设备之间调度副本以运行。然而,如果副本无法及时完成,则需要这些张量的计算将停止并导致性能下降。

在此实现中,data_flow_ops.StagingArea用于并行显式调度副本。最终的结果是当GPU开始计算时,所有的张量都已经可用了。

软件流水线

所有的阶段都能够被不同的处理器驱动, data_flow_ops.StagingArea所以它们之间使用它们是并行运行的。StagingArea是类似于队列的操作器,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各有一组数据。对于实际执行的每个步骤,从每个暂存区域消耗一组数据,并将一组数据添加到每个。

使用此方案的好处:

  • 所有阶段都是非阻塞的,因为在热身之后,分段区域总是具有一组数据。
  • 每个阶段都可以并行运行,因为它们都可以立即启动。
  • 分级缓冲区具有固定的内存开销。他们将至多有一组额外的数据。
  • 只需单次session.run()调用即可运行步骤的所有阶段,从而使分析和调试变得更加容易。

 

建立高性能模型的最佳实践

以下收集的是一些额外的最佳实践,可以提高性能并提高模型的灵活性。

用NHWC和NCHW建立模型

CNN使用的大多数TensorFlow操作都支持NHWC和NCHW数据格式。在GPU上,NCHW更快。但是在CPU上,NHWC有时更快。

建立一个支持日期格式的模型可以保持模型的灵活性,无论平台如何,都能够最佳地运行。CNN使用的大多数TensorFlow操作都支持NHWC和NCHW数据格式。基准脚本是为了支持NCHW和NHWC而编写的。在使用GPU进行培训时,应始终使用NCHW。NHWC有时在CPU上更快。可以使用NCHW在GPU上使用NHWC进行推理并从训练获得的权重对GPU进行灵活的模型训练。

使用融合批次标准化

TensorFlow中的默认批处理规范化被实现为复合操作。这是非常一般的,但往往导致次优的表现。另一种方法是使用融合批量标准化,这在GPU上经常具有更好的性能。以下是使用tf.contrib.layers.batch_norm 实现熔接批处理的示例。

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

可变分布和梯度聚合

在训练期间,训练变量值使用聚合渐变和三角形更新。在基准脚本中,我们展示了使用灵活和通用的TensorFlow原语,可以构建各种各样的高性能分布和聚合方案。

脚本中包含三个可变分布和聚合的示例:

  • parameter_server其中训练模型的每个副本从参数服务器读取变量并独立地更新变量。当每个模型需要变量时,它们将通过TensorFlow运行时添加的标准隐式副本进行复制。示例 脚本 说明了使用此方法进行本地训练,分布式同步训练和分布式异步训练。
  • replicated在每个GPU上放置每个训练变量的相同副本。随着可变数据立即可用,正向和反向计算可以立即开始。所有GPU中都会累积渐变,并将累计总数应用于每个GPU的变量副本,以使其保持同步。
  • distributed_replicated将每个GPU上的训练参数的副本与参数服务器上的主副本一起放置在一起。随着可变数据立即可用,正向和反向计算可以立即开始。梯度在每个服务器上的所有GPU中累积,然后将每个服务器的聚合渐变应用于主副本。所有工作人员都执行此操作后,每个工作人员将从主副本更新其变量的副本。

以下是有关每种方法的其他详细信息。

参数服务器变量

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

在分布式系统中,每个工作进程运行相同的模型,参数服务器进程拥有变量的主副本。当一个工作者需要一个参数服务器的变量时,它直接引用它。TensorFlow运行时会将隐式副本添加到图形中,使变量值在需要它的计算设备上可用。当在工作者上计算梯度时,将其发送到拥有特定变量的参数服务器,并使用相应的优化程序更新变量。

有一些提高吞吐量的技术:

  • 这些变量根据其大小在参数服务器之间进行扩展,用于负载平衡。
  • 当每个工作人员有多个GPU时,每个GPU都会累积梯度,并将一个聚合梯度发送到参数服务器。这减少了网络带宽和参数服务器完成的工作量。

对于工作人员之间的协调,一个非常常见的模式是异步更新,每个工作人员更新变量的主副本,而不与其他工作人员同步。在我们的模型中,我们证明在工作人员之间引入同步是相当容易的,所以在下一步开始之前,所有工作人员的更新将一步完成。

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

由于这种设置的简单性,这种架构在社区内获得了很多的普及。

此模式可以在脚本中通过传递 --variable_update=parameter_server

复制变量

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

变量和数据在培训开始时可用,所以训练的前进通过即可开始。梯度在设备之间进行聚合,然后将完全聚合的渐变应用于每个本地副本。

服务器上的渐变聚合可以通过不同的方式完成:

  • 使用标准TensorFlow操作在单个设备(CPU或GPU)上累积总数,然后将其复制回所有GPU。
  • 使用NVIDIA®NCCL,如下面NCCL部分所述。

此模式可以在脚本中通过传递--variable_update=replicated

分布式训练中的复制变量

变量的复制方法可以扩展到分布式训练。一种像复制模式一样的方法:将集群中的渐变聚合并将其应用于变量的每个本地副本。这可能会在此脚本的未来版本中显示; 脚本确实呈现出不同的变化,这里描述。

在这种模式下,除了每个GPU的变量副本之外,主副本也存储在参数服务器上。与复制模式一样,训练可以立即使用变量的本地副本开始。

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

  1. 来自同一工作人员的GPU的所有渐变都聚合在一起。
  2. 来自每个工作者的聚合渐变被发送到拥有变量的参数服务器,其中使用指定的优化器来更新变量的主副本。
  3. 每个工作人员从主机更新变量的本地副本。在示例模型中,这是通过等待所有工作人员完成更新变量的交叉副本屏障完成的,并且只有在所有副本发布屏障之后才获取新变量。一旦复制完成所有变量,这标志着培训步骤的结束,一个新的步骤可以开始。

虽然这听起来类似于参数服务器的标准使用,但在许多情况下,性能往往更好。这主要是由于计算可以没有任何延迟而发生的事实,早期梯度的大部分复制延迟可以被稍后的计算层隐藏。

此模式可以在脚本中通过传递 --variable_update=distributed_replicated

distributed_replicated模式

NCCL

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

但是,我们可以使用可选的NCCL(tf.contrib.nccl)支持。NCCL是一个NVIDIA®库,可以跨不同的GPU高效地广播和汇总数据。它在每个GPU上安排一个合作的内核,知道如何最好地利用底层硬件拓扑; 该内核使用单个SM的GPU。

在我们的实验中,我们证明尽管NCCL本身通常导致更快的数据聚合,但并不一定会导致更快的训练。我们的假设是,隐式副本基本上是免费的,因为它们在GPU上复制引擎,只要它的延迟可以被主计算本身隐藏起来。尽管NCCL可以更快地传输数据,但是它需要一个SM,并为底层的L2缓存增加了更多的压力。我们的研究结果表明,对于8-GPU,NCCL通常导致更好的性能。然而,对于较少的GPU,隐式副本通常性能更好。

分期变量

我们进一步介绍了一种分段变量模式,我们使用分段区域进行变量读取及其更新。类似于输入管道的软件流水线,这可以隐藏数据复制延迟。如果计算时间比复制和聚合时间长,复制本身基本上是空闲的。

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

 

执行脚本

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

注意:使用在TensorFlow 1.1之后引入的配置。直到TensorFlow 1.2从源头发布建议。tf_cnn_benchmarks.py force_gpu_compatible 

基本命令行参数

  • model:型号使用,例如resnet50inception3vgg16,和 alexnet
  • num_gpus:要使用的GPU数量。
  • data_dir:要处理的数据路径。如果未设置,则使用合成数据。要使用Imagenet数据,请使用这些指令(https://github.com/tensorflow/tensorflow/blob/master/tensorflow_models/inception#getting-started)作为起点。
  • batch_size:每个GPU的批量大小
  • variable_update:用于管理变量的方法:parameter_server ,replicateddistributed_replicatedindependent
  • local_parameter_device:用作参数服务器的设备:cpu或 gpu

单例实例

# 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

分布式命令行参数

  • ps_hosts:逗号分隔的主机列表中的格式参数服务器以使用<host>:port,例如10.0.0.2:50000
  • worker_hosts:以逗号分隔的主机名称作为工作人员使用<host>:port,例如10.0.0.2:50001
  • task_index:主机列表中ps_hosts或 worker_hosts正在启动的索引。
  • job_name:作业类型,例如psworker

分布式示例

以下是2台主机上的ResNet-50培训示例:host_0(10.0.0.1)和host_1(10.0.0.2)。该示例使用合成数据。使用真实数据传递 --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 
  • 无标签