在tensorflow中有时会将某个部分独立出来成为一个函数,比如把前向传播函数提取出来,成为一个独立的接口,这时就需要传递一定的调用参数,如隐藏层权重,偏置项等。当神经网络的结构非常复杂,参数更多时,就需要一个更好的方式来出传递和管理神经网络中的参数。
TensorFlow提供了通过变量名称来创建或获取一个变量的机制,通过这个机制,在不同的函数中可以直接通过变量名来获取变量,而不是将变量通过参数的形式到处传递。TensorFlow中通过变量名称获取变量的机制主要是通过 tf.get_variable
和 tf.variable_scope
函数实现的。
# 下面两个定义是等价的
v = tf.get_variable('v', shape=[1], initializer=tf.constant_initializer(1.0))
v = tf.Variable(tf.constant(1.0, shape=[1]), name='v')
可以看出两者的参数基本一致。
TensorFlow中提供了一些initializer函数进行变量初始化
初始化函数 | 功能 | 主要参数 |
---|---|---|
tf.constant_initializer | 将变量初始化为给定常数 | 常量的初值 |
tf.random_normal_initializer | 将变量初始化为满足正态分布的随机值 | 正态分布的均值和标准差 |
tf.truncated_normal_initializer | 将变量初始化为满足正态分布的随机值,但如果随机出来的值偏离平均值超过两个标准差,那么这个数将会被重新随机 | 正态分布的均值和标准差 |
tf.random_uniform_initializer | 将变量初始化为满足平均分布的随机值 | 最小、最大值 |
tf.uniform_unit_scaling_initializer | 将变量初始化为满足平均分布但不影响输出数量级的随机值 | factor(产生随机值时乘以的系数) |
tf.zeros_initializer | 将变量设置为全0 | 变量维度 |
tf.ones_initializer | 将变量设置为全1 | 变量维度 |
想要具体查看某个函数的介绍和参数等,可以使用 help
函数
如 help(tf.zeros_initializer)
有一定需要注意的是,在使用如 tf.zeros_initializer
或者 tf.ones_initializer
时,在 get_variable
函数中不应该指定 shape
参数,否则会报错
# ValueError: If initializer is a constant, do not specify shape.
tf.get_variable('v', shape=[1,2], initializer=tf.zeros_initializer([1,2]))
初始化常量值时不能指定shape参数,正确的做法应该是
v = tf.get_variable('v', initializer=tf.zeros_initializer([1,2]))
注意 tf.zeros_initializer([1,2])
其中的第一个参数是个列表,就是变量的维度,所以前面不需要指定。
还需要注意的是,这样定义的变量是需要初始化的。
sess.run(v.initializer)
sess.run(v)
使用
真正使用的 get_variable
时候一般会配合 variable_scope
加上一个命名空间。
# 在名字在foo的命名空间内创建名字为v的变量
with tf.variable_scope("foo"):
v = tf.get_variable("v", [1], initializer=tf.constant_instalizer(1.0))
# 因为在命名空间foo中已经存在名字为v的变量, 所以下面的代码会报错
with tf.variable_scope("foo"):
v = tf.get_variable("v")
# 在生成上下文管理器时,将参数reuse设置为True,
# 这样tf.get_variable函数将直接获取已经声明的变量
with tf.variable_scope("foo", reuse=True):
v1 = tf.get_variable("v")
print(v == v1) # 将输出 True 代表v1和v是相同的TensorFlow变量
# 设置了reuse为true时 tf.get_variable只会获取已存在的变量,而不会创建
# 因此下面的代码会报错 因为命名空间bar中还没有定义名字为v的变量
with tf.variable_scope("bar", reuse=True):
v2 = tf.get_variable("v")
TensorFlow中 tf.variable_scope
是可以嵌套的,当内嵌在别的scope中时,当前scope的reuse参数的取值和最接近的外面那层是一致的。
with tf.variable_scope("root"):
# 可以通过 tf.get_variable_scope() 函数来获取当前上下文管理器的reuse参数
print(tf.get_variable_scope().reuse) # 输出为False
# 新建一个嵌套的上下文管理器,并指定reuse参数为True
with tf.variable_scope("foo", reuse=True):
print(tf.get_variable_scope().reuse) # 输出为True
# 内嵌一个上下文管理器 不知道reuse参数
# 这时reuse的取值会和外面一层保持一致
with tf.variable_scope("bar"):
print(tf.get_variable_scope().reuse) # 输出为True
# 退出reuse设置为True的上下文后reuse的值又回到了False
print(tf.get_variable_scope().reuse) # 输出为False
tf.variable_scope
函数生成的上下文管理器也会创建一个TensorFlow中的命名空间,在命名空间中创建的变量名称都会带上这个命名空间名作为前缀。
v1 = tf.get_variable("v", [1])
# 输出v:0 "v"为变量名称 ":0"表示这个变量是生成变量这个运算的第一个结果
print(v1.name)
with tf.variable_scope("foo"):
v2 = tf.get_variable("v", [1])
# 输出foo/v:0 在tf.variable_scope中创建的变量,名称前面会加入命名空间的名称
# 并通过/来分割命名空间的名称和变量的名称
print(v2.name)
with tf.variable_scope("foo"):
with tf.variable_scope("bar"):
v3 = tf.get_variable("v", [1])
# 输出foo/bar/v:0 命名空间可以嵌套
# 同时变量的名称也会加入所有命名空间的名称作为前缀
print(v3.name)
v4 = tf.get_variable("v1", [1])
# 输出foo/v1:0 当命名空间退出之后
# 变量名称也就不会再加入其前缀了
print(v4.name)
# 创建一个名称为空的命名空间 并设置reuse=True
with tf.variable_scope("", reuse=True):
# 可以直接通过带命名空间名称的变量名来获取其它命名空间下的变量
v5 = tf.get_variable("foo/bar/v")
print(v5 == v3) # 输出True
v6 = tf.get_variable("foo/v1")
print(v6 == v4) # 输出True