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)
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
)
)
print(a)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(sess.run(a))
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)
print(b)
print(c)
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 or VariableScope : 要打开的 scope;default_name – 如果参数 name_or_scope 是 None , 则启用默认名;values – 要传递给 op 函数的张量参数列表(不清楚);reuse – True , None , or tf.AUTO_REUSE ;
- 如果为
True ,该 scope 及其 sub-scope(reuse=None)开启 reuse 模式,get_variable(name='v') 将获取已有的 name == v 的变量; - 如果为
tf.AUTO_REUSE , get_variable(name='v') 创建新变量 if v 不存在 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)
with tf.variable_scope(scope, reuse=True):
b = tf.get_variable(name='a')
print(a is b)
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() 的 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)
print(b)
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))
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)
print(b)
print(c)
print(d)
只有在 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)
print(b)
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)
print(b)
此时,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))
print(a)
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)
print(b)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(sess.run(a))
print(sess.run(b))
tf.get_variable() 和 tf.variable_scope() 是更强大的结合,可以设置 scope 下变量的默认属性。但对 tf.Variable() 来说,没有这些功能。
图 1 正则化器 regularizer 的输入只有 v_1
图 1 说明 a 的初始化是自己的 initial_value,regularizer 也未作用到 a 上。而 b 得到了这两个默认属性。
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))
print(sess.run(b))
print(sess.run(c))
| |
---|
get_variavble() | Variavble() |
图2 tf.variable_scope() reuse 的作用
可以看到,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)
print(b)
print(c)
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')
print(a)
print(b)
print(c)
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() 混用时,实际上是各自独立的
|