TensorFlow2.0中构建模型的两种风格,符号式和命令式

给你详细讲解TensorFlow2.0中两种不同构建模型风格的优缺点,以及什么时候用什么风格。

作者:Josh Gordon
编译:ronghuaiyang

导读

给你详细讲解TensorFlow2.0中两种不同构建模型风格的优缺点,以及什么时候用什么风格

关于TensorFlow 2.0,我最喜欢的一点是它提供了多层次的抽象,因此你可以为你的项目选择正确的抽象层次。在本文中,我将解释创建神经网络时可以使用的两种不同风格之间的权衡。第一种是符号风格,在这种风格中,你可以通过操作层图来构建模型。第二种是命令式,通过扩展类来构建模型。我会介绍这些风格,分享关于设计方面的重要的注意点,以及使用方面要注意的事项,并帮助你选择正确的风格。

符号(声明)APIs

当我们想到神经网络时,我们通常使用的心智模型是一个“层图”,如下图所示。

TensorFlow2.0中构建模型的两种风格,符号式和命令式

这个图可以是左边显示的有向无环图,也可以是右边显示的堆叠形式。当我们使用符号来构建模型时,我们通过描述这个图的结构来实现。

这样听起来可能太技术了,那么如果你用过Keras,那么你可能会发现你已经有这样做的经验了。下面是一个使用Keras的序列化API符号型地构建模型的快速示例。

TensorFlow2.0中构建模型的两种风格,符号式和命令式

在上面的示例中,我们定义了一组层,然后使用内置的训练循环model.fit对其进行训练。

用Keras构建模型就像“把乐高积木塞在一起”一样简单。为什么?除了匹配我们的心智模型之外,以这种方式构建的模型很容易调试。

TensorFlow2.0中构建模型的两种风格,符号式和命令式

图中显示了上面代码创建的模型(使用plot_model构建,你可以在下一个例子中重用这个代码片段)。

TensorFlow 2.0提供了另一个符号模型构建api:Keras Functional。序列化用于堆叠,你可能已经猜到了,函数用于DAGs。

TensorFlow2.0中构建模型的两种风格,符号式和命令式

一个快速的示例,使用函数API创建一个多输入/多输出模型

函数API是一种创建更灵活模型的方法。它可以处理非线性拓扑、具有共享层的模型和具有多个输入或输出的模型。基本上,函数API是一组用于构建这些层图的工具。现在,我们正在为你编写一些这种风格的新教程。

你可能有其他符号api的经验。例如,TensorFlow v1(和Theano)提供了一个更低级别的API。你可以通过创建一个ops图来构建模型,然后编译并执行它。有时,使用这个API会让你感觉像是直接与编译器进行交互。对许多人(包括作者)来说,这是很难共事的。

相比之下,在Keras中,抽象级别与我们的心智模型相匹配:一个层图,像乐高积木一样被塞在一起。使用这种方法感觉很自然,这是我们在TensorFlow 2.0中标准化的模型构建方法之一。现在我将描述另一个(很有可能你也使用过这个,或者很快就会有机会尝试一下)。

命令式(模型子类化)APIs

在命令式中,你可以像编写NumPy那样编写模型。用这种风格构建模型感觉就像面向对象的Python开发。下面是一个子类模型的快速例子:

TensorFlow2.0中构建模型的两种风格,符号式和命令式

从开发人员的角度来看,这种工作方式是扩展框架定义的模型类,实例化层,然后强制编写模型的正向传递(自动生成向后传递)。

TensorFlow 2.0通过Keras Subclassing API支持这种开箱即用的方式。与顺序api和函数api一样,这也是在TensorFlow 2.0中开发模型的推荐方法之一。

虽然这种风格对于TensorFlow来说是新的,但是当你知道它是由Chainer在2015年(时光飞梭!)从那时起,许多框架都采用了类似的方法,包括Gluon、PyTorch和TensorFlow(使用Keras子类化)。令人惊讶的是,在不同框架中以这种风格编写的代码看起来如此相似,可能很难区分!

这种风格为你提供了很大的灵活性,但同时也带来了不太明显的可用性和维护成本。稍后会详细介绍。

训练循环

以序列化、函数或子类样式定义的模型可以通过两种方式进行训练。你可以使用内置的训练例程和loss函数(参见第一个例子,其中我们使用了“model”)。,或者如果你需要给自定义训练循环增加点复杂性(例如,如果你想编写自己的梯度裁剪代码),或者损失函数,你可以这样做:

TensorFlow2.0中构建模型的两种风格,符号式和命令式

拥有这两种方法都是很重要的,并且可以方便地降低代码复杂度和维护成本。基本上,当它是有益的,你可以使用额外的复杂性,当它是不必要的,使用内置的方法,把你的时间花在你的研究或项目。

既然我们已经对符号和命令式有了一定的了解,让我们来看看折衷方案。

符号APIs的好处和局限性

好处

使用符号api,你的模型是一个类似于图的数据结构。这意味着可以检查或总结你的模型。

  • 你可以将它绘制为一个图像来显示图(使用keras.utils.plot_model),或者简单地使用model.summary() ,或者查看图层、权重和形状的描述。

同样,当将层连接在一起时,库设计人员可以运行层兼容性检查(在构建模型时和执行之前)。

  • 这类似于编译器中的类型检查,可以极大地减少开发人员的错误。
  • 大多数调试将在模型定义阶段进行,而不是在执行阶段。你可以保证任何编译的模型都会运行。这使得迭代更快,调试更容易。

符号模型提供一致的API。这使得它们易于重用和共享。例如,在迁移学习中,你可以访问中间层激活来从现有的模型构建新的模型,如下所示:

from tensorflow.keras.applications.vgg19 import VGG19
base = VGG19(weights=’imagenet’)
model = Model(inputs=base.input,
outputs=base_model.get_layer(‘block4_pool’).output)
image = load(‘elephant.png’)
block4_pool_features = model.predict(image)

符号模型是由数据结构定义的,这使得它们可以自然地复制或克隆。

  • 例如,序列化api和函数api为你提供了model.get_config()、 model.to_json() 、model.save()、clone_model(model)等功能,可以从数据结构中重新创建相同的模型(无需访问用于定义和训练模型的原始代码)。

虽然一个设计良好的API应该匹配我们的神经网络心智模型,但同样重要的是匹配我们作为程序员的心智模型。对于我们许多人来说,这是一种命令式的编程风格。在符号API中,你在操作“符号张量”(这些张量尚未包含任何值)来构建图形。Keras序列化API和函数API“感觉到”命令。它们的设计使得许多开发人员没有意识到他们一直在使用符号来定义模型。

局限性

当前生成的符号api是最适合开发的有向无环图模型。这在实践中占了大多数用例,尽管有一些特殊的用例不适合这种简洁的抽象,例如,动态网络(如树状rnns)和递归网络。

这就是为什么TensorFlow还提供了一种命令式的模型构建API风格(Keras子类化,如上所示)。你可以使用序列化api和函数api中所有熟悉的层、初始化器和优化器。这两种样式也是完全可互操作的,因此你可以混合和匹配(例如,你可以将一种模型类型嵌套到另一种模型类型中)。你可以将符号模型用作子类模型中的一个层,或者反过来。

命令式APIs的好处和局限性

好处

你的前向传递是强制编写的,这使得用你自己的实现替换库实现的部件(例如,层、激活或丢失函数)变得很容易。这对于编程来说是很自然的,也是深入学习深层知识的好方法。

  • 这使得快速尝试新想法变得容易(DL开发工作流变得与面向对象的Python相同),并且对研究人员特别有帮助。
  • 使用Python,在模型的正向传递中指定任意控制流也很容易。

命令式api为你提供了最大的灵活性,但这是有代价的。我也喜欢用这种风格编写代码,但是我想花点时间来强调一下它的局限性(注意权衡利弊是有好处的)。

局限性

重要的是,在使用命令式API时,模型是由类方法的主体定义的。你的模型不再是透明的数据结构,而是一段不透明的字节码。当使用这种风格时,你是在用可用性和重用性来换取灵活性。

调试发生在执行过程中,而不是在定义模型时。

  • 几乎没有对输入或层间兼容性的检查,所以当使用这种风格时,很多调试负担从框架转移到开发人员身上。

命令式模型可能更难于重用。例如,你不能使用一致的API访问中间层或激活。

  • 相反,提取激活的方法是用新的调用(或前向)方法编写一个新类。最初写可能很有趣,也很简单,但如果没有标准,它可能会为以后埋坑。

命令式模型也更难于检查、复制或克隆。

  • 例如,model.save()、model.get_config()和clone_model不能用于子类化的模型。类似地,model.summary()只给出一个层列表(不提供关于它们如何连接的信息,因为这是不可访问的)。

机器学习系统中的坑

重要的是要记住,模型构建只是在实践中使用机器学习的一小部分。这是我最喜欢的关于这个主题的插图之一。模型本身(指定层、训练循环等的代码部分)是位于中间的小框。

TensorFlow2.0中构建模型的两种风格,符号式和命令式

在实际的ML系统中,只有一小部分由ML代码组成,如中间的小黑盒子所示

符号定义的模型在可重用性、调试和测试方面具有优势。例如,在教学的时候,如果他们使用序列化API,我可以立即调试学生的代码。当他们使用子类模型时(不管框架是什么),会花费更长的时间(bug可能更微妙,而且有很多类型)。

最后的一些想法

TensorFlow 2.0支持这两种样式,所以你可以为你的项目选择合适的抽象级别(和复杂性)。

  • 如果你的目标是易用性、低概念开销,并且你喜欢将模型看作层图:使用Keras序列化API或函数API(如将LEGO积木连接在一起)和内置的训练循环。这是解决大多数问题的正确方法。
  • 如果你想把你的模型看作一个面向对象的Python/Numpy开发人员,并且你优先考虑灵活性和可编程性,Keras子类化是适合你的API。

英文原文:https://medium.com/tensorflow/what-are-symbolic-and-imperative-apis-in-tensorflow-2-0-dfccecb01021

本文为专栏文章,来自:AI公园,内容观点不代表本站立场,如若转载请联系专栏作者,本文链接:https://www.afenxi.com/66195.html 。

(0)
AI公园的头像AI公园专栏
上一篇 2019-08-04 16:30
下一篇 2019-08-05 22:48

相关文章

关注我们
关注我们
分享本页
返回顶部