tensorflow运行模式

tensorflow运行模式,第1张

一个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

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 <= 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 DAG

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代码,想这么做就很困难。

可以计算图是受限制的代码。每个结点看做一个抽象的函数。在计算图中没有面向对象编程,也无法搞复杂设计模式。其简单性能,边界性虽然给实现功能带来了困难,但是,给分布式执行,自动优化等提供了方便。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/727526.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-26
下一篇 2022-04-26

发表评论

登录后才能评论

评论列表(0条)

保存