Tensorflow 1.0—— Graph(图)和Session(会话)
Posted 玛丽莲茼蒿
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Tensorflow 1.0—— Graph(图)和Session(会话)相关的知识,希望对你有一定的参考价值。
借鉴:tensorflow中的Graph(图)和Session(会话)的关系 - 简书
借鉴:TensorFlow学习笔记1:graph、session和op - Jiax - 博客园
一、Graph和Session的关系
TensorFlow是一种“符号式编程框架”,首先要构造一个图(graph),然后在会话(Session)上根据这个图做真实的运算(op)。打个比方,graph就是“输入-处理-输出”这个pipeline中的处理部分,一个session就是建立了一个pipeline进行“输入-处理-输出”。graph具有一系列的加工步骤(加减乘除等运算),session把输入投进去,就能得到输出。不同的session都可以使用同一个graph,只要他们的加工步骤是一样的就行。同样的,一个graph可以供多个session使用,而一个session不一定需要使用graph的全部,可以只使用其中的一部分。
graph即tf.Graph(),session即tf.Session(),是两个完全独立的概念。
- graph定义了计算方式,是一些加减乘除等运算的组合。它本身不会进行任何计算,也不保存任何中间计算结果。
- session用来运行一个graph,或者运行graph的一部分。它类似于一个执行者,给graph灌入输入数据,得到输出,并保存中间的计算结果。同时它也给graph分配计算资源(如内存、显卡等)
下图是用tensorflow制作大盘鸡和红烧肉的过程,以此为例来说明graph和session的区别:
左图中绿色矩形为数据,黄色圆圈为中间结果,红色圆圈为最终结果,这是一个完整的制作大盘鸡和红烧肉的graph(相当于是一个菜谱),图中每一个独立单元都可以看成是一个op(操作,包括数据)。在tensorflow中只有graph是没法得到结果的,这就像只有菜谱不可能得到红烧肉是一个道理。于是就有了tf.Session(),他根据graph制定的步骤,将graph变成现实。
tf.Session()就相当于一个厨师长,他下面有很多办事的人(Session()下的各种方法),其中有一个非常厉害人叫tf.Session.run(),他不仅会烧菜,还会杀猪、酿酒、制作酱料等一系列工作,比如:
我的酱料 = sess.run(酱料):run收到制作“酱料”的命令,于是他看了下graph,需要“酵母”和“大豆”来制作酱料,最终他把酱料制作好了(这里酵母和大豆是graph定义好的,但也可以根据自己的喜好来换)。
我的料酒 = sess.run(料酒,feed_dic=米:泰国籼米):run又收到要制作“料酒”的命令,而且不用graph规定的“米”来做,需要用“泰国籼米”,没关系,run跑去买了泰国籼米,又把料酒给做了。
我的红烧肉 = sess.run(红烧肉):傍晚,run又收到了做一份完整红烧肉的命令,这下他有的忙了,必须将整个流程走一遍,才能完成个任务。
我的大盘鸡 = sess.run(大盘鸡): 后来,run又收到做大盘鸡的任务,这是一个独立的任务,跟红烧肉没有半点关系,但不影响,他只要按照步骤照做就可以了。
二、关于Graph
2.1 定义一个图:graph
#定义一个graph
g = tf.Graph()
#默认在g中定义下面op
a = tf.constant(2)
b = tf.constant(3)
x = tf.add(a, b)
上面就定义了一个graph。tensorflow会默认给我们建立一个graph,所以g = tf.Graph()这句其实是可以省略的。上面的graph包含3个操作(即op),但凡是op,都需要通过session运行之后,才能得到结果。如果你直接执行print(a),那么输出结果是:Tensor("a:0", shape=(), dtype=int32)。执行print(tf.Session().run(a))或with方法,才能得到2。可见,在tensorflow中,即使是最基本的对象Tensor(张量)也需要在Session中才能得到其值
2.2 定义多个图:多个graph
你可以定义多个graph,例如一个graph实现z = x + y,另一个graph实现u = 2 * v
g1 = tf.Graph() #定义一个graph
g2 = tf.Graph() #定义另一个graph
with g1.as_default(): #在指定graph中定义op
x = tf.constant(2)
y = tf.constant(3)
z = tf.add(x, y)
with g2.as_default(): #在指定graph中定义op
v = tf.constant(4)
u = tf.mul(2, v)
上述代码定义了两个graph并分别在其中定义了不同的op,但通常不建议这么做,原因如下:
- 运行多个graph需要多个session,而每个session会试图耗尽所有的计算资源,开销太大;
- graph之间没有数据通道,要人为通过python/numpy传数据。
事实上,我们可以把所有的op都定义在一个graph中:
# 没有显示定义graph,系统自动提供一个默认的graph
# 并在其中定义下面op
x = tf.constant(2)
y = tf.constant(3)
z = tf.add(x, y)
v = tf.constant(4)
u = tf.mul(2, v)
从上面graph的定义可以看到,x/y/z是一波,u/v是另一波,二者没有任何交集。这相当于在一个graph里有两个独立的subgraph。当你要计算z = x + y时,执行tf.Session().run(z);当你想计算u = 2 * v,就执行tf.Session().run(u),二者完全独立。但更重要的是,二者在同一个session上运行,系统会均衡地给两个subgraph分配合适的计算资源。
- 我们可以把所有的op(op之间不一定相互有联系,如上图中的红烧肉和大盘鸡,两者完全独立)都定义在同一个graph上,Session在执行某个op时,只执行跟该op有关联的其他op,与其不想关的op是不会被执行的。
三、 关于session
所有的节点计算都在session中完成,tf.Session()是一个大类,使用最多的方法是tf.Session.run()。
通常我们会显示地定义一个session来运行graph,通常采用with的方式(推荐),也可用其他方式:
# 以下在一个默认graph中定义了三个op
x = tf.constant(2)
y = tf.constant(3)
z = tf.add(x, y)
# 以下显示启动了一个Session并在其中执行z
with tf.Session() as sess:
result = sess.run(z)
print(result)
输出结果是5
四、关于op
graph就是由一系列op构成的。
一个特殊的op: tf.placeholder()
placeholder,翻译过来就是占位符。其实它类似于函数里的自变量。比如z = x + y,那么x和y就可以定义成占位符。占位符,顾名思义,就这是占一个位子,平时不用关心它们的值,当你做运算的时候,你再把你的数据灌进去就行了。是不是和自变量很像?看下面的代码:
a = tf.placeholder(tf.float32, shape=[3]) # a是一个3维向量
b = tf.constant([5, 5, 5], tf.float32)
c = a + b
with tf.Session() as sess:
print sess.run(c, feed_dict = a: [1, 2, 3]) # 把[1, 2, 3]灌到a里去
输出结果是[6, 7, 8]。上面代码中出现了feed_dict的概念,其实就是用[1, 2, 3]代替a的意思。相当于在本轮计算中,自变量a的取值为[1, 2, 3]。其实不仅仅是tf.placeholder才可以用feed_dict,很多op都可以。只要tf.Graph.is_feedable(tensor)返回值是True,那么这个tensor就可用用feed_dict来灌入数据。
那为什么不直接用tf.constant()去定义呢?
tf.constant()是直接定义在graph里的,它是graph的一部分,会随着graph一起加载。如果通过tf.constant()定义了一个维度很高的张量,那么graph占用的内存就会变大,加载也会变慢。而tf.placeholder就没有这个问题,所以如果数据维度很高的话,定义成tf.placeholder是更好的选择。
以上是关于Tensorflow 1.0—— Graph(图)和Session(会话)的主要内容,如果未能解决你的问题,请参考以下文章
tensorflow版本2.0和1.0兼容问题:The Session graph is empty. Add operations to the graph before calling run()
TensorFlow的图切割模块——Graph Partitioner