Tensorflow 和 PyTorch 设计权衡
Tensorflow 和 PyTorch 设计权衡
原文:https://medium.com/hackernoon/ml-framework-design-reliability-composition-flexibility-9314f72d2c73
ML 符合通用编程
最近的 Tensorflow Sucks 帖子不是新观点,但触动了我的神经,这是我的反应。
知道什么烂便宜。另一方面,知道什么是难是有价值的。
知道了什么是困难的,你就不会去买蛇油,因为蛇油可以解决所有人的所有问题。它让头脑专注于选择正确的问题,并对其他问题说不。最后,它的好处是提供实际的洞察力和真正进步的机会。
机器学习有一个独特的设计挑战,因为 ML 本身的领域正趋向于作为一个主题的通用计算,而不仅仅是实现细节。这意味着它将在我们所认为的“传统编程”中留下越来越多的足迹
随之而来的是传统的工程挑战。比如可靠性、重用、架构灵活性。这些问题的解决方案不会免费提供,因为这是一个新游戏。它们需要被设计成。
这篇文章是关于成为通用语言所带来的一些难题,以及与其他选择相比,Tensorflow 特别提供了什么解决方案。这并不是因为张量流是一种银弹,而是因为对于张量流的工作方式,似乎存在一种近乎 FUD 的普遍困惑。
ML 框架难在哪里?
虽然 ML 的过去是一种有限的、特定领域的语言,但 ML 的未来是一种通用的、通用的语言。
目前的事态处于中间状态。像许多 DSL 一样,ML 已经发展到需要通用编程原语:条件、循环、(喘息)递归。它还发展到包含一个更大的支持基础设施或“运行时”,如 IO、分布式计算、序列化等。
与许多 DSL 相比,ML 的另一个特点是,许多 DSL 都是非平凡的扩展。递归在 ML 程序中的含义与在 C 中的含义是不同的,因为它必须以特定的方式使用,以允许学习发生。
在构建 ML 框架时如何解决这个问题?你可以从头发明一种全新的语言,比如斯坦。您可以通过添加新的原语来扩展现有的语言,比如 PyTorch。或者你可以做一些中间的事情,通过构建一种新的语言,但是把它嵌入到一个现有的语言中来获得额外的功能。Tensorflow 是最后一个类别的一个例子。
每个选择都有问题。
从头发明一门语言意味着单干,但是如果你的语言语义不能被强加到现有的选项中,那可能是字面上唯一的选择。
采用一种现有的语言 X 对 X 的用户来说听起来很棒。但是因为语言 X 不是每个人都使用的,所以可能它并不是在所有事情上都很棒,并且它自己也有一些明显的缺点。如果你想让你的 ML 框架在 X 语言不能到达的地方使用,那么这不是一个选择。
将 DSL 嵌入另一种语言是第三种选择。你使用语言 X 作为制造机器来构建你的组件,一种元编程。元编程造成了跟踪编译时和运行时之间的区别的心理负担,并使调试更加困难。
由此我们可以看出,没有灵丹妙药。但是为了更深入,我们需要更具体地了解我们面临的问题。
99 个问题,模特不是一个
“我来了。我看到了。“我做模特”似乎是初露头角的人工智能爱好者的信条,他们正等着被镌刻在体现他们能力的纪念碑上。脑海中浮现出单枪匹马革新这个领域,或者至少获得不错的点击率提升的景象。别挡我的路,我在用魔法!
不幸的是,即使对那些以模特为生的人来说,模特的认知行为也是在舍入误差。在你花了 90%的时间清理数据之后,你还会花另外 90%的时间与其他系统集成,只是让事情运行起来。(是的这加起来至少是 180%,和经验一致。)
比特腐蚀、向后不兼容的破坏、拙劣的错误吞咽集成、一时兴起的未记录的假设、总线分解的遗留代码库仍然是甚至成熟的软件工程学科的祸害,并在 ML 中横行。
ML 不是工程最佳实践的先锋。部分原因是它的学术血统和相关的 DIY 编码文化。另一个原因是传统的软件工程在很大程度上忽略了科学所必需的基础:实验。直到最近十年,“数据”才成为一种东西。仅仅能够在 IDE 中绘制点在今天是一个进步的创新。ML 的版本控制是 TBD。
这就是说,在重要的规模上使用和部署这种新的计算范式存在大量的软件工程摩擦。
让我们从基础开始。你的模型运行吗,它会随着时间的推移继续运行吗(可靠性)?我能把它和其他模型组合在一起吗(重用)?以及如何将它与其他组件连接起来解决整体问题(架构灵活性)?
可靠性
Tensorflow 的图形抽象是一个自包含的模型定义。一个模型在今天和五年后的意义是一样的。这个图形抽象的一个关键方面是张量流模型不包含外部依赖。
如果运行您的模型需要 Python、Lua 或任何其他语言运行时,很难做出同样的保证。为了准确地再现模型,您必须准确地再现整个环境,从操作系统级别,通过第三方库,到用户代码。
今天,这意味着把它冷冻在一个码头集装箱里。这是一种严重的依赖性,无论是在规模上还是在对下游消费者的要求上。现在,每个模型消费者都必须应对虚拟机边界。
然而集装箱化的最大问题是它给用户带来的负担。可靠性现在是用户要解决的问题。如果每个人都做正确的事情,它或多或少会起作用。但是这并不能保证,而且自己解决问题也很复杂。
“设计可靠”是 Tensorflow 提供的一个有价值的属性。
合成和重用
如果你从事模特行业,很快你就会有很多模特。最终,由于某种原因,你会想同时使用两种型号。也许是为了服务,为了组成一个更大的模型,或者仅仅是为了进行比较。
在 Tensorflow 中,这是微不足道的,不是问题,因为如上所述,模型是自包含的描述,没有额外的依赖性。这足以构成进一步自动化的基础。
“采用 Python”方法有一个不同的故事。如果模型 A 依赖于整个环境,而模型 B 依赖于另一个独立的环境,那该怎么办?
如果模型 A 需要 Python 3.3,模型 B 需要 Python 3.4,也许可以,也许不行。如果这个模型被篡改了,您甚至可能无法反序列化它。或者,在某些方面更糟,您可能在您的无限依赖关系树中的某个地方有冲突的依赖关系。
传统的软件工程方法是合并代码库,解决冲突,并重新运行模型。但是再培训模型可能极其昂贵和缓慢。并且数据甚至可能仍然不可用。因此,有非常充分的理由说明为什么您希望能够可靠地重用现有的模型,而不做任何修改。
一个训练有素、技术熟练的团队可以通过首先最小化依赖性、通过测试以及通过强调向后兼容性来减轻这种风险。但是这如何在组织范围内扩展,跨越许多团队呢?给想玩 ML 进口宇宙的金融实习生?对于面临压力的高级工程师来说,离解决他们眼前的问题只有一步之遥?
“通过设计可组合和可重用”是 Tensorflow 提供的一个有价值的属性。
建筑灵活性
没有一个模型是孤岛。他们必须连接到数据源进行培训,并将基础架构部署为服务或部署在边缘设备中。除此之外,还有一组不受限制的开发和 ops 工具,它们可能与模型有关。
你的模型还可以集成哪些软件?
Tensorflow 可以集成任何可以调用简单 C api 的东西。它只涉及很少的概念(DAG、session、Tensors ),并且它的基本结构是以文档化的普通格式序列化的。本质上,任何重要的系统最终都会有一个 Tensorflow 集成,所以你甚至不需要自己去做。这允许极大的架构灵活性,因为您可以选择要集成的点。
如果运行您的模型需要 Python 或任何其他特定语言,情况就大不相同了。现在,您可以将 Python/Language X 拖到任何地方,或者使用桥,或者使用某种本地消息总线。
如果你是 Python 爱好者,这听起来可能很棒,但是对于投资于其他技术的人来说,这是一个复杂性和问题的来源。这不是一个“去任何地方都不用担心”的情况;您现在处于设计空间的“让我们确保 GCs 正确交互”分支。当您在错误的 Python 版本中反序列化模型时,希望所有定制的机器将优雅地、有意义地失败。
“与他人合作愉快”是 Tensorflow 的一项宝贵优势。
但是 Onnx!
Onnx 试图恢复 Tensorflow 的优点,本质上是通过提供相同类型的独立、自包含的声明图。通过跟踪 Python 执行,可以从命令式模型中恢复这个静态图。这是一个非常酷和有前途的项目。
但是,它也受到与 Tensorflow 相同的限制。它不容易表示复杂的递归逻辑。这并不意味着调用任意的 Python 函数。您的图现在是静态的和声明性的,就像在 TF 中一样。
好处是,您仍然可以在开发时使用命令式模型,并在调试后将其导出。你可以把它导出到像 Caffe2 这样可以在更多地方集成的框架中。从本质上来说,现有工具能发挥更大的作用。
成本是,嗯,没有保证你的模型将出口,直到你尝试。它只能代表 PyTorch 模型的子集。因为 PyTorch 模型只是具有任意依赖性的任意代码,所以您通常无法提前知道导出是否会正确工作。你怎么知道导出是正确的?那是你的责任。
即使您的模型满足正确的属性,仍然有多个 DL 框架的复杂性,行为差异的机会,更多的版本需要调整。不是给我,尤其是在游戏的这个阶段。权衡。
权衡是永远的?
在设计良好的系统中,权衡可能是暂时的,因为缺失的部分堆积在基础之上。Onnx 是一个很好的例子,说明如何继续协商权衡。Tensorflows 即将推出的渴望模式是另一个例子。还有一个是 XLA,Tensorflow 正在填补“无依赖性”故事中的空白,以实现高效的定制操作,而没有携带用户创建的 C++代码的环境负担。
然而,这需要知道问题所在,不要陷入无望的方向。最重要的是,不要让粉丝主义使人对正在解决的问题视而不见。希望这篇文章能对这个方向有所贡献。