tf.Variable() vs tf.get_variable()
tf.name_scope() vs tf.variable_scope()
1. 基础功能
1.1 tf.Variable()
是一个类,tensorflow.python.ops.variables.Variable
,其构造函数原型为
def __init__(
self,
initial_value: Any = None,
trainable: bool = True,
collections: Any = None,
validate_shape: bool = True,
caching_device: Any = None,
name: Any = None,
variable_def: Any = None,
dtype: Any = None,
expected_shape: Any = None,
import_scope: Any = None,
constraint: Any = None
) -> Any
创建一个初始值为 initial_value
的变量。
Params:(仅关注几个常用的)
initial_value
– 变量初始值,是一个Tensor
或者 可转换为Tensor
的 python 对象;trainable
– 可否训练;collections
– 一个 List[graph collections keys],变量将会加入这些collections
,列表默认为[GraphKeys.GLOBAL_VARIABLES]
;name
– Optional(可选) 变量的名字,默认为'Variable'
,如果已有同名变量,则新变量 name 为'Variable_num'
,其中num
递增;constraint
– 一个可选的投影函数,在被 Optimizer(例如,用于实现层权重的范数约束或值约束)更新之后应用于该变量。该函数必须将代表变量值的未投影张量作为输入,并返回投影值的张量(它必须具有相同的形状)。进行异步分布式训练时,约束条件不安全。
例子:
a = tf.Variable(
initial_value=[1.0, 2.0],
trainable=True,
name='va',
dtype=tf.float32
)
print(a) # <tf.Variable 'va:0' shape=(2,) dtype=float32_ref>
1.2 tf.get_variable()
函数,原型为:
def get_variable(
name: Any,
shape: Any = None,
dtype: Any = None,
initializer: Any = None,
regularizer: Any = None,
trainable: Any = None,
collections: Any = None,
caching_device: Any = None,
partitioner: Any = None,
validate_shape: bool = True,
use_resource: Any = None,
custom_getter: Any = None,
constraint: Any = None,
synchronization: VariableSynchronization = VariableSynchronization.AUTO,
aggregation: VariableAggregation = VariableAggregation.NONE
) -> Any
通过变量名来获取或创建一个变量。
Params:(仅关注几个常用的)
name
– 必填 变量的名字,如果已有同名变量,则报错(variable_scope(reuse=True)
除外);shape
– 变量形状;dtype
– 数据类型;本来猜测initializer
中的dtype
可能只决定随机值,最终类型由此参数决定,但initializer
中的dtype
似乎并不起作用;initializer
– 初始化器,如tf.tf.random_uniform_initializer(minval=1, maxval=10)
;regularizer
– 可能是传入一个正则化器,如 `tf.contrib.layers.l2_regularizer(0.1)';synchronization
– 以后再说(应该挺有用的);
例子:
a = tf.get_variable(
name='v',
shape=[1, 2],
dtype=tf.float64,
initializer=tf.random_uniform_initializer(
minval=1,
maxval=10,
dtype=tf.int32
) # initializer 里的 dtype 没什么卵用
)
print(a) # `<tf.Variable 'v:0' shape=(1, 2) dtype=float64_ref>`
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(sess.run(a)) # [[3.98646081 3.30710938]]
1.3 tf.name_scope()
def __init__(
self,
name: Any,
default_name: Any = None,
values: Any = None
) -> None
初始化上下文管理器。
Params:
name
– 传递给 OP 函数的 name 参数;default_name
– 如果 name 参数为 None 则使用默认的名称;values
– 要传递给 op 函数的张量参数列表(不清楚);
这篇博文 讲得不错。
例子:
with tf.name_scope(name='ns'):
a = tf.Variable(0.0)
b = tf.Variable(0.0)
c = a + b
print(a) # <tf.Variable 'ns/Variable:0' shape=() dtype=float32_ref>
print(b) # <tf.Variable 'ns/Variable_1:0' shape=() dtype=float32_ref>
print(c) # Tensor("ns/add:0", shape=(), dtype=float32)
1.4 tf.variable_scope()
def __init__(
self,
name_or_scope: Any,
default_name: Any = None,
values: Any = None,
initializer: Any = None,
regularizer: Any = None,
caching_device: Any = None,
partitioner: Any = None,
custom_getter: Any = None,
reuse: Any = None,
dtype: Any = None,
use_resource: Any = None,
constraint: Any = None,
auxiliary_name_scope: bool = True
) -> Any
初始化上下文管理器。
Params:
name_or_scope
–string
orVariableScope
: 要打开的 scope;default_name
– 如果参数name_or_scope
是None
, 则启用默认名;values
– 要传递给 op 函数的张量参数列表(不清楚);reuse
–True
,None
, ortf.AUTO_REUSE
;- 如果为
True
,该 scope 及其 sub-scope(reuse=None)开启 reuse 模式,get_variable(name='v')
将获取已有的 name ==v
的变量; - 如果为
tf.AUTO_REUSE
,get_variable(name='v')
创建新变量 ifv
不存在 esle 获取已有变量v
; - 如果为
None
,则继承其 parent-scope 的 reuse flag;
- 如果为
Returns:
A scope that can be captured and reused. 可以作为参数传递给 tf.variable_scope()
;
例子
with tf.variable_scope('vs') as scope:
a = tf.get_variable(name='a', shape=[2, 3], initializer=tf.random_uniform_initializer(0.0, 1.0))
print(a) # <tf.Variable 'vs/a:0' shape=(2, 3) dtype=float32_ref>
with tf.variable_scope(scope, reuse=True): # 需要指明 reuse
b = tf.get_variable(name='a')
print(a is b) # True
2. 四者之间的关系
2.1 tf.Variable() 和 tf.get_variable()
# 两个定义是等价的
v = tf.Variable(tf.constant(1.0, shape=[1]), name='v')
v = tf.get_variable(name='v', shape=[1], initializer=tf.constant_initializer(1.0))
print(v) # <tf.Variable 'v:0' shape=(1,) dtype=float32_ref>
- 最大的区别在于:
tf.Variable()
的name
是可选的;而tf.get_variable()
的name
是必填的; - 当已存在同名变量时:
tf.Variable()
会在变量名后加_n
其中n
递增;而tf.get_variable()
会报错(最后一个例子特殊,如果已存在tf.Variable()
创建的同名变量,则遵循 +1 规则);
例子
a = tf.Variable(tf.constant(1.0, shape=[1]), name='v')
b = tf.Variable(tf.constant(1.0, shape=[1]), name='v')
print(a) # <tf.Variable 'v:0' shape=(1,) dtype=float32_ref>
print(b) # <tf.Variable 'v_1:0' shape=(1,) dtype=float32_ref>
a = tf.get_variable(name='v', shape=[1], initializer=tf.constant_initializer(1.0))
b = tf.get_variable(name='v', shape=[1], initializer=tf.constant_initializer(1.0))
# ValueError: Variable v already exists, disallowed.
a = tf.Variable(1.0, name='v')
b = tf.get_variable(name='v', shape=[1], initializer=tf.constant_initializer(1.0))
c = tf.Variable(1.0, name='v1')
d = tf.get_variable(name='v1', shape=[1], initializer=tf.constant_initializer(1.0))
print(a) # <tf.Variable 'v:0' shape=() dtype=float32_ref>
print(b) # <tf.Variable 'v_1:0' shape=(1,) dtype=float32_ref>
print(c) # <tf.Variable 'v1:0' shape=() dtype=float32_ref>
print(d) # <tf.Variable 'v1_1:0' shape=(1,) dtype=float32_ref>
只有在 tf.get_variable()
& tf.get_variable()
的情况下重名才报错,其他情况遵循 +1 规则。
2.2 tf.Variable() 和 tf.name_scope()
with tf.name_scope('ns'):
a = tf.Variable(tf.constant(1.0, shape=[1]), name='v')
b = tf.Variable(tf.constant(1.0, shape=[1]), name='v')
print(a) # <tf.Variable 'ns/v:0' shape=(1,) dtype=float32_ref>
print(b) # <tf.Variable 'v:0' shape=(1,) dtype=float32_ref>
2.3 tf.Variable() 和 tf.variable_scope()
with tf.variable_scope('vs'):
a = tf.Variable(tf.constant(1.0, shape=[1]), name='v')
b = tf.Variable(tf.constant(1.0, shape=[1]), name='v')
print(a) # <tf.Variable 'vs/v:0' shape=(1,) dtype=float32_ref>
print(b) # <tf.Variable 'v:0' shape=(1,) dtype=float32_ref>
此时,tf.variable_scope()
== tf.name_scope()
2.4 tf.get_variable() 和 tf.name_scope()
with tf.name_scope('ns'):
a = tf.get_variable(name='v', shape=[1], initializer=tf.constant_initializer(1.0))
# b = tf.get_variable(name='v', shape=[1], initializer=tf.constant_initializer(1.0))
print(a) # <tf.Variable 'v:0' shape=(1,) dtype=float32_ref>
# print(b) # Err
tf.name_scope()
对 tf.get_variable()
不起作用。
2.5 tf.get_variable() 和 tf.variable_scope()
with tf.variable_scope(
'vs',
initializer=tf.constant_initializer(1.0),
regularizer=tf.contrib.layers.l2_regularizer(0.01)
):
a = tf.Variable(0.0, name='v')
b = tf.get_variable(name='v', shape=[1])
print(a) # <tf.Variable 'vs/v:0' shape=() dtype=float32_ref>
print(b) # <tf.Variable 'vs/v_1:0' shape=(1,) dtype=float32_ref>
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(sess.run(a)) # 0.0
print(sess.run(b)) # [1.]
tf.get_variable()
和tf.variable_scope()
是更强大的结合,可以设置 scope 下变量的默认属性。但对 tf.Variable() 来说,没有这些功能。
图 1 说明 a
的初始化是自己的 initial_value,regularizer 也未作用到 a
上。而 b
得到了这两个默认属性。
- reuse 功能给了编程更大的灵活性
def forward(input_tensor):
with tf.variable_scope('vs', reuse=tf.AUTO_REUSE, initializer=tf.constant_initializer(3.0)):
w = tf.get_variable(name='w', shape=[1])
return input_tensor * w
a = tf.get_variable(name='input', shape=[1], initializer=tf.constant_initializer(2.0))
b = forward(a)
c = forward(b)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(sess.run(a)) # [2.]
print(sess.run(b)) # [6.]
print(sess.run(c)) # [18.]
![]() | ![]() |
---|---|
get_variavble() | Variavble() |
可以看到,w
为 tf.get_variable()
时得到了 reuse,非常方便。而 w
为 tf.Variable()
则不 reuse。
结论
tf.get_variable()
和tf.variable_scope()
一起可以发挥强大灵活的作用。而tf.Variable()
只把tf.variable_scope()
当作简化的tf.name_scope()
使用。
2.6 tf.name_scope() 和 tf.variable_scope()
- 从
2.2
和2.3
可以看到,tf.name_scope()
和tf.variable_scope()
下,tf.Variable()
的名字都会加上前缀scope
;
2.4
说明tf.name_scope()
对tf.get_variable()
不起作用; tf.name_scope()
更像是tf.variable_scope()
的阉割版,从形参上看,前者较为简单,被后者完全覆盖;tf.variable_scope()
的形参中存在大量针对变量的参数(几乎是 name_scope + get_variable 参数的合集),如initializer
和regularizer
等,可以指定scope
内创建的变量的默认属性;- 重名+1:与
tf.Variable()
一样,空间重名 +1,(tf.variable_scope()
不使用tf.get_variable()
时)
with tf.name_scope('s'):
a = tf.Variable(0.0, name='v')
c = tf.get_variable(name='v', shape=[1], initializer=tf.constant_initializer(1.0))
with tf.name_scope('s'):
b = tf.Variable(0.0, name='v')
print(a) # <tf.Variable 's/v:0' shape=() dtype=float32_ref>
print(b) # <tf.Variable 's_1/v:0' shape=() dtype=float32_ref>
print(c) # <tf.Variable 'v:0' shape=(1,) dtype=float32_ref>
with tf.variable_scope('s'):
a = tf.Variable(0.0, name='v')
c = tf.get_variable(name='v', shape=[1], initializer=tf.constant_initializer(1.0))
with tf.variable_scope('s'):
b = tf.Variable(0.0, name='v')
# d = tf.get_variable(name='v', shape=[1], initializer=tf.constant_initializer(1.0))
print(a) # <tf.Variable 's_2/v:0' shape=() dtype=float32_ref>
print(b) # <tf.Variable 's_3/v:0' shape=() dtype=float32_ref>
print(c) # <tf.Variable 's/v_1:0' shape=(1,) dtype=float32_ref>,变量重名 +1
# print(d) # Err,对于 b = Variable() 而言,s+1,对于 d = tf.get_variable() 而言,s 内变量重名报错
Note1 可以看到,当用于 tf.Variable()
时,tf.variable_scope()
和 tf.name_scope()
没什么区别,重名的话都会使空间名自动 +1,即使混用,也一样;
Note2 发现一个很有意思的现象:
在第三个 scope 中,a 和 c 竟然不是同一个 scope,{a:s_2, c:s},可见,当
tf.variable_scope()
用于tf.get_variable()
,空间会另起炉灶(这个新炉灶不会+1)。
且新炉灶的第一个tf.get_variable()
前有tf.Variable()
创建的重名变量的话,则命名重复 +1。
所以,tf.variable_scope()
下,tf.get_variable()
和tf.Variable()
混用时,实际上是各自独立的。
总结
当使用
tf.Variable()
时,tf.name_scope()
和tf.variable_scope()
没什么区别;
当使用tf.get_variable()
时,tf.name_scope()
不起作用。
tf.variable_scope()
下,tf.get_variable()
和tf.Variable()
混用时,实际上是各自独立的