本文介绍了tensorflow中的tf.get_variable和tf.variable_scope方法。本文通过CNN网络为例,参考《深入理解Tensorflow架构设计与实现原理》。
局部变量及不足
下面给出两层卷积层模型的代码,下面的代码,看起来非常简单、清晰。是我们初学者非常喜欢的一种方式。这两层卷积层涉及到4个参数,分别是conv1_weights、conv1_biases、conv2_weights和conv2_biases。那么,我们在外部调用这个函数时,每次掉要,都需要在内存中申请一段内存,来初始化相同的变量。这样,如果在多次复用,将会导致内存溢出。
import tensorflow as tf
def my_image_filter(input_images):
# 第一层卷积
conv1_weight = tf.Variable(tf.random_normal([5, 5, 32, 32]), name='conv1_weights')
conv1_biases = tf.Variable(tf.zeros([32]), name='conv1_biases')
conv1 = tf.nn.conv2d(input_images, conv1_weight,
strides=[1, 1, 1, 1], padding='SAME')
relu1 = tf.nn.relu(conv1 + conv1_biases)
# 第二层卷积
conv2_weight = tf.Variable(tf.random_normal([5, 5, 32, 32]), name='conv2_weights')
conv2_biases = tf.Variable(tf.zeros([32]), name='conv2_biases')
conv2 = tf.nn.conv2d(relu1, conv2_weight,
strides=[1, 1, 1, 1], padding='SAME')
return tf.nn.relu(conv2 + conv2_biases)
全局变量及不足
那么,既然每次调用函数的时候,都需要对函数的成员变量进行初始化,那么下面的代码给出了将变量定义为全局变量的形式。这种方法,将模型参数提出来,保存在外部的字典中,这样,在模型多次复用的时候,就不需要多次初始化相同的值了,看上去是解决了模型复用问题,但是,它破坏了模型的封装性,降低了代码的可读性。读者在读取my_image_filter的时候,还需要关心variables_dict字典中的结构,代码可读性很差。而且一旦程序结构发生变化,我们需要修改模型方法my_image_filter和字典variables_dict的值。
import tensorflow as tf
variables_dict = {
'conv1_weights': tf.Variable(tf.random_normal([5, 5, 32, 32]), name='conv1_weights'),
'conv2_weights': tf.Variable(tf.random_normal([5, 5, 32, 32]), name='conv1_weights'),
'conv1_biases': tf.Variable(tf.zeros([32]), name='conv1_biases'),
'conv2_biases': tf.Variable(tf.zeros([32]), name='conv2_biases')
}
def my_image_filter(input_images, variables_dict):
conv1 = tf.nn.conv2d(input_images, variables_dict['conv1_weights'],
strides=[1, 1, 1, 1], padding='SAME')
relu1 = tf.nn.relu(conv1 + variables_dict['conv1_biases'])
conv2 = tf.nn.conv2d(relu1, variables_dict['conv2_weights'],
strides=[1, 1, 1, 1], padding='SAME')
return tf.nn.relu(conv2 + variables_dict['conv2_biases'])
result1 = my_image_filter(image1, variables_dict)
result2 = my_image_filter(image2, variables_dict)
tensorflow解决方法
tensorflow提供了一种巧妙的方式解决了这两种问题,就是通过变量的作用域方式。通过tf.get_variable和tf.variable_scope方法。
- tf.get_variable方法负责创建和获取指定名称的变量(是根据名字获取的!)
- tf.variable_scope负责管理命名空间
tf.get_variable
这种方法的主要输入参数有三个,分别是name,shape,initializer,分别表示变量的名称、形状、初始化方法。
import tensorflow as tf
def conv_relu(input, kernal_shape, bias_shape):
# 创建或获取名字叫weights的变量
weights = tf.get_variable('weights', kernal_shape,
initializer=tf.random_normal_initializer())
# 创建获取名字叫biases的变量
biases = tf.get_variable('biases', bias_shape,
initializer=tf.constant_initializer())
conv = tf.nn.conv2d(input, weights, strides=[1, 1, 1, 1], padding='SAME')
return tf.nn.relu(conv + biases)
tf.variable_scope
tf.variable_scope传入的第一个参数为命名空间的名字。
需要注意的是reuse这个参数。默认值为False。当reuse=False时,在第二次调用my_image_filter函数的时候,会抛出异常,显示变量已经存在。reuse=True表示共享该作用域内的参数,即可以多次调用。
import tensorflow as tf
# 变量作用域的使用
def my_image_filter(input_images):
# 创建conv1/weights和conv1/biases变量,reuse表示共享作用域的参数
with tf.variable_scope('conv1', reuse='True'):
relu1 = conv_relu(input_images, [5, 5, 32, 32], [32])
# 创建conv2/weights和conv2/biases变量,reuse表示共享作用域的参数
with tf.variable_scope('conv2', reuse='True'):
return conv_relu(relu1, [5, 5, 32, 32], [32])
参考资料
《深入理解Tensorflow架构设计与实现原理》