variable_scope and get_variable

variable_scope and get_variable

      • tensorflow计算图
      • get_variable()函数
            • (一)当我定义节点tensor的时候是用tf.Variable()来定义的。
            • (二)利用tf.get_variable()方式定义。

关于tensorflow计算图、以及variable_scope() 和 get_variable()函数的一些想法:

tensorflow计算图

计算图

关于计算图,tensorflow会默认设置一个默认的计算图,计算图中的节点可以为tensorflow op,函数等操作

各节点之间的联系即为tensorflow的tensor,用于表示数据的流向以及计算之间的依赖关系

基于TensorFlow这个编程系统中的每一个计算都是计算图上的一个节点,而节点与节点之间的连线则代表计算之间的依赖关系。

variable_scope and get_variable_第1张图片

我们定义一个模型其实就是在默认的计算图上放置相应的节点(各种函数操作),然后定义他们之间的依赖关系(即设置数据流过程,代表图中的各节点之间的边)(就像有时候画思维导图的时候一样,不断的定义各种框,然后框与框之间的连线等等),顺便说一句,占位符placeholder也就是在构建各操作的时候,我先定义该节点的相关属性,先放在图里面,目的是为了方便我去构建数据流图。(就像在思维导图中我先用一个白色的框放在导图中,因为我还没想好这里表示的是什么,但是我知道它的前一个和后一个分别代表什么,那我就可以先空着,然后等找到了相关资料,我再把信息补上去)。

variable_scope and get_variable_第2张图片

tensorflow会根据用户定义的操作构建一个完整的计算图,然后通过调用session来初始化各参数和占位符(如果有的话),来进行对图中各参数的计算和更新

variable_scope和get_variable是用来当一个模型需要使用其他模型创建的变量时,两个模型一起训练,比如对抗网络中的生成器模型与判别器模型。这个时候就需要在不同model里共用一套参数来进行训练,所以就需要get_variable()函数。

get_variable()函数

tensorflow会构建一个默认的图,使得我们可以在上面定义各种操作。类似于python中相同变量之间所产生的问题 ,对tensorflow的计算图来说,同一个图中同一路径下的变量也是唯一的。,所以当我们在定义各种操作过程时,也会出现使用同一变量时的情况,此时tensorflow是如何操作的呢?

首先分析一下tf.Variable()及变量的命名规则.

例如,当我调用多个相同的类的时候,我们就相当于是在默认的计算图中构建了多个相同的数据流图(假设每个类都定义了自己的数据流图),此时,某一变量也就会在该默认的计算图中出现多次,此时,会出现变量命名上的问题。而tensorflow的处理方式分为两种情况:

(一)当我定义节点tensor的时候是用tf.Variable()来定义的。

比如有如下设计:

lass make_variable(object):
    def __init__(self):
        with tf.variable_scope('MLP/'):
            self.w1 = tf.Variable(initial_value=0.0, name='w1', dtype=tf.float32)

            self.b1 = tf.get_variable(name='b1', shape=[5],
                                      dtype=tf.float32,
                                      initializer=tf.constant_initializer(0.0))

            self.w2 = tf.get_variable(name='w2', shape=[5, num_class],
                                      dtype=tf.float32,
                                      initializer=tf.random_normal_initializer(
                                          mean=0.0, stddev=tf.sqrt(1.0 / 3)
                                      ))
            self.b2 = tf.get_variable(name='b2', shape=[num_class],
                                      dtype=tf.float32,
                                      initializer=tf.constant_initializer(0.0))

            print('make_variables reuse: ', tf.get_variable_scope().reuse)
            
# tf.AUTO_REUSE即是自动使得第一次使用时先创建变量
# 然后之后使用的时候再直接取变量
with tf.variable_scope('make_variable', reuse=tf.AUTO_REUSE):
    model1 = make_variable()
    model2 = make_variable()
    w1 = model1.w1
    w2 = model2.w1
    print(w1, w1.name, w1.graph)
    print(w2, w2.name, w2.graph)

我们定义了一个类,并为其变量设置了一个域,同时,在调用这个类,生成两次相同的数据流图时,分别打印出两个类下的变量w1的名称和所属计算图。

 MLP/w1:0 
 MLP/w1_1:0 

这种方式相当于每次创建tensor时都是一个新的变量,所以对与多个"相同"的tensor而言,tensorflow将其以:‘MLP/w1:0’、tf.Variable 'MLP/w1_1:0’的形式命名,即利用下划线在后面添加标识。以这种方式命名可以视为每次都是创建一个新的tensor来进行操作。也可以发现两个类下的变量都是在同一个默认图下的。

(二)利用tf.get_variable()方式定义。

当我们在计算图中定义各节点tensor的时候,希望它能够复用另一节点的tensor值,而不是每次都是新建一个新的值的时候,就需要用到该函数来获取。该函数能够获取到指定路径下的tensor值 (如何该tensor值存在且允许被复用),如何没有,则会先自己初始化。这样一来,即可以使该tensor与指定的目标tensor值产生联系,相当于将目标节点的tensor放置在了该节点的位置上。

在上面的程序中,我们加入几行代码变成:

with tf.variable_scope('make_variable', reuse=tf.AUTO_REUSE):
    model1 = make_variable()
    model2 = make_variable()
    w1, w3 = model1.w1, model1.w2
    w2, w4 = model2.w1, model2.w2

    print(w1, w1.name, w1.graph)
    print(w2, w2.name, w2.graph)

    print(w3, w3.name, w3.graph)
    print(w4, w4.name, w4.graph)
    print(w3 == w4)

可以发现,w3,w4的输出结果为:

 make_variable/MLP//w2:0 
 make_variable/MLP//w2:0 
True

可以发现两个变量完全是一样的。

同时也可以发现,变量命名区域之间reuse的情况是可以继承的,比如子区域的reuse没有设置的时候,可以继承父区域的reuse设置。例如在该make_variable类中会打印出:

make_variables reuse:  _ReuseMode.AUTO_REUSE

reuse=tf.AUTO_REUSEreuse=True的区别就在于AUTO_REUSE可以使得当第一次的使用的时候先创建变量,然后之后使用的时候再直接取,而不是当reuse=True的时候代表的是直接取该变量使用,利用将上述程序中的reuse进行修改则会报错:
改为:

with tf.variable_scope('make_variable', reuse=True):

报错:

ValueError: Variable make_variable/MLP//b1 does not exist, or was not created with tf.get_variable(). Did you mean to set reuse=tf.AUTO_REUSE in VarScope?

说明第一次初始化类的时候,b1因为是用get_variable定义的,该变量还没创建不存在,所以无法直接reuse。

综上所述,其实简单起来理解就是,无论是tf.get_variable()还是tf.Variable()函数,在某一model下都是定义一个tensor变量,只是这个变量是一直新建还是可以在计算图中可以复用的。

也就是对于不同的model,每个model下都构建了自己的数据流图,而默认的计算图只有一个,所以调用不同的Model构建数据流图时,都是在同一个默认的计算图中进行扩展(可以想象一个比较大的图,然后再上面不断进行扩展添加节点和连线,,即若干个不同模型的数据流通共同构成了该默认计算图)。而在构建各自的数据流图中,由于每个变量在同一计算图中都是唯一的,所以在创建过程中,如果想要使用计算图中的某一变量,则就需要用get_variable()函数来进行获取,同时要使该目标变量在初始化时的scope范围的reuse=True。比如在编码器-解码器模型中,我们在encode类中设置了编码器的参数,而在解码器中,如果我们想使在解码器中的参数能够共用编码器中的权重参数,则我们可以在解码器中利用get_variable()获取到编码器中权重参数(参数的路径名要正确),因为这些OP操作都是定义在同一默认计算图下。然后再调用sesstion对计算图执行的时候,则会按照每一tensor所设置的条件来进行操作。

最后提一下的就是,我最初接触tensorflow的时候傻傻的以为reuse=True是使得模型在每次反向之后再向前迭代的时候来获取到那个变量的。。。
但其实再迭代更新损失函数的时候,每一次的迭代更新过程都会使数据流在计算图中流过,但并不是说会创建新的模型。第一次创建模型时,就在计算图中放置该数据流图下的各tensor op(各节点和连线),各变量即利用get_variable()函数第一次产生,则计算图中会产生该变量,每次迭代的过程会利用梯度值对参数进行更新。但并不是每次迭代的时候都会重新调用get_variable()对更新后的参数再进行获取,数据在每次迭代流过计算图时,会直接利用该参数进行相关运算。tf.get_variable()是在构建模型数据流图,定义所放置的tensor的时候所使用的,只是表明了该位置的tensor值的获取方式(第一次则自己初始化,或若在计算图中已有值则复用)。利用tf.get_variable()在迭代的时候,即是有了该参数的值(初始化的值或复用的值),然后每次迭代的过程中利用该参数继续进行相关操作。

你可能感兴趣的:(Tensorflow)