美文网首页
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