import tensorflow as tf
TensorFlow
提供了通过变量名称来创建或者获取一个变量的机制,通过这个机制,在不同的函数中可以直接通过变量的名字来使用变量,
而不需要将变量通过参数的形式到处传递,避免了参数太多而不好管理的情况出现。此机制通过 tf.variable_scope
和 tf.get_variable
函数实现。
TensorFlow
可通过tf.Variable
来创建变量;tf.get_variable
函数可创建(创建的功能基本等价于tf.Variable
)或者获取一个变量。
# 下面的两个定义是等价的
v1 = tf.Variable(tf.constant(1., shape = [1]), name= 'v1')
v2 = tf.get_variable('v2', shape=[1], initializer=tf.constant_initializer(1.))
TensorFlow
中的变量初始化函数
初始化函数 | 功能 | 主要参数 |
---|---|---|
tf.constant_initializer |
将变量初始化为给定常数 | 常数的取值 |
tf.random_normal_initializer |
将变量初始化为满足正态分布的随机值 | 正态分布的均值和标准差 |
tf.truncated_normal_initializer |
将变量初始化为满足正态分布的随机值,但若随机值偏离平均值超过2个标准差,则这个数会被重新随机 | 正态分布的均值和标准差 |
tf.random_uniform_initializer |
将变量初始化为满足平均分布的随机值 | 最大、最小值 |
tf.uniform_unit_scaling_initializer |
将变量初始化为满足平均分布但不影响输出数量级的随机值 | factor(产生随机值时乘以的系数) |
tf.zeros_initializer |
将变量初始化为全0 | 变量维度 |
tf.ones_initializer |
将变量初始化为全1 | 变量维度 |
更多参考:TensorFlow常用的函数
g1 = tf.Graph()
with g1.as_default():
v = tf.get_variable('v', shape = [1], initializer=tf.zeros_initializer())
tf.get_variable
和 tf.Variable
最大的区别在于指定变量名的参数:
tf.Variable
变量名为可选tf.get_variable
的变量名为必填项,tf.get_variable
会根据这个名字去创建或者获取变量tf.get_variable
首先会试图去创建一个名字为v
的参数,若创建失败(比如已经有了同名的参数),则程序会报错。这是为了避免无意识的变量复用造成的错误。
1. 在上下文管理器 foo
中创建变量 v
如果需要通过tf.get_variable
获取一个已经创建的变量,则需要通过tf.variable_scope
来生成一个上下文管理器,并明确指定在这个上下文管理器中tf.get_variable
将直接获取已经生成的变量。
# 在名字为foo的命名空间内创建名字为v的变量
with tf.variable_scope("foo"):
v = tf.get_variable("v", [1], initializer=tf.constant_initializer(1.0))
因为在命名空间foo中已经存在名字为v的变量,所以下面的程序会报错:ValueError
with tf.variable_scope("foo"):
v = tf.get_variable("v", [1])
在生成上下文管理器时,将参数reuse
设置为True
.这样tf.get_variable
会直接获取已经声明的变量。
with tf.variable_scope("foo", reuse=True):
v1 = tf.get_variable("v", [1])
print(v == v1) # 输出为 `True`代表v, v1 是相同的变量
True
将参数reuse
设置为True
时,tf.get_variable
将只能获取已经创建过的变量。
由于在命名空间bar中还没有创建名字为v的变量,所以下面的程序会报错
with tf.variable_scope("bar", reuse=True):
v = tf.get_variable("v", [1])
总结:
tf.variable_scope
函数,在生成上下文管理器时:
- 将参数
reuse
设置为True
.这样tf.get_variable
会直接获取已经声明的变量,如若变量不存在则会报错; reuse
为None
或False
时,tf.get_variable
会创建新的变量,如若同名的变量已经存在,则会报错。
2. 嵌套上下文管理器中reuse
参数的使用。
with tf.variable_scope("root"):
# 可以通过 `tf.get_variable_scope().reuse` 函数来获取当前上下文管理器中的 `reuse`参数的取值。
print(tf.get_variable_scope().reuse) # 输出 `False`,即最外层的`reuse`为 `False`
with tf.variable_scope("foo", reuse=True): # 新建一个嵌套的上下文管理器,
# 并指定 reuse=True
print(tf.get_variable_scope().reuse) # 输出 `True`
with tf.variable_scope("bar"): # 新建一个嵌套的上下文管理器,但不指定 reuse,这时 reuse 的
# 的取值会和外面一层保持一致
print(tf.get_variable_scope().reuse) # 输出 `True`
print(tf.get_variable_scope().reuse) # 输出 `False`。
# 退出 reuse 设置为 True的上下文之后,reuse 的值又回到了 False
False
True
True
False
3. 通过variable_scope来管理变量
tf.variable_scope
函数生成的上下文管理器也会创建一个TensoFlow中的命名空间,在命名空间内创建的变量名称都会带上这个命名空间名作为前缀。所以 tf.variable_scope
函数除了可以控制 tf.get_variable
执行的功能外,也提供了一个管理变量命名空间的方式。
v1 = tf.get_variable("u", [1])
print(v1.name) # 输出 `u:0`。 `u` 变量的名称,`:0` 表示这个变量是生成变量这个运算的第一个结果。
with tf.variable_scope("foo"):
v2 = tf.get_variable("u", [1])
print(v2.name) # 在 `tf.variable_scope` 中创建的变量,名称前面会加入命名空间的名称,
# 并通过 `/`来分隔命名空间名称和变量名称
u:0
foo/u:0
with tf.variable_scope("foo"):
with tf.variable_scope("bar"):
v3 = tf.get_variable("v", [1])
print (v3.name) # 命名空间可以嵌套,同时变量的名称也会加入所有的命名空间的名称作为前缀
v4 = tf.get_variable("v1", [1]) # 当命名空间退出后,变量名称也就不会被加入其前缀了
print( v4.name)
foo/bar/v:0
foo/v1:0
4. 我们可以通过变量的名称来获取变量。
- 创建一个名称为空的命名空间,并设置
reuse=True
with tf.variable_scope("",reuse=True):
# 可以直接通过带命名空间名称的变量名来获取其他命名空间下的变量
v5 = tf.get_variable("foo/bar/v", [1]) # 指定名称 "foo/bar/v" 来获取在命名空间 "foo/bar" 中创建的变量
print(v5 == v3) # 输出 `True`
v6 = tf.get_variable("foo/v1", [1])
print (v6 == v4) # 输出 `True`
True
True
通过 tf.variable_scope
和 tf.geT_variable
可对前向传播进行改进
def inference(input_tensor, reuse = False):
# 定义第一层神经网络的变量和前向传播结果
with tf.variable_scope('layer1', reuse = reuse):
# 根据传进来的`reuse`来判断是创建新的变量还是使用已经创建好了的。
# 在第一次构造网络时需要创建新的变量,以后每次调用这个函数都直接使用 `reuse = True`就不需要每次将变量传进来了
weights = tf.get_variable('weights', [INPUT_NODE, LAYER1_NODE], initializer = tf.truncated_normal_initializer(stddev=0.1))
biases = tf.get_variable("biases", [LAYER1_NODE], initializer=tf.constant_initializer(0.0))
layer1 = tf.nn.relu(tf.matmul(input_tensor, weights) + biases)
# 类似地定义第二层神经网络的变量和前向传播结果
with tf.variable_scope('layer2'):
weights = get_weight_variable([LAYER1_NODE, OUTPUT_NODE], regularizer)
biases = tf.get_variable("biases", [OUTPUT_NODE], initializer=tf.constant_initializer(0.0))
layer2 = tf.matmul(layer1, weights) + biases
# 返回最后的前向传播结果
return layer2
INPUT_NODE = 100
x = tf.placeholder(tf.float32, [None, INPUT_NODE], name='x-input')
y = inference(x)
在程序中需要使用训练好的神经网络进行推导时,可以直接调用 inference(new_x, True)
;
如果需要使用滑动平均模型,只需要将计算滑动平均的类传到函数中即可,获取或者创建变量的部分不需要改变。
new_x = ...
new_y = inference(new_x, True)
当神经网络结构更加复杂,参数更多时,使用这种变量管理的方式将大大提高程序的可读性。