发表 我最喜欢 TensorFlow 2.0 的一件事是它提供了多个抽象级别,因此您可以为您的项目选择合适的抽象级别。在本文中,我将解释可用于创建神经网络的两种风格之间的权衡。第一种是符号样式,您可以通过操作图层图来构建模型。第二种是命令式风格,通过扩展类来构建模型。我将介绍这些内容,分享有关重要设计和可用性注意事项的注释,并以快速建议结束,以帮助您选择正确的。

1. 符号(或声明性)API

当我们想到神经网络时,我们通常使用的心智模型是“层图”,如下图所示。
在这里插入图片描述
当我们想到神经网络时,我们通常使用的心智模型是层图(图像是Inception-ResNet的模式)。

该图可以是左侧显示的 DAG,也可以是右侧显示的堆栈。当我们象征性地构建模型时,我们通过描述该图的结构来做到这一点。

如果这听起来很技术性,那么如果您使用过 Keras,您可能会惊讶地发现您已经有这样做的经验。这是一个使用Keras Sequential API以符号方式构建模型的快速示例。在这里插入图片描述
使用 Keras Sequential API 以符号方式构建的神经网络。您可以在此处运行此示例。
在上面的例子中,我们定义了一堆层,然后使用内置的训练循环对其进行训练,model.fit.

使用 Keras 构建模型就像“将乐高积木拼在一起”一样简单。为什么?除了匹配我们的心智模型外,出于后面介绍的技术原因,这种方式构建的模型由于框架提供的深思熟虑的错误消息而易于调试。
在这里插入图片描述
显示由上述代码创建的模型的图表(使用plot_model 构建,有一个代码片段可以在本文的下一个示例中重用)。

TensorFlow 2.0 提供了另一个符号化模型构建 API:Keras Functional。Sequential 用于堆栈,您可能已经猜到,Functional 用于 DAG。
在这里插入图片描述
使用功能 API 创建多输入/多输出模型的快速示例。

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

您可能还会使用其他符号 API。例如,TensorFlow v1(和 Theano)提供了一个低得多的 API。您将通过创建一个您编译和执行的操作图来构建模型。有时,使用这个 API 会让人感觉像是在直接与编译器进行交互。对于许多人(包括作者)来说,这很难合作。

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

2. 命令式(或模型子类化)API

在命令式风格中,您可以像编写 NumPy 一样编写模型。以这种风格构建模型感觉就像是面向对象的 Python 开发。这是一个子类模型的快速示例:
在这里插入图片描述
使用命令式样式来构建带有注意力的图像字幕模型(注意:示例当前正在更新)

从开发人员的角度来看,它的工作方式是扩展框架定义的模型类,实例化层,然后命令式地编写模型的前向传递(自动生成后向传递)。TensorFlow 2.0 通过 Keras Subclassing API

开箱即用地支持这一点。与顺序和功能 API 一起,它是您在 TensorFlow 2.0 中开发模型的推荐方法之一。 虽然这种风格对于 TensorFlow 来说是新的,但得知它是由Chainer引入的,你可能会感到惊讶

2015 年(时间过得真快!)。从那时起,许多框架都采用了类似的方法,包括 Gluon、PyTorch 和 TensorFlow(带有 Keras 子类化)。令人惊讶的是,在不同框架中以这种风格编写的代码看起来如此相似,可能很难区分!

这种风格为您提供了极大的灵活性,但它带来的可用性和维护成本并不明显。稍后再谈。

3. 训练循环

以顺序、功能或子类风格定义的模型可以通过两种方式进行训练。您可以使用内置的训练例程和损失函数(参见第一个示例,我们使用model.fitand model.compile),或者如果您需要增加自定义训练循环的复杂性(例如,如果您想编写自己的渐变剪裁代码)或损失函数,您可以轻松地执行以下操作:
在这里插入图片描述
Pix2Pix 的自定义训练循环和损失函数的示例。
拥有这两种方法很重要,并且可以方便地降低代码复杂性和维护成本。基本上,您可以在有用时使用额外的复杂性,而在不必要时使用内置方法并将时间花在您的研究或项目上。

现在我们对象征性和命令式风格有了一定的了解,让我们来看看权衡。

4. 符号 API 的优点和限制

4.1 好处

使用符号 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)

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

  • 例如,Sequential 和 Functional API 使您model.get_config()能够model.to_json()仅从数据结构重新创建相同model.save()的clone_model(model)模型(无需访问用于定义和训练模型的原始代码)。

虽然设计良好的 API 应该与我们的神经网络心智模型相匹配,但与我们作为程序员的心智模型相匹配同样重要。对于我们中的许多人来说,这是一种命令式编程风格。在符号 API 中,您正在操纵“符号张量”(这些张量尚不包含任何值)来构建图形。Keras Sequential 和 Functional API “感觉”势在必行。它们的设计使许多开发人员没有意识到他们一直在象征性地定义模型。

4.2 限制

当前一代的符号 API 最适合开发有向无环层图的模型。这占了实践中的大多数用例,尽管有一些特殊的用例不适合这种简洁的抽象,例如树 RNN 等动态网络和递归网络。

这就是 TensorFlow 还提供命令式模型构建 API 样式的原因(Keras 子类化,如上所示)。您可以使用顺序和功能 API 中所有熟悉的层、初始化器和优化器。这两种样式也完全可互操作,因此您可以混合搭配(例如,您可以将一种模型类型嵌套在另一种模型类型中)。您采用符号模型并将其用作子类模型中的层,或反之。

5. 命令式 API 的优点和局限性

5.1 好处

您的前向传递是命令式编写的,可以轻松地将库实现的部分(例如,层、激活或损失函数)替换为您自己的实现。这对编程感觉很自然,并且是深入了解深度学习的基本要素的好方法。

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

命令式 API 为您提供最大的灵活性,但要付出一定的代价。我也喜欢用这种风格编写代码,但想花点时间来强调一下局限性(了解权衡是件好事)。

5.2 限制

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

调试发生在执行期间,而不是在定义模型时。

  • 几乎没有对输入或层间兼容性进行检查,因此在使用这种风格时,很多调试负担都从框架转移到了开发人员身上。

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

  • 相反,提取激活的方法是编写一个带有新调用(或转发)方法的新类。这最初写起来很有趣,而且做起来很简单,但可能是没有标准的技术债务的秘诀。

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

  • 例如,model.save()、model.get_config()和clone_model不适用于子类模型。同样,model.summary()只为您提供层列表(并且不提供有关它们如何连接的信息,因为无法访问)。

6. 机器学习系统中的技术债务

重要的是要记住,模型构建只是在实践中使用机器学习的一小部分。这是我最喜欢的关于该主题的插图之一。模型本身(您指定层、训练循环等的代码部分)是中间的小盒子。
在这里插入图片描述
现实世界中只有一小部分机器学习系统是由机器学习代码组成的,如中间的小黑框所示。来自机器学习系统中隐藏的技术债务。

符号定义的模型在可重用性、调试和测试方面具有优势。例如,在教学时——如果学生使用 Sequential API,我可以立即调试他们的代码。当他们使用子类模型(无论框架如何)时,需要更长的时间(错误可能更微妙,并且类型很多)。

7. 结束的想法

TensorFlow 2.0 开箱即用地支持这两种样式,因此您可以为您的项目选择正确的抽象级别(和复杂性)。

  • 如果您的目标是易用性、低概念开销,并且您喜欢将模型视为层图:使用 Keras Sequential 或 Functional API(例如将乐高积木拼在一起)和内置的训练循环。这是解决大多数问题的正确方法。
  • 如果您喜欢将您的模型视为面向对象的 Python/Numpy 开发人员,并且您优先考虑灵活性和可破解性,那么 Keras 子类化是适合您的 API。

我希望这是一个有用的概述,感谢阅读!要了解有关 TensorFlow 2.0 堆栈的更多信息,除了这些模型构建 API,请查看这篇文章。要了解有关 TensorFlow 和 Keras 之间关系的更多信息,请访问此处

参考

https://blog.tensorflow.org/2019/01/what-are-symbolic-and-imperative-apis.html

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐