【深度学习】村通网之——谈谈Tensorflow Eager Execution机制之静态图和动态图的区别(一)

前言

随着TensorFlow 1.4 Eager Execution的出现,TensorFlow的使用出现了革命性的变化。

介绍

我很早就听说过这样一句话:对于深度学习框架来说,在学术界中,PyTorch更受欢迎,在工业界中,Tensorflow更受欢迎。当时很不解,后来才知道,究其原因,不是因为PyTorch生态比Tensorflow优秀,是因为早期的PyTorch可以构建动态图,可以像正常程序一样去编写或者进行调试。而早期的Tensorflow只能构建静态图,网络的forward和backward都是黑箱操作,想在中间加一个print输出过程中结果都很麻烦,需要创建sess,然后通过sess.run()将数据feed进去,然后在fetch中得到中间结果。这个过程非常麻烦。

静态图(代表:早期的Tensorflow):只有定义好一个完整的网络结构(Graph),才能开始执行整个图(创建session,通过参数graph=xxx指定当前会话运行的计算图),且在运行过程中不能对图进行修改(比如添加网络结点、删除结点等操作)。整个过程和C语言编译很像,一旦构图完成开始执行训练,就不能对网络结构进行改变。

动态图(代表:PyTorch和1.4及以上版本的Tensorflow) :在动态图中,可以用各种逻辑控制语法(比如if else等),按照代码顺序执行,且可以在模型中间输出,整个过程和顺序执行的代码没什么两样。在前向传播中,一边执行一边生成本次的计算图,直到到最后一层生成完整的图,再进行反向传播。所以动态图每次喂入数据都会重新构建。这样有一个缺点,就是执行速度会比静态图稍慢。

我们可以总结为以下两点:
静态图: 声明式编程。一次构建,多次使用;构建过程稍微繁琐;性能好,速度快。
动态图: 命令式编程。图的构建很灵活,可以用Python的控制流;方便debug;操作可立即获得执行结果,无需等待图全部构建完成。

所以早期学术界更偏爱PyTorch动态图强大的debug能力,工业界更偏爱Tensorflow静态图快速稳定的能力。

随着Tensorflow 1.4版本的推出,Eager Execution终于出现了,这个版本在兼容老版本的基础上,将动态图机制加入到了Tensorflow中,使其像PyTorch一样能够构建动态图,从此Tensorflow既能构建动态图也能构建静态图。而对于这个Eager Execution版本,通俗的讲,有以下几个亮点:

  • 无需placeholder和feed,可以直接使用numpy数组作为输入
  • 可以立即执行Operation,例如输入图像数据(numpy数组),立即输出卷积结果,并将结果转换为numpy。即可以将老版本中定义静态图的Operation直接当做函数来立即执行一个Operation。(老版本需要先利用Operation定义静态图,然后到Session中执行不可变的静态图,并且获取Operation的结果需要通过sess.run()和feed,非常麻烦)。
  • 动态图特性使得Tensorflow可以使用python的if语句和循环语句来控制模型的结构,而不用通过tf.cond这种难用的函数来控制模型的解构。
  • 动态图特性使得模型更便于调试,模型的运行过程与代码的运行过程一致。而在老版本的静态图中,网络的forward与backward都是黑箱操作,想加个print来输出过程中的变量都很难。
  • 梯度的计算和更新也变成函数的调用,可由开发者自己调用,这就意味着开发者可以自己重写反向传播的方式,更大限度的针对模型进行优化。

以上亮点来自:【最新TensorFlow1.4.0教程01】TF1.4.0介绍与动态图机制 Eager Execution使用

目前Tensorflow2.0已经将Eager Execution作为Tensorflow的默认执行模式,这即意味着Tensorflow如同PyTorch那样,由编写静态计算图完全转向动态计算图,这使得开发者可以更简洁高效地搭建原型。当然你可以选择不使用eager模式,自己构建计算图。

搭建静态图

我使用的Tensorflow版本是1.6.0,不同的tf版本,代码可能稍有差异。
我们先使用tf搭建一个静态图:

import tensorflow as tf
import numpy as np

# 获得默认图,如果不用with语句显式指定所归属的计算图,
# 则所有的tensor和Operation都是在默认计算图中定义的,
# 使用tf.get_default_graph()函数可以获取当前默认的计算图句柄。
g=tf.get_default_graph()

x=[[2.]]
x=np.asarray(x)

result=tf.matmul(x,x)

print(result)

# 在graph=g上创建session声明
with tf.Session(graph=g) as sess:
	print(sess.run(result))

输出:

Tensor("MatMul:0", shape=(1, 1), dtype=float64)
[[4.]]

可以看到,第一个print并没有输出result的计算结果,而是输出了一个Operation对象(Matmul对象)。第二个print才通过sess.run()语句输出了计算结果。那我想直接在第一个print的位置得到result可以吗?可以,请看下面的动态图。

搭建动态图

上述的Eager Execution特性的实现,只需要在代码顶部加上这两行即可,其他的操作兼容老版本API:

import tensorflow.contrib.eager as tfe
tfe.enable_eager_execution()

例子:

import tensorflow as tf
import numpy as np
import tensorflow.contrib.eager as tfe

# 使用动态图机制
tfe.enable_eager_execution()

x=[[2.]]
x=np.asarray(x)
result=tf.matmul(x,x)

print("{}".format(result))

# 动态图不能使用自定义的session
# 因为在创建动态图的过程中,默认也建立一个session。所有的代码都在该session中进行,而且该session具有进程相同的生命周期。
# with tf.Session() as sess:
# 	print(sess.run(result))

输出:

[[4.]]

从动态图的代码和输出结果可以看出:
第一,我们直接在模型定义过程中使用print了result,从结果可以看出,可以直接输出result的值,而在静态图中输出的是Operation的对象,即动态图的操作在python代码中被调用后,其操作立即被执行,张量赋值也是如此。
第二,我们可以注意到,动态图不再需要tf.Session() 来建立对话了,因为在创建动态图的过程中,Tensorflow会默认建立一个session和graph。所有的张量和操作都属于计算图graph,所有的代码都在该session中进行,而且该session具有进程相同的生命周期。这也就是为什么不用写sess.run()就能得到计算结果的原因。而这表明一旦使用动态图就无法实现静态图中关闭session的功能。这是动态图的不足之处:无法实现多session操作,这使得在一个进程中同时跑多个模型成为困难的事情(在静态图中,我们可以创建多个sess与graph使用多个模型,不同计算图上的张量和运算都不会共享,计算图可以用来隔离张量和计算,使得模型之间相互无影响)。如果当前代码只需要一个session来完成的话,建议优先选择动态图Eager来实现。
针对第二点,我们做个实验,我们在动态图中,创建一个新graph,在graph中指定张量和操作,然后创建相对应的session执行,看看动态图中能否存在第二个session:

import tensorflow as tf
import numpy as np
import tensorflow.contrib.eager as tfe

# 使用动态图机制
tfe.enable_eager_execution()

x=[[2.]]
x=np.asarray(x)

m=tf.matmul(x,x)

print("{}".format(m))

g1 = tf.get_default_graph()
print(g1)

# 实验部分
g2 = tf.Graph()
with g2.as_default():
	x2=[[3.]]
	x2=np.asarray(x2)
	result2=tf.matmul(x2,x2)

with tf.Session(graph=g2) as sess:
	print(sess.run(result2))

结果报错:

RuntimeError: The Session graph is empty.  Add operations to the graph before calling run().

说明在动态图中不能手动创建session,就算创建了,也是无效的,所以在使用动态图的时候,无法实现多session的操作。

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值