Tensorflow原理

深度学习—Tensorflow内部原理(1)


对于神经网络的初学者,一般都停留在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
)
发布了19 篇原创文章 · 获赞 25 · 访问量 2443

猜你喜欢

转载自blog.csdn.net/fly975247003/article/details/100435991