美文网首页
[tf]理解图与会话

[tf]理解图与会话

作者: VanJordan | 来源:发表于2018-12-12 23:02 被阅读4次

引用

一些关键问题

  • with tf.Graph().as_default():之前不明白为什么要这么写呢,有人说是为了隔离多个图,但是实际上并没有需要多图才能完成的任务。这么写只是为了能更好的隔离变量和操作,其实并没有什么卵用。
  • with tf.Graph() as g:这个代码基本没人写的原因是如果不设置成默认的graph那么以后sess中要设置tf.Session(graph = g)比较麻烦,所以写这个语句不如写上面的语句。
  • tf.Graph().as_default()的含义和tf.Session().as_default()的实际含义是不一样的,因为Graph的as_default()出了这个上下文管理器就不能用了,只是表示在这个with语句里面讲这个图设为默认图,出了with语句就自动恢复原来默认图了。而Session的as_default()是出了with语句还能继续使用
# 1. Using Graph.as_default():
g = tf.Graph()
with g.as_default():
  c = tf.constant(5.0)
  assert c.graph is g

# 2. Constructing and making default:
with tf.Graph().as_default() as g:
  c = tf.constant(5.0)
  assert c.graph is g

什么是图

  • tensorflow中的数据和相应的运算都是定义在图上的
  • Graph(计算图)就是节点与边的集合,领域模型何其简单。计算图是一个DAG图,计算图的执行过程将按照DAG的拓扑排序,依次启动OP的运算。其中,如果存在多个入度为0的节点,TensorFlow运行时可以实现并发同时执行多个OP的运算,提高执行效率。
  • tensorflow中的大多数函数只会将操作和张量添加到默认图中,而不会执行实际计算。
  • tf.Graph() 表示实例化了一个类,一个用于tensorflow 计算和表示用的数据流图,通俗来讲就是:在代码中添加的操作(画中的结点)和数据(画中的线条)都是画在纸上的“画”,而图就是呈现这些画的纸,你可以利用很多线程生成很多张图,但是默认图就只有一张。
  • tf.Graph().as_default() 表示将这个实例即新生成的图作为整个 tensorflow 运行环境的默认图,如果只有一个主线程不写也没有关系,tensorflow 里面已经存好了一张默认图,可以使用tf.get_default_graph() 来调用(显示这张默认纸),当你有多个线程就可以创造多个tf.Graph(),就是你可以有一个画图本,有很多张图纸,这时候就会有一个默认图的概念了。
self.model = model
self.graph = tf.Graph()
with self.graph.as_default():
    self.sess = tf.Session()
    with self.sess.as_default():
        initializer = tf.contrib.layers.xavier_initializer(uniform = True)
        with tf.variable_scope("model", reuse=None, initializer = initializer):
            self.trainModel = self.model(config = self)
            if self.optimizer != None:
                pass
            elif self.opt_method == "Adagrad" or self.opt_method == "adagrad":
                self.optimizer = tf.train.AdagradOptimizer(learning_rate = self.alpha, initial_accumulator_value=1e-20)
            elif self.opt_method == "Adadelta" or self.opt_method == "adadelta":
                self.optimizer = tf.train.AdadeltaOptimizer(self.alpha)
            elif self.opt_method == "Adam" or self.opt_method == "adam":
                self.optimizer = tf.train.AdamOptimizer(self.alpha)
            else:
                self.optimizer = tf.train.GradientDescentOptimizer(self.alpha)
            grads_and_vars = self.optimizer.compute_gradients(self.trainModel.loss)
            self.train_op = self.optimizer.apply_gradients(grads_and_vars)
        self.saver = tf.train.Saver()
        self.sess.run(tf.initialize_all_variables())

空图

计算图的初始状态,并非是一个空图。实现添加了两个特殊的节点:Source与Sink节点,分别表示DAG图的起始节点与终止节点。其中,Source的id为0,Sink的id为1;依次论断,普通OP节点的id将大于1。

另外,Source与Sink之间,通过连接「控制依赖」的边,保证计算图的执行始于Source节点,终于Sink节点。它们之前连接的控制依赖边,其src_output, dst_input值都为-1。

习惯上,仅包含Source与Sink节点的计算图也常常称为空图。

图的可视化

# Build your graph.
x = tf.constant([[37.0, -23.0], [1.0, 4.0]])
w = tf.Variable(tf.random_uniform([2, 2]))
y = tf.matmul(x, w)
# ...
loss = ...
train_op = tf.train.AdagradOptimizer(0.01).minimize(loss)

with tf.Session() as sess:
  # `sess.graph` provides access to the graph used in a <a href="../api_docs/python/tf/Session"><code>tf.Session</code></a>.
  writer = tf.summary.FileWriter("/tmp/log/...", sess.graph)

  # Perform your computation...
  for i in range(1000):
    sess.run(train_op)
    # ...

  writer.close()

创建多个图

  • 训练模型时,整理代码的一种常用方法是使用一个图训练模型,然后使用另一个图对训练过的模型进行评估或推理。
  • 在许多情况下,推理图与训练图不同。
  • 默认图会存储与添加的每个 tf.Operationtf.Tensor 有关的信息。如果程序创建了大量未连接的子图,更有效的做法是使用另一个 tf.Graph 构建每个子图,以便回收不相关的状态。
  • 图是归属于线程的,如果想在不同的线程里面使用默认图,必须使用g.as_default()

The default graph is a property of the current thread. If you create a new thread, and wish to use the default graph in that thread, you must explicitly add a with g.as_default(): in that thread's function.

with g_1.as_default():
  # Operations created in this scope will be added to `g_1`.
  c = tf.constant("Node in g_1")

  # Sessions created in this scope will run operations from `g_1`.
  sess_1 = tf.Session()

g_2 = tf.Graph()
with g_2.as_default():
  # Operations created in this scope will be added to `g_2`.
  d = tf.constant("Node in g_2")

# Alternatively, you can pass a graph when constructing a <a href="../api_docs/python/tf/Session"><code>tf.Session</code></a>:
# `sess_2` will run operations from `g_2`.
sess_2 = tf.Session(graph=g_2)

assert c.graph is g_1
assert sess_1.graph is g_1

assert d.graph is g_2
assert sess_2.graph is g_2

执行过程

  • 在分布式的运行时环境中,Client 执行Session.run 时,传递整个计算图给后端的
    Master。此时,计算图是完整的,常称为Full Graph。随后,Master 根据Session.run 传递给它的fetches, feeds 参数列表,反向遍历Full Graph,并按照依赖关系,对其实施剪枝,最终计算得到最小的依赖子图,常称为Client Graph。
  • 接着,Master 负责将Client Graph 按照任务的名称分裂(SplitByTask) 为多个Graph Partition;其中,每个Worker 对应一个Graph Partition。随后,Master 将Graph Partition分别注册到相应的Worker 上,以便在不同的Worker 上并发执行这些Graph Partition。最后,Master 将通知所有Work 启动相应Graph Partition 的执行过程。其中,Work 之间可能存在数据依赖关系,Master 并不参与两者之间的数据交换,它们两两之间互相通信,独立地完成交换数据,直至完成所有计算。

图实例

  • 一般地,OP 注册到一个全局的唯一的隐式的默认的图实例中。特殊地,TensorFlow也可以显式地创建新的图实例g并调用g.as_default() 使其成为当前线程中唯一默认的图实例并在该上下文管理器中所创建的OP 都将自动注册到该图实例中
with tf.Graph().as_default() as g:
    c = tf.constant(5.0)
    assert c.graph is g
  • 事实上,g.as_default 从当前线程的图栈中返回了一个上下文管理器使得当前的图实例g 覆盖原来默认的图实例当退出了上下文管理器后,则恢复原来默认的图实例。但是,在任何一个时刻,在当前线程中有且仅有一个图实例成为默认的,可以调用tf.get_default_graph(),返回该默认的图实例。
  • 如果没有显式创建多个图实例,则所有OP 都默认地注册到该图实例中。

图上的device方法

  • 常常使用上下文管理器device(device_spec) 指定OP 设备规范,在该上下文的作用域
    内构造的OP 在运行时将被放置在指定设备上运行。
  • 其中,device 是Graph 的一个方法,它设计了一个栈式结构的上下文管理器,实现设
    备规范的闭包、合并、覆盖等特性。
with g.device('/gpu:0'):
# All OPs constructed here will be placed on GPU 0.

为什么会需要会话

  • 在Session 的生命周期中,将根据计算图的计算需求,按需分配系统资源,包括变量,队列,读取器等。
  • 当计算完成后,需要确保Session 被安全地关闭,以便安全释放所管理的系统资源。
  • 一个Session 实例,只能运行一个图实例;但是,一个图实例,可以运行在多个Session实例中。如果尝试在同一个Session 运行另外一个图实例,必须先关闭Session(不必销毁),再启动新图的计算过程。
  • 虽然一个Session 实例,只能运行一个图实例。但是,因为Session 是一个线程安全
    的类,可以并发地执行该图实例上的不同子图。例如,一个典型的机器学习训练模型中,可以使用同一个Session 实例,并发地运行输入子图,训练子图,以及Checkpoint 子图。
  • 为了提高效率,避免计算图频繁地创建与销毁,存在一种实现上的优化技术。在图实例中维护一个Session 的引用计数器,当且仅当Session 的数目为零时,才真正地销毁图实例

默认会话

  • 通过调用Session.as_default(),将该Session 置为默认Session,同时它返回了一个
    上下文管理器。在默认Session 的上文中,可以直接实施OP 的运算,或者Tensor 的求值。
  • 但是,Session.as_default() 并不会自动关闭Session,需要用户显式地调用Session.close 方法
hello = tf.constant('hello, world')
sess = tf.Session()
with sess.as_default():
     print(hello.eval())
sess.close()

张量求值

如上例代码,hello.eval() 等价于tf.get_default_session().run(hello)。其中,Tensor.
eval 如下代码实现。

class Tensor(_TensorLike):
    def eval(self, feed_dict=None, session=None):
        if session is None:
            session = get_default_session()
        return session.run(tensors, feed_dict)
  • 同理,当用户未显式提供Session,Operation.run 将自动获取默认的Session 实例,
    并按照当前OP 的依赖关系,以某个特定的拓扑排序执行该计算子图。

会话类型

  • 一般地,存在两种基本的会话类型:Session 与InteractiveSession。后者常常用于交
    互式环境,它在构造期间将其自身置为默认,简化默认会话的管理过程。
  • 此外,两者在运行时的配置也存在差异。例如,InteractiveSession 将GPUOptions.allow_growth
    置为True,避免在实验环境中独占整个GPU 的存储资源。

相关文章

网友评论

      本文标题:[tf]理解图与会话

      本文链接:https://www.haomeiwen.com/subject/fuymhqtx.html