【TF2】AutoGraph图机制

在tensorflow1.x的时候,代码默认的执行方式是graph execution(图执行),而从tensorflow2.0开始,改为了eager execution(即刻执行)。正如翻译的意思一样,eager execution会立即执行每一步代码,非常的饥渴。而graph execution会将所有代码组合成一个graph(图)后再执行。

1. tf.function的引出

在eager 模式下,代码的编写变得很自然很简单,而且因为代码会被立即执行,所以调试时也变得很方便。而graph 模式下,代码的执行效率要高一些;而且由于graph其实就是一个由操作指令和数据组成的一个数据结构,所以graph可以很方便地被导出并保存起来,甚至之后可以运行在其它非python的环境下(因为graph就是个数据结构,里面定义了一些操作指令和数据,所以任何地方只要能解释这些操作和数据,那么就能运行这个模型);也正因为graph是个数据结构,所以不同的运行环境可以按照自己的喜好来解释里面的操作和数据,这样一来,解释后生成的代码会更加符合当前运行的环境,这里一来代码的执行效率就更高了。

可能有些同学还无法理解上面所说的“graph是个数据结构…”。这里我打个比方来帮助大家理解。假设graph里面包含了两个数据x和y,另外还包含了一个操作指令“将x和y相加”。当C++的环境要运行这个graph时,“将x和y相加”这个操作就会被翻译成相应的C++代码,当Java环境下要运行这个graph时,就会被解释成相应的Java代码。graph里面只是一些数据和指令,具体怎么执行命令,要看当前运行的环境。

除了上面所说的,graph还有很多内部机制使代码更加高效运行。总之,graph execution可以让tensorflow模型运行得更快,效率更高,更加并行化,更好地适配不同的运行环境和运行设备。

graph 虽然运行很高效,但是代码却没有eager 的简洁,为了兼顾两种模式的优点,所以出现了tf.function。使用tf.function可以将eager 代码一键封装成graph。

既然是封装成graph,那为什么名字里使用function这个单词内,不应该是tf.graph吗?因为tf.function的作用就是将python function转化成包含了graph的tensorflow function。所以使用function这个单词也说得通。下面的代码可以帮助大家更好地理解。

import tensorflow as tf

import timeit

from datetime import datetime



# 定义一个 Python function.

def a_regular_function(x, y, b):

  x = tf.matmul(x, y)

  x = x + b

  return x



# `a_function_that_uses_a_graph` 是一个 TensorFlow `Function`.

a_function_that_uses_a_graph = tf.function(a_regular_function)



# 定义一些tensorflow tensors.

x1 = tf.constant([[1.0, 2.0]])

y1 = tf.constant([[2.0], [3.0]])

b1 = tf.constant(4.0)



orig_value = a_regular_function(x1, y1, b1).numpy()

# 在python中可以直接调用tenforflow Function。就像使用python自己的function一样。

tf_function_value = a_function_that_uses_a_graph(x1, y1, b1).numpy()

assert(orig_value == tf_function_value) 

tf.function不仅仅只作用于顶层的python function,它也作用于内嵌的python function。看下面的代码你就能明白了。

def inner_function(x, y, b):

  x = tf.matmul(x, y)

  x = x + b

  return x 

使用tf.function将outer_function变成一个tensorflow Function。注意,之前的代码是将tf.function当作是函数来使用,这样是被当作了修饰符来使用。这两种方式都是被支持的。

@tf.function

def outer_function(x):

 y = tf.constant([[2.0], [3.0]])

 b = tf.constant(4.0)



 return inner_function(x, y, b) 
# tf.function构建的graph中不仅仅包含了 `outer_function`还包含了它里面调用的`inner_function`。

outer_function(tf.constant([[1.0, 2.0]])).numpy() 


output:array([[12.]], dtype=float32) 

在使用tf.function进行graph转化时,tensorflow的代码会被直接进行转化,而python代码会被一个叫做AutoGraph (tf.autograph)的库来负责进行转化

同一个tensorflow function可能会生成不同的graph。因为每一个tf.Graph的input输入类型必须是固定的,所以如果在调用tensorflow function时传入了新的数据类型,那么这次的调用就会生成一个新的graph。输入的类型以及维度被称为signature(签名),tensorflow function就是根据签名来生成graph的,遇到新的签名就会生成新的graph

因为一个tensorflow function里面可以包含多个graph,所以说tensorflow function是具备多态性的。这种多态性使得tensorflow function可以任意支持不同的输入类型,非常的灵活;并且由于对每一个输入类型会生成一个特定的graph,这也会让代码执行时更加高效!

python print函数只在创建graph时被执行,而上面的3次调用中输入参数的类型都是一样的,所以只有一个graph被创建了一次,所以python print函数也只会被调用一次

graph execution模式还有一个特点,就是它会不执行那些无用的代码。看下面的代码

def unused_return_eager(x):

 # 当传入的x只包含一个元素时,下面的代码会报错,因为下面的代码是要获取x的第二个元素。PS:索引是从0开始的,1代表第二个元素

 tf.gather(x, [1]) # unused

 return x



try:

 print(unused_return_eager(tf.constant([0.0])))

except tf.errors.InvalidArgumentError as e:

 print(f'{type(e).__name__}: {e}') 

上面的代码是以eager的模式运行,所以每一行代码都会被执行,所以上面的异常会发生并且会被捕获到。而下面的代码是以graph模式运行的,则不会报异常。因为tf.gather(x, [1])这句代码其实没有任何用途(它只是获取了x的第二个元素,并没有赋值也没有改变任何变量),所以graph模式下它根本就没有被执行,所以也就不会报任何异常了。

前面我们说graph的执行效率会比eager的要高,那到底高多少呢?其实我们可以用下面的代码来计算graph模式到底能比eager模式提升多少效率。

x = tf.random.uniform(shape=[10, 10], minval=-1, maxval=2, dtype=tf.dtypes.int32)



def power(x, y):

 result = tf.eye(10, dtype=tf.dtypes.int32)

 for _ in range(y):

   result = tf.matmul(x, result)

 return result

print("Eager execution:", timeit.timeit(lambda: power(x, 100), number=1000)) 

output:Eager execution: 1.8983725069999764 


power_as_graph = tf.function(power)

print("Graph execution:", timeit.timeit(lambda: power_as_graph(x, 100), number=1000)) 

output:Graph execution: 0.5891194120000023 

从上面的代码可以看出graph比eager的执行时间缩短了近3倍。当然,因具体计算内容不同,效率的提升程度也是不同的。

当输入类型为python类型时,即使类型相同,但是值不同也会创建一个新的graph,所以最好是用tenforflow的数据类型作为function的输入参数

参考文章:
https://www.bilibili.com/read/cv12856573

https://blog.csdn.net/zkbaba/article/details/103915132/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

InceptionZ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值