TENSORFLOW:VARIABLES

Variables

TensorFlow里的variable是表示你程序操作的持续共享的状态的最好的方式。

变量是通过tf.Variable类操作的。一个tf.Variable表示一个tensor的值是可以通过操作来改变的。和tf.Tensor不同,tf.Variable是存在于单次session.run调用的context之外的。

在内部,一个tf.Variable存储着一个持久的tensor,特定的操作允许你读取或者修改这个tensor的值。而且这些修改是跨session的。所以多个worker可以看到同样值的tf.Variable.

创建一个Variable


最好的创建一个variable的办法是通过调用tf.get_variable方法。这个方法需要你设定变量的名字。别人可以通过这个名字来访问这个variable。同时这个名字还会在checkpointing和exporting model的时候给变量的值命名。tf.get_variable同时也允许你重用之前定义的同名变量。这样让你在定义模型时重用一些layer。

通过tf.get_variable创建一个变量的时候,只要简单的提供名字和shape

1my_variable = tf.get_variable("my_variable",[1,2,3]) 2

上边的代码创建了一个名叫my_variable的变量,它有3个维度,shape是[1,2,3].这个值默认是tf.float32类型的. 它的初始值会被tf.glorot_uniform_initializer随机初始化。

你也可以选在调用tf.get_variable的时候指定类型和初始化函数,比如:

1my_variable = tf.get_variable("my_variable",[1,2,3],dtype=tf.int32,initializer=tf.zeros_initializer) 2

TensorFlow提供了很多好用的initializers。另外你可以用一个tensor来出初始化一个tf.Variable.比如:

1other_variable = tf.get_variable("other_variable",dtype=tf.int32,initializer=tf.contant([23,42]) 2

如果是用tensor来初始化variable,你就不用在指定shape了,会用初始化的tensor的shape来初始化variable的shape。

变量集合

因为tensorflow程序的不同部分都会创建variable,有时候你想用单一的方式访问所有的变量。因此tensorflow提供了collection,它是一个命名的tensor或者其他对象的list,比如tf.Variable实例.

默认情况下,一个variable被放在了两个集合里:
– tf.GraphKeys.GLOBAL_VARIABLES 这里的变量会在多个设备间共享。
– tf.GraphKeys.TRAINABLE_VARIABLES 这里的变量tensorflow会对它们计算梯度。
如果你有一个variable,你不想训练它,你就把他加到tf.GraphKeys.LOCAL_VARIABLES列表里。

1my_local = tf.get_variable("my_local",shape=(),collections=[tf.GraphKeys.LOCAL_VARIABLES]) 2

或者,你也可以通过指定tf.get_variable的参数trainable=False来达到同样的目的:

1my_local = tf.get_variable("my_local",shape=(),trainable=False) 2

你也可以用你自己的集合,任意字符串都是有效的集合名。你不需要自己创建一个集合,只需要直接把一个变量(或者其他对象)加入到一个集合,调用时你只需要给出这个集合的名字:

1tf.add_to_collection("my_coolection_name",my_local) 2

获取你之前放入collection的变量或者其他对象的方法是:

1tf.get_collection("my_collection_name") 2

指定设备

就像其他的tensorflow的操作,你可以把变量放置到特定的设备上。比如下边的代码段创建了一个变量v,并把它放到第二个GPU上。

1with tf.device("/device:GPU:1"): 2    v = tf.get_variable("v",[1]) 3

在分布式的情况下,把variable放在合适的设备上是非常重要的。如果不小心把变量放在了worker上,而不是parameter机器上。会非常明显的降低训练速度。最差的情况,让每个worker都没有意识到自己在自己独立的一份variable上训练。因此,我们提供了tf.train.replica_device_setter,它可以自动的把variable放到parameter server上。比如:

1cluster_spec = { 2    "ps":["ps0:2222","ps1:2222"], 3    "worker":["work0:2222","worker1:2222","work2:2222"] 4} 5with tf.device(tf.train.replica_device_setter(cluster=cluster_spec)): 6    v = tf.get_variable("v",shape=[20,20]) # 通过指定replica_device_setter,这个变量会放在 7                                                                                 # parameter server上。 8

初始化变量

在你使用一个变量前,你需要初始化。如果你用一些底层的tensorflow API编程(也就是你自己创建graph和session),你必须自己初始化这些变量。高层的API,比如tf.contrib.slim,tf.estimator.Estimator和Keras会在你训练模型前自动的初始化变量。

明确的初始化变量值非常有用,当你load一个训练了一半的模型时,可以设置之前训练了一半的变量值。当你在分布式环境下可以给各个机器设置同样的随机初始值。

一次设置所有的可以训练的变量,在训练开始前,你可以调用tf.gloabl_variables_initializer().这个方法返回一个operation,它负责初始化所有的在tf.GraphKeys.GLOBAL_VARIABLES集合里的变量。运行这个operation,会初始化所有的变量。比如:

1sess.run(tf.global_variables_initializer()) 2#现在所有的变量都初始化了。 3

如果你需要自己初始化变量,你可以运行变量自己的initializer。比如:

1sess.run(my_variable.initializer) 2

你也可以通过下边的代码查看哪些变量还没有被初始化:

1print(sess.run(tf.report_uninitialized_variables())) 2

默认情况下,tf.global_variables_initializer是不指定变量初始化顺序的。因此如果一个变量的初始化值依赖于另一个变量的值,你可能会得到一个error。所以你最好在代码里,在所有的变量没有被初始化的时候,不要直接用一个variable的值,而使用它的initialized_value.比如:

1= tf.get_variable("v",shape=(),initialize=tf.zeros_initializer()) 2= tf.get_variable("v",initializer=v.initialized_value()+1) 3

使用变量


在tensorflow的graph里,使用variable就像使用tensor一样。

1= tf.get_variable("v",shape=(),initalizer=tf.zeros_initializer()) 2= v+1 # w是一个基于变量v计算而来的tensor。只要变量出现在一个表达式里, 3                #它会自动转化成一个tensor来计算。这个tensor有着变量的值。 4

给变量赋一个值,可以使用方法assign,assign_add,和tf.Variable里的其他方法,比如:

1= tf.get_variable("v",shape=(),initializer=tf.zeros_initializer()) 2assignment = v.assign_add(1) 3tf.global_variables_initializer().run() 4sess.run(assignment) # 或者assignment.op.run(),或者assignment.eval() 5

绝大多数的tensorflow optimizer有专用的操作来高效的更新变量的值,依据像梯度下降这样的算法。tf.train.Optimizer的文档详细说了如何使用optimizer。

因为变量是可以更改的。所以有时知道变量的版本是很有用的。在某些操作后强制去重新读一个变量的值,你可以用tf.Variable.read_value。比如:

1= tf.get_variable("v",shape=(),initializer=tf.zeros_initializer()) 2assignment = v.assign_add(1) 3with tf.control_dependencies([assignment]): 4    w = v.read_value() # w就可以保证反映了v在assign_add操作执行之后的值。 5

共享变量


TensorFlow有两种共享变量的方式:
– 直接传递variable对象。
– 把variable对象封装到tf.variable_scope对象里。

通过代码传递variable对象非常清晰。但是有时候我们在写tensorflow的function的时候,用隐式变量比较方便。大多数tf.layer里的layers和所有的tf.metrics以及一些工具库都是用这种方式。

比如我们这里创建一个激活函数为relu的卷积层:

1def conv_relu(input,kernel_shape,bias_shape): 2    # 创建名字为weights的变量 3    weights = tf.get_variable("weights",kernel_shape,initializer=tf.random_normal_initializer()) 4    # 创建bias变量 5    biases = tf.get_variable("biases",bias_shape,initializer=tf.constant_initializer(0.0)) 6    conv = tf.nn.conv2d(input,weights,strides=[1,1,1,1],padding='SAME') 7    return tf.nn.relu(conv+biases) 8

我们给变量起了weight和bias这样通用的名字。但是在一个深度卷积神经网络里,我们会创建很多卷积层。我们如果重复的调用这个funciton就会有问题。

1input1 = tf.random_normal([1,10,10,32]) 2input2 = tf.random_normal([1,20,20,32]) 3= conv_relu(input1,kernel_shape=[5,5,32,32],bias_shape=[32]) 4= conv_relu(x,kernel_shap=[5,5,32,32],bias_shape=[32]) #这个调用会失败 5

因为期望的行为并不清晰,你到底是想创建新的变量还是重用老的变量。所以执行会失败。在不同变量域里就不会有这个歧义。下边的代码就会创建新的变量:

1def my_image_filter(input_images): 2    with tf.variable_scope("cov1"): 3        # 在这里创建的变量名是 conv1/weights 和conv1/biases 4        relu1=conv_relu(input_iamges,[5,5,32,32],[32]) 5    with tf.variable_scope("conv2"): 6        # 在这里创建的变量名是 conv2/weights 和conv2/biases 7        relu2=conv_relu(relu1,[5,5,32,32],[32]) 8

如果你想变量被共享。你有两种选择,你可以定义一个和之前同名的变量scope,并且把reuse参数设置为True:

1with tf.variable_scope("model")2    output1 = my_image_filter(input1) 3with tf.variable_scope("model",reuse=True): 4    output2 = my_iamge_filter(input2) 5

你也可以用scope.reuse_variables()方法来触发一个重用:

1with tf.variable_scope("model") as scope: 2    output1 = my_image_filter(input1) 3    scope.reuse_variables() 4    output2 = my_iamge_filter(input2) 5

因为用string来定义scope可能你觉得不安全,你也可以用已有的scope对象来初始化另一个scope:

1with tf.variable_scope("model") as scope: 2    output1 = my_image_filter(input1) 3with tf.variable_scope(scope,reuse=True): 4    output2 = my_image_filter(input2) 5

 


原文:http://www.rethink.fun/index.php/2018/03/11/tensorflow4/

代码交流 2021