版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。


[参考文献]:

1.scope 命名方法

2.name&variable scope

3.tf.variable_scope和tf.name_scope的用法


1.scope是干什么的

顾名思义“scope”的意思是“范围”,那么name_scope和variable_scope就是针对name所做的范围定义。典型的 TensorFlow 可以有数以千计的节点,在构建各op的过程中,命名要做到不重复,那么在编写程序的时候就要特别注意,例如“x”、“y”、”W”、”B”甚至“weight”等经常使用的变量/常量命名就很可能会重复,否则就要增加一个字符串来表示不同的op所属,例如在x前加train成为train_x,在x前加scan成为scan_x等等,一方面表明此变量、常量所属“范围”的标识,这个name_scope和variable_scope就是干这个事情的,另一方面是从name上保证此变量命名的唯一性。有了这个scope的存在,其中描述的变量和常量就会在机器当中自动给添加前缀描述,作为对各个变量、常量的区分。在这里的编程和以前使用C、C++不同之处就是,C/C++当中的变量名实际相当于我们这里的XXX.name()而不是XXX,计算机区分的是XXX.name()。简单说,有了scope,那么在tensorflow当中就是用op/tensor名来划定范围,实现对变量或者常量名的唯一性定义,避免冲突。

2.scope是怎么干的

接下来我们要分析的问题是:究竟name_scope()和variable_scope()对可以产生变量的tf.get_variable()和tf.Variable()会有什么不同?为了一探究竟,接下来做一些测试:

2.1 tf.name_scope对tf.get_variable()的影响

import tensorflow as tf

with tf.name_scope("a_name_scope") as myscope:
initializer = tf.constant_initializer(value=1)
var1 = tf.get_variable(name='var1', shape=[1], dtype=tf.float32, initializer=initializer)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(var1.name) # var1:0
print(sess.run(var1)) # [ 1.]
 
var1:0
[ 1.]

[结论]:

name_scope()没有对 tf.get_variable()产生的变量增加“范围”。于是,如果再有相同的变量名生成,即便name_scope使用了新的名字“a_name_scope_new”,仍旧会破坏“唯一性”,从而导致出错“该变量已经定义过了!”,试验一下:

with tf.name_scope("a_name_scope_new") as myscope:
initializer = tf.constant_initializer(value=1)
var1 = tf.get_variable(name='var1', shape=[1], dtype=tf.float32, initializer=initializer)
 
---------------------------------------------------------------------------

ValueError: Variable var1 already exists, disallowed. Did you mean to set reuse=True in VarScope?

【结论】:

果然出错了!“Variable var1 already exists, disallowed.”,说明name_scope没有对get_variable()增加“范围”。那我现在还想生成一个新的变量var2而且要让其内容与var1相同怎么办?

with tf.name_scope("a_name_scope") as myscope:
#initializer = tf.constant_initializer(value=1)
var2 = tf.get_variable(name='var2', dtype=tf.float32, initializer=var1)
#var2=var1
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(var1.name) # var1:0
print(sess.run(var1)) # [ 1.]
print(var2.name) # var2:0
print(sess.run(var2)) # [ 1.]
 
var1:0
[ 1.]
var2:0
[ 1.]
 

【结论】:

看结果,的确生成了新的变量var2。如果我不想生成新的变量var2,而只想做个变量的名称更改怎么办?注意,这里就和C、C++有不同了。
 
  • 1
with tf.name_scope("a_name_scope") as myscope:
var3=var1
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(var1.name) # var1:0
print(sess.run(var1)) # [ 1.]
print(var3.name) # var1:0
print(sess.run(var3)) # [ 1.]
 
var1:0
[ 1.]
var1:0
[ 1.]
 

【结论】:

虽然有一个变量var3,但是其name和值都是与var1相同的,计算机认的是name,认为这两个是完全一样的,并不是新建,只是引用,我们可以理解为是C语言中的指针,指针的名字可以千奇百怪,但是其指向的地址内容才是其真正的归属。所以,这个变量var3你可以随时设置,不会引起重复性、唯一性的错误——“Variable var1 already exists, disallowed.”。
 
  • 1

变量的名称只是指针名,变量的name是地址

with tf.name_scope("a_name_scope") as myscope:
var3=var1
 

【结论】:

的确没有引起错误提示。
 

2.2 variable_scope对get_variable的影响

那么接着分析,既然name_scope对get_variable没有增加“范围”,那variable_scope呢?
 
with tf.variable_scope("a_variable_scope") as myscope:
initializer = tf.constant_initializer(value=1)
var1 = tf.get_variable(name='var1', shape=[1], dtype=tf.float32, initializer=initializer)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(var1.name) # a_variable_scope/var1:0
print(sess.run(var1)) # [ 1.]
 
a_variable_scope/var1:0
[ 1.]
 

【结论】:variable_scope对get_variable有命名的影响

看到var1指向的内容被新建了,其name变成了“a_variable_scope/var1:0”,也就是说对计算机而言这已经是一个新的内容了,需要一个新的内存来存储,这个和之前的var1.name = var1:0已经不是一个东西了.同时,我们想要找到之前的var1的name已经是不能够了,这个完全就是C/C++指针的意思,指针指向了一个新的内存,如果不使用一个新的指针指向旧的地址,那么就会导致旧地址的内容无法检索了。验证一下:
 
  • 1
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(var1.name) # a_variable_scope/var1:0
print(sess.run(var1)) # [ 1.]
print(var3.name) # var1:0
print(sess.run(var3)) # [ 1.]
 
a_variable_scope/var1:0
[ 1.]
var1:0
[ 1.]
 

【结论】: 变量名就是指针,变量的name属性是地址

var3是前面对旧的var1的内容的指针,虽然在前面var1指向了新建的内容(a_variable_scope/var1:0),但是原有内容的指针给了var3,所以现在仍旧可以通过指针找到旧地址内容。前面看到了,使用variable_scope能够产生新的变量,即增加了“范围”标识的变量,那么这个能否再次执行产生新的变量呢?也就是说,新产生的name为(a_variable_scope/var1:0)的地址能否再次被新建?
 
with tf.variable_scope("a_variable_scope") as myscope:
initializer = tf.constant_initializer(value=1)
var1 = tf.get_variable(name='var1', shape=[1], dtype=tf.float32, initializer=initializer)
 
---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)
ValueError: Variable a_variable_scope/var1 already exists, disallowed. Did you mean to set reuse=True in VarScope?
 

【结论】:get_variable取值的唯一性仍旧需要保证

可见虽然variable_scope可以增加name“范围”,但是get_variable取值的唯一性仍旧需要维护,否则就会出错,你是不可以任性新建的。那我现在就是想重用此变量,我就是想更改一个变量名而已,行吗?当然行,之前我们就采用了直接变量名赋值就行了“var3=var1”,这样变量名改为了var3,但是其name仍旧是var1的name,从c理解就是,指针随便建,地址唯一不变,街道名你随便改,可是街道你不能随便拆改。那还有一种材料中介绍的方法,就是使用reuse_variables(),但是一旦重用的对象本身不存在就会报错。这里说的对象不是“指针”,而是指针地址,而且是带有variable_scope指定的“范围”的指针地址。
 
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(var1.name) # a_variable_scope/var1:0
print(sess.run(var1)) # [ 1.]
print(var2.name) # var2:0
print(sess.run(var2)) # [ 1.]
print(var3.name) # var1:0
print(sess.run(var3)) # [ 1.]
 
a_variable_scope/var1:0
[ 1.]
var2:0
[ 1.]
var1:0
[ 1.]
 

【分析】:

看看上面三个变量,如果在 tf.variable_scope(“a_variable_scope”)下则会增加“范围”——(a_variable_scope),显然,var1是已存在的对象,而var2虽然也是有已有的地址,但是如果在 tf.variable_scope当中使用get_variable来获取则会新建一个name为:(a_variable_scope/var2:0),所以var2不能当做是已有地址的变量来被重用,否则会出错。var1是可以被重用的,因为他的确是一个已有的范围内的地址。

with tf.variable_scope("a_variable_scope") as myscope:
initializer = tf.constant_initializer(value=1)
myscope.reuse_variables()
var2 = tf.get_variable(name='var1', shape=[1], dtype=tf.float32, initializer=initializer)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(var1.name) # a_variable_scope/var1:0
print(sess.run(var1)) # [ 1.]
print(var2.name) # a_variable_scope/var1:0
print(sess.run(var2)) # [ 1.]
 
a_variable_scope/var1:0
[ 1.]
a_variable_scope/var1:0
[ 1.]
 

**【结论】:**var1被var2重用了

with tf.variable_scope("a_variable_scope") as myscope:
initializer = tf.constant_initializer(value=1)
myscope.reuse_variables()
var2 = tf.get_variable(name='var2', shape=[1], dtype=tf.float32, initializer=initializer)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(var1.name) # a_variable_scope/var1:0
print(sess.run(var1)) # [ 1.]
print(var2.name) # a_variable_scope/var1:0
print(sess.run(var2)) # [ 1.]
 
---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

ValueError: Variable a_variable_scope/var7 does not exist, or was not created with tf.get_variable(). Did you mean to set reuse=None in VarScope?
 

【结论】:

在上面var1被var2重用,这个是正确的,但是紧接着想重新新建一个就会立马报错的,说明这个重用操作“myscope.reuse_variables()”在with范围内都是生效的。如果name_scope下使用reuse_variables()则会报错。
 
with tf.name_scope("a_name_scope") as myscope:
initializer = tf.constant_initializer(value=1)
myscope.reuse_variables()
var1 = tf.get_variable(name='var1', shape=[1], dtype=tf.float32, initializer=initializer) with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(var1.name) # var1:0
print(sess.run(var1)) # [ 1.]
 
---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)
AttributeError: 'str' object has no attribute 'reuse_variables'
 

2.3 name_scope()对variable的影响

with tf.name_scope("a_name_scope") as myscope:
var2 = tf.Variable(name='var2', initial_value=[2], dtype=tf.float32)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(var1.name) # var1:0
print(sess.run(var1)) # [ 1.]
print(var2.name) # a_name_scope/var2:0
print(sess.run(var2)) # [ 2.]
 
a_variable_scope/var1:0
[ 1.]
a_name_scope_5/var2:0
[ 2.]
 

【结论】:

很棒!name_scope对variable有效,新增了“范围”名。那这个范围名是否与get_variable一样有唯一性?
 
with tf.name_scope("a_name_scope") as myscope:
var2 = tf.Variable(name='var2', initial_value=[2], dtype=tf.float32)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(var1.name) # var1:0
print(sess.run(var1)) # [ 1.]
print(var2.name) # a_name_scope/var2:0
print(sess.run(var2)) # [ 2.]
 
a_variable_scope/var1:0
[ 1.]
a_name_scope_6/var2:0
[ 2.]
 

【结论】:

从上面结果看,唯一性仍旧保证了,原因是自动的将var2.name从(a_name_scope_1/var2:0)变为了(a_name_scope_2/var2:0).这里使用了“变为”这个词实际是错误的,因为根本不是变为,而是新建了一个name 为(a_name_scope_2/var2:0)的变量,这个变量和前面的那个变量的名都为var2但是name属性却是不同的。
 

2.4 variable_scope对variable的影响

with tf.variable_scope("my_variable_scope") as myscope:
var2 = tf.Variable(name='var2', initial_value=[initializer.value], dtype=tf.float32)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(var1.name) # var1:0
print(sess.run(var1)) # [ 1.]
print(var2.name) # a_name_scope/var2:0
print(sess.run(var2)) # [ 2.]
a_variable_scope/var1:0
[ 1.]
my_variable_scope/var2:0
[ 1.]
 

【结论】:

variable_scope对variable有效,而且从下面的运行结果看,唯一性可以得到保证,新建了一个新的变量,这点和name_scope一致。
 
  • 1
with tf.variable_scope("my_variable_scope") as myscope:
var2 = tf.Variable(name='var2', initial_value=[initializer.value], dtype=tf.float32)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(var1.name) # var1:0
print(sess.run(var1)) # [ 1.]
print(var2.name) # a_name_scope/var2:0
print(sess.run(var2)) # [ 2.]
 
a_variable_scope/var1:0
[ 1.]
my_variable_scope_1/var2:0
[ 1.]
 

总结:

[1]. name_scope 对 get_variable新建变量的name属性无影响;对variable新建变量的name属性增加了“范围”标识。

[2]. variable_scope对get_variable新建变量的name属性和variable新建变量的name属性都增加了“范围”标识。

[3]. get_variable新建变量如果遇见重复的name则会因为重复而报错。

[4]. variable新建的变量如果遇见重复的name则会自动修改前缀,以避免重复出现。

name_scope与variable_scope 详解的更多相关文章

  1. 基础 | batchnorm原理及代码详解

    https://blog.csdn.net/qq_25737169/article/details/79048516 https://www.cnblogs.com/bonelee/p/8528722 ...

  2. TextCNN 代码详解(附测试数据集以及GitHub 地址)

    前言:本篇是TextCNN系列的第三篇,分享TextCNN的优化经验 前两篇可见: 文本分类算法TextCNN原理详解(一) 一.textCNN 整体框架 1. 模型架构 图一:textCNN 模型结 ...

  3. Batchnorm原理详解

    Batchnorm原理详解 前言:Batchnorm是深度网络中经常用到的加速神经网络训练,加速收敛速度及稳定性的算法,可以说是目前深度网络必不可少的一部分. 本文旨在用通俗易懂的语言,对深度学习的常 ...

  4. 深度学习基础(CNN详解以及训练过程1)

    深度学习是一个框架,包含多个重要算法: Convolutional Neural Networks(CNN)卷积神经网络 AutoEncoder自动编码器 Sparse Coding稀疏编码 Rest ...

  5. 超详细的Tensorflow模型的保存和加载(理论与实战详解)

    1.Tensorflow的模型到底是什么样的? Tensorflow模型主要包含网络的设计(图)和训练好的各参数的值等.所以,Tensorflow模型有两个主要的文件: a) Meta graph: ...

  6. 无所不能的Embedding6 - 跨入Transformer时代~模型详解&代码实现

    上一章我们聊了聊quick-thought通过干掉decoder加快训练, CNN-LSTM用CNN作为Encoder并行计算来提速等方法,这一章看看抛开CNN和RNN,transformer是如何只 ...

  7. 中文NER的那些事儿2. 多任务,对抗迁移学习详解&代码实现

    第一章我们简单了解了NER任务和基线模型Bert-Bilstm-CRF基线模型详解&代码实现,这一章按解决问题的方法来划分,我们聊聊多任务学习,和对抗迁移学习是如何优化实体识别中边界模糊,垂直 ...

  8. Linq之旅:Linq入门详解(Linq to Objects)

    示例代码下载:Linq之旅:Linq入门详解(Linq to Objects) 本博文详细介绍 .NET 3.5 中引入的重要功能:Language Integrated Query(LINQ,语言集 ...

  9. 架构设计:远程调用服务架构设计及zookeeper技术详解(下篇)

    一.下篇开头的废话 终于开写下篇了,这也是我写远程调用框架的第三篇文章,前两篇都被博客园作为[编辑推荐]的文章,很兴奋哦,嘿嘿~~~~,本人是个很臭美的人,一定得要截图为证: 今天是2014年的第一天 ...

随机推荐

  1. Mybatis中传递多个参数的方法总结

    一.单个参数: public List<XXBean> getXXBeanList(String xxCode); <select id="getXXXBeanList&q ...

  2. HashMap,HashSet

    HashMap,HashSet 摘自:https://www.cnblogs.com/skywang12345/p/3310887.html#a1 目录 一.    HashMap(键值对形式存取,键 ...

  3. zabbix Server 4.0 监控TCP的12种状态

      zabbix Server 4.0 监控TCP的12种状态 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 大家对TCP三次握手比较熟悉了,都知道当发生DOSS攻击时,客户端发送 ...

  4. 使用ansible部署CDH 5.15.1大数据集群

    使用ansible离线部署CDH 5.15.1大数据集群 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 在此之前,我之前分享过使用shell自定义脚本部署大数据集群,不管是部署CD ...

  5. SpringCloud学习心得之Eureka注册中心的基本使用

      SpringCloud学习心得——Eureka注册中心 示范代码链接 定义 SpringCloud Eureka是 SpringCloud Netflix微服务套件的一部分,基于 REST 的服务 ...

  6. springboot rabbitmq 死信队列应用场景和完整demo

    何为死信队列? 死信队列实际上就是,当我们的业务队列处理失败(比如抛异常并且达到了retry的上限),就会将消息重新投递到另一个Exchange(Dead Letter Exchanges),该Exc ...

  7. .net System.IO.Stream 流操作类(FileStream等)

    Stream 是所有流的抽象基类.流是字节序列的抽象概念. 流涉及到的3个基本操作: 读取流,读取是指从流到数据结构(如字节数组)的数据传输. 写入流,写入是指从数据结构到流的数据传输. 流查找,查找 ...

  8. Linux下dstat的安装(适用任何版本)

    dstat下载地址:https://pan.baidu.com/s/1jHTEoWe 1.上传后,解压: 2.进入解压后的目录:cd dstat-0.7.3/ 3.make 4.make instal ...

  9. python的种类有哪些?

    CPython 当我们从Python官方网站下载并安装好Python 3.6后,我们就直接获得了一个官方版本的解释器:CPython.这个解释器是用C语言开发的,所以叫CPython.在命令行下运行p ...

  10. scanf()函数的调用:编写求正方形面积的通用程序

    #include<stdio.h>void main(){ int a, area; scanf("%d",&a); //等待用户从键盘输入一个整数// are ...