教程 | TensorFlow 官方解读:如何在多系统和网络拓扑中构建高性能模型(2)
2017-05-07 编辑:
scope=scope)
变量分布和梯度聚合
训练期间,训练的变量值通过聚合的梯度和增量进行更新。在基准脚本中,展示了通过使用灵活和通用的 Tensorflow 原语,我们可以构建各种各样的高性能分布和聚合方案。
在基准脚本中包括 3 个变量分布和聚合的例子:
参数服务器,训练模型的每个副本都从参数服务器中读取变量并独立更新变量。当每个模型需要变量时,它们将被复制到由 Tensorflow 运行时添加的标准隐式副本中。示例脚本介绍了使用此方法如何进行本地训练、分布式同步训练和分布式异步训练。
拷贝,在每个 GPU 上放置每个训练变量相同的副本,在变量数据立即可用时,正向计算和反向计算立即开始。所有 GPU 中的梯度都会被累加,累加的总和应用于每个 GPU 变量副本,以使其保持同步。
分布式复制,将每个 GPU 中的训练参数副本与参数服务器上的主副本放置在一起,在变量数据可用时,正向计算和反向计算立即开始。一台服务器上每个 GPU 的梯度会被累加,然后每个服务器中聚合的梯度会被应用到主副本中。当所有的模块都执行此操作后,每个模块都将从主副本中更新变量副本。
以下是有关每种方法的其他细节。
参数服务器变量
在 Tensorflow 模型中管理变量的最常见方式是参数服务器模式。
在分布式系统中,每个工作器(worker)进程运行相同的模型,参数服务器处理其自有的变量主副本。当一个工作器需要一个来自参数服务器的变量时,它可从其中直接引用。Tensorflow 在运行时会将隐式副本添加到图形中,这使得在需要它的计算设备上变量值可用。当在工作器上计算梯度时,这个梯度会被传输到拥有特定变量的参数服务器中,而相应的优化器被用于更新变量。
以下是一些提高吞吐量的技术:
为了使负载平衡,这些变量根据其大小在参数服务器之间传输。
当每个工作器有多个 GPU 时,累加每个 GPU 的梯度,并把这个单一的聚合梯度发送到参数服务器。这将降低网络带宽,减少参数服务器的工作量。
为了协调工作器,常常采用异步更新模式,其中每个工作器更新变量的主副本,而不与其他工作器同步。在我们的模型中,我们展示了在工作器中引入同步机制是非常容易的,所以在下一步开始之前所有的工作器必须完成更新。
这个参数服务器方法同样可以应用在本地训练中,在这种情况下,它们不是在参数服务器之间传播变量的主副本,而是在 CPU 上或分布在可用的 GPU 上。
由于该设置的简单性,这种架构在社区中获得广泛的推广。
通过传递参数 variable_update=parameter_server,也可以在脚本中使用此模式。
带有 3 个变量的参数服务器模式中,变量读取和更新的单个工作器。
变量复制
在这种设计中,服务器中的每个 GPU 都有自己的变量副本。通过将完全聚合的梯度应用于变量的每个 GPU 副本,使得这些值在 GPU 之间保持同步。
因为变量和数据在训练的初始阶段就准备好了,所以训练的前向计算可以立即开始。聚合各个设备的梯度以得到一个完全聚合的梯度,并将该梯度应用到每个本地副本中。
服务器间的梯度聚合可通过不同的方法实现:
使用 Tensorflow 标准操作在单个设备上(CPU 或 GPU)累加整和,然后将其拷贝回所有的 GPU。
使用英伟达 NCCL,这个将在下面的 NCCL 章节阐述。
分布式训练中的变量复制
上述变量复制的方法可扩展到分布式训练中。一种类似的方法是:完全地聚合集群中的梯度,并将它们应用于每个本地副本。这种方法在未来版本的脚本中可能会出现,但是当前的脚本采用不同的方法。描述如下。
在这一模式中,除了变量的每一个 GPU 副本之外,主副本被存储在参数服务器之中。借助这一复制模式,可使用变量的本地副本立刻开始训练。
随着权重的梯度可用,它们会被送回至参数服务器,并所有的本地副本都会被更新:
同一个工作器中把 GPU 所有的梯度聚合在一起。
将来自各个工作器的聚合梯度发送至自带变量的参数服务器中,其中使用特殊的优化器来更新变量的主副本。
每个工作器从主副本中更新变量的本地副本。在示例模型中,这是在一个拥有交叉副本的负载中在等待所有的模块完成变量更新后进行的,并且只有在负载被所有副本释放以后才能获取新的变量。一旦所有的变量完成复制,这就标志着一个训练步骤的完成,和下一个训练步骤的开始。
尽管这些听起来与参数服务器的标准用法很相似,但是其性能在很多案例中表现更佳。这很大程度因为计算没有任何延迟,早期梯度的大部分复制延迟可被稍后的计算层隐藏。
通过传递参数 variable_update=distributed_replicated 可以在脚本中使用该模式。