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–stringorVariableScope: 要打开的 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()混用时,实际上是各自独立的


876

被折叠的 条评论
为什么被折叠?



