图是TensorFlow最基本的结构,一切计算都是基于图结构运行的,图结构包含张量(Tensor)和操作(Operation)。
TensorFlow1.x采用的是静态计算图,需要先创建计算图,然后再开启会话Session,显式执行计算图。在TensorFlow2.0中,采用的是动态计算图,即每使用一个算子后,该算子会被动态加入到默认计算图中即即执行,无需开启Session。
1、计算图
计算图由节点(nodes)和线(edges)组成。
节点表示操作符Operator,线表示计算间的依赖关系。实线表示有数据传递依赖,传递的数据即张量。虚线通常可以表示控制依赖,即执行先后顺序。
2、静态计算图
静态计算则意味着程序在编译执行时将先生成神经网络的结构,然后再执行相应操作。
从理论上讲,静态计算这样的机制允许编译器进行更大程度的优化,但是这也意味着你所期望的程序与编译器实际执行之间存在着更多的代沟。代码中的错误将更加难以发现(比如,如果计算图的结构出现问题,你可能只有在代码执行到相应操作的时候才能发现它)。
import tensorflow as tf
g = tf.compat.v1.Graph()
with g.as_default():
x = tf.compat.v1.placeholder(name='x', shape=[], dtype=tf.string)
y = tf.compat.v1.placeholder(name='y', shape=[], dtype=tf.string)
z = tf.strings.join([x,y],name = "join",separator = " ")
with tf.compat.v1.Session(graph = g) as sess:
# fetches的结果非常像一个函数的返回值,而feed_dict中的占位符相当于函数的参数序列。
result = sess.run(fetches = z,feed_dict = {x:"hello",y:"world"})
print(result)
3、动态计算图
动态计算意味着程序将按照我们编写命令的顺序进行执行。
这种机制将使得调试更加容易,并且也使得我们将大脑中的想法转化为实际代码变得更加容易。
# 动态计算图在每个算子处都进行构建,构建后立即执行
x = tf.constant("hello")
y = tf.constant("world")
z = tf.strings.join([x,y],separator=" ")
tf.print(z)
4、AutoGraph
TensorFlow 2.0主要使用的是动态计算图和Autograph。
Autograph机制可以将动态图转换成静态计算图,兼收执行效率和编码效率之利。动态计算图易于调试,编码效率较高,但执行效率偏低。静态计算图执行效率很高,但较难调试。而Autograph机制通过@tf.function
装饰器,可以将动态图转换成静态计算图,达到兼顾执行效率和编码效率的目的。
AutoGraph将Python控制流转换为TensorFlow表达式,允许用户在装饰有tf.function的函数中编写常规Python,例如while,if,break,continue和return,支持嵌套。这意味着可以在while和if语句的条件下使用Tensor表达式,或者在for循环中迭代Tensor。当然Autograph机制能够转换的代码并不是没有任何约束的,有一些编码规范需要遵循,否则可能会转换失败或者不符合预期,在使用过程中有以下三个注意事项:
- 使用tf内部函数,避免直接使用python函数,因为无法嵌入进计算图;
- 避免定义 tf.Variable, 以为它是动态的,每次迭代都会更新;
- 不可以修改列表字典等数据结构。 ```python import tensorflow as tf import numpy as np
@tf.function(autograph=True) def myadd(a,b): for i in tf.range(3): tf.print(i) c = a+b print(“tracing”) return c
程序运行会经历两个步骤:<br />第一步,会创建一个静态计算图,跟踪执行一遍函数体中的Python代码,确定各个变量的Tensor类型,并根据执行顺序将算子添加到计算图中。 在这个过程中,如果开启了autograph=True(默认开启),会将Python控制流转换成TensorFlow图内控制流。主要是将if语句转换成tf.cond算子表达,将while和for循环语句转换成tf.while_loop算子表达,并在必要的时候添加tf.control_dependencies指定执行顺序依赖关系。<br />第二步,执行计算图。
```python
myadd(tf.constant("hello"),tf.constant("world"))
# 第二次调用函数,只进行第二步,执行计算图。因此打印结果中没有"tracing"。
myadd(tf.constant("hello"),tf.constant("world"))
# 由于输入参数的类型已经发生变化,已经创建的计算图不能够再次使用。需要重新创建新的计算图、执行计算图。
myadd(tf.constant(1),tf.constant(2))
# 如果调用被@tf.function装饰的函数时输入的参数不是Tensor类型,则每次都会重新创建计算图。
myadd("hello","world")