TensorFlow 版本兼容性

本文档适用于需要跨不同 TensorFlow 版本(代码或数据)进行向后兼容性的用户,以及希望在保持兼容性的同时修改 TensorFlow 的开发人员。

语义版本控制 2.0

TensorFlow 遵循语义版本控制 2.0 (semver) 用于其公共 API。每个 TensorFlow 版本的发布版本都具有 MAJOR.MINOR.PATCH 的形式。例如,TensorFlow 版本 1.2.3 具有 MAJOR 版本 1、MINOR 版本 2 和 PATCH 版本 3。每个数字的更改具有以下含义

  • MAJOR:可能存在向后不兼容的更改。与以前主要版本兼容的代码和数据不一定与新版本兼容。但是,在某些情况下,现有的 TensorFlow 图和检查点可能可以迁移到较新的版本;有关数据兼容性的详细信息,请参阅 图和检查点的兼容性

  • MINOR:向后兼容的功能、速度改进等。与以前次要版本兼容的代码和数据以及仅依赖于非实验性公共 API 的代码和数据将继续按原样工作。有关什么是公共 API、什么不是公共 API 的详细信息,请参阅 涵盖范围

  • PATCH:向后兼容的错误修复。

例如,版本 1.0.0 引入了版本 0.12.1 的向后不兼容更改。但是,版本 1.1.1 与版本 1.0.0 向后兼容

涵盖范围

只有 TensorFlow 的公共 API 在次要版本和修补程序版本之间向后兼容。公共 API 包括

  • 所有在 tensorflow 模块及其子模块中记录的 Python 函数和类,除了

    • 私有符号:任何以 _ 开头的函数、类等
    • 实验性和 tf.contrib 符号,有关详细信息,请参阅 下方

    请注意,examples/tools/ 目录中的代码无法通过 tensorflow Python 模块访问,因此不受兼容性保证的约束。

    如果某个符号可以通过 tensorflow Python 模块或其子模块访问,但未记录,则它被视为公共 API 的一部分。

  • 兼容性 API(在 Python 中,tf.compat 模块)。在主要版本中,我们可能会发布实用程序和额外的端点,以帮助用户过渡到新的主要版本。这些 API 符号已弃用且不受支持(即,我们不会添加任何功能,也不会修复除修复漏洞之外的错误),但它们确实属于我们的兼容性保证。

  • TensorFlow C API

  • 以下协议缓冲区文件

TensorFlow Lite 的独立版本号

目前,TensorFlow Lite 作为 TensorFlow 的一部分进行分发。但是,我们保留在未来版本中根据与其他 TensorFlow API 不同的时间表更改 TensorFlow Lite API 的权利,甚至将 TensorFlow Lite 移动到与 TensorFlow 不同的源代码分发和/或源代码存储库中。

因此,我们为 TensorFlow Lite 使用不同的版本号(TFLITE_VERSION_STRINGtensorflow/lite/version.h 中,以及 TfLiteVersion()tensorflow/lite/c/c_api.h 中),而不是 TensorFlow(TF_VERSION_STRINGtensorflow/core/public/version.h 中,以及 TF_Version()tensorflow/c/c_api.h 中)。目前,这两个版本号恰好具有相同的值。但在未来,它们可能会发生分歧;例如,我们可能会增加 TensorFlow Lite 的主要版本号,而不会增加 TensorFlow 的主要版本号,反之亦然。

TensorFlow Lite 版本号涵盖的 API 表面由以下公共 API 组成

实验性符号不受涵盖;有关详细信息,请参阅 下方

TensorFlow Lite 扩展 API 的独立版本号

TensorFlow Lite 提供 C API,用于使用“自定义操作”扩展 TensorFlow Lite 解释器,这些操作在图中提供用户定义的操作,或者使用“委托”,这些委托允许将图(或图的子集)的计算委托给自定义后端。这些 API(我们统称为“TensorFlow Lite 扩展 API”)需要与 TensorFlow Lite 实现的某些细节有更紧密的依赖关系。

我们保留在未来版本中更改这些 API 的权利,可能包括非向后兼容的更改,更改时间表可能与其他 TensorFlow Lite API 不同。因此,我们为 TensorFlow Lite 扩展 API 使用不同的版本号,而不是 TensorFlow Lite 或 TensorFlow 的版本号(如上一节所述)。我们在 TensorFlow Lite 版本 2.15 中引入了一些新的 API,以获取 TensorFlow Lite 扩展 API 版本(TFLITE_EXTENSION_APIS_VERSION_STRINGtensorflow/lite/version.h 中,以及 TfLiteExtensionApisVersion() 在 tensorflow/lite/c/c_api.h 中)。TensorFlow Lite 扩展 API 的版本号目前与 TensorFlow 和 TensorFlow Lite 的版本号相同。但在未来,它们可能会发生分歧;例如,我们可能会增加 TensorFlow Lite 扩展 API 的主要版本号,而不会增加 TensorFlow Lite 的主要版本号,反之亦然。

TensorFlow Lite 扩展 API 版本号涵盖的 API 表面由以下公共 API 组成

同样,实验性符号不受涵盖;有关详细信息,请参阅 下方

涵盖的内容

TensorFlow 的某些部分可以在任何时候以向后不兼容的方式更改。这些包括

  • 实验性 API:为了便于开发,我们免除了明确标记为实验性的某些 API 符号的兼容性保证。特别是,以下内容不受任何兼容性保证的约束

    • tf.contrib 模块或其子模块中的任何符号;
    • 任何名称包含 experimentalExperimental 的符号(模块、函数、参数、属性、类、常量、类型、包等);或
    • 任何完全限定名称包含本身是实验性的模块或类或包的符号。这包括任何名为 experimental 的协议缓冲区的字段和子消息。
  • 其他语言:TensorFlow API 在 Python 和 C 以外的语言中,例如

    以及 TensorFlow Lite API 在 Java/Kotlin、C、Objective-C 和 Swift 以外的语言中,特别是

  • 复合操作的细节:Python 中的许多公共函数扩展到图中的多个基本操作,这些细节将成为任何保存到磁盘为 GraphDef 的图的一部分。这些细节可能会在次要版本中发生变化。特别是,检查图之间完全匹配的回归测试可能会在次要版本之间中断,即使图的行为应该保持不变,并且现有检查点仍然有效。

  • 浮点数值细节:操作计算的特定浮点值可能会随时更改。用户应该只依赖近似精度和数值稳定性,而不是计算的特定位。次要版本和补丁版本中数值公式的更改应该导致可比的或改进的精度,但需要注意的是,在机器学习中,特定公式的精度提高可能会导致整个系统的精度降低。

  • 随机数:计算的特定随机数可能会随时更改。用户应该只依赖近似正确的分布和统计强度,而不是计算的特定位。有关详细信息,请参阅 随机数生成 指南。

  • 分布式 Tensorflow 中的版本偏差:在单个集群中运行两个不同版本的 TensorFlow 不受支持。关于线协议的向后兼容性没有任何保证。

  • 错误:如果当前实现明显错误,即如果它与文档相矛盾,或者如果由于错误而未正确实现众所周知且定义明确的预期行为,我们保留进行向后不兼容行为(但不是 API)更改的权利。例如,如果优化器声称实现了一种众所周知的优化算法,但由于错误而与该算法不匹配,那么我们将修复优化器。我们的修复可能会破坏依赖于错误行为以进行收敛的代码。我们将在发行说明中记录此类更改。

  • 未使用的 API:我们保留对我们发现没有记录使用情况的 API 进行向后不兼容更改的权利(通过执行 GitHub 搜索来审核 TensorFlow 使用情况)。在进行任何此类更改之前,我们将在 announce@ 邮件列表 上宣布我们进行更改的意图,提供有关如何解决任何中断(如果适用)的说明,并等待两周,以使我们的社区有机会分享他们的反馈。

  • 错误行为:我们可能会用非错误行为替换错误。例如,我们可能会更改函数以计算结果,而不是引发错误,即使该错误已记录在案。我们还保留更改错误消息文本的权利。此外,错误的类型可能会更改,除非文档中指定了特定错误条件的异常类型。

SavedModels、图和检查点的兼容性

SavedModel 是 TensorFlow 程序中首选的序列化格式。SavedModels 包含两个部分:一个或多个编码为 GraphDefs 的图和一个检查点。图描述了要运行的操作的数据流,检查点包含图中变量的保存张量值。

许多 TensorFlow 用户创建 SavedModels,并使用更高版本的 TensorFlow 加载和执行它们。根据 semver,使用一个版本的 TensorFlow 编写的 SavedModels 可以使用更高版本的 TensorFlow(具有相同的主要版本)加载和评估。

对于受支持的 SavedModels,我们提供额外的保证。我们称使用 **仅限于 TensorFlow 主版本 N 中的非弃用、非实验性、非兼容性 API** 创建的 SavedModel 为版本 N 中受支持的 SavedModel。任何在 TensorFlow 主版本 N 中受支持的 SavedModel 都可以在 TensorFlow 主版本 N+1 中加载和执行。但是,构建或修改此类模型所需的功能可能不再可用,因此此保证仅适用于未修改的 SavedModel。

我们将尽力尽可能长时间地保持向后兼容性,以便序列化文件在很长一段时间内可用。

GraphDef 兼容性

图通过 GraphDef 协议缓冲区序列化。为了便于对图进行向后不兼容的更改,每个 GraphDef 都有一个与 TensorFlow 版本分开的版本号。例如,GraphDef 版本 17 弃用了 inv 操作,转而使用 reciprocal。语义是

  • 每个版本的 TensorFlow 都支持一个 GraphDef 版本的间隔。此间隔在修补程序版本中将保持不变,并且仅在次要版本中增长。对 GraphDef 版本的支持仅在 TensorFlow 的主要版本中才会发生(并且仅与对 SavedModels 的版本支持保证保持一致)。

  • 新创建的图将被分配最新的 GraphDef 版本号。

  • 如果给定版本的 TensorFlow 支持图的 GraphDef 版本,它将加载并评估,其行为与用于生成它的 TensorFlow 版本相同(浮点数值细节和随机数除外,如上所述),与 TensorFlow 的主要版本无关。特别是,与 TensorFlow 的一个版本中的检查点文件兼容的 GraphDef(例如,在 SavedModel 中的情况)将在后续版本中保持与该检查点兼容,只要 GraphDef 受支持。

    请注意,这仅适用于 GraphDefs(和 SavedModels)中序列化的图:代码读取检查点可能无法读取由运行不同版本的 TensorFlow 的相同代码生成的检查点。

  • 如果 GraphDef上限在(次要)版本中增加到 X,那么至少在下限增加到 X 之前会有六个月的时间。例如(我们在这里使用假设的版本号)

    • TensorFlow 1.2 可能支持 GraphDef 版本 4 到 7。
    • TensorFlow 1.3 可以添加 GraphDef 版本 8 并支持版本 4 到 8。
    • 至少六个月后,TensorFlow 2.0.0 可以放弃对版本 4 到 7 的支持,只保留版本 8。

    请注意,由于 TensorFlow 的主要版本通常发布间隔超过 6 个月,因此对受支持的 SavedModels 的保证比对 GraphDefs 的 6 个月保证要强得多。

最后,当对 GraphDef 版本的支持被放弃时,我们将尝试提供工具来自动将图转换为更新的受支持的 GraphDef 版本。

扩展 TensorFlow 时图和检查点兼容性

本节仅在对 GraphDef 格式进行不兼容的更改时才相关,例如添加操作、删除操作或更改现有操作的功能。对于大多数用户来说,上一节就足够了。

向后和部分向前兼容性

我们的版本控制方案有三个要求

  • 向后兼容性,以支持加载使用旧版本的 TensorFlow 创建的图和检查点。
  • 向前兼容性,以支持图或检查点的生产者在消费者升级到更新版本的 TensorFlow 之前升级到更新版本的 TensorFlow 的场景。
  • 以不兼容的方式发展 TensorFlow。例如,删除操作、添加属性和删除属性。

请注意,虽然 GraphDef 版本机制与 TensorFlow 版本是分开的,但对 GraphDef 格式的向后不兼容更改仍然受语义版本控制的限制。这意味着功能只能在 TensorFlow 的 MAJOR 版本之间删除或更改(例如,1.72.0)。此外,向前兼容性在修补程序版本中强制执行(例如,1.x.11.x.2)。

为了实现向后和向前兼容性,并了解何时强制执行格式更改,图和检查点具有元数据,描述了它们是在何时生成的。以下部分详细介绍了 TensorFlow 的实现以及演化 GraphDef 版本的指南。

独立数据版本方案

图和检查点有不同的数据版本。两种数据格式彼此演化的速度不同,与 TensorFlow 的演化速度也不同。两种版本控制系统都在 core/public/version.h 中定义。每当添加新版本时,都会在标题中添加一条注释,详细说明了更改内容和日期。

数据、生产者和消费者

我们区分以下几种数据版本信息

  • 生产者:生成数据的二进制文件。生产者具有一个版本 (producer) 和一个与其兼容的最小消费者版本 (min_consumer)。
  • 消费者:使用数据的二进制文件。消费者具有一个版本 (consumer) 和一个与其兼容的最小生产者版本 (min_producer)。

每个版本化的数据都具有一个 VersionDef versions 字段,记录了生成数据的 producer、与其兼容的 min_consumer 以及一个 bad_consumers 版本列表,这些版本是不允许的。

默认情况下,当生产者生成一些数据时,数据将继承生产者的 producermin_consumer 版本。如果已知特定消费者版本包含错误并且必须避免,则可以设置 bad_consumers。如果以下所有条件都为真,则消费者可以接受一块数据

  • consumer >= 数据的 min_consumer
  • 数据的 producer >= 消费者的 min_producer
  • consumer 不在数据的 bad_consumers

由于生产者和消费者都来自同一个 TensorFlow 代码库,因此 core/public/version.h 包含一个主数据版本,根据上下文,该版本被视为 producerconsumer,以及 min_consumermin_producer(分别由生产者和消费者需要)。具体来说,

  • 对于 GraphDef 版本,我们有 TF_GRAPH_DEF_VERSIONTF_GRAPH_DEF_VERSION_MIN_CONSUMERTF_GRAPH_DEF_VERSION_MIN_PRODUCER
  • 对于检查点版本,我们有 TF_CHECKPOINT_VERSIONTF_CHECKPOINT_VERSION_MIN_CONSUMERTF_CHECKPOINT_VERSION_MIN_PRODUCER

向现有操作添加具有默认值的属性

遵循以下指南,只有在操作集没有更改的情况下才能获得向前兼容性

  1. 如果需要向前兼容性,请在使用 SavedModelBuilder 类的 tf.saved_model.SavedModelBuilder.add_meta_graph_and_variablestf.saved_model.SavedModelBuilder.add_meta_graph 方法或 tf.estimator.Estimator.export_saved_model 导出模型时,将 strip_default_attrs 设置为 True
  2. 这将在生成/导出模型时剥离默认值属性。这确保了导出的 tf.MetaGraphDef 在使用默认值时不包含新的操作属性。
  3. 拥有这种控制权可以允许过时的消费者(例如,落后于训练二进制文件的服务二进制文件)继续加载模型并防止模型服务中断。

演化 GraphDef 版本

本节说明如何使用此版本控制机制对 GraphDef 格式进行不同类型的更改。

添加操作

同时将新操作添加到消费者和生产者,并且不要更改任何 GraphDef 版本。这种类型的更改自动向后兼容,并且不会影响向前兼容性计划,因为现有的生产者脚本不会突然使用新功能。

添加操作并切换现有 Python 包装器以使用它

  1. 实现新的消费者功能并增加 GraphDef 版本。
  2. 如果可以使包装器仅在以前不起作用的情况下使用新功能,则现在可以更新包装器。
  3. 更改 Python 包装器以使用新功能。不要增加 min_consumer,因为不使用此操作的模型不应该中断。

删除或限制操作的功能

  1. 修复所有生产者脚本(不是 TensorFlow 本身)以不使用被禁止的操作或功能。
  2. 增加 GraphDef 版本并实现新的消费者功能,该功能禁止对版本及更高版本的 GraphDefs 使用已删除的操作或功能。如果可能,使 TensorFlow 停止生成包含被禁止功能的 GraphDefs。为此,请添加 REGISTER_OP(...).Deprecated(deprecated_at_version, message)
  3. 为了向后兼容性,请等待主要版本发布。
  4. min_producer 增加到 (2) 中的 GraphDef 版本,并完全删除该功能。

更改操作的功能

  1. 添加一个名为 SomethingV2 或类似名称的新类似操作,并完成添加它并切换现有 Python 包装器以使用它的过程。为了确保向前兼容性,请在更改 Python 包装器时使用 compat.py 中建议的检查。
  2. 删除旧操作(由于向后兼容性,只能在主要版本更改时进行)。
  3. 增加 min_consumer 以排除使用旧操作的消费者,将旧操作添加回作为 SomethingV2 的别名,并完成切换现有 Python 包装器以使用它的过程。
  4. 完成删除 SomethingV2 的过程。

禁止单个不安全的消费者版本

  1. 增加 GraphDef 版本,并将错误版本添加到所有新 GraphDefs 的 bad_consumers 中。如果可能,仅对包含特定操作或类似操作的 GraphDefs 添加到 bad_consumers 中。
  2. 如果现有消费者具有错误版本,请尽快将它们推出。