集群式的tensorflow是从单机多卡演化出来的,当硬件限制了单机上的GPU数量以及内存容量。此时我们需要进行集群式的训练,换句话说就是分布式训练。在极度无聊于深度学习设计网络和充当民工训练的过程中,我深感于人生正在被极度的浪费。人生还是要苦中作乐一些,于是我发现了Tensorflow的分布式模块,想要设计出一套可以易于填充的集群处理框架。由于我个人的水平问题,并没有在Python中发现类似于Spring中IOC和AOP的功能,所以只能用我自己写的一些丑陋的代码去实现类似的功能。

基础原理

  1. 分布式的基础原理:反向传播(通过链式法则实现): Imgur
    • W5为第五层layer中存储的参数,我们通过W5进行正向传播,也就是我们需要优化的参数。
    • n为先学习率。
    • W5+为更新后的参数,用于下一次的正向传播。
    • ∂E/∂w(gradient)就是当前的loss对于当前层的参数的的导数,就是我们所说的梯度。
  2. 通过研究链式法则,当前层的参数是已知的,需要求出更新后的参数, 其中learning rate是我们设置的,唯一需要使用的参数就是梯度。所以把握了梯度就是把握了网络更新的钥匙。
  3. 当我们向网络传入不同的训练集时,对于不同的数据,我们得到的loss是不同的,我们要维持网络中的所有参数都是相同的,这就要保证对于每一层的更新梯度是相同的,所以此处我们要通过求均值的方法获得平均的梯度。这就是多节点的核心思想。

单机多GPU训练

Imgur

  1. 所有的网络参数都存在CPU上,CPU作为Server存在,可以发现GPU模型并从GPU模型中获取梯度信息,通过梯度信息更新CPU中的模型。
  2. 每个GPU都是一个Tower模型,在GPU中会进行正向传播并算出loss并求出gradiants,Tower模型会返回Gradiant给统一更新的CPU。
  3. 上述方法保证了每个GPU中使用的模式是一致的,这是同步更新,由于是单机操作,很小的开销被用于数据的交互。 具体的代码实现可以参照GMAN深度去雾项目

多机多GPU训练(分布式)

分布式训练的思路和单机多GPU类似,但是我们需要关注两个问题:

  1. 决定运算在哪个设备上运行。
  2. 管理设备之间的数据传递。

参数服务器(Parameter server)

模型的参数越来越多,多到模型参数的更新,一台机器的性能都不够的时候,很自然的我们就会想到把参数分开放到不同的机器去存储和更新。因为碰到上面提到的那些问题, 所有参数服务器就被单独拧出来, 于是就有了参数服务器的概念。

服务间通信 gRPC (google remote procedure call)

多个业务之间的通信模式,google自己开发的RPC系统,实现微服务之间的通信。

分布式中的角色

  1. Task:每台机器上的一个进程,可以理解为实际的业务。
  2. Job: Task的集合,或者理解为职能的抽象行为。具体分成两种:
    • 参数服务器:用于平均梯度,更新参数。
    • 计算服务器: 深度学习,正向传播,计算loss,返回梯度。
  3. Cluster: 集群,这是一种抽象的概念。抽象在于我们认为抽象的是Job的集合。

分布式模式

分布式模式主要分成两种,一种是In-graph模式,另一种是Between-graph模式。

In-graph模式:将模型的计算图的不同部分放在不同的机器上执行

这很像J2EE中Micro-service, 每个结点的执行业务不同,这就意味着强依赖于网络间的数据传播,要将当前的结果分发到下一个结点。而大量的数据在这种网络模型下是会导致阻塞的。

Between-graph模式:数据并行,每台机器使用完全相同的计算图

这就像我在DPDK中使用的run to complete模型。我们让每个结点都运行自己完整的业务,只有在需要整合的时候进行整合。对于深度学习而言,就是直到获取了梯度才会进行通讯。而图间模式分成两种:同步更新和异步更新。 在同步更新的时候, 每次梯度更新,要等所有分发出去的数据计算完成后,返回回来结果之后,把梯度累加算了均值之后,再更新参数。 这样的好处是loss的下降比较稳定, 但是这个的坏处也很明显, 处理的速度取决于最慢的那个分片计算的时间。 Imgur 在异步更新的时候, 所有的计算节点,各自算自己的, 更新参数也是自己更新自己计算的结果, 这样的优点就是计算速度快, 计算资源能得到充分利用,但是缺点是loss的下降不稳定, 抖动大。 Imgur

引用

  1. TensorFlow分布式全套(原理,部署,实例)
  2. 深度学习(五十五)tensorflow分布式训练