购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

2.4 TensorFlow 2.0进阶
——AutoGraph和tf.function

TensorFlow 2.0相对于TensorFlow 1.x来说并不是小打小闹的修订和维护,而是做出了重大的升级与改变。

从前面的介绍可知,TensorFlow 2.0摒弃了一直使用的Graph(图)模式,即先构建一个动态图,使用占位符将各个节点填满,之后开启会话(Session)不停地使用馈送(Feed)方法对Graph模式进行更新。TensorFlow默认使用2.0版本的Eager执行模式。从目前演示的例子来看,读者可能会觉得Eager模式并没有太大的提升,但是随着对本书内容的学习,就会对TensorFlow 2.0新的模型训练方式感到喜爱,Eager模式能够让模型执行得更加简洁和明确,对多层和多模块的模型处理也更加简洁和方便。

一直以来,TensorFlow 1.x长期存在的一个诟病是无法使用Python支持的常用代码,而必须使用标准的TensorFlow所内置的API和函数,然而这些函数往往因为编写复杂和使用困难,所以并不能被大多数程序编写者所掌握。

针对这种情况,TensorFlow 2.0推出了一个新的运行理念,即AutoGraph。

一个典型的例子就是在TensorFlow 1.x中如果对数据进行判断或循环,是无法使用Python原生的if和for函数的,而必须使用TensorFlow 1.x自带的函数tf.while、tf.cond等复杂的算子来实现动态流程控制。

1. 第一步:首先定义一个函数

    def check_fun(input_num):
    if input_num > 1:
    output = input_num * input_num
    else:
    output = 1 - input_num
    return output

这里用Python的固有方式定义了一个非常简单的check函数,当输入数据大于1时,返回输入值的平方;当输入数据小于1时,返回与1的差值。

2. 第二步:调用AutoGraph模型对数据进行计算

   print('Eager results:',(check_fun(tf.constant(2.0))," and ",check_fun
(tf.constant(0.2))))

打印输出的结果如下:

当运行这段代码时,TensorFlow 2.0自动调用Eager模式执行函数,这是在内部所完成的,没有任何问题。然而,对于有TensorFlow 1.x编写经验的程序编写者来说,这是不支持的。此时在TensorFlow 2.0中自动调用AutoGraph对Python原生函数进行了包装,即在内部自动完成了以下函数:

    tf_ check_fun  =  tf.autograph.to_graph(check_fun)

如果读者想进一步了解AutoGraph的过程,可以使用如下函数:

    print(tf.autograph.to_code(check_fun))

将这个过程打印出来。现在读者只需要知道对于Python原生的关键词,可以将其看作TensorFlow 2.0自带的一个常规运行函数,无差异地执行。

相比较而言,对于TensorFlow 2.0所采用的Eager模式,在极大地提高了程序编写易用性的基础上,牺牲了部分TensorFlow Graph模式的效率。可以说TensorFlow函数在原生的Graph模式下的执行效率是最高的,Eager模式下的效率次之,经AutoGraph转换后的代码效率最低。

TensorFlow 2.0使用的Eager模式在代码的编写上更为简洁,而原生的Graph模式效率却是最高的,因此为了解决这个差异,TensorFlow 2.0在AutoGraph的基础上还引入了一个新的函数包装方法—tf.function。

简单地说,就是tf.function分层次地将一个函数的操作自动构建成一个TensorFlow所能够接受的Graph,这样在调用时执行这个Graph,从而使得执行效率更高。使用方法如下:

    tf_ check_fun = tf.function(check_fun)

check_fun 函数被tf.function重新装饰,并将以Graph模式执行,可以把其想象成一个封装了Graph的TensorFlow原生函数,直接调用它也会立即得到Tensor结果。

tf.function包装后的函数在其内部是高效执行的。当在内部打印Tensor时,Eager模式时执行会直接打印Tensor的值,而Graph模式打印的是Tensor句柄,无法调用numpy函数取出值,这和TF 1.x的Graph模式是一致的。由于tf.function装饰的函数是以Graph模式执行,其执行速度一般要比Eager模式快,当Graph包含很多小操作时差距更为明显。

但是,这样会带来一个问题—tf.function内部管理了一系列Graph,并控制了Graph的执行。另外,虽然函数内部定义了一系列的操作,但是对于不同的输入需要不同的计算图。例如,函数输入Tensor的shape或者dtype不同,那么计算图是不同的。

【程序2-7】

    import tensorflow as tf
    @tf.function
    def get_num(input_num):
      print("input is:", input_num)
      return input_num + input_num
    print("result is :",get_num(tf.constant(1)))
    print("---------")
    print("result is :",get_num(tf.constant(1.0)))
    print("---------")
    print("result is :",get_num(tf.constant([1, 2])))

该程序的运行结果如图2.4所示。

图2.4 程序2-7的运行结果

注意函数内部的打印,当输入Tensor的shape或者类型发生变化时,打印的东西也会相应改变。所以,它们的计算图(静态的)并不一样。tf.function这种多态特性其实是背后追踪了(Tracing)不同的计算图。具体来说,被tf.function装饰的函数f接受一定的Tensor,并返回0到任意Tensor。当装饰后的函数F被执行时:

(1)根据输入Tensor的shape和dtype确定一个“trace_cache_key”。

(2)每个“trace_cache_key”映射了一个Graph,当新的“trace_cache_key”要建立时,f将构建一个新的Graph,若“trace_cache_key”已经存在,则需要从缓存中查找已有的Graph。

(3)将输入的Tensor放进这个Graph,然后执行得到输出的Tensor。

这种多态性是程序编写时所需要的,因为有时候会输入具有不同shape与dtype的Tensor,但是当“trace_cache_key”越来越多时,就意味着要缓存庞大的Graph,这时就要注意了。特别是对于具有不同维度的输入数据,即根据不同的维度自动更新模型维度的图处理框架,影响是巨大的。实际上每次由于维度的变化,TensorFlow内部维护的一系列Graph(图)都会随之发生变化,从而影响性能。这一点需引起程序编写者的注意。

tf.function的另外一个参数是autograph,默认是True,意思是在构建Graph时将自动使用AutoGraph。这样就可以在函数内部使用Python原生的条件判断以及循环语句了,因为它们会被tf.cond和tf.while_loop转化为Graph代码。需要注意的是判断分支和循环必须依赖于Tensor才会被转化,当autograph为False时,如果存在判断分支和循环必须依赖于Tensor的情况时将会出错。 k1FkmhbbmsqvNeyBFDGNO7CYgzg8gWbH4xliDKrPVUqS51pPenSarU66rKQKnSpR

点击中间区域
呼出菜单
上一章
目录
下一章
×