理论与概念
不看网上教程了,直接看官网的介绍。也不用keras,keras一点都不灵活,还各种bug。
直接看源码,地址为https://github.com/tensorflow/models/blob/master/tutorials/image/cifar10/cifar10_input.py
我的理解是这样,首先共享变量的定义是什么?是如何共享的?
答:变量有一个主要存储的地方,如cpu1,而其余的引用都是借用,而原版永远都是在cpu1中。所以共享的方式就是有一个拷贝在cpu1中,利用with device来借用这些变量,在with device的设备里面借用并计算。
with tf.device('/gpu:%d' % i):
# Dequeues one batch for the GPU
image_batch, label_batch = batch_queue.dequeue()
logits = cifar10.inference(images)
上面的inference里的所有推导计算都是这样的:
kernel = _variable_with_weight_decay('weights',
shape=[5, 5, 3, 64],
stddev=5e-2,
wd=None)
conv = tf.nn.conv2d(images, kernel, [1, 1, 1, 1], padding='SAME')
biases = _variable_on_cpu('biases', [64], tf.constant_initializer(0.0))
pre_activation = tf.nn.bias_add(conv, biases)
conv1 = tf.nn.relu(pre_activation, name=scope.name)
可以看到都是在cpu中建立变量,因此所有变量的本体都在cpu里,gpu只是借用来计算。按照这个思路基本上就搞清楚了。
在用多gpu时,流程为
1,先在cpu里建立一个网络,这些网络的变量名字定义好。
2,用with device gpu来借用这些变量并计算,计算的结果也存到cpu。
3,更新cpu中的权重
4,重复2
实践与验证
写一个最简单的网络,然后用cpu存参数,用多个gpu来借用参数并计算。
def _variable_on_cpu(name, shape, initializer): """Helper to create a Variable stored on CPU memory. Args: name: name of the variable shape: list of ints initializer: initializer for Variable Returns: Variable Tensor """ with tf.device('/cpu:0'): var = tf.get_variable(name, shape, initializer=initializer) return var ccc = _variable_on_cpu('ccc', [2, 3, 4], tf.random_normal_initializer()) ddd=tf.get_variable_scope() ddd.reuse_variables() ccc1=_variable_on_cpu('ccc', [2, 3, 4], tf.random_normal_initializer()) init = tf.global_variables_initializer() sess = tf.Session() print(ccc==ccc1)#True
不加那个reuse_variables()会报错。
接下来做一个验证,即是cpu上建立本体变量,gpu借用这个变量,然后改变cpu本体变量,gpu借用这个变量,看变化。
ccc = _variable_on_cpu('ccc', tf.ones([2, 3, 4])) ddd = tf.get_variable_scope() ddd.reuse_variables() init = tf.global_variables_initializer() sess = tf.Session() sess.run(init) with tf.device('/cpu:0'): with tf.device('/gpu:0'): ccc1=tf.get_variable('ccc') ccc2=ccc1*2 print(sess.run(ccc2)) fff=ccc.assign_add(tf.ones([2, 3, 4])) sess.run(fff) print(sess.run(ccc2))
这里就一目了然了,上面的代码说明我的猜想是正确的,接下来就是利用这个机制来搭一个模型,多gpu训练。要注意的就是变量名字要唯一就好了,假如所有变量都是没有要区分可以reuse和不能reuse的话,那么就把所有变量都放到一个variable_scope,并且都设置为reusable。