gzyueqian
13352868059

Tensorflow学习: 变量及共享变量-Python人工智能

更新时间: 2018-09-06 17:55:24来源: java培训浏览量:5359

    TensorFlow中变量主要用来表示机器学习模型中的参数,变量通过 tf.Variable 类进行操作。tf.Variable 表示张量,通过运行 op 可以改变它的值。与 tf.Tensor 对象不同,tf.Variable 存在于单个 session.run 调用的上下文之外。
    在内部,tf.Variable 存储持久张量。具体 op 允许您读取和修改此张量的值。这些修改在多个 tf.Session 之间是可见的,因此对于一个 tf.Variable,多个工作器可以看到相同的值。
    1. tf.Variable 创建变量
    tf.Variable的初始化函数如下所示
    其中参数
    initial_value 表示初始化值,用Tensor表示
    trainable 表示变量是否被训练,如果被训练,将加入到tf.GraphKeys.TRAINABLE_VARIABLES集合中,TensorFlow将计算其梯度的变量
    collections 表示一个graph collections keys的集合,这个创建的变量将被添加到这些集合中,默认集合是[GraphKeys.GLOBAL_VARIABLES].
    name: 变量的命名,默认是'Variable'
    dtype 表示类型
    例如我们创建一个变量,并且查看其name和shape
    可以看到在命名的时候,如果指定的name重复,那么w2就会被命名为"name_1:0" 这样累加下去。
    2. 变量集合 collections
    默认情况下,每个tf.Variable都放置在以下两个集合中:*tf.GraphKeys.GLOBAL_VARIABLES- 可以在多个设备共享的变量,*tf.GraphKeys.TRAINABLE_VARIABLES- TensorFlow 将计算其梯度的变量。
    2.1 查看集合变量列表
    要查看放置在某个集合中的所有变量的列表,可以采用如下方式
    可以看到输出结果是所有变量的列表
    2.2 创建变量集合
    如果您不希望变量被训练,可以将其添加到 tf.GraphKeys.LOCAL_VARIABLES 集合中。例如,以下代码段展示了如何将名为 my_local 的变量添加到此集合中:
    或者,您可以指定 trainable=False 为 tf.get_variable 的参数:
    我们测试效果如下所示,可以看到b2的trainable=False,那么输出collection没有b2
    您也可以使用自己的集合。集合名称可为任何字符串,且您无需显式创建集合。创建变量(或任何其他对象)后,要将其添加到集合,请调用 tf.add_to_collection。例如,以下代码将名为 my_local 的现有变量添加到名为     my_collection_name 的集合中:
    3. 共享变量
    我们查看下面的代码,表示一个卷积神经网络,其中包括conv1_weights, conv1_biases, conv2_weights, conv2_biases四个参数,也就是4个变量
    假设我们利用这个函数对两张图片进行相同的操作,也就是调用两次,那么每次都会创建4个变量,假设我们在函数内对变量进行了优化求解,那么每次都会重新创建变量,这样就无法复用参数,导致训练过程无效
    TensowFlow通过变量范围(variable scope)和tf.get_variable方法解决了共享变量(参数)的问题。
    3.1 tf.variable_scope和tf.get_variable
    tf.Variable()方法每次被调用都会创建新的变量,这样就无法解决共享变量的问题,而tf.get_variable结合作用域即可表明我们是想创建新的变量,还是共享变量,变量作用域允许在调用隐式创建和使用变量的函数时控制变量重用。作用域还允许您以分层和可理解的方式命名变量。tf.get_variable()的机制跟tf.Variable()有很大不同,如果指定的变量名已经存在(即先前已经用同一个变量名通过get_variable()函数实例化了变量),那么get_variable()只会返回之前的变量,否则才创造新的变量。我们举例进行说明。
    例如上面的例子中有两个卷积层,我们先来编写一个函数创建一个卷积/relu层,这个函数使命的变量名称是'weights'和'biases'

    在真实模型中需要多个卷积层,我们通过变量域来区分不同层的变量,不同的变量域下的变量名车为:scope_name/variable_name, 如下所示,个卷积层的变量名称是'conv1/weights', 'conv1/biases', 第二个卷积层的变量名称是 'conv2/weights', 'conv2/biases'。

    但即便这样,如果多次调用该函数,也会抛出异常,
    因为用get_variable()创建两个相同名字的变量是会报错的,默认上它只是检查变量名,防止重复,如果要变量共享,就需要指定在哪个域名内可以共享变量。
    开启共享变量有两种方式
    方法1
    采用scope.reuse_variables()触发重用变量,如下所示
    方法2
    使用reuse=True 创建具有相同名称的作用域
    理解变量域的工作机理非常重要,我们对其进行梳理,当我们调用tf.get_variable(name, shape, dtype, initializer)时,这背后到底做了什么
    首先,TensorFlow 会判断是否要共享变量,也就是判断 tf.get_variable_scope().reuse 的值,如果结果为 False(即你没有在变量域内调用scope.reuse_variables()),那么 TensorFlow 认为你是要初始化一个新的变量,紧接着它会判断这个命名的变量是否存在。如果存在,会抛出 ValueError 异常,否则,就根据 initializer 初始化变量:
    而如果 tf.get_variable_scope().reuse == True,那么 TensorFlow 会执行相反的动作,就是到程序里面寻找变量名为 scope name + name 的变量,如果变量不存在,会抛出 ValueError 异常,否则,就返回找到的变量:
    变量域可以多层重叠,例如,下面的变量上有两层的变量域,那么变量名是'foo/var/v:0'
    在同一个变量域中,如果需要调用同名变量,那么需要重用变量即可,例如v1和v两个变量时相同的,因为变量名都是'foo/v'
    总结:
    tf.get_variable()默认上它只检查变量名,如果变量名重复,那么就会报错;tf.Variable()每次被调用都创建相应的变量,即便变量名重复,也会创建新的变量,因此无法共享变量名。
    如果scope中开启共享变量,那么调用tf.get_variable()就会查找相同变量名的变量,如果有,就直接返回该变量,如果没有,就创建一个新的变量;
    如果scope没有开启共享变量(默认模式),那么 调用tf.get_variable()发现已有相同变量名的变量,就会报错,如果没有,就创建一个新的变量。
    要重用变量,需要在scope中开启共享变量,有两种方法,推荐种

免费预约试听课