<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Copyright 2020 The TensorFlow Authors.
Step1:  使用 tf.function 提高性能
<table class="tfo-notebook-buttons" align="left">
  <td><a target="_blank" href="https
Step2:  定义一个辅助函数来演示可能遇到的错误类型：
Step3:  基础知识
用法
您定义的 Function 就像核心 TensorFlow 运算：您可以在 Eager 模式下执行，可以计算梯度，等等。
Step4:  Function 中可以嵌套其他 Function。
Step5:  Function 的执行速度比 Eager 代码快，尤其是对于包含很多简单运算的计算图。但是，对于包含一些复杂运算（如卷积）的计算图，速度提升不会太明显。
Step6:  跟踪
Python 的动态类型意味着您可以调用包含各种参数类型的函数，在各种场景下，Python 的行为可能有所不同。
但是，创建 TensorFlow 计算图需要静态 dtype 和形状维度。tf.function 通过包装一个 Python 函数来创建 Function 对象，弥补了这一缺陷。根据提供的输入，Function 为其选择相应的计算图，从而在必要时追溯 Python 函数。理解发生跟踪的原因和时机后，有效运用 tf.function 就会容易得多！
您可以通过调用包含不同类型参数的 Function 来切实观察这种多态行为。
Step7:  请注意，如果重复调用包含相同参数类型的 Function，TensorFlow 会重复使用之前跟踪的计算图，因为后面的调用生成的计算图将相同。
Step8:  （以下更改存在于 TensorFlow Nightly 版本中，并且将在 TensorFlow 2.3 中提供。）
您可以使用 pretty_printed_concrete_signatures() 查看所有可用跟踪：
Step9:  目前，您已经了解 tf.function 通过 TensorFlow 的计算图跟踪逻辑创建缓存的动态调度层。对于术语的含义，更具体的解释如下：
tf.Graph 与语言无关，是对计算的原始可移植表示。
ConcreteFunction 是 tf.Graph 的 Eeager 执行包装器。
Function 管理 ConcreteFunction 的缓存，并为输入选择正确的缓存。
tf.function 包装 Python 函数，并返回一个 Function 对象。
获取具体函数
每次跟踪函数时都会创建一个新的具体函数。您可以使用 get_concrete_function 直接获取具体函数。
Step10:  （以下更改存在于 TensorFlow Nightly 版本中，并且将在 TensorFlow 2.3 中提供。）
打印 ConcreteFunction 会显示其输入参数（及类型）和输出类型的摘要。
Step11:  您也可以直接检索具体函数的签名。
Step12:  对不兼容的类型使用具体跟踪会引发错误
Step13:  您可能会注意到，在具体函数的输入签名中对 Python 参数进行了特别处理。TensorFlow 2.3 之前的版本会将 Python 参数直接从具体函数的签名中删除。从 TensorFlow 2.3 开始，Python 参数会保留在签名中，但是会受到约束，只能获取在跟踪期间设置的值。
Step14:  获取计算图
每个具体函数都是 tf.Graph 的可调用包装器。虽然一般不需要检索实际 tf.Graph 对象，不过，您可以从任何具体函数轻松获得实际对象。
Step15:  调试
通常，在 Eager 模式下调试代码比在 tf.function 中简单。在使用 tf.function 进行装饰之前，进行装饰之前，您应该先确保代码可在 Eager 模式下无错误执行。为了帮助调试，您可以调用 tf.config.run_functions_eagerly(True) 来全局停用和重新启用 tf.function。
追溯仅在 tf.function 中出现的问题时，可参考下面的几点提示：
普通旧 Python print 调用仅在跟踪期间执行，可用于追溯（重新）跟踪函数的时间。
tf.print 调用每次都会执行，可用于追溯执行过程中产生的中间值。
利用 tf.debugging.enable_check_numerics 很容易追溯到 NaN 和 Inf 在何处创建。
pdb 可以帮助您理解跟踪的详细过程。（提醒：使用 PDB 调试时，AutoGraph 会自动转换 Python 源代码。）
跟踪语义
缓存键规则
通过从输入的参数和关键词参数计算缓存键，Function 可以确定是否重复使用跟踪的具体函数。
为 tf.Tensor 参数生成的键是其形状和 dtype。
从 TensorFlow 2.3 开始，为 tf.Variable 参数生成的键是其 id()。
为 Python 基元生成的键是其值。为嵌套 dict、 list、 tuple、 namedtuple 和 attr 生成的键是扁平化元祖。（由于这种扁平化处理，如果调用的具体函数的嵌套结构与跟踪期间使用的不同，则会导致 TypeError）。
对于所有其他 Python 类型，键基于对象 id()，以便为类的每个实例独立跟踪方法。
控制回溯
回溯可以确保 TensorFlow 为每组输入生成正确的计算图。但是，跟踪操作非常消耗资源！如果 Function 为每一次调用都回溯新的计算图，您会发现代码的执行速度远不如不使用 tf.function。
要控制跟踪行为，可以采用以下技巧：
在 tf.function 中指定 input_signature 来限制跟踪。
Step16:  在 tf.TensorSpec 中指定 [None] 维度可灵活运用跟踪重用。
由于 TensorFlow 根据其形状匹配张量，因此，对于可变大小输入，使用 None 维度作为通配符可以让 Function 重复使用跟踪。对于每个批次，如果有不同长度的序列或不同大小的计算图，则会出现可变大小输入（请参阅 Transformer 和 Deep Dream 教程了解示例）。
Step17:  将 Python 参数转换为张量以减少回溯。
通常，Python 参数用于控制超参数和计算图构造，例如 num_layers=10、training=True 或 nonlinearity='relu'。所以，如果 Python 参数改变，则有必要回溯计算图。
但是，Python 参数有可能并未用于控制计算图构造。在这些情况下，Python 值的改变可能触发非必要的回溯。例如，在此训练循环中，AutoGraph 会动态展开。尽管有多个跟踪，但生成的计算图实际上是相同的，所以没有必要进行回溯。
Step18:  如果需要强制执行回溯，可以创建一个新的 Function。单独的 Function 对象肯定不会共享跟踪记录。
Step19:  Python 副作用
Python 副作用（如打印、追加到列表、改变全局变量）仅在第一次使用一组输入调用 Function 时才会发生。随后重新执行跟踪的 tf.Graph，而不执行 Python 代码。
一般经验法则是仅使用 Python 副作用来调试跟踪记录。另外，对于每一次调用，TensorFlow 运算（如 tf.Variable.assign、tf.print 和 tf.summary）是确保代码得到 TensorFlow 运行时跟踪并执行的最佳方法。
Step20:  很多 Python 功能（如生成器和迭代器）依赖 Python 运行时来跟踪状态。通常，虽然这些构造在 Eager 模式下可以正常工作，但由于跟踪行为，tf.function 中会发生许多意外情况：
举一个例子，推进迭代器状态是 Python 的一个副作用，因此只在跟踪过程中发生。
Step21:  某些迭代构造通过 AutoGraph 获得支持。有关概述，请参阅 AutoGraph 转换部分。
如果希望在每次调用 Function 时都执行 Python 代码，tf.py_function 可以作为退出舱口。tf.py_function 的缺点是不可移植，性能不高，并且在分布式（多 GPU、TPU）设置中效果不佳。另外，由于 tf.py_function 必须连接到计算图中，它会将所有输入/输出转换为张量。
tf.gather、tf.stack 和 tf.TensorArray 之类的 API 可帮助您在原生 TensorFlow 中实现常见循环模式。
Step22:  变量
在函数中创建新的 tf.Variable 时可能遇到错误。该错误是为了防止重复调用发生行为背离：在 Eager 模式下，每次调用函数时都会创建一个新变量，但是在 Function 中则不一定，这是因为重复使用了跟踪记录。
Step23:  您也可以在 Function 内部创建变量，不过只能在第一次执行该函数时创建这些变量。
Step24:  您可能遇到的另一个错误是变量被回收。与常规 Python 函数不同，具体函数只会保留对它们闭包时所在变量的弱引用，因此，您必须保留对任何变量的引用。
Step25:  AutoGraph 转换
AutoGraph 是一个库，在 tf.function 中默认处于启用状态。它可以将 Python Eager 代码的子集转换为与计算图兼容的 TensorFlow 运算。这包括 if、for、while 等控制流。
tf.cond 和 tf.while_loop 等 TensorFlow 运算仍然可以运行，但是使用 Python 编写时，控制流通常更易于编写，代码也更易于理解。
Step26:  如果您有兴趣，可以检查 Autograph 生成的代码。
Step27:  条件语句
AutoGraph 会将某些 if &lt;condition&gt; 语句转换为等效的 tf.cond 调用。如果 &lt;condition&gt; 是张量，则会执行这种替换，否则会将 if 语句作为 Python 条件语句执行。
Python 条件语句在跟踪时执行，因此会将该条件语句的一个分支添加到计算图。如果不使用 AutoGraph，当存在依赖于数据的控制流时，此跟踪计算图将无法选择替代分支。
tf.cond 跟踪并将条件的两个分支添加到计算图，在执行时动态选择分支。跟踪可能产生意外的副作用；有关详细信息，请参阅 AutoGraph 跟踪作用。
Step28:  有关 AutoGraph 转换的 if 语句的其他限制，请参阅参考文档。
循环
AutoGraph 会将某些 for 和 while 语句转换为等效的 TensorFlow 循环运算，例如 tf.while_loop。如果不转换，则会将 for 或 while 循环作为 Python 循环执行。
以下情形会执行这种替换：
for x in y：如果 y 是一个张量，则转换为 tf.while_loop。在特殊情况下，如果 y 是 tf.data.Dataset，则会生成 tf.data.Dataset 运算的组合。
while &lt;condition&gt;：如果 &lt;condition&gt; 是张量，则转换为 tf.while_loop。
Python 循环在跟踪时执行，因而循环每迭代一次，都会将额外的运算添加到 tf.Graph。
TensorFlow 循环会跟踪循环体，并在执行时动态选择迭代的运行次数。循环体仅在生成的 tf.Graph 中出现一次。
有关 AutoGraph 转换的 for 和 while 语句的其他限制，请参阅参考文档。
在 Python 数据上循环
一个常见陷阱是在 tf.function 中的 Python/Numpy 数据上循环。此循环在跟踪过程中执行，因而循环每迭代一次，都会将模型的一个副本添加到 tf.Graph。
如果要在 tf.function 中包装整个训练循环，最安全的方法是将数据包装为 tf.data.Dataset，以便 AutoGraph 动态展开训练循环。
Step29:  在数据集中包装 Python/Numpy 数据时，要注意 tf.data.Dataset.from_generator 与 tf.data.Dataset.from_tensors。前者将数据保留在 Python 中，并通过 tf.py_function 获取，这可能会影响性能；后者将数据的副本捆绑成计算图中的一个大 tf.constant() 节点，这可能会消耗较多内存。
通过 TFRecordDataset/CsvDataset 等从文件中读取数据是最高效的数据使用方式，因为这样 TensorFlow 就可以自行管理数据的异步加载和预提取，不必利用 Python。要了解详细信息，请参阅 tf.data 指南。
累加循环值
一种常见模式是不断累加循环的中间值。通常，这可以通过将元素追加到 Python 列表或将条目添加到 Python 字典来实现。但是，由于存在 Python 副作用，在动态展开循环中，这些方法无法达到预期效果。要从动态展开循环累加结果，可以使用 tf.TensorArray 来实现。
<ASSISTANT_TASK:>
Python Code:
#@title Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
Explanation: Copyright 2020 The TensorFlow Authors.
End of explanation
import tensorflow as tf
Explanation: 使用 tf.function 提高性能
<table class="tfo-notebook-buttons" align="left">
  <td><a target="_blank" href="https://tensorflow.google.cn/guide/function" class=""><img src="https://tensorflow.google.cn/images/tf_logo_32px.png" class="">在 TensorFlow.org 上查看</a></td>
  <td><a target="_blank" href="https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/zh-cn/guide/function.ipynb" class=""><img src="https://tensorflow.google.cn/images/colab_logo_32px.png" class="">在 Google Colab 中运行</a></td>
  <td><a target="_blank" href="https://github.com/tensorflow/docs-l10n/blob/master/site/zh-cn/guide/function.ipynb" class=""><img src="https://tensorflow.google.cn/images/GitHub-Mark-32px.png" class="">在 GitHub 上查看源代码</a></td>
  <td><a href="https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/zh-cn/guide/function.ipynb" class=""><img src="https://tensorflow.google.cn/images/download_logo_32px.png" class="">下载笔记本</a></td>
</table>
在 TensorFlow 2 中，默认情况下会打开 Eager Execution 模式。这种模式下的用户界面非常灵活直观（执行一次性运算要简单快速得多），但可能会牺牲一定的性能和可部署性。
您可以使用 tf.function 将程序转换为计算图。这是一个转换工具，用于从 Python 代码创建独立于 Python 的数据流图。它可以帮助您创建高效且可移植的模型，并且如果要使用 SavedModel，则必须使用此工具。
本指南介绍 tf.function 的底层工作原理，让您形成概念化理解，从而有效地加以利用。
要点和建议包括：
先在 Eager 模式下调试，然后使用 @tf.function 进行装饰。
不依赖 Python 的副作用，如对象变异或列表追加。
tf.function 最适合处理 TensorFlow 运算；NumPy 和 Python 调用会转换为常量。
设置
End of explanation
import traceback
import contextlib
# Some helper code to demonstrate the kinds of errors you might encounter.
@contextlib.contextmanager
def assert_raises(error_class):
  try:
    yield
  except error_class as e:
    print('Caught expected exception \n  {}:'.format(error_class))
    traceback.print_exc(limit=2)
  except Exception as e:
    raise e
  else:
    raise Exception('Expected {} to be raised but no error was raised!'.format(
        error_class))
Explanation: 定义一个辅助函数来演示可能遇到的错误类型：
End of explanation
@tf.function
def add(a, b):
  return a + b
add(tf.ones([2, 2]), tf.ones([2, 2]))  #  [[2., 2.], [2., 2.]]
v = tf.Variable(1.0)
with tf.GradientTape() as tape:
  result = add(v, 1.0)
tape.gradient(result, v)
Explanation: 基础知识
用法
您定义的 Function 就像核心 TensorFlow 运算：您可以在 Eager 模式下执行，可以计算梯度，等等。
End of explanation
@tf.function
def dense_layer(x, w, b):
  return add(tf.matmul(x, w), b)
dense_layer(tf.ones([3, 2]), tf.ones([2, 2]), tf.ones([2]))
Explanation: Function 中可以嵌套其他 Function。
End of explanation
import timeit
conv_layer = tf.keras.layers.Conv2D(100, 3)
@tf.function
def conv_fn(image):
  return conv_layer(image)
image = tf.zeros([1, 200, 200, 100])
# warm up
conv_layer(image); conv_fn(image)
print("Eager conv:", timeit.timeit(lambda: conv_layer(image), number=10))
print("Function conv:", timeit.timeit(lambda: conv_fn(image), number=10))
print("Note how there's not much difference in performance for convolutions")
Explanation: Function 的执行速度比 Eager 代码快，尤其是对于包含很多简单运算的计算图。但是，对于包含一些复杂运算（如卷积）的计算图，速度提升不会太明显。
End of explanation
# Functions are polymorphic
@tf.function
def double(a):
  print("Tracing with", a)
  return a + a
print(double(tf.constant(1)))
print()
print(double(tf.constant(1.1)))
print()
print(double(tf.constant("a")))
print()
Explanation: 跟踪
Python 的动态类型意味着您可以调用包含各种参数类型的函数，在各种场景下，Python 的行为可能有所不同。
但是，创建 TensorFlow 计算图需要静态 dtype 和形状维度。tf.function 通过包装一个 Python 函数来创建 Function 对象，弥补了这一缺陷。根据提供的输入，Function 为其选择相应的计算图，从而在必要时追溯 Python 函数。理解发生跟踪的原因和时机后，有效运用 tf.function 就会容易得多！
您可以通过调用包含不同类型参数的 Function 来切实观察这种多态行为。
End of explanation
# This doesn't print 'Tracing with ...'
print(double(tf.constant("b")))
Explanation: 请注意，如果重复调用包含相同参数类型的 Function，TensorFlow 会重复使用之前跟踪的计算图，因为后面的调用生成的计算图将相同。
End of explanation
print(double.pretty_printed_concrete_signatures())
Explanation: （以下更改存在于 TensorFlow Nightly 版本中，并且将在 TensorFlow 2.3 中提供。）
您可以使用 pretty_printed_concrete_signatures() 查看所有可用跟踪：
End of explanation
print("Obtaining concrete trace")
double_strings = double.get_concrete_function(tf.constant("a"))
print("Executing traced function")
print(double_strings(tf.constant("a")))
print(double_strings(a=tf.constant("b")))
# You can also call get_concrete_function on an InputSpec
double_strings_from_inputspec = double.get_concrete_function(tf.TensorSpec(shape=[], dtype=tf.string))
print(double_strings_from_inputspec(tf.constant("c")))
Explanation: 目前，您已经了解 tf.function 通过 TensorFlow 的计算图跟踪逻辑创建缓存的动态调度层。对于术语的含义，更具体的解释如下：
tf.Graph 与语言无关，是对计算的原始可移植表示。
ConcreteFunction 是 tf.Graph 的 Eeager 执行包装器。
Function 管理 ConcreteFunction 的缓存，并为输入选择正确的缓存。
tf.function 包装 Python 函数，并返回一个 Function 对象。
获取具体函数
每次跟踪函数时都会创建一个新的具体函数。您可以使用 get_concrete_function 直接获取具体函数。
End of explanation
print(double_strings)
Explanation: （以下更改存在于 TensorFlow Nightly 版本中，并且将在 TensorFlow 2.3 中提供。）
打印 ConcreteFunction 会显示其输入参数（及类型）和输出类型的摘要。
End of explanation
print(double_strings.structured_input_signature)
print(double_strings.structured_outputs)
Explanation: 您也可以直接检索具体函数的签名。
End of explanation
with assert_raises(tf.errors.InvalidArgumentError):
  double_strings(tf.constant(1))
Explanation: 对不兼容的类型使用具体跟踪会引发错误
End of explanation
@tf.function
def pow(a, b):
  return a ** b
square = pow.get_concrete_function(a=tf.TensorSpec(None, tf.float32), b=2)
print(square)
assert square(tf.constant(10.0)) == 100
with assert_raises(TypeError):
  square(tf.constant(10.0), b=3)
Explanation: 您可能会注意到，在具体函数的输入签名中对 Python 参数进行了特别处理。TensorFlow 2.3 之前的版本会将 Python 参数直接从具体函数的签名中删除。从 TensorFlow 2.3 开始，Python 参数会保留在签名中，但是会受到约束，只能获取在跟踪期间设置的值。
End of explanation
graph = double_strings.graph
for node in graph.as_graph_def().node:
  print(f'{node.input} -&gt; {node.name}')
Explanation: 获取计算图
每个具体函数都是 tf.Graph 的可调用包装器。虽然一般不需要检索实际 tf.Graph 对象，不过，您可以从任何具体函数轻松获得实际对象。
End of explanation
@tf.function(input_signature=(tf.TensorSpec(shape=[None], dtype=tf.int32),))
def next_collatz(x):
  print("Tracing with", x)
  return tf.where(x % 2 == 0, x // 2, 3 * x + 1)
print(next_collatz(tf.constant([1, 2])))
# We specified a 1-D tensor in the input signature, so this should fail.
with assert_raises(ValueError):
  next_collatz(tf.constant([[1, 2], [3, 4]]))
# We specified an int32 dtype in the input signature, so this should fail.
with assert_raises(ValueError):
  next_collatz(tf.constant([1.0, 2.0]))
Explanation: 调试
通常，在 Eager 模式下调试代码比在 tf.function 中简单。在使用 tf.function 进行装饰之前，进行装饰之前，您应该先确保代码可在 Eager 模式下无错误执行。为了帮助调试，您可以调用 tf.config.run_functions_eagerly(True) 来全局停用和重新启用 tf.function。
追溯仅在 tf.function 中出现的问题时，可参考下面的几点提示：
普通旧 Python print 调用仅在跟踪期间执行，可用于追溯（重新）跟踪函数的时间。
tf.print 调用每次都会执行，可用于追溯执行过程中产生的中间值。
利用 tf.debugging.enable_check_numerics 很容易追溯到 NaN 和 Inf 在何处创建。
pdb 可以帮助您理解跟踪的详细过程。（提醒：使用 PDB 调试时，AutoGraph 会自动转换 Python 源代码。）
跟踪语义
缓存键规则
通过从输入的参数和关键词参数计算缓存键，Function 可以确定是否重复使用跟踪的具体函数。
为 tf.Tensor 参数生成的键是其形状和 dtype。
从 TensorFlow 2.3 开始，为 tf.Variable 参数生成的键是其 id()。
为 Python 基元生成的键是其值。为嵌套 dict、 list、 tuple、 namedtuple 和 attr 生成的键是扁平化元祖。（由于这种扁平化处理，如果调用的具体函数的嵌套结构与跟踪期间使用的不同，则会导致 TypeError）。
对于所有其他 Python 类型，键基于对象 id()，以便为类的每个实例独立跟踪方法。
控制回溯
回溯可以确保 TensorFlow 为每组输入生成正确的计算图。但是，跟踪操作非常消耗资源！如果 Function 为每一次调用都回溯新的计算图，您会发现代码的执行速度远不如不使用 tf.function。
要控制跟踪行为，可以采用以下技巧：
在 tf.function 中指定 input_signature 来限制跟踪。
End of explanation
@tf.function(input_signature=(tf.TensorSpec(shape=[None], dtype=tf.int32),))
def g(x):
  print('Tracing with', x)
  return x
# No retrace!
print(g(tf.constant([1, 2, 3])))
print(g(tf.constant([1, 2, 3, 4, 5])))
Explanation: 在 tf.TensorSpec 中指定 [None] 维度可灵活运用跟踪重用。
由于 TensorFlow 根据其形状匹配张量，因此，对于可变大小输入，使用 None 维度作为通配符可以让 Function 重复使用跟踪。对于每个批次，如果有不同长度的序列或不同大小的计算图，则会出现可变大小输入（请参阅 Transformer 和 Deep Dream 教程了解示例）。
End of explanation
def train_one_step():
  pass
@tf.function
def train(num_steps):
  print("Tracing with num_steps = ", num_steps)
  tf.print("Executing with num_steps = ", num_steps)
  for _ in tf.range(num_steps):
    train_one_step()
print("Retracing occurs for different Python arguments.")
train(num_steps=10)
train(num_steps=20)
print()
print("Traces are reused for Tensor arguments.")
train(num_steps=tf.constant(10))
train(num_steps=tf.constant(20))
Explanation: 将 Python 参数转换为张量以减少回溯。
通常，Python 参数用于控制超参数和计算图构造，例如 num_layers=10、training=True 或 nonlinearity='relu'。所以，如果 Python 参数改变，则有必要回溯计算图。
但是，Python 参数有可能并未用于控制计算图构造。在这些情况下，Python 值的改变可能触发非必要的回溯。例如，在此训练循环中，AutoGraph 会动态展开。尽管有多个跟踪，但生成的计算图实际上是相同的，所以没有必要进行回溯。
End of explanation
def f():
  print('Tracing!')
  tf.print('Executing')
tf.function(f)()
tf.function(f)()
Explanation: 如果需要强制执行回溯，可以创建一个新的 Function。单独的 Function 对象肯定不会共享跟踪记录。
End of explanation
@tf.function
def f(x):
  print("Traced with", x)
  tf.print("Executed with", x)
f(1)
f(1)
f(2)
Explanation: Python 副作用
Python 副作用（如打印、追加到列表、改变全局变量）仅在第一次使用一组输入调用 Function 时才会发生。随后重新执行跟踪的 tf.Graph，而不执行 Python 代码。
一般经验法则是仅使用 Python 副作用来调试跟踪记录。另外，对于每一次调用，TensorFlow 运算（如 tf.Variable.assign、tf.print 和 tf.summary）是确保代码得到 TensorFlow 运行时跟踪并执行的最佳方法。
End of explanation
external_var = tf.Variable(0)
@tf.function
def buggy_consume_next(iterator):
  external_var.assign_add(next(iterator))
  tf.print("Value of external_var:", external_var)
iterator = iter([0, 1, 2, 3])
buggy_consume_next(iterator)
# This reuses the first value from the iterator, rather than consuming the next value.
buggy_consume_next(iterator)
buggy_consume_next(iterator)
Explanation: 很多 Python 功能（如生成器和迭代器）依赖 Python 运行时来跟踪状态。通常，虽然这些构造在 Eager 模式下可以正常工作，但由于跟踪行为，tf.function 中会发生许多意外情况：
举一个例子，推进迭代器状态是 Python 的一个副作用，因此只在跟踪过程中发生。
End of explanation
external_list = []
def side_effect(x):
  print('Python side effect')
  external_list.append(x)
@tf.function
def f(x):
  tf.py_function(side_effect, inp=[x], Tout=[])
f(1)
f(1)
f(1)
# The list append happens all three times!
assert len(external_list) == 3
# The list contains tf.constant(1), not 1, because py_function casts everything to tensors.
assert external_list[0].numpy() == 1
Explanation: 某些迭代构造通过 AutoGraph 获得支持。有关概述，请参阅 AutoGraph 转换部分。
如果希望在每次调用 Function 时都执行 Python 代码，tf.py_function 可以作为退出舱口。tf.py_function 的缺点是不可移植，性能不高，并且在分布式（多 GPU、TPU）设置中效果不佳。另外，由于 tf.py_function 必须连接到计算图中，它会将所有输入/输出转换为张量。
tf.gather、tf.stack 和 tf.TensorArray 之类的 API 可帮助您在原生 TensorFlow 中实现常见循环模式。
End of explanation
@tf.function
def f(x):
  v = tf.Variable(1.0)
  v.assign_add(x)
  return v
with assert_raises(ValueError):
  f(1.0)
Explanation: 变量
在函数中创建新的 tf.Variable 时可能遇到错误。该错误是为了防止重复调用发生行为背离：在 Eager 模式下，每次调用函数时都会创建一个新变量，但是在 Function 中则不一定，这是因为重复使用了跟踪记录。
End of explanation
class Count(tf.Module):
  def __init__(self):
    self.count = None
  
  @tf.function
  def __call__(self):
    if self.count is None:
      self.count = tf.Variable(0)
    return self.count.assign_add(1)
c = Count()
print(c())
print(c())
Explanation: 您也可以在 Function 内部创建变量，不过只能在第一次执行该函数时创建这些变量。
End of explanation
external_var = tf.Variable(3)
@tf.function
def f(x):
  return x * external_var
traced_f = f.get_concrete_function(4)
print("Calling concrete function...")
print(traced_f(4))
del external_var
print()
print("Calling concrete function after garbage collecting its closed Variable...")
with assert_raises(tf.errors.FailedPreconditionError):
  traced_f(4)
Explanation: 您可能遇到的另一个错误是变量被回收。与常规 Python 函数不同，具体函数只会保留对它们闭包时所在变量的弱引用，因此，您必须保留对任何变量的引用。
End of explanation
# Simple loop
@tf.function
def f(x):
  while tf.reduce_sum(x) &gt; 1:
    tf.print(x)
    x = tf.tanh(x)
  return x
f(tf.random.uniform([5]))
Explanation: AutoGraph 转换
AutoGraph 是一个库，在 tf.function 中默认处于启用状态。它可以将 Python Eager 代码的子集转换为与计算图兼容的 TensorFlow 运算。这包括 if、for、while 等控制流。
tf.cond 和 tf.while_loop 等 TensorFlow 运算仍然可以运行，但是使用 Python 编写时，控制流通常更易于编写，代码也更易于理解。
End of explanation
print(tf.autograph.to_code(f.python_function))
Explanation: 如果您有兴趣，可以检查 Autograph 生成的代码。
End of explanation
@tf.function
def fizzbuzz(n):
  for i in tf.range(1, n + 1):
    print('Tracing for loop')
    if i % 15 == 0:
      print('Tracing fizzbuzz branch')
      tf.print('fizzbuzz')
    elif i % 3 == 0:
      print('Tracing fizz branch')
      tf.print('fizz')
    elif i % 5 == 0:
      print('Tracing buzz branch')
      tf.print('buzz')
    else:
      print('Tracing default branch')
      tf.print(i)
fizzbuzz(tf.constant(5))
fizzbuzz(tf.constant(20))
Explanation: 条件语句
AutoGraph 会将某些 if &lt;condition&gt; 语句转换为等效的 tf.cond 调用。如果 &lt;condition&gt; 是张量，则会执行这种替换，否则会将 if 语句作为 Python 条件语句执行。
Python 条件语句在跟踪时执行，因此会将该条件语句的一个分支添加到计算图。如果不使用 AutoGraph，当存在依赖于数据的控制流时，此跟踪计算图将无法选择替代分支。
tf.cond 跟踪并将条件的两个分支添加到计算图，在执行时动态选择分支。跟踪可能产生意外的副作用；有关详细信息，请参阅 AutoGraph 跟踪作用。
End of explanation
def measure_graph_size(f, *args):
  g = f.get_concrete_function(*args).graph
  print("{}({}) contains {} nodes in its graph".format(
      f.__name__, ', '.join(map(str, args)), len(g.as_graph_def().node)))
@tf.function
def train(dataset):
  loss = tf.constant(0)
  for x, y in dataset:
    loss += tf.abs(y - x) # Some dummy computation.
  return loss
small_data = [(1, 1)] * 3
big_data = [(1, 1)] * 10
measure_graph_size(train, small_data)
measure_graph_size(train, big_data)
measure_graph_size(train, tf.data.Dataset.from_generator(
    lambda: small_data, (tf.int32, tf.int32)))
measure_graph_size(train, tf.data.Dataset.from_generator(
    lambda: big_data, (tf.int32, tf.int32)))
Explanation: 有关 AutoGraph 转换的 if 语句的其他限制，请参阅参考文档。
循环
AutoGraph 会将某些 for 和 while 语句转换为等效的 TensorFlow 循环运算，例如 tf.while_loop。如果不转换，则会将 for 或 while 循环作为 Python 循环执行。
以下情形会执行这种替换：
for x in y：如果 y 是一个张量，则转换为 tf.while_loop。在特殊情况下，如果 y 是 tf.data.Dataset，则会生成 tf.data.Dataset 运算的组合。
while &lt;condition&gt;：如果 &lt;condition&gt; 是张量，则转换为 tf.while_loop。
Python 循环在跟踪时执行，因而循环每迭代一次，都会将额外的运算添加到 tf.Graph。
TensorFlow 循环会跟踪循环体，并在执行时动态选择迭代的运行次数。循环体仅在生成的 tf.Graph 中出现一次。
有关 AutoGraph 转换的 for 和 while 语句的其他限制，请参阅参考文档。
在 Python 数据上循环
一个常见陷阱是在 tf.function 中的 Python/Numpy 数据上循环。此循环在跟踪过程中执行，因而循环每迭代一次，都会将模型的一个副本添加到 tf.Graph。
如果要在 tf.function 中包装整个训练循环，最安全的方法是将数据包装为 tf.data.Dataset，以便 AutoGraph 动态展开训练循环。
End of explanation
batch_size = 2
seq_len = 3
feature_size = 4
def rnn_step(inp, state):
  return inp + state
@tf.function
def dynamic_rnn(rnn_step, input_data, initial_state):
  # [batch, time, features] -&gt; [time, batch, features]
  input_data = tf.transpose(input_data, [1, 0, 2])
  max_seq_len = input_data.shape[0]
  states = tf.TensorArray(tf.float32, size=max_seq_len)
  state = initial_state
  for i in tf.range(max_seq_len):
    state = rnn_step(input_data[i], state)
    states = states.write(i, state)
  return tf.transpose(states.stack(), [1, 0, 2])
  
dynamic_rnn(rnn_step,
            tf.random.uniform([batch_size, seq_len, feature_size]),
            tf.zeros([batch_size, feature_size]))
Explanation: 在数据集中包装 Python/Numpy 数据时，要注意 tf.data.Dataset.from_generator 与 tf.data.Dataset.from_tensors。前者将数据保留在 Python 中，并通过 tf.py_function 获取，这可能会影响性能；后者将数据的副本捆绑成计算图中的一个大 tf.constant() 节点，这可能会消耗较多内存。
通过 TFRecordDataset/CsvDataset 等从文件中读取数据是最高效的数据使用方式，因为这样 TensorFlow 就可以自行管理数据的异步加载和预提取，不必利用 Python。要了解详细信息，请参阅 tf.data 指南。
累加循环值
一种常见模式是不断累加循环的中间值。通常，这可以通过将元素追加到 Python 列表或将条目添加到 Python 字典来实现。但是，由于存在 Python 副作用，在动态展开循环中，这些方法无法达到预期效果。要从动态展开循环累加结果，可以使用 tf.TensorArray 来实现。
End of explanation
<END_TASK>