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使用新浪微博api上传图片到微博示例
Jan 10 Python
python实现倒计时的示例
Feb 14 Python
Python实现合并两个列表的方法分析
May 28 Python
Python之列表的插入&amp;替换修改方法
Jun 28 Python
python制作简单五子棋游戏
Jun 18 Python
如何用Python破解wifi密码过程详解
Jul 12 Python
python基于Selenium的web自动化框架
Jul 14 Python
Django工程的分层结构详解
Jul 18 Python
详解Python中namedtuple的使用
Apr 27 Python
python获取命令行参数实例方法讲解
Nov 02 Python
Python OpenCV中的numpy与图像类型转换操作
Dec 11 Python
python使用matplotlib的savefig保存时图片保存不完整的问题
Jan 08 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
德劲1103二次变频版的打磨
2021/03/02 无线电
php预定义常量
2006/12/25 PHP
攻克CakePHP系列一 连接MySQL数据库
2008/10/22 PHP
destoon整合ucenter后注册页面不跳转的解决方法
2014/06/21 PHP
CodeIgniter与PHP5.6的兼容问题
2015/07/16 PHP
PHP7.0安装笔记整理
2015/08/28 PHP
关于ThinkPHP中的异常处理详解
2018/05/11 PHP
PHP7新特性
2021/03/09 PHP
一个可拖拽列宽表格实例演示
2012/11/26 Javascript
window.open以post方式将内容提交到新窗口
2012/12/26 Javascript
HTML长文本截取含有HTML代码同样适用的两种方法
2013/07/31 Javascript
JavaScript验证图片类型(扩展名)的函数分享
2014/05/05 Javascript
node.js中的console.warn方法使用说明
2014/12/09 Javascript
node.js中的fs.readFile方法使用说明
2014/12/15 Javascript
jQuery实现弹出窗口中切换登录与注册表单
2015/06/05 Javascript
javascript表单控件实例讲解
2016/09/13 Javascript
通过bootstrap全面学习less
2016/11/09 Javascript
vue-router 路由基础的详解
2017/10/17 Javascript
利用Vconsole和Fillder进行移动端抓包调试方法
2019/03/05 Javascript
Vue.js轮播图走马灯代码实例(全)
2019/05/08 Javascript
微信小程序实现下拉框功能
2019/07/16 Javascript
python机器人行走步数问题的解决
2018/01/29 Python
Python 移动光标位置的方法
2019/01/20 Python
Python urlencode和unquote函数使用实例解析
2020/03/31 Python
Python优秀开源项目Rich源码解析的流程分析
2020/07/06 Python
Flask中sqlalchemy模块的实例用法
2020/08/02 Python
数据库的约束含义
2012/09/09 面试题
JS原生实现轮播图的几种方法
2021/03/23 Javascript
空中乘务员岗位职责
2014/03/08 职场文书
社区党务公开实施方案
2014/03/18 职场文书
新春联欢会主持词
2014/03/24 职场文书
演讲稿的写法
2014/05/19 职场文书
2014国庆65周年领导讲话稿(3篇)
2014/09/21 职场文书
教师见习报告范文
2014/11/03 职场文书
初中家长评语大全
2014/12/26 职场文书
JavaScript与JQuery框架基础入门教程
2021/07/15 Javascript