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连接mysql实例分享
Oct 09 Python
Python安装lz4-0.10.1遇到的坑
May 20 Python
Python3数据库操作包pymysql的操作方法
Jul 16 Python
python爬虫-模拟微博登录功能
Sep 12 Python
使用python绘制温度变化雷达图
Oct 18 Python
numpy:找到指定元素的索引示例
Nov 26 Python
python图形开发GUI库pyqt5的详细使用方法及各控件的属性与方法
Feb 14 Python
Pycharm 安装 idea VIM插件的图文教程详解
Feb 21 Python
python适合做数据挖掘吗
Jun 16 Python
详解python实现可视化的MD5、sha256哈希加密小工具
Sep 14 Python
深度学习详解之初试机器学习
Apr 14 Python
Pandas实现DataFrame的简单运算、统计与排序
Mar 31 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
php数组函数序列之sort() 对数组的元素值进行升序排序
2011/11/02 PHP
php 创建以UNIX时间戳命名的文件夹(示例代码)
2014/03/08 PHP
php实现在多维数组中查找特定value的方法
2015/07/29 PHP
php爬取天猫和淘宝商品数据
2018/02/23 PHP
PHP笛卡尔积实现算法示例
2018/07/30 PHP
javascript 屏蔽鼠标键盘的几段代码
2008/01/02 Javascript
文本框获得焦点和失去焦点的判断代码
2012/03/18 Javascript
JQuery文本改变触发事件如聚焦事件、失焦事件
2014/01/15 Javascript
用jQuery与JSONP轻松解决跨域访问的问题
2014/02/04 Javascript
jQuery中:last选择器用法实例
2014/12/30 Javascript
js获取及判断键盘按键的方法
2015/12/01 Javascript
Ubuntu 16.04 64位中搭建Node.js开发环境教程
2016/10/19 Javascript
JS实用的带停顿的逐行文本循环滚动效果实例
2016/11/23 Javascript
JavaScript中localStorage对象存储方式实例分析
2017/01/12 Javascript
整理关于Bootstrap表单的慕课笔记
2017/03/29 Javascript
微信小程序之裁剪图片成圆形的实现代码
2018/10/11 Javascript
js实现上下左右键盘控制div移动
2020/01/16 Javascript
jQuery实现移动端笔触canvas电子签名
2020/05/21 jQuery
Vue 同步异步存值取值实现案例
2020/08/05 Javascript
JavaScript常用8种数组去重代码实例
2020/09/09 Javascript
vue 递归组件的简单使用示例
2021/01/14 Vue.js
Python实现基于多线程、多用户的FTP服务器与客户端功能完整实例
2017/08/18 Python
Python文本处理之按行处理大文件的方法
2018/04/09 Python
python通过tcp发送xml报文的方法
2018/12/28 Python
python自动化测试之如何解析excel文件
2019/06/27 Python
Python Opencv图像处理基本操作代码详解
2020/08/31 Python
医药学专业大学生职业生涯规划书论文
2014/01/21 职场文书
《和田的维吾尔》教学反思
2014/04/14 职场文书
国旗下演讲稿
2014/05/08 职场文书
新文化运动的口号
2014/06/21 职场文书
小学生国庆演讲稿
2014/09/05 职场文书
见习报告的格式
2014/10/31 职场文书
中考百日冲刺决心书
2015/09/22 职场文书
启动Tomcat时出现大量乱码的解决方法
2021/06/21 Java/Android
CSS 使用 resize 实现图片拖拽切换预览功能(强大功能)
2021/08/23 HTML / CSS
Django数据库(SQlite)基本入门使用教程
2022/07/07 Python