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/…) 的手動測試執行。

  • no_oss 會將您的測試排除在官方 TF OSS 測試基礎架構中的執行之外。

  • no_macno_windows 標籤可用於將您的測試排除在相關的作業系統測試套件之外。

  • no_gpu 標籤可用於將您的測試排除在 GPU 測試套件之外。

驗證測試在預期的測試套件中執行

TF 有相當多的測試套件。有時,它們可能難以設定。可能會有一些不同的問題導致您的測試從持續建置中省略。因此,您應該驗證您的測試是否如預期般執行。若要執行此操作

  • 等待您的提取請求 (PR) 的預先提交完成執行。
  • 捲動至您的 PR 底部以查看狀態檢查。
  • 按一下任何 Kokoro 檢查右側的「詳細資料」連結。
  • 檢查「目標」清單以尋找您新加入的目標。

每個類別/單元都應有自己的單元測試檔案

個別的測試類別有助於我們更好地隔離失敗和資源。它們可以產生更短且更易於閱讀的測試檔案。因此,您所有的 Python 檔案都應至少有一個對應的測試檔案 (對於每個 foo.py,它應該有 foo_test.py)。對於更精細的測試,例如需要不同設定的整合測試,可以新增更多測試檔案。

速度和執行時間

應盡可能少用分片

請考慮以下事項,而不是分片

  • 縮小測試規模
  • 如果上述方法不可行,請拆分測試

分片有助於減少測試的整體延遲,但透過將測試分解為較小的目標也可以達到相同的效果。拆分測試讓我們可以更精細地控制每個測試,最大限度地減少不必要的預先提交執行,並減少因建置管理員因錯誤的測試案例而停用整個目標所造成的覆蓋率損失。此外,分片會產生不明顯的隱藏成本,例如執行所有分片的所有測試初始化程式碼。基礎架構團隊已將此問題升級為產生額外負載的來源。

較小的測試更好

您的測試執行速度越快,人們就越有可能執行您的測試。您的測試多一秒鐘可能會累積成開發人員和我們的基礎架構花費更多小時來執行您的測試。盡量讓您的測試在 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 函式可能是造成不穩定的主要原因。尤其是在使用多個執行緒時,使用 sleep 等待另一個執行緒永遠不會是確定性的。這是因為系統無法保證不同執行緒或程序的任何執行順序。因此,偏好確定性同步建構,例如互斥鎖。

檢查測試是否不穩定

不穩定性會導致建置管理員和開發人員損失許多小時。它們難以偵測,而且難以偵錯。即使有自動化系統來偵測不穩定性,它們也需要累積數百次測試執行,才能準確地將測試列入拒絕清單。即使它們偵測到,它們也會將您的測試列入拒絕清單,並且測試覆蓋率會遺失。因此,測試作者在撰寫測試時應檢查其測試是否不穩定。這可以透過使用以下旗標執行測試輕鬆完成:--runs_per_test=1000

使用 TensorFlowTestCase

TensorFlowTestCase 採取必要的預防措施,例如植入所有用於降低不穩定性的隨機數字產生器。當我們發現並修正更多不穩定性來源時,這些都將新增至 TensorFlowTestCase。因此,在為 tensorflow 撰寫測試時,您應該使用 TensorFlowTestCase。TensorFlowTestCase 在此處定義:tensorflow/python/framework/test_util.py

撰寫獨立測試

獨立測試不需要任何外部資源。它們已封裝了所需的一切,並且它們只會啟動它們可能需要的任何虛設服務。任何非您的測試的服務都是不確定性的來源。即使其他服務的可用性達到 99%,網路也可能會不穩定,rpc 回應可能會延遲,而且您可能會遇到難以解釋的錯誤訊息。外部服務可能是但不限於 GCS、S3 或任何網站。