权值共享常使用的情况,一些需要共享的变量,比如神经网络里面的权重,word embedding这种变量,这样就会选择使用共享变量。而比如global_step这种仅仅用来追踪训练步数的变量,它并不是trainable的,那么直接用tf.Variable()方法。
tf.get_variable( name, #name是一个必填参数,与之前见过的函数不一样!!!! shape=None, dtype=None, initializer=None, regularizer=None, trainable=True, collections=None, caching_device=None, partitioner=None, validate_shape=True, use_resource=None, custom_getter=None )
name:新变量或现有变量的名称,这个参数是必须的,函数会根据变量名称去创建或者获取变量。
shape:新变量或现有变量的形状或者维度。
dtype:新变量或现有变量的类型(默认为 DT_FLOAT)。
initializer:创建变量的初始化器,初始化变量。初始化的方式在下面会有一个归纳
regularizer:一个函数(张量 - >张量或无);将其应用于新创建的变量的结果将被添加到集合 tf.GraphKeys.REGULARIZATION_LOSSES 中,并可用于正则化。
trainable:如果为 True,还将变量添加到图形集合:GraphKeys.TRAINABLE_VARIABLES。
collections:要将变量添加到其中的图形集合键的列表。默认为 [GraphKeys.LOCAL_VARIABLES]。
partitioner:(可选)可调用性,它接受要创建的变量的完全定义的 TensorShape 和 dtype,并且返回每个坐标轴的分区列表(当前只能对一个坐标轴进行分区)
validate_shape:如果为假,则允许使用未知形状的值(也就是shape=[],方括号里面不填任何东西,包括空格)初始化变量。如果为真,则默认情况下,initial_value 的形状必须是已知的。
use_resource:如果为假,则创建一个常规变量。如果为真,则创建一个实验性的 ResourceVariable,而不是具有明确定义的语义。默认为假(稍后将更改为真)。
最后一个参数
initializer是变量初始化的方式归纳,初始化的方式有以下几种:
tf.constant_initializer:常量初始化函数
tf.random_normal_initializer:正态分布
tf.truncated_normal_initializer:截取的正态分布
tf.random_uniform_initializer:均匀分布
tf.zeros_initializer:全部是0
tf.ones_initializer:全是1
tf.uniform_unit_scaling_initializer:满足均匀分布,但不影响输出数量级的随机值
例如
import tensorflow as tf; import numpy as np; import matplotlib.pyplot as plt; a1 = tf.get_variable(name='a1', shape=[2,3], initializer=tf.random_normal_initializer(mean=0, stddev=1)) a2 = tf.get_variable(name='a2', shape=[1], initializer=tf.constant_initializer(1)) a3 = tf.get_variable(name='a3', shape=[2,3], initializer=tf.ones_initializer()) with tf.Session() as sess: sess.run(tf.initialize_all_variables()) print sess.run(a1) print sess.run(a2) print sess.run(a3) #输出 [[ 0.42299312 -0.25459203 -0.88605702] [ 0.22410156 1.34326422 -0.39722782]] [ 1.] [[ 1. 1. 1.] [ 1. 1. 1.]]
注意:不同的变量之间不能有相同的名字,除非你定义了variable_scope,这样才可以有相同的名字
tf.Variable( initial_value=None,# 第一个参数是必填参数 trainable=True, collections=None, validate_shape=True, caching_device=None, name=None, variable_def=None, dtype=None, expected_shape=None, import_scope=None)
定义图变量的方法有两种,一种是上面的tf.get_variable(),另外一种就是这里的tf.Variable()
initial_value:变量的初始值,以下会有一个总结
trainable:bool类型,如果为True,会把它加入到GraphKeys.TRAINABLE_VARIABLES,才能对它使用Optimizer
collections:是一个list类型,指定该图变量的类型、默认为[GraphKeys.GLOBAL_VARIABLES]
validate_shape:bool类型,如果为False,则不进行类型和维度检查
name:是string类型,变量的名称,如果没有指定则系统会自动分配一个唯一的值(可选参数)
剩余的参数以后再来补充讲解
可以作为初始化值的归纳,可分为两大类型
1.random Tensor
tf.random_normal(shape, mean=0.0, stddev=1.0, dtype=tf.float32, seed=None, name=None) tf.truncated_normal(shape, mean=0.0, stddev=1.0, dtype=tf.float32, seed=None, name=None) tf.random_uniform(shape, minval=0, maxval=None, dtype=tf.float32, seed=None, name=None) tf.random_shuffle(value, seed=None, name=None) tf.random_crop(value, size, seed=None, name=None) tf.multinomial(logits, num_samples, seed=None, name=None) tf.random_gamma(shape, alpha, beta=None, dtype=tf.float32, seed=None, name=None) tf.set_random_seed(seed)
2.constant value Tensor
tf.zeros(shape, dtype=tf.float32, name=None) tf.zeros_like(tensor, dtype=None, name=None) tf.ones(shape, dtype=tf.float32, name=None) tf.ones_like(tensor, dtype=None, name=None) tf.fill(dims, value, name=None) tf.constant(value, dtype=None, shape=None, name='Const')
1.name。使用tf.Variable
时,如果检测到命名冲突,系统会自己处理。使用tf.get_variable()
时,系统不会处理冲突,而会报错。实际上由于tf.Variable()
每次都在创建新对象,所有reuse=True
和它并没有什么关系。对于get_variable()
,来说,在某个作用域下如果已经有创建的变量对象,同时要满足标签reuse=True
,就把那个对象返回(也就是共享)没有创建变量;如果没有存在想要创建的变量对象的话,同时标签reuse=False(默认),
就创建一个新的(这个时候才创建新对象)。
(1)variable可以创建两个name相同(都为W_1)的变量
import tensorflow as tf
w_1 = tf.Variable(3,name="w_1")
w_2 = tf.Variable(1,name="w_1")
print w_1.name
print w_2.name
#输出实际上创建了两个不同的变量
#w_1:0
#w_1_1:0
(2)get_variable在同一个作用域下(这个作用域是默认的)不能创建两个name相同(都为W_1)的变量
import tensorflow as tf
w_1 = tf.get_variable(name="w_1",initializer=1)
w_2 = tf.get_variable(name="w_1",initializer=2)
#下面是错误信息
#ValueError: Variable w_1 already exists, disallowed. Did
#you mean to set reuse=True in VarScope?
(3)在不同作用域下,创建name相同的变量是允许的因为创建了不同的变量,它在于实际的变量全称还要加上作用域
import tensorflow as tf
with tf.variable_scope("t"):
v1=tf.get_variable("W_1",[1])
with tf.variable_scope("f"):
v2=tf.get_variable("W_1",[1])
下面是输出:
v1.name
Out[5]: 't/W_1:0'
v2.name
Out[6]: 'f/W_1:0'
tf.get_variable()
它的工作方式根据当前的变量域(Variable Scope)的reuse
属性变化而变化,我们可以通过tf.get_variable_scope().reuse
来查看这个属性,它默认是False
。
想象一下,如果我们正在定义一个循环神经网络RNN,想复用上一层的参数以提高模型最终的表现效果,应该怎么做呢?图变量的复用,reuse为True。
1.
当tf.get_variable_scope().reuse == False
时,作用域就是为创建新变量所设置的.
创建变量的全称将会由【当前变量作用域名+所提供的名字
】所组成,并且还会检查来确保没有任何变量使用这个全称.如果这个全称已经有一个变量使用了,那么方法将会抛出ValueError
错误.如果一个变量被创建,他将会用initializer(shape)
进行初始化.比如:(1)变量的实际名称=作用域+name的字符窜 with tf.variable_scope("foo"): # reuse默认是False v = tf.get_variable("v", [1]) assert v.name == "foo/v:0" (2)因为变量的名字是这样组成的,所以当作用域发生变化的时候,即使使用同一个name用来创建变量也是正确的,因为变量的实际名字是不一样的也就是不一样的变量 with tf.variable_scope("a"): g1 = tf.get_variable("g", [1]) with tf.variable_scope("f"): g2 = tf.get_variable("g", [1]) 不会报错 # 输出是: # g1.name # Out[7]: 'a/g:0' # g2.name # Out[8]: 'a/f/g:0'
2.
当
tf.get_variable_scope().reuse == True
时,作用域是为重用变量所设置
这种情况下,调用就会搜索一个已经存在的变量,他的全称和当前变量的作用域名+所提供的名字
是否相等.如果不存在相应的变量,就会抛出ValueError
错误.如果变量找到了,就返回这个变量.如下:with tf.variable_scope("foo"): v = tf.get_variable("v", [1]) with tf.variable_scope("foo", reuse=True): v1 = tf.get_variable("v", [1]) assert v1 == v 输出可以知道 v1.name==v.name=='foo/v:0'
设置reuse=True的方法为:
# 定义处 import tensorflow as tf; import numpy as np; import matplotlib.pyplot as plt; with tf.variable_scope('V1'): a1 = tf.get_variable(name='a1', shape=[1], initializer=tf.constant_initializer(1)) with tf.variable_scope('V1', reuse=True): #直接reuse=True a3 = tf.get_variable('a1') with tf.Session() as sess: sess.run(tf.initialize_all_variables()) print a1.name print sess.run(a1) print a3.name print sess.run(a3) # 调用reuse_variables()方法设置成Ture import tensorflow as tf; import numpy as np; import matplotlib.pyplot as plt; with tf.variable_scope('V1') as scope: a1 = tf.get_variable(name='a1', shape=[1], initializer=tf.constant_initializer(1)) scope.reuse_variables() #调用方法 # tf.get_variable_scope().reuse_variables()用这个替换上面的 a3 = tf.get_variable('a1') with tf.Session() as sess: sess.run(tf.initialize_all_variables()) print a1.name print sess.run(a1) print a3.name print sess.run(a3)
接下来自然的要谈一谈作用域了
tf.variable_scope()
基础嵌套的作用域附加名字所用的规则和文件目录的规则很类似:
with tf.variable_scope("foo"):
with tf.variable_scope("bar"):
v = tf.get_variable("v", [1])
assert v.name == "foo/bar/v:0"
当前变量作用域可以用tf.get_variable_scope()
里面不需要参数进行检索并且reuse
标签可以通过调用tf.get_variable_scope().reuse_variables()里面也不需要参数
设置为True
.设置方法在上面有举例。注意你不能设置reuse
标签为False。
即使你不能直接设置 reuse
为 False
,但是你可以输入一个重用变量作用域,然后就释放掉,就成为非重用的变量.当打开一个变量作用域时,使用reuse=True
作为参数是可以的.但也要注意,同一个原因,reuse
参数是不可继承.所以当你打开一个重用变量作用域,那么所有的子作用域也将会被重用.
with tf.variable_scope("root"):
# At start, the scope is not reusing.
assert tf.get_variable_scope().reuse == False
with tf.variable_scope("foo"):
# Opened a sub-scope, still not reusing.
assert tf.get_variable_scope().reuse == False
with tf.variable_scope("foo", reuse=True): 这里打开共享
# Explicitly opened a reusing scope.
assert tf.get_variable_scope().reuse == True
with tf.variable_scope("bar"):
# Now sub-scope inherits the reuse flag.
assert tf.get_variable_scope().reuse == True
# Exited the reusing scope, back to a non-reusing one.
assert tf.get_variable_scope().reuse == False
在上面的所有例子中,我们共享参数只因为他们的名字(也就是变量的全称)是一致的,那是因为我们开启一个变量作用域重用时刚好用了同一个字符串.在更复杂的情况,他可以通过变量作用域对象来使用,而不是通过依赖于括号里面的名字来使用.为此,变量作用域可以被获取并使用,而不是仅作为当开启一个新的变量作用域的名字.
with tf.variable_scope("foo") as foo_scope: # foo_scope就是作用域对象,‘foo'是名字
v = tf.get_variable("v", [1])
with tf.variable_scope(foo_scope) # 使用作用域对象最为参数,实际上不创造新的作用域,反而指代的就是同一个作用域
w = tf.get_variable("w", [1])
with tf.variable_scope(foo_scope, reuse=True)
v1 = tf.get_variable("v", [1])
w1 = tf.get_variable("w", [1])
assert v1 == v
assert w1 == w
当开启一个变量作用域,使用一个预先已经存在的作用域时,我们会跳过当前变量作用域的前缀而直接成为一个完全不同的作用域.这就是我们做得完全独立的地方.
with tf.variable_scope("foo") as foo_scope: # 第一个作用域
assert foo_scope.name == "foo"
with tf.variable_scope("bar") # 第二个完全不同的作用域
with tf.variable_scope("baz") as other_scope: # 第三个嵌套在第二个的完全不同的作用域
assert other_scope.name == "bar/baz"
with tf.variable_scope(foo_scope) as foo_scope2: # 嵌套在最里面的,但是使用前面第一个作用域对象的作用域
assert foo_scope2.name == "foo" # Not changed.# 和结论阐述的一样,是独立的
变量作用域可以携带一个默认的初始化器.他可以被子作用域继承并传递给tf.get_variable()
调用.但是如果其他初始化器被明确地指定,那么他将会被重写.
with tf.variable_scope("foo", initializer=tf.constant_initializer(0.4)):
v = tf.get_variable("v", [1]) # 注意shape=[1]
assert v.eval() == 0.4 # Default initializer as set above.
w = tf.get_variable("w", [1], initializer=tf.constant_initializer(0.3)):
assert w.eval() == 0.3 # Specific initializer overrides the default.
with tf.variable_scope("bar"):
v = tf.get_variable("v", [1])
assert v.eval() == 0.4 # Inherited default initializer.
with tf.variable_scope("baz", initializer=tf.constant_initializer(0.2)):
v = tf.get_variable("v", [1])
assert v.eval() == 0.2 # Changed default initializer.
tf.variable_scope()
中ops的名称我们讨论 tf.variable_scope
怎么处理变量的名字.但是又是如何在作用域中影响到 其他ops的名字的呢?ops在一个变量作用域的内部创建,那么他应该是共享他的名字,这是很自然的想法.出于这样的原因,当我们用with tf.variable_scope("name")
时,这就间接地开启了一个tf.name_scope("name")
.比如:
with tf.variable_scope("foo"):
x = 1.0 + tf.get_variable("v", [1])
assert x.op.name == "foo/add"
相当于
with tf.name_scope('foo'):
x = 1.0 + tf.get_variable('v',[1])
assert x.op.name == "foo/add"
名称作用域可以被开启并添加到一个变量作用域中,然后他们只会影响到ops的名称,而不会影响到变量.
(1)
with tf.variable_scope("foo"): 变量作用域在前
with tf.name_scope("bar"): 名称作用域插入在内
v = tf.get_variable("v", [1])
x = 1.0 + v
assert v.name == "foo/v:0" 变量
assert v.op.name == "foo/v:0" 变量
assert x.name == 'foo/bar/add:0' 操作数
assert x.op.name == "foo/bar/add" 操作数
(2)
with tf.name_scope("bar"): 名称作用域在前
with tf.variable_scope("foo"): 变量作用域插入在内
v = tf.get_variable("v", [1])
x = 1.0 + v
assert v.name == "foo/v:0" 变量
assert v.op.name == "foo/v:0" 变量
assert x.name == 'bar/foo/add:0' 操作数
assert x.op.name == "bar/foo/add" 操作数