深度学习—Tensorflow内部原理(1)
- 1、name_scope
- 2、variable_scope
对于神经网络的初学者,一般都停留在tensorflow的使用阶段,而对于其内部原理不是特别清楚,针对这一现状,我通过查询相关资料,学习tensirflow的内部原理。
Tensorflow是一个基于计算图的运算框架,它的核心操作分为三步:
(1)构建计算图。
(2)分发计算任务。
(3)执行计算任务。
tensorflow中为每一个变量命名是一个十分重要的环节,此外,还需要给每一个操作创建名字。为了更好地组织变量和操作的名字,需要使用层次结构定义名字,此时就需要scoe这个概念。
scope对分离变量的命名空间有十分重要的作用,下面通过一段代码展示它的用法。
import tensorflow as tf
with tf.name_scope('123'):
with tf.name_scope('456'):
with tf.variable_scope('789'):
a = tf.Variable(1, name='a')
print(a.name)
b = tf.get_variable('b', 1)
print(b.name)
运行结果:
从结果看,对直接构建的Variable变量来说,每一个scope都会对名字产生影响;而对使用函数get_variable创建的变量来说,并不是所有的scope都会被它考虑在内。只有variable_scope会对它的名字产生影响,而name_scope不会。
而对于如下代码:
import tensorflow as tf
with tf.name_scope('123'):
with tf.name_scope('456'):
with tf.variable_scope('789'):
a = tf.Variable(1, name='a')
print(a.name)
b = tf.get_variable('b', 1)
print(b.name)
with tf.name_scope('456'):
with tf.variable_scope('789'):
c = tf.Variable(1, name='c')
print(c.name)
d = tf.get_variable('d', 1)
print(d.name)
运行结果:
结果中,在同一个scope内,同样名字的name_scope被声明第二次时,scope的名字并不会直接被复用出现,而是通过改名的形式创建一个全新的scope。
1、name_scope
在Tensorflow源代码tensorflow/tensorflow/python/framework/ops.py中,找到name_scope函数的定义,抛开一些与核心功能无关的代码,这个函数首先要找到框架中的默认计算图(Default Graph),在调用这个默认计算图的name_scope方法中生成scope。
g = _get_graph_from_inputs(self._values)
with name_scope(name, default_name=default_name, values=values) as scope:
yield scope
在name_scop方法中,如果传入的scope name在结尾包含’/’,把这个符号删除,就可以作为命名空间的名字了。如果不是这样,就要进入一个叫做unique_name的方法中,和自己的父级别的命名空间拼起来,在名字库中对比,如果有重名,就采用“原名_%d”的方式进行修改并尝试是否重名,直到不再重名为止。有关代码如下:
old_stack = self._name_stack
if not name: # Both for name=None and name="" we re-set to empty scope.
new_stack = None
elif name[-1] == "/":
new_stack = _name_from_scope_name(name)
else:
new_stack = self.unique_name(name)
self._name_stack = new_stack
try:
yield "" if new_stack is None else new_stack + "/"
finally:
self._name_stack = old_stack
unique_name方法的核心代码如下:
def unique_name(self, name, mark_as_used=True):
if self._name_stack:
name = self._name_stack + "/" + name
# For the sake of checking for names in use, we treat names as case
# insensitive (e.g. foo = Foo).
name_key = name.lower()
i = self._names_in_use.get(name_key, 0)
# Increment the number for "name_key".
if mark_as_used:
self._names_in_use[name_key] = i + 1
if i > 0:
base_name_key = name_key
# Make sure the composed name key is not already used.
while name_key in self._names_in_use:
name_key = "%s_%d" % (base_name_key, i)
i += 1
# Mark the composed name_key as used in case someone wants
# to call unique_name("name_1").
if mark_as_used:
self._names_in_use[name_key] = 1
# Return the new name with the original capitalization of the given name.
name = "%s_%d" % (name, i-1)
return name
2、variable_scope
用于定义创建变量(层)的操作的上下文管理器。此上下文管理验证values是否来自于同一图形,确保图形是默认的图形,并推送名称范围和变量范围。
如果name_or_scope不是None,则使用as is。如果scope是None,则使用default_name。在这种情况下,如果以前在同一范围内使用过相同的名称,则通过添加_N来使其具有唯一性。变量的范围允许创建新变量并共享已创建的变量,同时提供检查以防止意外创建或共享。
本文中提供几个基本的示例:
示例1,如何创建一个新变量:
with tf.variable_scope("foo"):
with tf.variable_scope("bar"):
v = tf.get_variable("v", [1])
assert v.name == "foo/bar/v:0"
示例2,共享变量AUTO_REUSE:
def foo():
with tf.variable_scope("foo", reuse=tf.AUTO_REUSE):
v = tf.get_variable("v", [1])
return v
v1 = foo() # Creates v.
v2 = foo() # Gets the same, existing v.
assert v1 == v2
示例3,使用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 == v
示例4、通过捕获范围并设置重用来共享变量:
with tf.variable_scope("foo") as scope:
v = tf.get_variable("v", [1])
scope.reuse_variables()
v1 = tf.get_variable("v", [1])
assert v1 == v
为了防止意外共享变量,我们在获取非重用范围中的现有变量时引发异常
with tf.variable_scope("foo"):
v = tf.get_variable("v", [1])
v1 = tf.get_variable("v", [1])
# Raises ValueError("... v already exists ...")
同样,我们在尝试获取重用模式中不存在的变量时引发异常。
with tf.variable_scope("foo", reuse=True):
v = tf.get_variable("v", [1])
# Raises ValueError("... v does not exists ...")
方法:
__init__(
name_or_scope,
default_name=None,
values=None,
initializer=None,
regularizer=None,
caching_device=None,
partitioner=None,
custom_getter=None,
reuse=None,
dtype=None,
use_resource=None,
constraint=None,
auxiliary_name_scope=True
)
该方法用于初始化上下文管理器。
参数:
- name_or_scope:string或者VariableScope表示打开的范围。
- default_name:如果name_or_scope参数为None,则使用默认的名称,该名称将是唯一,如果提供了name_or_scope,它将不被使用,因此它不是必需的,并且可以是None。
- values:传递给操作数的Tensor参数列表。
- initializer:此范围内变量的默认初始值设定项。
- 此范围内变量的默认正规划器。
- caching_device:此范围内变量的默认缓存设备。
- partitioner:此范围内变量的默认分区程序。
- custom_getter:此范围内变量的默认自定义吸气。
- reuse:可以是True、None或tf.AUTO_REUSE;如果是true,则我们进入此范围的重用模式以及所有子范围;如果是tf.AUTO_REUSE,则我们创建变量(如果它们不存在),否者返回它们,如果是None,则我们继承父范围的重用标志。当启用紧急执行时,该参数总是被强制为tf.AUTO_REUSE。
- dtype:在此范围内创建的变量类型(默认为传入范围中的类型,或从父类范围继承)。
- use_resource:如果为false,则所有变量都将是常规变量;如果为true,则将使用具有明确定义的语义实验性。
- ResourceVariables。默认为false(稍后将更改为true)。当启用紧急执行时,该参数总是被强制为true。
- constraint:一个可选的投影函数,在被Optimizer(例如用于实现层权重的范围约束或值约束)更新之后应用于该变量。该函数必须将代表变量值的未投影张量作为输入,并返回投影值的张量(它必须具有相同的形状)。进行异步分布式培训时,约束体哦阿健的使用是不安全的。
- auxillary_name_scope:如果为True,则我们用范围差个内奸一个辅助名称范围;如果为False,则我们不接触名称范围。
- ValueError:在差个内奸范围内尝试重用时,或在重用范围内创建时。
- TypeError:某些参数的类型不合适时。
enter:
__enter__()
__exit__
__exit__(
type_arg,
value_arg,
traceback_arg
)