2017年7月12日星期三

TensorFlow编程手册:sharing variables

(本文来自于 官网,仅供学习使用,文中[*]表示见解释【*】)
目前为止,你已经能 create, initialize, save and load single variables了。但是当建立复杂的网络时,你需要共享大量的权值并且也许你需要一次性初始化它们,下面 介绍tf.variable_scope()和tf.get_variable().

1.想象你创建一个具有两个卷积层的网络,如果使用tf.Variable,模型将会是下面的样子
def my_image_filter(input_images):
    conv1_weights = 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_weights,
        strides=[1, 1, 1, 1], padding='SAME')
    relu1 = tf.nn.relu(conv1 + conv1_biases)

    conv2_weights = 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_weights,
        strides=[1, 1, 1, 1], padding='SAME')
    return tf.nn.relu(conv2 + conv2_biases)
可以看出,模型一旦变得更复杂,就会造成更多的复杂性,即使现在只有两层,也已经有了4个不同的变量。
当你想重用这个模型的时候,问题就会出现了。假定你想应用你的模型到2个不同的图image1和image2时,你想要它们通过相同的模型(并且带有相同的参数),你可以两次运行my_image_filter(),但是他创建了2个集合,每个集合四个变量,总共8个变量。
#
#first call creates one set of 4 varaibles
result = my_image_filter(image)
#another set of 4 variables is creates in the second call
result2 = my_image_filter(image2)

2.下面有一个共享变量的方法
variables_dict = {
    "conv1_weights" :tf.Variable(tf.random_normal([5,5,32,32])[1],
            name="conv1_weights")
    "conv1_biases" : tf.Variable(tf.zeros([32]),name="conv1_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"])

# Both calls to my_image_filter() now use the same variables
result1 = my_image_filter(image1, variables_dict)
result2 = my_image_filter(image2, variables_dict)
虽然方便,但在代码之外创建如上所述的变量会破坏封装:

     构建图形的代码必须记录要创建的变量的名称,类型和形状。
     代码更改时,调用者可能必须创建更多或更少或不同的变量。

解决问题的一种方法是使用类创建一个模型,其中类负责管理所需的变量。 对于较轻的解决方案,不涉及类,TensorFlow提供了一个variable scope机制,允许在构建图形时轻松共享命名变量。

3.variable scope example
TF中的Variable scope机制包括两个主要功能:
    tf.get_variable(<name>,<shape>,<initializer>):创建或返回具有给定名称的变量。
    tf.variable_scope(<scope_name>):管理传递给tf.get_variable()的名称的名称空间
函数tf.get_variable()被用于获取或创建一个variable而不是直接调用tf.Variable, 它使用initializer而不是直接传递值,如在tf.Variable中。 一个initializer是一个函数,它获取shape,并提供一个具有该shape的张量。 以下是TensorFlow中的一些initializer:
     tf.constant_initializer(value)将所有内容初始化为提供的值,
     tf.random_uniform_initializer(a,b) initializes uniformly from [a, b],
     tf.random_normal_initializer(mean,stddev)从给定的平均值和标准偏差的正态分布初始化。

tf.get_variable到底是如何解决我们上述提到的问题的呢?首先,让我们重构代码,讲卷积的代码写到一个函数里,称为conv_relu.
def conv_relu(input, kernel_shape, bias_shape):
    # Create variable named "weights".
    weights = tf.get_variable("weights", kernel_shape,
        initializer=tf.random_normal_initializer())
    # Create variable named "biases".
    biases = tf.get_variable("biases", bias_shape,
        initializer=tf.constant_initializer(0.0))
    conv = tf.nn.conv2d(input, weights,
        strides=[1, 1, 1, 1], padding='SAME')
    return tf.nn.relu(conv + biases)

此功能使用短名称“weights”和“biases”。 我们希望将它用于conv1和conv2,但变量需要具有不同的名称。 这就是tf.variable_scope()发挥作用的地方:它指定变量的命名空间。

def my_image_filter(input_images):
    with tf.variable_scope("conv1"):
        # Variables created here will be named "conv1/weights", "conv1/biases".
        relu1 = conv_relu(input_images, [5, 5, 32, 32], [32])
    with tf.variable_scope("conv2"):
        # Variables created here will be named "conv2/weights", "conv2/biases".
        return conv_relu(relu1, [5, 5, 32, 32], [32])

现在,让我们看看当我们调用my_image_filter()两次时会发生什么
result1 = my_image_filter(image1)
result2 = my_image_filter(image2)
# Raises ValueError(... conv1/weights already exists ...)
为了共享他们,你需要通过设定reuse_variables()来指定他们
with tf.variable_scope("image_filters") as scope:
    result1 = my_image_filter(image1)
    scope.reuse_variables()
    result2 = my_image_filter(image2)
这是一个共享权重的好方法

4.Variable scope是怎么工作的
为了理解variable scope是怎么工作的,首先要理解tf.get_variable()是怎么工作的,调用如下:
v = tf.get_variable(name, shape, dtype, initializer)
这种调用的出现有两种情况。
case1: 创建新的变量,此时tf.get_variable_scope().resuse == False
在这种情况下,v将是新创建的具有shape和data type的tf.Variable。 创建的变量的全名将被设置为当前变量范围名称+提供的名称,并且将执行检查以确保没有存在此全名的变量。 如果具有此全名的变量已存在,则该函数将引发ValueError。 如果创建了一个新变量,它将被初始化为值initializer(shape)。 例如:

with tf.variable_scope("foo"):
    v = tf.get_variable("v",[1])
assert v.name == "foo/v:0"

case 2:变量已经存在,此时tf.get_variable_scope().resuse ==True
with tf.variable_scope("foo")
    v = tf.get_variable("v",[1])
with tf.variable_scope("foo",reuse=True)
    v1 = tf.get_variable("v",[1])
assert v1 is v


4.1 Basics of tf.variable_scope()
知道get_variable如何工作之后,会让你更容易理解variable scope. variable scope的主要功能是携带将用作变量名称的前缀的名称和重用标志来区分上述两种情况。 嵌套变量作用域以类似于目录的工作方式追加其名称:
variable_scope就相当于给变量加前缀,为了区分,相加几个就加几个
with tf.variable_scope("foo"):
    with tf.variable_scope("bar"):
        v = tf.get_variable("v", [1])
assert v.name == "foo/bar/v:0"


[1]解释关于random_normal函数:http://blog.csdn.net/youngdreamnju/article/details/53993719




























没有评论:

发表评论

leetcode 17

17.   Letter Combinations of a Phone Number Medium Given a string containing digits from   2-9   inclusive, return all possible l...