2021 年 11 月 10 日 — 由 Goldie Gadde 和 Josh Gordon 代表 TensorFlow 团队发布 TensorFlow 2.7 现已发布!此 版本 通过更清晰的错误消息、简化的堆栈跟踪,并为迁移到 TF2 的用户添加了新的工具和文档,提高了可用性。 改善的调试体验 调试代码的过程是机器学习框架用户体验的一个基本部分。在此版本中,…
由 Goldie Gadde 和 Josh Gordon 代表 TensorFlow 团队发布
TensorFlow 2.7 现已发布!此 版本 通过更清晰的错误消息、简化的堆栈跟踪,并为迁移到 TF2 的用户添加了新的工具和文档,提高了可用性。
调试代码的过程是机器学习框架用户体验的一个基本部分。在此版本中,我们通过以下三项重大更改,大大改善了 TensorFlow 调试体验,使其更具生产力和趣味性:简化的堆栈跟踪、在源自自定义 Keras 层的错误中显示更多上下文信息,以及对 Keras 和 TensorFlow 中所有错误消息进行广泛的审计。
TensorFlow 现在默认情况下会过滤显示在错误时的堆栈跟踪,以隐藏源自 TensorFlow 内部代码的任何帧,并将信息重点放在对你重要的内容上:你自己的代码。这使得堆栈跟踪更简单、更短,也更容易理解和修复代码中的问题。
如果你实际上是在调试 TensorFlow 代码库本身(例如,因为你要为 TensorFlow 准备一个 PR),你可以通过调用 tf.debugging.disable_traceback_filtering()
来关闭过滤机制。
编写低级代码的最常见用例之一是创建自定义 Keras 层,因此我们希望使调试你的层尽可能容易和高效。调试层时,你首先要做的是打印其输入的形状和数据类型,以及其 training
和 mask
参数的值。现在,我们会自动将此信息添加到所有源自自定义 Keras 层的堆栈跟踪中。
在下面的图像中查看堆栈跟踪过滤和调用上下文信息显示的实际效果。
TensorFlow 2.7 中的简化堆栈跟踪 |
最后,我们已经审计了 Keras 和 TensorFlow 代码库中的每个错误消息(数千个错误位置!),并对它们进行了改进,以确保它们遵循 UX 最佳实践。好的错误消息应该告诉你框架预期什么,你做了什么与框架的预期不符,并应该提供修复问题的提示。
我们改进了两种常见的 tf.function
错误消息类型:运行时错误消息和“图”张量错误消息,方法是在用户代码中包含指向错误源的回溯。对于其他模糊且不准确的 tf.function
错误消息,我们也更新了它们,使其更清晰、更准确。
对于用户代码引起的运行时错误消息
@tf.function
def f():
l = tf.range(tf.random.uniform((), minval=1, maxval=10, dtype=tf.int32))
return l[20]
旧错误消息的摘要如下所示
# … Python stack trace of the function call …
InvalidArgumentError: slice index 20 of dimension 0 out of bounds.
[[node strided_slice (defined at <'ipython-input-8-250c76a76c0e'>:5) ]] [Op:__inference_f_75]
Errors may have originated from an input operation.
Input Source operations connected to node strided_slice:
range (defined at <ipython-input-8-250c76a76c0e >':4)
Function call stack:
f
新错误消息的摘要如下所示
# … Python stack trace of the function call …
InvalidArgumentError: slice index 20 of dimension 0 out of bounds.
[[node strided_slice
(defined at <ipython-input-3-250c76a76c0e>:5)
]] [Op:__inference_f_15]
Errors may have originated from an input operation.
Input Source operations connected to node strided_slice:
In[0] range (defined at <ipython-input-3-250c76a76c0e>:4)
In[1] strided_slice/stack:
In[2] strided_slice/stack_1:
In[3] strided_slice/stack_2:
Operation defined at: (most recent call last)
# … Stack trace of the error within the function …
>>> File "<ipython-input-3-250c76a76c0e>", line 7, in <module>
>>> f()
>>>
>>> File "<ipython-input-3-250c76a76c0e>", line 5, in f
>>> return l[20]
>>>
主要区别在于,在执行 tf.function 时引发的运行时错误现在包含一个回溯,该回溯显示了错误的来源,即用户代码中的位置。
# … Original error message and information …
# … More stack frames …
>>> File "<ipython-input-3-250c76a76c0e>", line 7, in <module>
>>> f()
>>>
>>> File "<ipython-input-3-250c76a76c0e>", line 5, in f
>>> return l[20]
>>>
对于以下用户代码引起的“图”张量错误消息
x = None
@tf.function
def leaky_function(a):
global x
x = a + 1 # Bad - leaks local tensor
return a + 2
@tf.function
def captures_leaked_tensor(b):
b += x
return b
leaky_function(tf.constant(1))
captures_leaked_tensor(tf.constant(2))
旧错误消息的摘要如下所示
# … Python stack trace of the function call …
TypeError: An op outside of the function building code is being passed
a "Graph" tensor. It is possible to have Graph tensors
leak out of the function building context by including a
tf.init_scope in your function building code.
For example, the following function will fail:
@tf.function
def has_init_scope():
my_constant = tf.constant(1.)
with tf.init_scope():
added = my_constant * 2
The graph tensor has name: add:0
新错误消息的摘要如下所示
# … Python stack trace of the function call …
TypeError: Originated from a graph execution error.
The graph execution error is detected at a node built at (most recent call last):
# … Stack trace of the error within the function …
>>> File <ipython-input-5-95ca3a98778f>, line 6, in leaky_function
# … More stack trace of the error within the function …
Error detected in node 'add' defined at: File "<ipython-input-5-95ca3a98778f>", line 6, in leaky_function
TypeError: tf.Graph captured an external symbolic tensor. The symbolic tensor 'add:0' created by node 'add' is captured by the tf.Graph being executed as an input. But a tf.Graph is not allowed to take symbolic tensors from another graph as its inputs. Make sure all captured inputs of the executing tf.Graph are not symbolic tensors. Use return values, explicit Python locals or TensorFlow collections to access it. Please see https://tensorflowcn.cn/guide/function#all_outputs_of_a_tffunction_must_be_return_values for more information.
主要区别在于,尝试捕获从不可达图中泄漏的张量的错误现在包含一个回溯,该回溯显示了在用户代码中创建张量的位置。
# … Original error message and information …
# … More stack frames …
>>> File <ipython-input-5-95ca3a98778f>, line 6, in leaky_function
Error detected in node 'add' defined at: File "<ipython-input-5-95ca3a98778f>", line 6, in leaky_function
TypeError: tf.Graph captured an external symbolic tensor. The symbolic tensor 'add:0' created by node 'add' is captured by the tf.Graph being executed as an input. But a tf.Graph is not allowed to take symbolic tensors from another graph as its inputs. Make sure all captured inputs of the executing tf.Graph are not symbolic tensors. Use return values, explicit Python locals or TensorFlow collections to access it. Please see https://tensorflowcn.cn/guide/function#all_outputs_of_a_tffunction_must_be_return_values for more information.
用户定义的类型可以使你的项目更具可读性、模块化和可维护性。TensorFlow 2.7.0 引入了 ExtensionType API,它可用于创建与 TensorFlow API 无缝协作的用户定义的面向对象类型。扩展类型是跟踪和组织复杂模型使用的张量的好方法。扩展类型还可以用于定义新的类似张量的类型,这些类型专门化或扩展了“张量”的基本概念。要创建扩展类型,只需定义一个以 tf.experimental.ExtensionType 为基类的 Python 类,并使用 类型注释 为每个字段指定类型。
class TensorGraph(tf.experimental.ExtensionType):
"""A collection of labeled nodes connected by weighted edges."""
edge_weights: tf.Tensor # shape=[num_nodes, num_nodes]
node_labels: typing.Mapping[str, tf.Tensor] # shape=[num_nodes]; dtype=any
class MaskedTensor(tf.experimental.ExtensionType):
"""A tensor paired with a boolean mask, indicating which values are valid."""
values: tf.Tensor
mask: tf.Tensor # shape=values.shape; false for missing/invalid values.
class CSRSparseMatrix(tf.experimental.ExtensionType):
"""Compressed sparse row matrix (https://en.wikipedia.org/wiki/Sparse_matrix)."""
values: tf.Tensor # shape=[num_nonzero]; dtype=any
col_index: tf.Tensor # shape=[num_nonzero]; dtype=int64
row_index: tf.Tensor # shape=[num_rows+1]; dtype=int64
ExtensionType
基类添加了一个构造函数和基于字段类型注释的特殊方法(类似于标准 Python 库中的 typing.NamedTuple
和 @dataclasses.dataclass
)。你可以选择通过覆盖这些默认值或添加新方法、属性或子类来自定义类型。
以下 TensorFlow API 支持扩展类型。
Models
和 Layers
的输入和输出。Datasets
中,并由数据集 Iterators
返回。tf.hub
模块的输入和输出。SavedModel
函数的输入和输出。@tf.function
装饰器包装的函数的参数和返回值。tf.cond
和 tf.while_loop
。这包括由自动生成添加的控制流操作。tf.py_function
的 func
参数的参数和返回值。tf.matmul
、tf.gather
和 tf.reduce_sum
),使用 调度装饰器。有关扩展类型的更多信息,请参阅 扩展类型指南。
注意:tf.experimental
前缀表示这是一个新的 API,我们希望收集来自实际使用情况的反馈;除非遇到不可预见的設計問題,否则我们计划根据 TF 实验政策 将 ExtensionType
从实验包中迁移出去。
为了支持有兴趣将工作负载从 TF1 迁移到 TF2 的用户,我们在 TensorFlow 网站上创建了一个新的 迁移到 TF2
选项卡,其中包含更新的指南和全新的文档,其中包含 Colab 中的具体可运行示例。
已添加了一个 新的 shim 工具,它极大地简化了基于 variable_scope 的模型到 TF2 的迁移。预计它将使大多数 TF1 用户能够在 TF2 管道中按原样运行现有模型架构(或仅进行少量调整),而无需重写你的建模代码。你可以在 模型映射 指南中了解有关它的更多信息。
自从上次 TensorFlow 版本发布以来,社区齐心协力,在 TensorFlow Hub 上提供了许多新的模型。现在你可以找到诸如 MLP-Mixer、Vision Transformers、Wav2Vec2、RoBERTa、ConvMixer、DistillBERT、YoloV5 等许多模型。所有这些模型都已准备就绪,可以通过 TensorFlow Hub 使用。你可以在 此处 了解有关发布模型的更多信息。
查看 发行说明 以获取更多信息。要保持最新状态,你可以阅读 TensorFlow 博客、关注 twitter.com/tensorflow 或订阅 youtube.com/tensorflow。如果你构建了一些想分享的内容,请将其提交到 goo.gle/TFCS 中的社区亮点。对于反馈,请在 GitHub 上提交问题或发布到 TensorFlow 论坛。谢谢!
2021 年 11 月 10 日 — 由 Goldie Gadde 和 Josh Gordon 代表 TensorFlow 团队发布 TensorFlow 2.7 现已发布!此 版本 通过更清晰的错误消息、简化的堆栈跟踪,并为迁移到 TF2 的用户添加了新的工具和文档,提高了可用性。 改善的调试体验 调试代码的过程是机器学习框架用户体验的一个基本部分。在此版本中,…