TENSORFLOW变量作用域(VARIABLE SCOPE)


Posted in Python onJanuary 10, 2020

举例说明

TensorFlow中的变量一般就是模型的参数。当模型复杂的时候共享变量会无比复杂。

官网给了一个case,当创建两层卷积的过滤器时,每输入一次图片就会创建一次过滤器对应的变量,但是我们希望所有图片都共享同一过滤器变量,一共有4个变量:conv1_weights,conv1_biases,conv2_weights, and conv2_biases。

通常的做法是将这些变量设置为全局变量。但是存在的问题是打破封装性,这些变量必须文档化被其他代码文件引用,一旦代码变化,调用方也可能需要变化。

还有一种保证封装性的方式是将模型封装成类。

不过TensorFlow提供了Variable Scope 这种独特的机制来共享变量。这个机制涉及两个主要函数:

tf.get_variable(<name>, <shape>, <initializer>) 创建或返回给定名称的变量
tf.variable_scope(<scope_name>) 管理传给get_variable()的变量名称的作用域

在下面的代码中,通过tf.get_variable()创建了名称分别为weights和biases的两个变量。

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)

但是我们需要两个卷积层,这时可以通过tf.variable_scope()指定作用域进行区分,如with tf.variable_scope("conv1")这行代码指定了第一个卷积层作用域为conv1,

在这个作用域下有两个变量weights和biases。

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])

最后在image_filters这个作用域重复使用第一张图片输入时创建的变量,调用函数reuse_variables(),代码如下:

with tf.variable_scope("image_filters") as scope:
  result1 = my_image_filter(image1)
  scope.reuse_variables()
  result2 = my_image_filter(image2)

tf.get_variable()工作机制

tf.get_variable()工作机制是这样的:

当tf.get_variable_scope().reuse == False,调用该函数会创建新的变量

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

当tf.get_variable_scope().reuse == 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

变量都是通过作用域/变量名来标识,后面会看到作用域可以像文件路径一样嵌套。

tf.variable_scope理解

tf.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"

当前环境的作用域可以通过函数tf.get_variable_scope()获取,并且reuse标志可以通过调用reuse_variables()设置为True,这个非常有用,如下

with tf.variable_scope("foo"):
  v = tf.get_variable("v", [1])
  tf.get_variable_scope().reuse_variables()
  v1 = tf.get_variable("v", [1])
assert v1 is v

作用域中的resuse默认是False,调用函数reuse_variables()可设置为True,一旦设置为True,就不能返回到False,并且该作用域的子空间reuse都是True。如果不想重用变量,那么可以退回到上层作用域,相当于exit当前作用域,如

with tf.variable_scope("root"):
  # At start, the scope is not reusing.
  assert tf.get_variable_scope().reuse == False
  with tf.variable_scope("foo"):
    # Opened a sub-scope, still not reusing.
    assert tf.get_variable_scope().reuse == False
  with tf.variable_scope("foo", reuse=True):
    # Explicitly opened a reusing scope.
    assert tf.get_variable_scope().reuse == True
    with tf.variable_scope("bar"):
      # Now sub-scope inherits the reuse flag.
      assert tf.get_variable_scope().reuse == True
  # Exited the reusing scope, back to a non-reusing one.
  assert tf.get_variable_scope().reuse == False

一个作用域可以作为另一个新的作用域的参数,如:

with tf.variable_scope("foo") as foo_scope:
  v = tf.get_variable("v", [1])
with tf.variable_scope(foo_scope):
  w = tf.get_variable("w", [1])
with tf.variable_scope(foo_scope, reuse=True):
  v1 = tf.get_variable("v", [1])
  w1 = tf.get_variable("w", [1])
assert v1 is v
assert w1 is w

不管作用域如何嵌套,当使用with tf.variable_scope()打开一个已经存在的作用域时,就会跳转到这个作用域。

with tf.variable_scope("foo") as foo_scope:
  assert foo_scope.name == "foo"
with tf.variable_scope("bar"):
  with tf.variable_scope("baz") as other_scope:
    assert other_scope.name == "bar/baz"
    with tf.variable_scope(foo_scope) as foo_scope2:
      assert foo_scope2.name == "foo" # Not changed.

variable scope的Initializers可以创递给子空间和tf.get_variable()函数,除非中间有函数改变,否则不变。

with tf.variable_scope("foo", initializer=tf.constant_initializer(0.4)):
  v = tf.get_variable("v", [1])
  assert v.eval() == 0.4 # Default initializer as set above.
  w = tf.get_variable("w", [1], initializer=tf.constant_initializer(0.3)):
  assert w.eval() == 0.3 # Specific initializer overrides the default.
  with tf.variable_scope("bar"):
    v = tf.get_variable("v", [1])
    assert v.eval() == 0.4 # Inherited default initializer.
  with tf.variable_scope("baz", initializer=tf.constant_initializer(0.2)):
    v = tf.get_variable("v", [1])
    assert v.eval() == 0.2 # Changed default initializer.

算子(ops)会受变量作用域(variable scope)影响,相当于隐式地打开了同名的名称作用域(name scope),如+这个算子的名称为foo/add

with tf.variable_scope("foo"):
  x = 1.0 + tf.get_variable("v", [1])
assert x.op.name == "foo/add"

除了变量作用域(variable scope),还可以显式打开名称作用域(name scope),名称作用域仅仅影响算子的名称,不影响变量的名称。另外如果tf.variable_scope()传入字符参数,创建变量作用域的同时会隐式创建同名的名称作用域。如下面的例子,变量v的作用域是foo,而算子x的算子变为foo/bar,因为有隐式创建名称作用域foo

with tf.variable_scope("foo"):
  with tf.name_scope("bar"):
    v = tf.get_variable("v", [1])
    x = 1.0 + v
assert v.name == "foo/v:0"
assert x.op.name == "foo/bar/add"

注意: 如果tf.variable_scope()传入的不是字符串而是scope对象,则不会隐式创建同名的名称作用域。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python实现删除Android工程中的冗余字符串
Jan 19 Python
python中使用序列的方法
Aug 03 Python
ubuntu中配置pyqt4环境教程
Dec 27 Python
python爬虫之线程池和进程池功能与用法详解
Aug 02 Python
win10下tensorflow和matplotlib安装教程
Sep 19 Python
Django2.1集成xadmin管理后台所遇到的错误集锦(填坑)
Dec 20 Python
Python 根据日志级别打印不同颜色的日志的方法示例
Aug 08 Python
关于python3中setup.py小概念解析
Aug 22 Python
pip install python 快速安装模块的教程图解
Oct 08 Python
Django框架表单操作实例分析
Nov 04 Python
Python3 Click模块的使用方法详解
Feb 12 Python
使用Keras实现简单线性回归模型操作
Jun 12 Python
python numpy数组复制使用实例解析
Jan 10 #Python
关于Pytorch的MNIST数据集的预处理详解
Jan 10 #Python
详解pycharm连接不上mysql数据库的解决办法
Jan 10 #Python
pycharm双击无响应(打不开问题解决办法)
Jan 10 #Python
python ubplot使用方法解析
Jan 10 #Python
Pytorch使用MNIST数据集实现基础GAN和DCGAN详解
Jan 10 #Python
Pytorch使用MNIST数据集实现CGAN和生成指定的数字方式
Jan 10 #Python
You might like
150kHz到30Mhz完全冲浪手册
2020/03/20 无线电
PHP中MD5函数使用实例代码
2008/06/07 PHP
通俗易懂的php防注入代码
2010/04/07 PHP
PHP去掉json字符串中的反斜杠\及去掉双引号前的反斜杠
2015/09/30 PHP
PHP读MYSQL中文乱码的快速解决方法
2016/10/01 PHP
php+croppic.js实现剪切上传图片功能
2018/08/14 PHP
Jquery方式获取iframe页面中的 Dom元素
2014/05/07 Javascript
JS使用for循环遍历Table的所有单元格内容
2014/08/21 Javascript
JavaScript的null和undefined区别示例介绍
2014/09/15 Javascript
深入分析jquery解析json数据
2014/12/09 Javascript
如何使用HTML5地理位置定位功能
2015/04/27 Javascript
jquery读取xml文件实现省市县三级联动的方法
2015/05/29 Javascript
纯JavaScript实现的分页插件实例
2015/07/14 Javascript
node.js插件nodeclipse安装图文教程
2020/10/19 Javascript
将html页面保存成图片,图片写入pdf的实现方法(推荐)
2016/09/17 Javascript
详解webpack 配合babel 将es6转成es5 超简单实例
2017/05/02 Javascript
关于vue-resource报错450的解决方案
2017/07/24 Javascript
微信小程序实现图片压缩功能
2018/01/26 Javascript
jquery动态添加带有样式的HTML标签元素方法
2018/02/24 jQuery
AngularJS 监听变量变化的实现方法
2018/10/09 Javascript
python判断windows系统是32位还是64位的方法
2015/05/11 Python
对Python的Django框架中的项目进行单元测试的方法
2016/04/11 Python
python调用Delphi写的Dll代码示例
2017/12/05 Python
Python操作MySQL数据库的三种方法总结
2018/01/30 Python
python实现代码统计程序
2019/09/19 Python
初探CSS3中的calc()功能
2015/07/14 HTML / CSS
三星俄罗斯授权在线商店:Samsung俄罗斯
2019/09/28 全球购物
乌克兰在线电子产品商店:MTA
2019/11/14 全球购物
班级入场式解说词
2014/02/01 职场文书
《自然之道》教学反思
2014/02/11 职场文书
工作决心书范文
2014/03/11 职场文书
小学作文评语大全
2014/04/21 职场文书
医院竞聘演讲稿
2014/05/16 职场文书
班级学雷锋活动总结
2014/06/26 职场文书
秋季校运会广播稿100字
2014/09/18 职场文书
2014大四本科生自我鉴定总结
2014/10/04 职场文书