一个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蓝图
蓝图是手动构建图。不用编程。
tensorflowwhile_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 <= 100
def 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 <= 100
def loop_body(i, v):
i += 1
v += i
return i, v
ii, 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=.
从图中可以看出,两个变量分别是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, v
ii, 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()
Spark DAG
如下代码,只有在执行collect时才真正开始提交运行图。而上边的代码都是在构图,实际并未运行
Flink DAGFlink 原理与实现:架构和拓扑概览
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代码,想这么做就很困难。
可以计算图是受限制的代码。每个结点看做一个抽象的函数。在计算图中没有面向对象编程,也无法搞复杂设计模式。其简单性能,边界性虽然给实现功能带来了困难,但是,给分布式执行,自动优化等提供了方便。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)