学习《深度学习之TensorFlow》时的一些实践。
认识TF中的Variable
TF通过name来标识变量(Variable),这和调用者定义的程序里的"变量名"无关。当不指定name时,由TF自己指定,当创建的变量的name已经存在时,TF会为其改名。
变量的创建和name指定
# 两个未命名的变量,TF会自动给名字
a = tf.Variable(1.0)
print("a:", a.name)
b = tf.Variable(2.0)
print("b:", b.name)
# 两个name一样的变量,TF会为第二个改名字
c = tf.Variable(3.0, name='var')
print("c:", c.name)
d = tf.Variable(4.0, name='var')
print("d:", d.name)
a: Variable:0
b: Variable_1:0
c: var:0
d: var_1:0
读取变量的值
# 在Session中读取变量的值
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print("a:", a.eval())
print("b:", b.eval())
print("c:", c.eval())
print("d:", d.eval())
a: 1.0
b: 2.0
c: 3.0
d: 4.0
使用tf.get_variable()
通过一系列参数,获取一个已经存在的变量,或者创建一个新的变量。
下面即创建一个名为ok,shape为[1]的变量,初始化为6.6。
ok = tf.get_variable("ok", [1], initializer=tf.constant_initializer(6.6))
print("ok:", ok.name)
ok: ok:0
变量的scope
要使用相同name的变量,一般要指定在不同的scope里。当使用tf.get_variable()
创建变量时,会去检查计算任务中是否已经创建过这个变量,如果创建过了,而且本次没有使用共享方式,就会出错。
with tf.variable_scope("v1"):
ok = tf.get_variable("ok", [1], initializer=tf.constant_initializer(6.6))
print("ok:", ok.name)
with tf.variable_scope("v2"):
ok = tf.get_variable("ok", [1], initializer=tf.constant_initializer(6.6))
print("ok:", ok.name)
ok: v1/ok:0
ok: v2/ok:0
嵌套scope
变量作用域可以嵌套。
with tf.variable_scope("v4"):
ok4 = tf.get_variable("ok", [1], initializer=tf.constant_initializer(6.6))
with tf.variable_scope("v5"):
ok45 = tf.get_variable("ok", [1], initializer=tf.constant_initializer(6.6))
print("ok4:", ok4.name)
print("ok45:", ok45.name)
ok4: v4/ok:0
ok45: v4/v5/ok:0
Variable的重用
指向同一个Variable的程序变量即重用(共享)了。这在有些需要协作的模型(如GAN)里是比较关键的。
reuse_variables()
用上面的方式指定的两个变量是不同的,如果要在一个作用域里指定两个变量是相同的,可以在该作用域下开启变量重用。
with tf.variable_scope("v3") as v3:
p1 = tf.get_variable("p", [1], initializer=tf.constant_initializer(2.2))
print("p1:", p1.name)
v3.reuse_variables() # 开启变量重用
p2 = tf.get_variable("p")
print("p2:", p2.name)
p1: v3/p:0
p2: v3/p:0
# 查看这两个变量的值
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(sess.run(p1), sess.run(p2))
[2.2] [2.2]
共享变量的一般用法
一般是将共享的一个作用域里的变量放到两个网络中去,也就是在某个网络中:
with tf.variable_scope("v6"):
g1 = tf.get_variable("g1", [1], initializer=tf.constant_initializer(1.1))
with tf.variable_scope("v7"):
g2 = tf.get_variable("g2", [1], initializer=tf.constant_initializer(2.2))
在另一个网络中,相应的scope开启reuse(这里的reuse可以级联传递):
with tf.variable_scope("v6", reuse=True):
g3 = tf.get_variable("g1", [1], initializer=tf.constant_initializer(1.1))
with tf.variable_scope("v7"):
g4 = tf.get_variable("g2", [1], initializer=tf.constant_initializer(2.2))
最终g1和g3共享了,g2和g4共享了:
print(g1.name, g3.name)
print(g2.name, g4.name)
v6/g1:0 v6/g1:0
v6/v7/g2:0 v6/v7/g2:0
关于自动重用
如果在第一个v6上开启reuse=True
,那么会报错:
ValueError: Variable v6/g1 does not exist, or was not created with tf.get_variable(). Did you mean to set reuse=tf.AUTO_REUSE in VarScope?
也就是说,开启重用的scope会去找里面先前已经定义好的变量,并重用之,如果找不到就会出问题。
在某些情况下,开启自动重用(reuse=tf.AUTO_REUSE
)是比较合适的,它可以兼顾“重用”和“创建新变量”,即能重用就重用,不能就直接创建新变量。
with tf.variable_scope("v3", reuse=tf.AUTO_REUSE) as v3:
p1 = tf.get_variable("p", [1], initializer=tf.constant_initializer(2.2))
print("p1:", p1.name)
p2 = tf.get_variable("p")
print("p2:", p2.name)
p1: v3/p:0
p2: v3/p:0
Variable在scope上的初始化
在scope上可以指定initializer,对于未指定initializer的Variable和子scope,它会级联传递;对于指定了initializer的子scope,它会被覆盖,并以覆盖后的值向下级联传递。
with tf.variable_scope("s1", initializer=tf.constant_initializer(1.1)):
s1a = tf.get_variable("s1a", [1])
s1b = tf.get_variable("s1b", [1], initializer=tf.constant_initializer(1.2))
with tf.variable_scope("s2"):
s2a = tf.get_variable("s1a", [1])
with tf.variable_scope("s3", initializer=tf.constant_initializer(2.1)):
s3a = tf.get_variable("s1a", [1])
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(sess.run(s1a), sess.run(s1b), sess.run(s2a), sess.run(s3a))
[1.1] [1.2] [1.1] [2.1]