Best practice per i test di TensorFlow

Queste sono le pratiche consigliate per testare il codice nel repository TensorFlow .

Prima di iniziare

Prima di contribuire con il codice sorgente a un progetto TensorFlow, esamina il file CONTRIBUTING.md nel repository GitHub del progetto. (Ad esempio, vedere il file CONTRIBUTING.md per il repository principale di TensorFlow .) Tutti i contributori del codice devono firmare un contratto di licenza per collaboratore (CLA).

Principi generali

Dipende solo da ciò che usi nelle tue regole COSTRUISCI

TensorFlow è una libreria di grandi dimensioni e, a seconda del pacchetto completo, quando si scrive uno unit test per i suoi sottomoduli è stata una pratica comune. Tuttavia, questo disabilita l'analisi basata sulle dipendenze di bazel . Ciò significa che i sistemi di integrazione continua non possono eliminare in modo intelligente test non correlati per le esecuzioni di pre-invio / post-invio. Se dipendi solo dai sottomoduli che stai testando nel tuo file BUILD , risparmierai tempo per tutti gli sviluppatori TensorFlow e molta preziosa potenza di calcolo.

Tuttavia, la modifica della dipendenza di compilazione per omettere gli obiettivi TF completi comporta alcune limitazioni per ciò che è possibile importare nel codice Python. Non sarai più in grado di utilizzare import tensorflow as tf istruzione import tensorflow as tf nei tuoi unit test. Ma questo è un compromesso utile poiché salva tutti gli sviluppatori dall'esecuzione di migliaia di test non necessari.

Tutto il codice dovrebbe avere unit test

Per qualsiasi codice che scrivi, dovresti anche scrivere i suoi unit test. Se scrivi un nuovo file foo.py , dovresti inserire i suoi unit test in foo_test.py e foo_test.py all'interno della stessa modifica. Punta a una copertura incrementale del test> 90% per tutto il tuo codice.

Evita di utilizzare regole di test bazel native in TF

TF ha molte sottigliezze durante l'esecuzione dei test. Abbiamo lavorato per nascondere tutte queste complessità nelle nostre macro bazel. Per evitare di dover affrontare questi problemi, utilizzare quanto segue invece delle regole di test native. Nota che tutti questi sono definiti in tensorflow/tensorflow.bzl Per i test CC, usa tf_cc_test , tf_gpu_cc_test , tf_gpu_only_cc_test . Per i test Python, usa tf_py_test o gpu_py_test . Se hai bisogno di qualcosa di molto vicino alla regola nativa py_test , usa invece quella definita in tensorflow.bzl. Devi solo aggiungere la seguente riga all'inizio del file BUILD: load(“tensorflow/tensorflow.bzl”, “py_test”)

Sii consapevole di dove viene eseguito il test

Quando scrivi un test, il nostro test infra può occuparsi di eseguire i tuoi test su CPU, GPU e acceleratori se li scrivi di conseguenza. Abbiamo test automatizzati che girano su Linux, macos, windows, che hanno sistemi con o senza GPU. Devi semplicemente scegliere una delle macro elencate sopra e quindi utilizzare i tag per limitare la posizione in cui vengono eseguite.

  • manual tag manual escluderà il tuo test dall'esecuzione ovunque. Ciò include le esecuzioni di test manuali che utilizzano modelli come bazel test tensorflow/…

  • no_oss escluderà il tuo test dall'esecuzione nell'infrastruttura di test TF OSS ufficiale.

  • no_mac tag no_mac o no_windows possono essere utilizzati per escludere il test dalle suite di test del sistema operativo pertinenti.

  • no_gpu tag no_gpu può essere utilizzato per escludere il test dall'esecuzione nelle suite di test GPU.

Verificare che i test vengano eseguiti nelle suite di test previste

TF ha parecchie suite di test. A volte, possono creare confusione nella configurazione. Potrebbero esserci diversi problemi che causano l'omissione dei test dalle compilazioni continue. Pertanto, è necessario verificare che i test vengano eseguiti come previsto. Per farlo:

  • Attendi che i tuoi presubmits sulla tua richiesta pull (PR) vengano eseguiti fino al completamento.
  • Scorri fino alla fine del tuo PR per vedere i controlli di stato.
  • Fare clic sul collegamento "Dettagli" sul lato destro di qualsiasi assegno Kokoro.
  • Controlla l'elenco "Target" per trovare i nuovi target aggiunti.

Ogni classe / unità dovrebbe avere il proprio file di unit test

Classi di test separate ci aiutano a isolare meglio gli errori e le risorse. Portano a file di test molto più brevi e facili da leggere. Pertanto, tutti i tuoi file Python dovrebbero avere almeno un file di test corrispondente (per ogni foo.py , dovrebbe avere foo_test.py ). Per test più elaborati, come i test di integrazione che richiedono configurazioni diverse, è possibile aggiungere più file di test.

Velocità e tempi di percorrenza

Lo sharding dovrebbe essere usato il meno possibile

Invece di sharding, considera:

  • Rendere i tuoi test più piccoli
  • Se quanto sopra non è possibile, suddividere i test

Lo sharding aiuta a ridurre la latenza complessiva di un test, ma lo stesso può essere ottenuto suddividendo i test in target più piccoli. La suddivisione dei test ci offre un livello di controllo più preciso su ogni test, riducendo al minimo le esecuzioni di presubmit non necessarie e riducendo la perdita di copertura da un buildcop che disabilita un intero target a causa di un testcase che si comporta male. Inoltre, lo sharding comporta costi nascosti che non sono così ovvi, come l'esecuzione di tutto il codice di inizializzazione del test per tutti i frammenti. Questo problema ci è stato inoltrato dai team infra come fonte che crea un carico aggiuntivo.

I test più piccoli sono migliori

Più velocemente vengono eseguiti i test, più è probabile che le persone eseguano i test. Un secondo in più per il tuo test può accumularsi in ore di tempo extra speso per eseguire il test dagli sviluppatori e dalla nostra infrastruttura. Prova a eseguire i tuoi test in meno di 30 secondi (in modalità non opt!) E rendili piccoli. Contrassegna i tuoi test solo come medi come ultima risorsa. L'infra non esegue test di grandi dimensioni come presubmits o postubmits! Pertanto, scrivi un test di grandi dimensioni solo se intendi organizzare dove verrà eseguito. Alcuni suggerimenti per velocizzare i test:

  • Esegui meno iterazioni di allenamento nel tuo test
  • Prendi in considerazione l'utilizzo dell'inserimento delle dipendenze per sostituire le dipendenze pesanti del sistema sottoposto a test con semplici falsi.
  • Prendi in considerazione l'utilizzo di dati di input più piccoli nei test unitari
  • Se nient'altro funziona, prova a suddividere il file di prova.

I tempi del test dovrebbero mirare a metà del timeout della dimensione del test per evitare i fiocchi

Con i target di test bazel , i test piccoli hanno timeout di 1 minuto. I timeout di test medi sono di 5 minuti. I test di grandi dimensioni semplicemente non vengono eseguiti dal test infra di TensorFlow. Tuttavia, molti test non sono deterministici nella quantità di tempo che richiedono. Per vari motivi i tuoi test potrebbero richiedere più tempo di tanto in tanto. E, se contrassegni un test che viene eseguito in media per 50 secondi come piccolo, il tuo test si sfalderà se programmato su una macchina con una vecchia CPU. Pertanto, mirare a un tempo di esecuzione medio di 30 secondi per piccoli test. Obiettivo per 2 minuti e 30 secondi di tempo di esecuzione medio per i test medi.

Riduci il numero di campioni e aumenta le tolleranze per la formazione

I test a esecuzione lenta scoraggiano i contributori. L'esecuzione dell'allenamento nei test può essere molto lento. Preferisci tolleranze più elevate per poter utilizzare meno campioni nei tuoi test per mantenerli sufficientemente veloci (2,5 minuti max).

Elimina il non determinismo e le scaglie

Scrivi test deterministici

I test unitari dovrebbero essere sempre deterministici. Tutti i test eseguiti su TAP e chitarra dovrebbero essere eseguiti allo stesso modo ogni volta, se non ci sono cambiamenti di codice che li interessano. Per garantire ciò, di seguito sono riportati alcuni punti da considerare.

Semina sempre qualsiasi fonte di stocasticità

Qualsiasi generatore di numeri casuali o qualsiasi altra fonte di stocasticità può causare desquamazione. Pertanto, ciascuno di questi deve essere seminato. Oltre a rendere i test meno friabili, questo rende tutti i test riproducibili. Diversi modi per impostare alcuni semi che potresti dover impostare nei test TF sono:

# 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)

Evita di utilizzare sleep sospensione nei test multithread

L'uso della funzione sleep nei test può essere una delle principali cause di desquamazione. Soprattutto quando si utilizzano più thread, l'utilizzo della sospensione per attendere un altro thread non sarà mai determinante. Ciò è dovuto al fatto che il sistema non è in grado di garantire alcun ordine di esecuzione di thread o processi diversi. Pertanto, preferisci costrutti di sincronizzazione deterministici come i mutex.

Controlla se il test è traballante

I fiocchi fanno perdere molte ore ai buildcops e agli sviluppatori. Sono difficili da rilevare e sono difficili da eseguire il debug. Anche se esistono sistemi automatizzati per rilevare la desquamazione, devono accumulare centinaia di esecuzioni di test prima di poter escludere accuratamente i test. Anche quando rilevano, negano l'elenco dei test e la copertura del test viene persa. Pertanto, gli autori dei test dovrebbero verificare se i loro test sono instabili durante la scrittura dei test. Questo può essere fatto facilmente eseguendo il test con il flag: --runs_per_test=1000

Usa TensorFlowTestCase

TensorFlowTestCase prende le precauzioni necessarie come il seeding di tutti i generatori di numeri casuali utilizzati per ridurre il più possibile la desquamazione. Man mano che scopriamo e correggiamo altre fonti di desquamazione, queste verranno tutte aggiunte a TensorFlowTestCase. Pertanto, è necessario utilizzare TensorFlowTestCase durante la scrittura di test per tensorflow. TensorFlowTestCase è definito qui: tensorflow/python/framework/test_util.py

Scrivi test ermetici

I test ermetici non richiedono risorse esterne. Sono pieni di tutto ciò di cui hanno bisogno e iniziano solo i servizi falsi di cui potrebbero aver bisogno. Qualsiasi servizio diverso dai tuoi test è fonte di non determinismo. Anche con la disponibilità del 99% di altri servizi, la rete può deteriorarsi, la risposta rpc può essere ritardata e potresti ritrovarti con un messaggio di errore inspiegabile. I servizi esterni possono essere, ma non limitati a, GCS, S3 o qualsiasi sito web.