Autograph机制小结
有三种计算图的构建方式:静态计算图,动态计算图,以及Autograph。
TensorFlow 2.0主要使用的是动态计算图和Autograph。
动态计算图易于调试,编码效率较高,但执行效率偏低。
静态计算图执行效率很高,但较难调试。
而Autograph机制可以将动态图转换成静态计算图,兼收执行效率和编码效率之利。
当然Autograph机制能够转换的代码并不是没有任何约束的,有一些编码规范需要遵循,否则可能会转换失败或者不符合预期。
我们将着重介绍Autograph的编码规范和Autograph转换成静态图的原理。
并介绍使用tf.Module来更好地构建Autograph。
本篇我们介绍使用Autograph的编码规范。
Autograph编码规范总结
-
被
@tf.function
修饰的函数应尽可能使用TensorFlow中的函数而不是Python中的其他函数。例如使用tf.print而不是print,使用tf.range而不是range,使用tf.constant(True)而不是True. -
避免在
@tf.function
修饰的函数内部定义tf.Variable. -
被
@tf.function
修饰的函数不可修改该函数外部的Python列表或字典等数据结构变量
Autograph的机制原理
当我们使用@tf.function
装饰一个函数的时候,后面到底发生了什么呢?
|
|
第一次调用这个被@tf.function
装饰的函数时,后面到底发生了什么
第一件事情是创建计算图。
即创建一个静态计算图,跟踪执行一遍函数体中的Python代码,确定各个变量的Tensor类型,并根据执行顺序将算子添加到计算图中。 在这个过程中,如果开启了autograph=True(默认开启),会将Python控制流转换成TensorFlow图内控制流。 主要是将if语句转换成 tf.cond算子表达,将while和for循环语句转换成tf.while_loop算子表达,并在必要的时候添加 tf.control_dependencies指定执行顺序依赖关系。
相当于在 tensorflow1.0执行了类似下面的语句:
|
|
第二件事情是执行计算图。
相当于在 tensorflow1.0中执行了下面的语句:
|
|
当我们再次用相同的输入参数类型调用这个被@tf.function
装饰的函数时,发生了什么:
|
|
由于输入参数的类型已经发生变化,已经创建的计算图不能够再次使用。
需要重新做2件事情:创建新的计算图、执行计算图。
所以我们又会先看到的是第一个步骤的结果:即Python调用标准输出流打印"tracing"语句。
然后再看到第二个步骤的结果:TensorFlow调用标准输出流打印1,2,3。
需要注意的是,如果调用被@tf.function
装饰的函数时输入的参数不是Tensor类型,则每次都会重新创建计算图。
|
|
Autograph和tf.Module
ensorFlow提供了一个基类tf.Module,通过继承它构建子类,我们不仅可以非常方便地管理变量,还可以非常方便地管理它引用的其它Module,最重要的是,我们能够利用tf.saved_model保存模型并实现跨平台部署使用。
tf.keras.models.Model,tf.keras.layers.Layer
都是继承自tf.Module的,提供了方便的变量管理和所引用的子模块管理的功能。
利用tf.Module提供的封装,再结合TensoFlow丰富的低阶API,实际上我们能够基于TensorFlow开发任意机器学习模型(而非仅仅是神经网络模型),并实现跨平台部署使用。
应用tf.MOdule
封装Autograph
|
|
利用tf.Module
的子类化将其封装一下:
|
|
|
|
在tensorboard中查看计算图,模块会被添加模块名demo_module,方便层次化呈现计算图结构。
|
|
|
|
除了利用tf.Module的子类化实现封装,我们也可以通过给tf.Module添加属性的方法进行封装。
|
|
|
|
tf.Module和tf.keras.Model,tf.keras.layers.Layer
tf.keras中的模型和层都是继承tf.Module实现的,也具有变量管理和子模块管理功能。
|
|
|
|
|
|