tensorflow使用多个gpu训练

关于多gpu训练,tf并无给太多的学习资料,比较官方的只有:tensorflow-models/tutorials/image/cifar10/cifar10_multi_gpu_train.py网络

但代码比较简单,只是针对cifar作了数据并行的多gpu训练,利用到的layer、activation类型很少,针对更复杂网络的状况,并无给出指导。本身摸了很多坑以后,算是基本走通了,在此记录下app

 

1、思路函数

单GPU时,思路很简单,前向、后向都在一个GPU上进行,模型参数更新时只涉及一个GPU。多GPU时,有模型并行和数据并行两种状况。模型并行指模型的不一样部分在不一样GPU上运行。数据并行指不一样GPU上训练数据不一样,但模型是同一个(至关因而同一个模型的副本)。在此只考虑数据并行,这个在tf的实现思路以下:学习

模型参数保存在一个指定gpu/cpu上,模型参数的副本在不一样gpu上,每次训练,提供batch_size*gpu_num数据,并等量拆分红多个batch,分别送入不一样GPU。前向在不一样gpu上进行,模型参数更新时,将多个GPU后向计算获得的梯度数据进行平均,并在指定GPU/CPU上利用梯度数据更新模型参数。ui

假设有两个GPU(gpu0,gpu1),模型参数实际存放在cpu0上,实际一次训练过程以下图所示:spa

 

2、tf代码实现code

大部分须要修改的部分集中在构建计算图上,假设在构建计算图时,数据部分基于tensorflow1.4版本的dataset类,那么代码要按照以下方式编写:blog

 1 next_img, next_label = iterator.get_next()
 2 image_splits = tf.split(next_img, num_gpus)
 3 label_splits = tf.split(next_label, num_gpus)
 4 tower_grads = []
 5 tower_loss = []
 6 counter = 0
 7 for d in self.gpu_id:
 8     with tf.device('/gpu:%s' % d):
 9         with tf.name_scope('%s_%s' % ('tower', d)):
10             cross_entropy = build_train_model(image_splits[counter], label_splits[counter], for_training=True)
11             counter += 1
12             with tf.variable_scope("loss"):
13                 grads = opt.compute_gradients(cross_entropy)
14                 tower_grads.append(grads)
15                 tower_loss.append(cross_entropy)
16                 tf.get_variable_scope().reuse_variables()
17 
18 mean_loss = tf.stack(axis=0, values=tower_loss)
19 mean_loss = tf.reduce_mean(mean_loss, 0)
20 mean_grads = util.average_gradients(tower_grads)
21 update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
22 with tf.control_dependencies(update_ops):
23     train_op = opt.apply_gradients(mean_grads, global_step=global_step)

第1行获得image和对应labelci

第2-3行对image和label根据使用的gpu数量作平均拆分(默认两个gpu运算能力相同,若是gpu运算能力不一样,能够本身设定拆分策略)get

第 4-5行,保存来自不一样GPU计算出的梯度、loss列表

第7-16行,开始在每一个GPU上建立计算图,最重要的是14-16三行,14,15把当前GPU计算出的梯度、loss值append到列表后,以便后续计算平均值。16行表示同名变量将会复用,这个是什么意思呢?假设如今gpu0上建立了两个变量var0,var1,那么在gpu1上建立计算图的时候,若是还有var0和var1,则默认复用以前gpu0上的建立的那两个值。

第18-20行计算不一样GPU获取的grad、loss的平均值,其中第20行使用了cifar10_multi_gpu_train.py中的函数。

第23行利用梯度平均值更新参数。

 

注意:上述代码中,全部变量(vars)都放在了第一个GPU上,运行时会发现第一个GPU占用的显存比其余GPU多一些。若是想把变量放在CPU上,则须要在建立计算图时,针对每层使用到的变量进行设备指定,很麻烦,因此建议把变量放在GPU上。

相关文章
相关标签/搜索