版权声明:本文由洛北辰南原创,转载请注明原创地址,谢谢。 https://blog.csdn.net/qq_18941713/article/details/88553964
文章目录
5.3 变量管理
前一节将计算神经网络前向传播结果的过程抽象成了一个函数。通过这种方式在训练和测试的过程中可以统一调用同一个函数来得到模型的前向传播结果,其定义为:
def inference(input_tensor, avg_class, weights1, biases1, weights2, biases2):
这个函数的参数包括了神经网络中的所有参数。然而当神经网络结构更加复杂、参数更多时,就需要一个更好的方式来传递和管理神经网络中的参数了。
TensorFlow提供了通过变量名称来创建或者获取一个变量的机制。通过这个机制,可以在不同函数中直接通过变量名字来使用变量,而不用传递参数。
# 下面这两个定义是等价的
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中的变量初始化函数
初始化函数 | 功能 | 主要参数 |
---|---|---|
tf.constant_initializer | 将变量初始化为给定常量 | 常量的取值 |
tf.random_normal_initializer | 将变量初始化为满足正态分布的随机值 | 正态分布的均值和标准差 |
tf.truncated_nornal_initializer | 将变量初始化为满足正态分布的随机值,但如果随机出来的值偏离平均值超过2个标准差,那么这个数将被重新随机 | 正态分布的均值和标准差 |
tf.random_uniform_initializer | 将变量初始化为满足平均分布的随机值 | 最大最小值 |
tf.uniform_unit_scaling_initializer | 将变量初始化为满足平均分布但不影响输出数量级的随机值 | factor(产生随机值所乘的系数) |
tf.zeros_initializer | 将变量全部设置为0 | 变量维度 |
tf.ones_initializer | 将变量全部设置为1 | 变量维度 |
变量管理样例
# -*- coding: utf-8 -*-
# @Time : 2019/3/15 10:22
# @Author : Chord
import tensorflow as tf;
# 名字为foo的命名空间内创建名字为v的变量
with tf.variable_scope("foo"):
v = tf.get_variable(
"v", [1], initializer=tf.constant_initializer(1.0)
)
# 因为命名空间foo中已经存在名字为v的变量,所以下代码会报错
# ValueError: Variable foo/v already exists, disallowed.
# Did you mean to set reuse=True or reuse=tf.AUTO_REUSE in VarScope?
with tf.variable_scope("foo"):
v = tf.get_variable("v")
with tf.variable_scope("foo", reuse=True):
v1 = tf.get_variable("v")
print(v == v1) # 输出True,代表成功获取了命名空间foo的变量v
# 将参数reuser设置为True时, tf.variable_scope将只能获取已经创建过的变量。
# 因为在命名空间bar中还没有创建变量v,所以以下代码会报错:
# ValueError: Variable bar/v does not exist, or was not created with tf.get_variable().
# Did you mean to set reuse=tf.AUTO_REUSE in VarScope?
with tf.variable_scope("bar", reuse=True):
v = tf.get_variable("v", [1])
以上样例简单说明了通过tf.variable_scope函数可以控制tf.get_variable函数的语义。当reuse=True时,tf.get_variable函数会直接获取已经创建的变量。如果变量不存在,则函数会报错;相反如果reuse=False时,tf.get_variable函数会创建新变量。若同名变量已存在,则函数报错。
tf.variable_scope函数是可以嵌套的,下面的程序说明了当其嵌套时,reuse的值是如何确定的。
with tf.variable_scope("root"):
# 可以通过tf.get_variable_scope().reuse函数来
# 获取当前上下文管理器的reuse值
print(tf.get_variable_scope().reuse)
with tf.variable_scope("foo", reuse=True):
print(tf.get_variable_scope().reuse)
with tf.variable_scope("bar"):
print(tf.get_variable_scope().reuse)
print(tf.get_variable_scope().reuse)
# output
False
True
True
False
以下代码显示了如何通过tf.variable_scope来管理变量的名称
v1 = tf.get_variable("v", [1])
print(v1.name) # 输出v:0,"v"是变量名字,"0"表示这个变量是生成变量这个运算的第一个结果
with tf.variable_scope("foo"):
v2 = tf.get_variable("v", [1])
print(v2.name) # 输出foo/v:0,在命名空间中创建的变量,名字中会带有命名空间作为前缀
with tf.variable_scope("foo"):
with tf.variable_scope("bar"):
v3 = tf.get_variable("v", [1])
print(v3.name) # 输出foo/bar/v:0,命名空间可以嵌套
v4 = tf.get_variable("v1", [1])
print(v4.name) # 输出foo/v1:0
# 创建一个名为空的命名空间,设置reuse=True
with tf.variable_scope("", reuse=True):
v5 = tf.get_variable("foo/bar/v", [1]) # 可以通过带命名空间的变量名
# 来获取其他命名空间的下变量
print(v5 == v3)
v6 = tf.get_variable("foo/v1", [1])
print(v6 == v4)
# output
v:0
foo/v:0
foo/bar/v:0
foo/v1:0
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", reuse=reuse):
weights = tf.get_variable("weights", [LAYER1_NODE, OUTPUT_NODE],
initializer=tf.truncated_normal_initializer(stddev=0.1))
biases = tf.get_variable("biases", [OUTPUT_NODE],
initializer=tf.constant_initializer(0.0))
layer2 = tf.matmul(layer1, weights) + biases
# 返回最后的前向传播结果
return layer2
x = tf.placeholder(tf.float32, [None, INPUT_NODE], name='x-input')
y = inference(x)
使用上述代码所示的方式,就不在需要将所有变量作为参数传递到不同的函数中了。当神经网络结构更加复杂、参数更多时,使用这种变量管理方式将大大提高程序的可读性。