美文网首页
5. 图概述

5. 图概述

作者: hdszzwy | 来源:发表于2022-07-30 17:53 被阅读0次

    本章将会大致介绍Tensor Flow和keras的底层逻辑以理解TensorFlow是如何工作的。并且如何使用tf.function来将你的代码从Eager模式切换至graph模式。

    图模式

    前面几个章节,我们运行TensorFlow均为eager模式。即TesnorFlow的操作被Python逐个被Python解释执行。尽管eager执行模型是有自己的执行优点,但是图(graph)模式提供了Python环境之外执行的可能并且拥有更好的性能。图执行模式意味着张量计算被解释为TensorFlow图的一部分,也被称为tf.Graph或直接简称为“图”。
    图是包含有tf.Operation(表示计算操作)和tf.Tensor对象(表示为计算操作之间流动的数据)的集合。作为数据结构,图可以被存储、运行和重载(纵然没有原始的Python程序)。

    图模式的一些优点

    图模式提供了更多的灵活性,你可以在任意的环境(如移动设备、嵌入式设备或后端服务器)上执行TensorFlow图,哪怕没有Python解释器。
    图更容易被编译器优化,方法如下:

    • 自动得到常量节点的值。
    • 将相互没有依赖的计算划分入不同的线程或者设备中。
    • 通过代数合并等方法消除子表达式。
      图的优化是由一整个优化系统来执行的。
      简单来说,图模式计算更快,并行更高,可执行的环境更灵活。

    图之初体验

    不论是用tf.function函数还是注解,均可实现图的创建。tf.function将一个传统的Python函数转换为一个TensorFlow的Function对象,而这个TensorFlow的Function对象是就像Python中Function对象一样,是一个可调用的函数(感觉有点儿像C++的函数指针)。

    def a_regular_function(x, y, b):
        x = tf.matmul(x, y)
        x = x + b
        return x
    
    
    a_function_that_uses_a_graph = tf.function(a_regular_function)
    
    
    x1 = tf.constant([[1., 2.]])
    y1 = tf.constant([[2.], [3.]])
    b1 = tf.constant(4.0)
    
    orig_value = a_regular_function(x1, y1, b1).numpy()
    
    tf_function_value = a_function_that_uses_a_graph(x1, y1, b1)
    assert(orig_value == tf_function_value)
    

    表面上看来,TensorFlow创建的Function对象与普通的函数没有什么区别。但是,内部上有很大的差异。通过一个API,一个Function对象封装了一个到多个的tf.Graph对象(多态性)。一个Function对象可以使你享受到图模型带来的所有好处,比如速度和部署灵活的特点。
    tf.function的使用是有递归性的,意味着一旦使用tf.function来转换一个函数,那么这个函数调用的所有函数也会被转换为图模型。

    def inner_function(x, y, b):
        x = tf.matmul(x, y)
        x = x + b
        return x
    
    
    @tf.function
    def outer_function(x):
        y = tf.constant([[2.], [3.]])
        b = tf.constant(4.0)
        return inner_function(x, y, b)
    
    
    print(outer_function(tf.constant([[1.0, 2.0]])).numpy())
    

    结果为:

    [[12.]]
    

    将Python函数转为图

    通常情况下,你编写的Python程序不仅包含了TensorFlow内置的操作,还会用到Python的逻辑控制,例如(if-else操作,循环,break,return,continue等)。尽管图模型很容易捕获TensorFlow的内置操作,对于Python自带的逻辑操作还需要额外的转换才能成为一个图。tf.function使用AutoGraph库来将Python代码转换为图模型。

    def simple_relu(x):
        if tf.greater(x, 0):
            return x
        else:
            return 0
    
    
    tf_simple_relu = tf.function(simple_relu)
    
    print("First branch, with graph:", tf.simple_relu(tf.constant(1)).numpy())
    print("Second branch, with graph:", tf.simple_relu(tf.constant(-1)).numpy())
    

    多态性

    一个tf.Graph对象特指有特定输入的函数(例如特定的dtype或特定的类)。每次你调用Function时,TensorFlow会检查内存中是否已经存在可以处理输入参数的tf.Graph对象。若是存在,则可以直接调用;若是不存在,则会根据输入参数的类型来创建新的tf.Graph实例。tf.Graph对象的特定输入被称为“输入签名”或“签名”。
    tf.Function会将已经创建的tf.Graph对象存储为ConcreteFunction,一个ConcreteFunction就是一个tf.Graph的包装器。这种多态性的存在,使得tf.Graph在表达上更加通用,优化起来也更加方便。

    tf.function的使用

    目前为止,已经演示了如何使用tf.function(注解或调用)来将Python函数转换为图。实际上,让tf.function起到效果还需要花点儿功夫。

    Graph execution和eager execution

    tf.Function代码可以以eager模式或graph模式被执行。默认情况下,tf.Function以graph模式被执行。
    为了验证Function的图执行结果和Python的执行结果(即以eager模式执行)相同,可以通过设置tf.config.run_functions_eagerly(True)来强制代码以eager模式执行,这个设置将会关闭图的创建和运行的功能。但是,eager模式和graph模式终究有所区别。纯粹的Python计算在图创建后的首次运行中被执行但不进行捕获,这就是说再次调用的时候,纯粹的Python计算是不会被执行的。

    # 若设置为False,则“Calculating MSE”会打印三次。若设置为True,则“Calculating MSE”只会打印一次。
    tf.config.run_functions_eagerly(False)
    
    
    @tf.function
    def get_MSE(y_true, y_pred):
        print("Calculating MSE")
        sq_diff = tf.pow(y_true - y_pred, 2)
        return tf.reduce_mean(sq_diff)
    
    
    x = tf.random.uniform([5], maxval=10, dtype=tf.float32)
    y = tf.random.uniform([5], maxval=10, dtype=tf.float32)
    
    get_MSE(x, y)
    get_MSE(x, y)
    get_MSE(x, y)
    

    惰性计算

    Graph模式只会执行产生可观测结果的必要操作,包括:

    • 函数的返回值
    • 副作用语句:
      • 输入输出语句,例如tf.print
      • 测试语句,例如tf.debugging中的assert
      • tf.Variable的赋值语句
        这个行为被称为惰性计算,有别于eager模型下逐行执行(不论是否必要)的计算行为。特别注意,运行时的错误处理不认为是一个可观测的必要操作,若是一个操作被跳过了,那它不会产生任何运行时错误。
    # 当设置为True时,会打印出错误结果,即执行了except下的print操作;当设置为False时,直接在tf.matmul上出现了抛出了异常然后中止执行,
    tf.config.run_functions_eagerly(False)
    
    
    @tf.function
    def unused_return_graph(x):
        tf.matmul(x, tf.constant([0.0, 1.0]))
        return x
    
    
    try:
        print(unused_return_graph(tf.constant([0.0, 1.0])))
    except Exception as e:
        print("===========================")
        print(f"{type(e).__name__}: {e}")
    

    当tf.config.run_functions_eagerly设置为False时,执行结果为:

    ===========================
    ValueError: in user code:
    
        File "D:\PycharmProjects\pythonProject1\tensor_slicing.py", line 9, in unused_return_graph  *
            tf.matmul(x, tf.constant([0.0, 1.0]))
    
        ValueError: Shape must be rank 2 but is rank 1 for '{{node MatMul}} = MatMul[T=DT_FLOAT, transpose_a=false, transpose_b=false](x, Const)' with input shapes: [2], [2].
    

    当tf.config.run_functions_eagerly设置为True时,执行结果为:

    ===========================
    InvalidArgumentError: In[0] and In[1] ndims must be == 2: 1 [Op:MatMul]
    

    这和预想的差距太大了。原理的理解上应该有所出入,需要重新学习。

    tf.function使用指导

    相关文章

      网友评论

          本文标题:5. 图概述

          本文链接:https://www.haomeiwen.com/subject/tgsbirtx.html