在TensorFlow中,声明变量可以使用接口tf.Variable和tf.get_variable,而tf.name_scope,tf.variable_scope提供变量空间管理功能。下面举例说明:
1. tf.name_scope,tf.variable_scope
这2个接口的功能都是提供变量空间空间管理功能,不同点在于tf.name_scope一般用于使网络关系在tensorboard上显示的时候更加清晰,比如输入层有哪些op,输出层有哪些op等。而tf.variable_scope一般与tf.get_variable搭配使用获取变量。下面会举例说明:
2. tf.Variable
创建变量最简单的方法就是使用接口tf.Variable,可以使用随机数生成函数或者常数生成函数对其进行初始化:
import tensorflow as tf
a = tf.Variable(tf.random_normal([3, 4], stddev=1))
with tf.Session() as sess:
sess.run(tf.global_variables_initializer()) # 使用变量一定要先初始化
print ('a = ', a)
print ('a.value = ', sess.run(a))
# 以下是打印结果:
('a = ', <tf.Variable 'Variable:0' shape=(3, 4) dtype=float32_ref>)
('a.value = ', array([[ 0.33135629, -0.09183439, -0.59041327, 0.44321144],
[ 0.03753942, 1.42844248, 0.05517919, 0.6813044 ],
[-0.42053661, -1.7622211 , -0.41865498, -0.91136199]], dtype=float32))
常用的随机数生成函数有3个:
函数名称 | 随机数分布 | 主要参数 |
random_normal | 正态分布 | 形状,平均值,标准差,数据类型 |
truncated_normal | 正态分布,生成的数字大小最多偏离平均值2个标准差 | 同上 |
random_uniform | 平均分布 | 形状,最大值,最小值,数据类型 |
常见的常数生成函数有4个:
函数名称 | 功能 |
zeros | 生成全是0的数组 |
ones | 生成全是1的数组 |
fill | 生成全是某个数的数据 |
constant | 可以实现fill的功能,但同时可以生成给定常值得数据 如tf.constant([1,2,3]), 则可以生成一个数组,值为[1, 2, 3] |
eye | 生成对角阵数组 |
# 常用的几个参数含义:
tf.Variable(initial_value=None, trainable=True, collections=None, name=None)
# 1. initial_value为上述初始化值;
# 2. trainable表示这个变量是否可以训练,默认为True,一般来说,网络中的权重weights,
# 偏置bias等量都使用True,但类似于学习率,global_step等值都是非trainable的;
# 3. collections:默认所有的变量都会被添加到 GraphKeys.GLOBAL_VARIABLES中,
# 这是一个TensorFlow默认的集合。此处也可以设置自定义的collections,
# 之后可以通过接口tf.get_collection获取所有包含在此collections的variable,
# 这在使用l2正则中非常常见,下面会举例说明;
# 4. name:顾名思义就是给这个variable取名字,默认都是'Variable:0','Variable:1'.
3. tf.get_variable
tf.get_variable既可以生成变量,如果这个变量从来没有生成过,也可以获取之前已经生成好的变量。
# 常用 的几个参数含义:
tf.get_variable(name, shape=None, dtype=None, initializer=None, regularizer=None, trainable=True, collections=None)
# 1. name:顾名思义就是待获取的这个variable的名字,此值必须要指定.
# 2. shape:数组的形状,比如shape[1,2]就是生成一个一行两列的二维数据;
# 3. dtype:数据类型,常见的有tf.int32, tf.float32等;
# 4. initializer:初始化函数,下面会给出几个常见的,此处和tf.Variable类似;
# 5. regularizer:设置正则化函数,比如l2正则tf.contrib.layers.l2_regularizer(0.1)
# 6. trainable:这个变量是否可以训练,与tf.Variable类似;
# 7. collections:与tf.Variable类似;
常见的初始化函数:
初始化函数 | 功能 |
constant_initializer() | 初始化为常数 |
random_normal_initializer() | 初始化满足正态分布的随机数值 |
truncated_normal_initializer() | 截断正态分布,即生成的数值都在2个标准差以内 |
random_uniform_initializer() | 满足平均分布 |
zeros_initializer() | 生成全0值 |
ones_initializer() | 生成全1值 |
首先说一下什么是tensor,它不是一个向量,而是保存运算结果的属性,这个属性包含3向内容:op的名字,数据类型,数据维度shape。以上面打印的变量a来说,结果为 <tf.Variable 'Variable:0' shape=(3, 4) dtype=float32_ref>,变量a就是一个tensor,名字为 'Variable:0',shape=(3,4),数据类型是float32。
tf.name_scope只改变tf.Variable变量的名字,不改变tf.get_variable的名字,但tf.variable_scope会改变。
with tf.name_scope("test1"):
a = tf.Variable(tf.random_normal([3, 4], stddev=1), name='a')
b = tf.get_variable(name='b', shape=[1,2], initializer=tf.random_normal_initializer(seed=0))
print (a) # <tf.Variable 'test1/a:0' shape=(3, 4) dtype=float32_ref>
print (b) # <tf.Variable 'b:0' shape=(1, 2) dtype=float32_ref>
with tf.variable_scope('test2'):
c = tf.Variable(tf.random_normal([3, 4], stddev=1), name='c')
d = tf.get_variable(name='d', shape=[1,2], initializer=tf.random_normal_initializer(seed=0))
print (c) # <tf.Variable 'test2/c:0' shape=(3, 4) dtype=float32_ref>
print (d) # <tf.Variable 'test2/d:0' shape=(1, 2) dtype=float32_ref>
with tf.variable_scope('test2'):
e = tf.Variable(tf.random_normal([3, 4], stddev=1), name='c') # 不报错
# f会报错,理由是f的名字与d的名字一样都是test2/d:0,数据类型和shape也一样,但是tf.variable_scope没有设置成reuse的
f = tf.get_variable(name='d', shape=[1,2], initializer=tf.random_normal_initializer(seed=0))
with tf.variable_scope('test2', reuse=True):
# g不报错,如果上面的tf.variable_scope接口的reuse=tf.AUTO_REUSE或者True都可以
g = tf.get_variable(name='d', shape=[1,2], initializer=tf.random_normal_initializer(seed=0))
h = tf.Variable(tf.random_normal([3, 4], stddev=1), name='c')
print (h) # <tf.Variable 'test2_2/c:0' shape=(3, 4) dtype=float32_ref>
最后说一下接口中的参数regularizer和collections的具体用法:
在网络训练的时候经常用到l2正则或者l1正则来防止网络产生过拟合,给权值加上正则的方法很简单,就是调用接口tf.contrib.layers.l2_regularizer(0.1)或者tf.contrib.layers.l1_regularizer(0.1)其中0.1表示正则化系数,而使用了正则,如何在loss中体现这一部分呢?使用正则后,默认将该op归类为tf.GraphKeys.REGULARIZATION_LOSSES,在计算loss的时候通过接口tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)取出即可:
with tf.variable_scope('test'):
# 为方便我们计算检验,此处选择l1正则,正则系数选择1.0
a = tf.get_variable(name='a',
shape=[1, 2],
initializer=tf.random_normal_initializer(seed=0),
regularizer=tf.contrib.layers.l1_regularizer(1.0))
loss = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print (sess.run(a)) # [[-0.39915764 2.1044395 ]]
print (sess.run(loss)) # [2.5035973]
# 可以发现2.5035973 = 1.0*(|-0.39915764| + |2.1044395|)
问题:
接口tf.get_variable中的参数collections按道理来说可以自定义,但此处自定义之后,之后运行一直报错如下,不明白原因:
with tf.variable_scope('test'):
a = tf.get_variable(name='a',
shape=[1,2],
initializer=tf.random_normal_initializer(seed=0),
regularizer=tf.contrib.layers.l1_regularizer(1.0),
collections=['abc'])
loss = tf.get_collection('abc')
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print (sess.run(a))
# 报错:tensorflow.python.framework.errors_impl.FailedPreconditionError: Attempting to use uninitialized value test/a
print (sess.run(loss))
4. tf.get_variable_scope()
顾名思义:就是获取当前的variable_scope,用在何处?参见这篇博客,就是说当人家已经把模型训练好了,此时我们想要使用人家训练好的结果那就没有办法修改人家设计的图,但我们又想要实现变量共享,那么就使用这个接口。tf.get_variable_scope().reuse_variables() 将在当前的variable_scope下,将变量设置为reuse = True,看名字也能知道代表的意义,就是两个输入可以共享变量了,具体的例子见那篇博客,不多赘述。