Google 致力于为黑人社区推动种族平等。查看具体举措
此页面由 Cloud Translation API 翻译。
Switch to English

TensorFlow测试最佳实践

这些是在TensorFlow存储库中测试代码的推荐做法。

开始之前

在向TensorFlow项目贡献源代码之前,请查看该项目的GitHub存储库中的CONTRIBUTING.md文件。 (例如,请参阅核心TensorFlow回购CONTRIBUTING.md文件 。)要求所有代码提供者签署“提供者许可协议” (CLA)。

一般原则

仅取决于您在BUILD规则中使用的内容

TensorFlow是一个大型库,在为其子模块编写单元测试时取决于整个软件包是一种常见的做法。但是,这将禁用基于bazel依赖关系的分析。这意味着连续集成系统无法智能地消除提交前/提交后运行的无关测试。如果仅依赖于BUILD文件中正在测试的子模块,则将为所有TensorFlow开发人员节省时间,并节省大量宝贵的计算能力。

但是,修改构建依赖项以忽略完整的TF目标会给您可以在Python代码中导入的内容带来一些限制。您将无法再在单元测试中将import tensorflow as tf语句使用。但这是一个值得权衡的选择,因为它可以避免所有开发人员运行数千个不必要的测试。

所有代码都应具有单元测试

对于您编写的任何代码,还应该编写其单元测试。如果您编写一个新文件foo.py ,则应将其单元测试放在foo_test.py ,并在同一更改中提交它。争取所有代码的增量测试覆盖率均超过90%。

避免在TF中使用本机bazel测试规则

TF在运行测试时有很多微妙之处。我们已经努力将所有这些复杂性隐藏在我们的bazel宏中。为避免不得不处理这些问题,请使用以下内容代替本机测试规则。请注意,所有这些都在tensorflow/tensorflow.bzl中定义。对于CC测试,请使用tf_cc_testtf_gpu_cc_testtf_gpu_only_cc_test 。对于python测试,请使用tf_py_testgpu_py_test 。如果您需要真正接近本机py_test规则的内容,请改用tensorflow.bzl中定义的内容。您只需要在BUILD文件的顶部添加以下行: load(“tensorflow/tensorflow.bzl”, “py_test”)

注意测试在哪里执行

当您编写测试时,如果您进行相应的编写,我们的测试基础结构可以帮助您在CPU,GPU和加速器上运行测试。我们有在Linux,macos,Windows上运行的自动化测试,这些测试具有带或不带GPU的系统。您只需要选择上面列出的宏之一,然后使用标签来限制它们的执行位置。

  • manual标记会将您的测试排除在任何地方。这包括使用诸如bazel test tensorflow/…模式的手动测试执行bazel test tensorflow/…

  • no_oss将您的测试从正式的TF OSS测试基础结构中排除。

  • 可以使用no_macno_windows标记将您的测试从相关的操作系统测试套件中排除。

  • no_gpu标记可用于将测试从GPU测试套件中排除。

验证测试是否在预期的测试套件中运行

TF有很多测试套件。有时,他们可能会混淆设置。可能存在不同的问题,导致您的测试从连续构建中被忽略。因此,您应该验证测试是否按预期执行。去做这个:

  • 等待您对Pull Request(PR)的预提交运行完毕。
  • 滚动到PR的底部以查看状态检查。
  • 单击任何Kokoro支票右侧的“详细信息”链接。
  • 检查“目标”列表以找到您新添加的目标。

每个类/单元应具有自己的单元测试文件

单独的测试类可帮助我们更好地隔离故障和资源。它们导致更短,更容易阅读测试文件。因此,您的所有Python文件都应至少具有一个相应的测试文件(对于每个foo.py ,它都应具有foo_test.py )。对于更复杂的测试,例如需要不同设置的集成测试,可以添加更多测试文件。

速度和运行时间

分片应尽可能少地使用

请不要考虑分片:

  • 缩小测试范围
  • 如果以上方法均不可行,请拆分测试

分片有助于减少测试的整体延迟,但是可以通过将测试分解为较小的目标来实现。拆分测试为我们在每个测试中提供了更好的控制级别,最大程度地减少了不必要的提交前运行,并减少了由于测试用例异常而使buildcop禁用整个目标而导致的覆盖范围损失。而且,分片会带来不太明显的隐藏成本,例如为所有分片运行所有测试初始化​​代码。基础架构团队已将此问题逐步升级为我们,认为这会造成额外的负担。

测试越小越好

您的测试运行得越快,人们越有可能运行您的测试。开发人员多花一秒钟的时间,可能会使开发人员和我们的基础架构花费额外的时间进行测试。尝试使您的测试在30秒内运行(在非优化模式下!),并使其变小。只能将测试标记为中级,作为不得已的手段。红外线不会像提交前或提交后那样运行任何大型测试!因此,仅在要安排运行位置时才编写大型测试。使测试运行更快的一些技巧:

  • 在测试中进行较少的训练迭代
  • 考虑使用依赖注入,用简单的伪造品代替被测系统的大量依赖。
  • 考虑在单元测试中使用较小的输入数据
  • 如果没有其他效果,请尝试拆分测试文件。

测试时间应为测试大小超时的一半,以避免剥落

对于bazel测试目标,小型测试会超时1分钟。中度测试超时为5分钟。 TensorFlow测试基础设施不执行大型测试。但是,许多测试不能确定所花费的时间。由于各种原因,您的测试可能会不时地花费更多时间。而且,如果您将平均可以运行50秒的测试标记为小,那么如果该测试计划在使用旧CPU的计算机上进行,则测试将会失败。因此,小型测试的平均运行时间应为30秒。对于中等测试,目标是平均运行时间为2分钟30秒。

减少样本数量并提高培训容忍度

运行缓慢的测试阻止了贡献者。在测试中进行训练可能会非常缓慢。最好选择较高的公差,以便在测试中使用较少的样品,以使测试足够快(最多2.5分钟)。

消除不确定性和剥落

编写确定性测试

单元测试应该始终是确定性的。如果没有影响他们的代码更改,则每次在TAP和Guitar上运行的所有测试都应以相同的方式运行。为确保这一点,以下是一些要考虑的方面。

始终播种任何随机性来源

任何随机数生成器或任何其他随机性源都可能导致片状。因此,每个种子都必须播种。除了减少测试的片状性之外,这还使所有测试都具有可重复性。您可能需要在TF测试中设置种子的不同方法是:

# Python RNG
import random
random.seed(42)

# Numpy RNG
import numpy as np
np.random.seed(42)

# TF RNG
from tensorflow.python.framework import random_seed
random_seed.set_seed(42)

避免在多线程测试中使用sleep

在测试中使用sleep功能可能是导致虚脱的主要原因。特别是在使用多个线程时,使用睡眠来等待另一个线程永远不会是确定的。这是由于系统无法保证不同线程或进程的执行顺序。因此,最好使用确定性同步结构,例如互斥体。

检查测试是否为片状

剥落会导致buildcops和开发人员损失很多时间。它们很难检测,也很难调试。即使有自动系统可以检测到片状感,但在准确拒绝列表测试之前,它们仍需要累积数百次测试运行。即使他们检测到,他们也会拒绝列出您的测试,并且测试范围也会丢失。因此,测试作者在编写测试时应检查其测试是否不可靠。通过使用以下标志运行测试可以轻松完成:-- --runs_per_test=1000

使用TensorFlowTestCase

TensorFlowTestCase采取必要的预防措施,例如为所有随机数生成器播种,以尽可能减少剥落。随着我们发现并修复更多片状来源,所有这些都将添加到TensorFlowTestCase中。因此,在编写张量流测试时应使用TensorFlowTestCase。 TensorFlowTestCase在这里定义: tensorflow/python/framework/test_util.py

编写密封测试

气密测试不需要任何外部资源。他们挤满了所需的一切,并且只是启动了可能需要的任何虚假服务。除测试以外的任何服务都是不确定性的来源。即使其他服务的可用性达到99%,网络也可能断断续续,rpc响应可能会延迟,您最终可能会收到莫名其妙的错误消息。外部服务可能是但不限于GCS,S3或任何网站。