变量管理

import tensorflow as tf

TensorFlow提供了通过变量名称来创建或者获取一个变量的机制,通过这个机制,在不同的函数中可以直接通过变量的名字来使用变量,
而不需要将变量通过参数的形式到处传递,避免了参数太多而不好管理的情况出现。此机制通过 tf.variable_scopetf.get_variable 函数实现。

  1. TensorFlow 可通过 tf.Variable 来创建变量;
  2. 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_variabletf.Variable 最大的区别在于指定变量名的参数:

  1. tf.Variable 变量名为可选
  2. tf.get_variable 的变量名为必填项,tf.get_variable会根据这个名字去创建或者获取变量
  3. 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函数,在生成上下文管理器时:

  1. 将参数reuse设置为True.这样tf.get_variable会直接获取已经声明的变量,如若变量不存在则会报错;
  2. reuseNoneFalse 时,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_scopetf.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)

当神经网络结构更加复杂,参数更多时,使用这种变量管理的方式将大大提高程序的可读性。

posted @ 2017-11-03 13:48  xinet  阅读(1892)  评论(0编辑  收藏  举报