[tf]理解图与会话

引用

一些关键问题

  • 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 的存储资源。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,372评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,368评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,415评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,157评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,171评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,125评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,028评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,887评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,310评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,533评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,690评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,411评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,004评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,812评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,693评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,577评论 2 353

推荐阅读更多精彩内容

  • 1 Java的引用 对于Java中的垃圾回收机制来说,对象是否被应该回收的取决于该对象是否被引用。因此,引用也是J...
    高级java架构师阅读 392评论 0 1
  • Java GarbageCollection(GC) Java不能像C/C++那样直接对内存进行操作(内存分配和垃...
    狮_子歌歌阅读 2,387评论 0 3
  • 1.远古时代的故事那些经历过手工管理内存(MRC)时代的人们,一定对 iOS 开发中的内存管理记忆犹新。那个时候大...
    MissHector阅读 209评论 0 1
  • 今天是教师节,也是我九月份以来失眠最崩溃的一天。我的失眠史已经长达六年。不知道如何形容这种感觉,也不知道如何向不失...
    小xiao婷子阅读 182评论 0 0
  • 作者: 余宗林 我母亲是受过很多苦的,作为外公的大女儿,...
    兜一阅读 454评论 0 0