自动将代码升级到 TensorFlow 2

在 TensorFlow.org 上查看 在 Google Colab 中运行 在 GitHub 上查看源代码 下载笔记本

TensorFlow 2.0 包含许多 API 变更,例如重新排序了参数,重命名了符号,更改了参数的默认值。手动执行所有这些修改可能很乏味,而且很容易出错。为了简化更改,尽可能地让您无缝过渡到 TF 2.0,TensorFlow 团队创建了 tf_upgrade_v2 实用工具,帮助您将旧版代码转换至新的 API。

注:TensorFlow 1.13 和更高版本(包括所有 TF 2.0 版本)会自动安装 tf_upgrade_v2

典型的用法如下:

tf_upgrade_v2 \
--intree my_project/ \
--outtree my_project_v2/ \
--reportfile report.txt

将现有 TensorFlow 1.x Python 脚本转换为 TensorFlow 2.0 脚本可以加快升级流程。

转换脚本会尽可能实现自动化处理,但仍有一些语法和样式变更无法通过脚本执行转换。

兼容性模块

某些 API 符号无法通过简单的字符串替换进行升级。为了确保代码在 TensorFlow 2.0 中仍受支持,升级脚本包含了一个 compat.v1 模块。该模块可将 TF 1.x 符号(如 tf.foo)替换为等效的 tf.compat.v1.foo 引用。虽然该兼容性模块效果不错,但我们仍建议人工校对替换,并尽快将代码迁移到 tf.* 命名空间(而不是 tf.compat.v1 命名空间)中的新 API。

由于 TensorFlow 2.x 模块弃用(例如,tf.flagstf.contrib),切换到 compat.v1 无法解决某些更改。升级此代码可能需要其他库(例如,absl.flags)或切换到 tensorflow/addons 中的软件包。

推荐的升级流程

本指南的剩余部分演示如何使用升级脚本。虽然升级脚本的使用非常简单,我们仍强烈建议在以下流程中使用脚本:

  1. 单元测试:确保要升级的代码包含具有合理覆盖范围的单元测试套件。这是 Python 代码,该语言并不会帮助您避免各种类型的错误。同时为了与 TensorFlow 2.0 兼容,还要确保升级所有依赖项。

  2. 安装 TensorFlow 1.14:将 TensorFlow 升级到最新的 TensorFlow 1.x 版本(最低为 1.14 版本)。其中包括 tf.compat.v2 中的最终 TensorFlow 2.0 API。

  3. 通过 1.14 版本进行测试:确保此时可通过单元测试。在升级过程中,您将反复进行测试,因此,从无错误的代码开始非常重要。

  4. 运行升级脚本:对整个源代码树运行 tf_upgrade_v2(已包含测试)。这样可将代码升级为仅使用 TensorFlow 2.0 中所提供的符号的格式。被弃用的符号将通过 tf.compat.v1 进行访问。最终需要人工检查这些升级,但不是现在。

  5. 通过 TensorFlow 1.14 运行转换的测试:代码在 TensorFlow 1.14 中应该仍可以正常运行。再次运行单元测试。测试在此时产生任何错误都意味着升级脚本存在错误。请通知我们

  6. 检查更新报告中的警告和错误:该脚本会编写一个对需要复查的转换或需要执行的人工操作进行解释的报告文件。例如:contrib 的所有剩余实例需要通过人工操作删除。请查阅 RFC 中的详细说明

  7. 安装 TensorFlow 2.0:此时应该可以安全切换到 TensorFlow 2.0

  8. 使用 v1.disable_v2_behavior 进行测试:使用测试主函数中的 v1.disable_v2_behavior() 重新运行测试产生的结果应与在 1.14 下运行时产生的结果相同。

  9. 启用 V2 行为:现在,使用 v2 API 已经成功通过了测试,不妨开始考虑启用 v2 行为。这可能需要执行一些更改,具体取决于代码编写方式。有关详细信息,请参阅迁移指南

使用升级脚本

设置

开始之前,请确保已安装 TensorlFlow 2.0。

import tensorflow as tf

print(tf.__version__)
2.3.0

克隆 tensorflow/models git 仓库,以便获得一些要测试的代码:

git clone --branch r1.13.0 --depth 1 https://github.com/tensorflow/models
Cloning into 'models'...
remote: Enumerating objects: 2927, done.[K
remote: Counting objects: 100% (2927/2927), done.[K
remote: Compressing objects: 100% (2449/2449), done.[K
remote: Total 2927 (delta 509), reused 2036 (delta 403), pack-reused 0[K
Receiving objects: 100% (2927/2927), 369.04 MiB | 47.25 MiB/s, done.
Resolving deltas: 100% (509/509), done.
Checking out files: 100% (2768/2768), done.

读取帮助

脚本应当随 TensorFlow 安装。下面是内置帮助命令:

tf_upgrade_v2 -h
2020-09-22 19:28:20.178809: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library libcudart.so.10.1
usage: tf_upgrade_v2 [-h] [--infile INPUT_FILE] [--outfile OUTPUT_FILE]
                     [--intree INPUT_TREE] [--outtree OUTPUT_TREE]
                     [--copyotherfiles COPY_OTHER_FILES] [--inplace]
                     [--no_import_rename] [--no_upgrade_compat_v1_import]
                     [--reportfile REPORT_FILENAME] [--mode {DEFAULT,SAFETY}]
                     [--print_all]

Convert a TensorFlow Python file from 1.x to 2.0

Simple usage:
  tf_upgrade_v2.py --infile foo.py --outfile bar.py
  tf_upgrade_v2.py --infile foo.ipynb --outfile bar.ipynb
  tf_upgrade_v2.py --intree ~/code/old --outtree ~/code/new

optional arguments:
  -h, --help            show this help message and exit
  --infile INPUT_FILE   If converting a single file, the name of the file to
                        convert
  --outfile OUTPUT_FILE
                        If converting a single file, the output filename.
  --intree INPUT_TREE   If converting a whole tree of files, the directory to
                        read from (relative or absolute).
  --outtree OUTPUT_TREE
                        If converting a whole tree of files, the output
                        directory (relative or absolute).
  --copyotherfiles COPY_OTHER_FILES
                        If converting a whole tree of files, whether to copy
                        the other files.
  --inplace             If converting a set of files, whether to allow the
                        conversion to be performed on the input files.
  --no_import_rename    Not to rename import to compat.v2 explicitly.
  --no_upgrade_compat_v1_import
                        If specified, don't upgrade explicit imports of
                        `tensorflow.compat.v1 as tf` to the v2 apis.
                        Otherwise, explicit imports of the form
                        `tensorflow.compat.v1 as tf` will be upgraded.
  --reportfile REPORT_FILENAME
                        The name of the file where the report log is
                        stored.(default: report.txt)
  --mode {DEFAULT,SAFETY}
                        Upgrade script mode. Supported modes: DEFAULT: Perform
                        only straightforward conversions to upgrade to 2.0. In
                        more difficult cases, switch to use compat.v1. SAFETY:
                        Keep 1.* code intact and import compat.v1 module.
  --print_all           Print full log to stdout instead of just printing
                        errors

TF1 代码示例

下面是一个简单的 TensorFlow 1.0 脚本示例:

head -n 65 models/samples/cookbook/regression/custom_regression.py | tail -n 10
  # Calculate loss using mean squared error
  average_loss = tf.losses.mean_squared_error(labels, predictions)

  # Pre-made estimators use the total_loss instead of the average,
  # so report total_loss for compatibility.
  batch_size = tf.shape(labels)[0]
  total_loss = tf.to_float(batch_size) * average_loss

  if mode == tf.estimator.ModeKeys.TRAIN:
    optimizer = params.get("optimizer", tf.train.AdamOptimizer)

对于安装的 TensorFlow 2.0,它不会运行:

(cd models/samples/cookbook/regression && python custom_regression.py)
/bin/bash: -c: line 0: syntax error near unexpected token `;&'
/bin/bash: -c: line 0: `(cd models/samples/cookbook/regression && python custom_regression.py)'

单个文件

升级脚本可以在单个 Python 文件上运行:

!tf_upgrade_v2 \
  --infile models/samples/cookbook/regression/custom_regression.py \
  --outfile /tmp/custom_regression_v2.py
2020-09-22 19:28:22.682546: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library libcudart.so.10.1
INFO line 38:8: Renamed 'tf.feature_column.input_layer' to 'tf.compat.v1.feature_column.input_layer'
INFO line 43:10: Renamed 'tf.layers.dense' to 'tf.compat.v1.layers.dense'
INFO line 46:17: Renamed 'tf.layers.dense' to 'tf.compat.v1.layers.dense'
INFO line 57:17: tf.losses.mean_squared_error requires manual check. tf.losses have been replaced with object oriented versions in TF 2.0 and after. The loss function calls have been converted to compat.v1 for backward compatibility. Please update these calls to the TF 2.0 versions.
INFO line 57:17: Renamed 'tf.losses.mean_squared_error' to 'tf.compat.v1.losses.mean_squared_error'
INFO line 61:15: Added keywords to args of function 'tf.shape'
INFO line 62:15: Changed tf.to_float call to tf.cast(..., dtype=tf.float32).
INFO line 65:40: Renamed 'tf.train.AdamOptimizer' to 'tf.compat.v1.train.AdamOptimizer'
INFO line 68:39: Renamed 'tf.train.get_global_step' to 'tf.compat.v1.train.get_global_step'
INFO line 83:9: tf.metrics.root_mean_squared_error requires manual check. tf.metrics have been replaced with object oriented versions in TF 2.0 and after. The metric function calls have been converted to compat.v1 for backward compatibility. Please update these calls to the TF 2.0 versions.
INFO line 83:9: Renamed 'tf.metrics.root_mean_squared_error' to 'tf.compat.v1.metrics.root_mean_squared_error'
INFO line 142:23: Renamed 'tf.train.AdamOptimizer' to 'tf.compat.v1.train.AdamOptimizer'
INFO line 162:2: Renamed 'tf.logging.set_verbosity' to 'tf.compat.v1.logging.set_verbosity'
INFO line 162:27: Renamed 'tf.logging.INFO' to 'tf.compat.v1.logging.INFO'
INFO line 163:2: Renamed 'tf.app.run' to 'tf.compat.v1.app.run'
TensorFlow 2.0 Upgrade Script
-----------------------------
Converted 1 files
Detected 0 issues that require attention
--------------------------------------------------------------------------------


Make sure to read the detailed log 'report.txt'


如果无法找到解决代码问题的方法,该脚本会打印错误消息。

目录树

典型项目(包括下面的简单示例)会使用远不止一个文件。通常需要升级整个软件包,所以该脚本也可以在目录树上运行:

# upgrade the .py files and copy all the other files to the outtree
!tf_upgrade_v2 \
    --intree models/samples/cookbook/regression/ \
    --outtree regression_v2/ \
    --reportfile tree_report.txt
2020-09-22 19:28:24.957823: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library libcudart.so.10.1
WARNING line 125:15: Changing dataset.make_one_shot_iterator() to tf.compat.v1.data.make_one_shot_iterator(dataset). Please check this transformation.

INFO line 58:10: tf.estimator.LinearRegressor: Default value of loss_reduction has been changed to SUM_OVER_BATCH_SIZE; inserting old default value tf.keras.losses.Reduction.SUM.

INFO line 101:2: Renamed 'tf.logging.set_verbosity' to 'tf.compat.v1.logging.set_verbosity'
INFO line 101:27: Renamed 'tf.logging.INFO' to 'tf.compat.v1.logging.INFO'
INFO line 102:2: Renamed 'tf.app.run' to 'tf.compat.v1.app.run'
INFO line 40:7: Renamed 'tf.test.mock' to 'tf.compat.v1.test.mock'
INFO line 72:10: tf.estimator.DNNRegressor: Default value of loss_reduction has been changed to SUM_OVER_BATCH_SIZE; inserting old default value tf.keras.losses.Reduction.SUM.

INFO line 96:2: Renamed 'tf.logging.set_verbosity' to 'tf.compat.v1.logging.set_verbosity'
INFO line 96:27: Renamed 'tf.logging.INFO' to 'tf.compat.v1.logging.INFO'
INFO line 97:2: Renamed 'tf.app.run' to 'tf.compat.v1.app.run'
INFO line 82:10: tf.estimator.LinearRegressor: Default value of loss_reduction has been changed to SUM_OVER_BATCH_SIZE; inserting old default value tf.keras.losses.Reduction.SUM.

INFO line 105:2: Renamed 'tf.logging.set_verbosity' to 'tf.compat.v1.logging.set_verbosity'
INFO line 105:27: Renamed 'tf.logging.INFO' to 'tf.compat.v1.logging.INFO'
INFO line 106:2: Renamed 'tf.app.run' to 'tf.compat.v1.app.run'
INFO line 38:8: Renamed 'tf.feature_column.input_layer' to 'tf.compat.v1.feature_column.input_layer'
INFO line 43:10: Renamed 'tf.layers.dense' to 'tf.compat.v1.layers.dense'
INFO line 46:17: Renamed 'tf.layers.dense' to 'tf.compat.v1.layers.dense'
INFO line 57:17: tf.losses.mean_squared_error requires manual check. tf.losses have been replaced with object oriented versions in TF 2.0 and after. The loss function calls have been converted to compat.v1 for backward compatibility. Please update these calls to the TF 2.0 versions.
INFO line 57:17: Renamed 'tf.losses.mean_squared_error' to 'tf.compat.v1.losses.mean_squared_error'
INFO line 61:15: Added keywords to args of function 'tf.shape'
INFO line 62:15: Changed tf.to_float call to tf.cast(..., dtype=tf.float32).
INFO line 65:40: Renamed 'tf.train.AdamOptimizer' to 'tf.compat.v1.train.AdamOptimizer'
INFO line 68:39: Renamed 'tf.train.get_global_step' to 'tf.compat.v1.train.get_global_step'
INFO line 83:9: tf.metrics.root_mean_squared_error requires manual check. tf.metrics have been replaced with object oriented versions in TF 2.0 and after. The metric function calls have been converted to compat.v1 for backward compatibility. Please update these calls to the TF 2.0 versions.
INFO line 83:9: Renamed 'tf.metrics.root_mean_squared_error' to 'tf.compat.v1.metrics.root_mean_squared_error'
INFO line 142:23: Renamed 'tf.train.AdamOptimizer' to 'tf.compat.v1.train.AdamOptimizer'
INFO line 162:2: Renamed 'tf.logging.set_verbosity' to 'tf.compat.v1.logging.set_verbosity'
INFO line 162:27: Renamed 'tf.logging.INFO' to 'tf.compat.v1.logging.INFO'
INFO line 163:2: Renamed 'tf.app.run' to 'tf.compat.v1.app.run'
TensorFlow 2.0 Upgrade Script
-----------------------------
Converted 7 files
Detected 1 issues that require attention
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
File: models/samples/cookbook/regression/automobile_data.py
--------------------------------------------------------------------------------
models/samples/cookbook/regression/automobile_data.py:125:15: WARNING: Changing dataset.make_one_shot_iterator() to tf.compat.v1.data.make_one_shot_iterator(dataset). Please check this transformation.



Make sure to read the detailed log 'tree_report.txt'


注意关于 dataset.make_one_shot_iterator 函数的一条警告。

现在,对于 TensorFlow 2.0,该脚本已经可以发挥作用:

请注意,凭借 tf.compat.v1 模块,转换的脚本在 TensorFlow 1.14 中也可以运行。

(cd regression_v2 && python custom_regression.py 2>&1) | tail
/bin/bash: -c: line 0: syntax error near unexpected token `;&'
/bin/bash: -c: line 0: `(cd regression_v2 && python custom_regression.py 2>&1) | tail'

详细报告

该脚本还会报告一个详细更改列表。在本例中,它发现了一个可能不安全的转换,因此在文件顶部包含了一条警告:

head -n 20 tree_report.txt
TensorFlow 2.0 Upgrade Script
-----------------------------
Converted 7 files
Detected 1 issues that require attention
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
File: models/samples/cookbook/regression/automobile_data.py
--------------------------------------------------------------------------------
models/samples/cookbook/regression/automobile_data.py:125:15: WARNING: Changing dataset.make_one_shot_iterator() to tf.compat.v1.data.make_one_shot_iterator(dataset). Please check this transformation.

================================================================================
Detailed log follows:

================================================================================
================================================================================
Input tree: 'models/samples/cookbook/regression/'
================================================================================
--------------------------------------------------------------------------------
Processing file 'models/samples/cookbook/regression/automobile_data.py'
 outputting to 'regression_v2/automobile_data.py'

再次注意关于 Dataset.make_one_shot_iterator 函数的一条警告。

在其他情况下,对于非常用更改,输出会解释原因:

%%writefile dropout.py
import tensorflow as tf

d = tf.nn.dropout(tf.range(10), 0.2)
z = tf.zeros_like(d, optimize=False)
Writing dropout.py

!tf_upgrade_v2 \
  --infile dropout.py \
  --outfile dropout_v2.py \
  --reportfile dropout_report.txt > /dev/null
/bin/bash: gt: command not found
/bin/bash: /dev/null: Permission denied

cat dropout_report.txt
cat: dropout_report.txt: No such file or directory

以下是经过修改的文件内容,请注意脚本如何通过添加参数名来处理移动和重命名的参数:

cat dropout_v2.py
cat: dropout_v2.py: No such file or directory

更大的项目可能会包含一些错误,例如转换 DeepLab 模型:

!tf_upgrade_v2 \
    --intree models/research/deeplab \
    --outtree deeplab_v2 \
    --reportfile deeplab_report.txt > /dev/null
/bin/bash: gt: command not found
/bin/bash: /dev/null: Permission denied

它会生成输出文件:

ls deeplab_v2
ls: cannot access 'deeplab_v2': No such file or directory

但是其中包含错误。该报告会帮助您找到确保代码可以正常运行所需要解决的错误。下面是前三个错误:

cat deeplab_report.txt | grep -i models/research/deeplab | grep -i error | head -n 3
cat: deeplab_report.txt: No such file or directory

“安全”模式

该转换脚本还有一种介入度相对较低的 SAFETY 模式。在此模式下,只需更改导入来使用 tensorflow.compat.v1 模块:

cat dropout.py
import tensorflow as tf

d = tf.nn.dropout(tf.range(10), 0.2)
z = tf.zeros_like(d, optimize=False)

tf_upgrade_v2 --mode SAFETY --infile dropout.py --outfile dropout_v2_safe.py > /dev/null
/bin/bash: gt: command not found
/bin/bash: /dev/null: Permission denied

cat dropout_v2_safe.py
cat: dropout_v2_safe.py: No such file or directory

如您所见,这不会升级代码,但允许 TensorFlow 1 代码在 TensorFlow 2 中运行

注意事项

  • 在运行此脚本之前,不要手动更新代码的某些部分。尤其是更改了参数顺序的函数(如 tf.argmaxtf.batch_to_space),否则会导致代码无法正确添加与现有代码匹配的关键字参数。

  • 该脚本假定使用 import tensorflow as tf 导入 tensorflow

  • 该脚本不会更改参数顺序,但是会将关键字参数添加到本身已更改参数顺序的函数。

  • 请查阅 tf2up.ml,找到一款方便的工具来升级 GitHub 仓库中的 Jupyter 笔记本和 Python 文件。

要报告升级脚本错误或提出功能请求,请在 GitHub 上提交问题。如果您在测试 TensorFlow 2.0,我们非常希望了解您的反馈意见!请加入 TF 2.0 测试社区,将您的问题和讨论发送到 testing@tensorflow.org