tensorflow|tensorflow运行模式

一个c++或者java,python程序员,一开始接触到tensorflow,必然会被其运行方式所迷惑。 因为tensorflow是先构图,再执行。c=tf.add(a,b),在执行这行代码时,只是在构图,并没有进行计算。所以进行print(c)时,只是打出来了未计算的结点引用。
然而,tensorflow这种先构图再计算的方式,并不是首创。像spark, flink这两个计算引擎也是先构图再执行。因此构图也是一种解决问题的模式。 而且还能进行图优化,非常适合分布式计算任务。在游戏引擎中大名鼎鼎的UE4引擎,支持蓝图式编程。可以看作是手动构图。非常适合非程序员职业的人构建程序。而tensorflow是用python去构图,还是有一定的门槛。
从tensorflow2.0开始默认会启动Eager, 这种eager模式下,所有计算会立即执行,非常方便调试。
下面以一个计算1+2+3+...+100的例子来演示它们的区别。
普通python计算

sum = 0 #变量sum用于累计 for i in range(1, 101): #1到100 sum += i print(sum) #输出5050

ue5蓝图 蓝图是手动构建图。不用编程。
tensorflow|tensorflow运行模式
文章图片

tensorflow|tensorflow运行模式
文章图片

tensorflow while_loop的核心要求,conditiion, loop_body的输入参数就是loop_vars
loop_body返回值也必须是loop_vars, 参数数量必须一样
condition的返回值是bool类型的tensor
while_loop返回的也是loop_body最后返回的
import tensorflow.compat.v1 as tf tf.disable_v2_behavior() tf.disable_eager_execution ()def condition(i, v): #循环结束条件 return i <= 100def loop_body(i, v): return i + 1, v + i #输入i,v,返回必须也是两个形状类型相同的tensor,会再次输入i, v = 1, 0 #初始循环变量值 ii, vv = tf.while_loop(condition, loop_body, loop_vars=[i, v]) #这里相当于ue5中蓝图里的for_loop结点#以上是构图,下面执行图得到结果 with tf.Session() as sess: print(sess.run([ii, vv]))#最后输出:[101, 5050]

import tensorflow.compat.v1 as tf tf.disable_v2_behavior() tf.disable_eager_execution ()i = tf.Variable(1) v = tf.Variable(0)def condition(i, v): return i <= 100def loop_body(i, v): i += 1 v += i return i, vii, vv = tf.while_loop(condition, loop_body, loop_vars=[i, v])init_op = tf.global_variables_initializer() #导出图 summary_writer = tf.summary.FileWriter('tb_log/',flush_secs=1) with tf.Session() as sess: sess.run([init_op]) print(sess.run([ii, vv])) summary_writer.add_graph(sess.graph)#添加graph图 summary_writer.close()#运行tensorboard #tensorboard --logdir tb_log #然后打开浏览器就能查看:http://localhost:6006/#graphs&run=.

tensorflow|tensorflow运行模式
文章图片

从图中可以看出,两个变量分别是i,v。while中的两个add和一个less_equal对就了loop_body,和condition

加上变量名再展示会更好:
import tensorflow.compat.v1 as tf tf.disable_v2_behavior() tf.disable_eager_execution ()i = tf.Variable(1, name="i") v = tf.Variable(0, name="v")def condition(i, v): return tf.less_equal(i, 100, name="ile100")def loop_body(i, v): i = tf.add(i, 1, name="addi") v = tf.add(i, v, name="addiv") return i, vii, vv = tf.while_loop(condition, loop_body, loop_vars=[i, v])init_op = tf.global_variables_initializer() summary_writer = tf.summary.FileWriter('tb_log/',flush_secs=1) with tf.Session() as sess: sess.run([init_op]) print(sess.run([ii, vv])) summary_writer.add_graph(sess.graph)#添加graph图 summary_writer.close()

tensorflow|tensorflow运行模式
文章图片

Spark DAG 如下代码,只有在执行collect时才真正开始提交运行图。而上边的代码都是在构图,实际并未运行
tensorflow|tensorflow运行模式
文章图片


Flink DAG tensorflow|tensorflow运行模式
文章图片

Flink 原理与实现:架构和拓扑概览
flink中级篇-DAG图的剖析_kangzai98的博客-CSDN博客_dag图解析
C++也能构图后延时计算:boost phoniex 看到下图中的for_,是延时执行的。采用lambda延时计算技术,可以达到类似构图再计算的效果。
for_ Statement - 1.64.0
#include int iii; std::for_each(c.begin(), c.end(), ( for_(ref(iii) = 0, ref(iii) < arg1, ++ref(iii)) [ cout << arg1 << ", " ], cout << val("\n") ) );

使用计算图的优势 1. 图是抽象的,与实现无关:比如tf.add这么一个OP, 也就是图中的一个结点.只是定义了一个加法和其输入输出参数,多个结点连接形成的计算图也是抽象的描述了整个计算程序。可看做是声明式编程的一种方式。
2. 不同平台不同实现:图构建好后,可以直接跨平台运行。不同平台只要实现自己的add kernel即可。特别的,比如GPU与CPU的实现可能不同。ARM和x86实现不同。服务器与Android客户端可以实现不同。
3. 优化与剪枝:可以对图进行各种优化,提高性能
4. 分布式并行计算:分析图可以得到哪些计算可以并行,就可以调度到不同机器上并行计算机。同样图可以分块拆解,并放到不同机器上执行。
例如PS-Worker架构,就是把图中的variable等存储结点拆解到PS上。而计算结点拆解到Worker上,并在中间插入通信代码。可见计算图做这种事十分方便。相反,如果是一段C++或者python代码,想这么做就很困难。
【tensorflow|tensorflow运行模式】可以计算图是受限制的代码。每个结点看做一个抽象的函数。在计算图中没有面向对象编程,也无法搞复杂设计模式。其简单性能,边界性虽然给实现功能带来了困难,但是,给分布式执行,自动优化等提供了方便。

    推荐阅读