单卡
尽管机器上多个CPU,但是对于TF来说,所有的CPU都是/cpu:0
多个GPU时,设备名称为/gpu:n
,n从0开始
查看运行每一个操作的设备
CPU上
1 | import tensorflow as tf |
输出
1 | Device mapping: no known devices. |
GPU版本
1 | import tensorflow as tf |
输出
1 | Device mapping: |
一些操作无法被放到GPU上,但是可以通过配置来智能的将不能放到GPU上的放到CPU上
1 | sess = tf.Session(config=tf.ConfigProto(allow_soft_placement=True, log_device_placement=True)) |
并行模式
同步和异步,并行环节为计算和参数更新。
深度学习模型训练流程图
异步模式深度学习模型训练流程图
可以简单的认为异步模式就是单机模式复制了多份,每一份使用不同的训练数据进行训练。不同设备之间是完全独立的。
存在的问题:多个设备对参数进行更新,可能会更新过头。如下图,黑球为初始状态,2个设备读取到此时的参数,均使黑球向左移动,而由于不同步问题,设备1先使得黑球移动到灰球,然后设备2再让灰球移动了白球位置。使得无法达到最优点。
同步模式深度学习模型训练流程图
所有设备会同时读取参数的取值,然后等待所有的设备的传播算法计算完成后,统一更新参数(取平均值)。
缺点:受所有设备中最慢的设备制约。
一机多卡
因为一机上的GPU卡性能相似,所以采用同步方式。
关于数据存储,因为需要为不同的GPU提供不同的训练数据,所以通过placeholder的方式就需要手动准备多份数据。为了方便训练数据的获取过程,可以采用输入队列的方式从TFRecord中读取数据,于是在这里提供的数据文件路径为将MNIST训练数据转化为TFRecords格式后的路径,如何将MNIST转化为TFRecord见TensorFlow TFRecord格式
关于损失函数,对于给定的训练数据、正则化损失计算规则和命名空间,计算在这个命名空间下的总损失。之所以需要给定命名空间是因为不同的GPU上计算得出的正则化损失都会加入名为loss的集合,如果不通过命名空间就会将不同的GPU上的正则化损失都加进来。
1 | from datetime import datetime |
分布式Tensorflow
原理
最简单的本地版单机集群
1 | import tensorflow as tf |
输出如下:
1 | I] Initialize GrpcChannelCache for job local -> {0 -> localhost:57480} |
上述代码中,首先通过tf.train.Server.create_local_server
函数在本地建立了一个只有一台机器的TF集群。然后在该集群上生成了一个会话,然后通过会话将运算运行在TF集群上。
TF集群通过一系列的任务(tasks)来执行TF计算图中的运算。
TF集群中的任务被聚合成工作(jobs),每个工作可以包含一个或者多个任务。比如在训练深度学习模型时,一台运行反向传播的机器是一个任务,而所有运行反向传播机器的集合是一种工作。
本地运行两个任务的TF集群代码如下:
- 生成一个有两个任务的集群,一个任务跑在本地2222端口,一个跑在2223端口
- 通过集群配置生成Server,并通过
job_name
和task_index
指定当前所启动的任务 - 通过server.target生成会话来使用TensorFlow集群中的资源。
代码1
1 | import tensorflow as tf |
代码2
1 | import tensorflow as tf |
输出1
1 | I] Initialize GrpcChannelCache for job local -> {0 -> localhost:2222, 1 -> localhost:2223} |
输出2
1 | I] Initialize GrpcChannelCache for job local -> {0 -> localhost:2222, 1 -> localhost:2223} |
代码运行后无法自动结束,也无法ctrl+c结束,必须手动kill
ps-worker架构
一般在做深度学习训练时,会定义两个工作,一个工作专门负责存储、获取以及更新变量的取值,这个工作中的所有任务称为参数服务器(parameter server ,ps),另外一个工作负责运行反向传播算法来获取参数梯度,这个工作中的所有任务被称之为计算服务器(worker)。
常见集群配置方法
1 | tf.train,ClusterSpec({ |
其中的
tf-worker(i)
和tf-ps(i)
均为服务器地址
使用分布式TF训练深度学习时,一般有两种方式:计算图内分布式(in-graph relication)和计算图间分布式(between-graph relication)。
图内分布式表示所有的任务参数使用的是一份,只不过运算被分发到了其他GPU上。
图间分布式表示在每个服务器上创建一个独立的计算图,一些相同参数需要统一放到参数服务器上去。
模型训练
在八卡服务器上用docker模拟4台2卡服务器:
1 | docker pull tensorflow/tensorflow:1.3.0-gpu-py3 |
注:docker-compose文件中所用的tensorflow:1.3.0-gpu-py3
镜像为自定义后的安装了SSH的镜像。
使用nvidia-docker-compose up -d
启动
将mnist_inference.py
和MNIST_data拷贝到容器内。
异步分布式代码dist_tf_mnist_async.py
运行代码
1 | python dist_tf_mnist_async.py \ |
1 | python dist_tf_mnist_async.py \ |
1 | python dist_tf_mnist_async.py \ |
1 | python dist_tf_mnist_async.py \ |
- 若运行失败,按
ctrl
+z
退出任务,并执行命令ps -ef|grep dist_tf_mnist|awk '{print $2}'|xargs kill -9
来停止进程。或者直接按ctrl
+\
。 - 最好按照顺序来指定,出现过gRPC错误。
- 训练步数最好长一点,出现过work1和work2结束了,work0还没开始跑,然后一直在等待work1和work2。
- 出现过Save相关的错误,将缓存文件删除后正常。
- 任务结束后PS没有退出,Worker们正常退出。
附日志输出:
ps
1 | 2017-11-02 19:44:15.985988: I tensorflow/core/distributed_runtime/rpc/grpc_channel.cc:215] Initialize GrpcChannelCache for job ps -> {0 -> localhost:2222} |
work0
1 | 2017-11-02 19:47:39.608580: I tensorflow/core/distributed_runtime/rpc/grpc_channel.cc:215] Initialize GrpcChannelCache for job ps -> {0 -> tf1:2222} |
work1
1 | 2017-11-02 19:46:00.304995: I tensorflow/core/distributed_runtime/rpc/grpc_channel.cc:215] Initialize GrpcChannelCache for job ps -> {0 -> tf1:2222} |
work2
1 | 2017-11-02 19:46:22.534712: I tensorflow/core/distributed_runtime/rpc/grpc_channel.cc:215] Initialize GrpcChannelCache for job ps -> {0 -> tf1:2222} |
运行代码把dist_tf_mnist_async.py
替换为dist_tf_mnist_sync.py
日志
ps
1 |
work0
1 |
work1
1 | ``` |
```