ML Community Day is November 9! Join us for updates from TensorFlow, JAX, and more Learn more

Bonnes pratiques de test TensorFlow

Voici les pratiques recommandées pour tester le code dans le référentiel TensorFlow .

Avant de commencer

Avant de contribuer au code source d'un projet TensorFlow, veuillez consulter le fichier CONTRIBUTING.md dans le référentiel GitHub du projet. (Par exemple, consultez le fichier CONTRIBUTING.md pour le référentiel TensorFlow principal .) Tous les contributeurs de code doivent signer un contrat de licence de contributeur (CLA).

Principes généraux

Dépend uniquement de ce que vous utilisez dans vos règles BUILD

TensorFlow est une grande bibliothèque, et dépendre du package complet lors de l'écriture d'un test unitaire pour ses sous-modules est une pratique courante. Cependant, cela désactive l'analyse basée sur les dépendances bazel . Cela signifie que les systèmes d'intégration continue ne peuvent pas éliminer intelligemment les tests non liés pour les exécutions de pré-soumission / post-soumission. Si vous ne dépendez que des sous-modules que vous testez dans votre fichier BUILD , vous gagnerez du temps pour tous les développeurs TensorFlow et beaucoup de puissance de calcul précieuse.

Cependant, la modification de votre dépendance de construction pour omettre les cibles TF complètes entraîne certaines limitations pour ce que vous pouvez importer dans votre code Python. Vous ne pourrez plus utiliser l' import tensorflow as tf instruction import tensorflow as tf dans vos tests unitaires. Mais c'est un compromis intéressant car il évite à tous les développeurs d'exécuter des milliers de tests inutiles.

Tout le code doit avoir des tests unitaires

Pour tout code que vous écrivez, vous devez également écrire ses tests unitaires. Si vous écrivez un nouveau fichier foo.py , vous devez placer ses tests unitaires dans foo_test.py et le soumettre dans le même changement. Visez une couverture de test incrémentielle> 90% pour tout votre code.

Évitez d'utiliser des règles de test bazel natives dans TF

TF a beaucoup de subtilités lors de l'exécution des tests. Nous avons travaillé pour cacher toutes ces complexités dans nos macros bazel. Pour éviter d'avoir à vous en occuper, utilisez ce qui suit au lieu des règles de test natives. Notez que tous ces éléments sont définis dans tensorflow/tensorflow.bzl Pour les tests CC, utilisez tf_cc_test , tf_gpu_cc_test , tf_gpu_only_cc_test . Pour les tests python, utilisez tf_py_test ou gpu_py_test . Si vous avez besoin de quelque chose de vraiment proche de la règle native py_test , veuillez utiliser celle définie dans tensorflow.bzl à la place. Il vous suffit d'ajouter la ligne suivante en haut du fichier BUILD: load(“tensorflow/tensorflow.bzl”, “py_test”)

Soyez conscient de l'endroit où le test s'exécute

Lorsque vous écrivez un test, notre infra test peut se charger d'exécuter vos tests sur CPU, GPU et accélérateurs si vous les écrivez en conséquence. Nous avons des tests automatisés qui s'exécutent sur Linux, macos, Windows, qui ont des systèmes avec ou sans GPU. Vous devez simplement choisir l'une des macros répertoriées ci-dessus, puis utiliser des balises pour limiter leur exécution.

  • manual balise manual empêchera votre test de s'exécuter n'importe où. Cela inclut les exécutions de tests manuels qui utilisent des modèles tels que bazel test tensorflow/…

  • no_oss exclura votre test de l'exécution dans l'infrastructure de test officielle TF OSS.

  • no_mac balises no_mac ou no_windows peuvent être utilisées pour exclure votre test des suites de tests pertinentes du système d'exploitation.

  • no_gpu balise no_gpu peut être utilisée pour no_gpu votre test de s'exécuter dans les suites de tests GPU.

Vérifier les tests exécutés dans les suites de tests attendues

TF a pas mal de suites de tests. Parfois, ils peuvent être déroutants à mettre en place. Différents problèmes peuvent entraîner l'omission de vos tests des builds continus. Par conséquent, vous devez vérifier que vos tests s'exécutent comme prévu. Pour faire ça:

  • Attendez que vos pré-soumissions sur votre Pull Request (PR) se terminent.
  • Faites défiler vers le bas de votre PR pour voir les vérifications de statut.
  • Cliquez sur le lien "Détails" sur le côté droit de tout chèque Kokoro.
  • Consultez la liste «Cibles» pour trouver vos nouvelles cibles ajoutées.

Chaque classe / unité doit avoir son propre fichier de test unitaire

Des classes de test séparées nous aident à mieux isoler les échecs et les ressources. Ils conduisent à des fichiers de test beaucoup plus courts et plus faciles à lire. Par conséquent, tous vos fichiers Python doivent avoir au moins un fichier de test correspondant (pour chaque foo.py , il doit avoir foo_test.py ). Pour des tests plus élaborés, tels que des tests d'intégration qui nécessitent des configurations différentes, il est bon d'ajouter plus de fichiers de test.

Vitesse et temps de marche

Le sharding doit être utilisé le moins possible

Au lieu de partitionner, veuillez considérer:

  • Rendre vos tests plus petits
  • Si ce qui précède n'est pas possible, divisez les tests

Le partage permet de réduire la latence globale d'un test, mais il est possible d'obtenir la même chose en divisant les tests en cibles plus petites. Le fractionnement des tests nous donne un niveau de contrôle plus fin sur chaque test, minimisant les exécutions de pré-soumission inutiles et réduisant la perte de couverture d'un buildcop désactivant une cible entière en raison d'un cas de test qui se comporte mal. De plus, le partitionnement entraîne des coûts cachés qui ne sont pas si évidents, tels que l'exécution de tout le code d'initialisation de test pour toutes les partitions. Ce problème nous a été signalé par les équipes infra en tant que source qui crée une charge supplémentaire.

Des tests plus petits sont meilleurs

Plus vos tests seront exécutés rapidement, plus les gens seront susceptibles d'exécuter vos tests. Une seconde supplémentaire pour votre test peut s'accumuler en heures de temps supplémentaire passé à exécuter votre test par les développeurs et notre infrastructure. Essayez de faire fonctionner vos tests en moins de 30 secondes (en mode non opt!), Et réduisez-les. Ne marquez vos tests comme moyens qu'en dernier recours. L'infra n'exécute aucun test de grande envergure en tant que pré-soumission ou post-soumission! Par conséquent, n'écrivez un test volumineux que si vous souhaitez organiser son exécution. Quelques conseils pour accélérer les tests:

  • Exécutez moins d'itérations d'entraînement dans votre test
  • Envisagez d'utiliser l'injection de dépendances pour remplacer les dépendances lourdes du système testé par de simples faux.
  • Envisagez d'utiliser des données d'entrée plus petites dans les tests unitaires
  • Si rien d'autre ne fonctionne, essayez de diviser votre fichier de test.

Les temps de test doivent viser la moitié du délai d'expiration de la taille du test pour éviter les flocons

Avec les cibles de test bazel , les petits tests ont des délais d'attente d'une minute. Les délais de test moyens sont de 5 minutes. Les tests volumineux ne sont tout simplement pas exécutés par le test TensorFlow ci-dessous. Cependant, de nombreux tests ne sont pas déterministes quant au temps qu'ils prennent. Pour diverses raisons, vos tests peuvent prendre plus de temps de temps en temps. Et, si vous marquez un test qui s'exécute pendant 50 secondes en moyenne comme petit, votre test échouera s'il est programmé sur une machine avec un ancien processeur. Par conséquent, visez une durée moyenne de 30 secondes pour les petits tests. Visez 2 minutes 30 secondes de temps de fonctionnement moyen pour les tests moyens.

Réduisez le nombre d'échantillons et augmentez les tolérances pour la formation

Les tests à exécution lente dissuadent les contributeurs. L'exécution de l'entraînement aux tests peut être très lente. Préférez des tolérances plus élevées pour pouvoir utiliser moins d'échantillons dans vos tests afin de maintenir vos tests suffisamment rapides (2,5 minutes max).

Élimine le non-déterminisme et les flocons

Rédiger des tests déterministes

Les tests unitaires doivent toujours être déterministes. Tous les tests exécutés sur TAP et guitare doivent se dérouler de la même manière à chaque fois, si aucun changement de code ne les affecte. Pour garantir cela, voici quelques points à considérer.

Toujours semer toute source de stochasticité

Tout générateur de nombres aléatoires ou toute autre source de stochasticité peut provoquer des flocons. Par conséquent, chacun de ceux-ci doit être ensemencé. En plus de rendre les tests moins floconneux, cela rend tous les tests reproductibles. Différentes façons de définir certaines graines que vous devrez peut-être définir dans les tests TF sont:

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

Évitez d'utiliser le sleep dans les tests multithreads

L'utilisation de sleep fonction de sleep dans les tests peut être une cause majeure de desquamation. Surtout lors de l'utilisation de plusieurs threads, l'utilisation de sleep pour attendre un autre thread ne sera jamais déterministe. Cela est dû au fait que le système ne peut garantir aucun ordre d'exécution des différents threads ou processus. Par conséquent, préférez les constructions de synchronisation déterministes telles que les mutex.

Vérifiez si le test est floconneux

Les flocons font perdre de nombreuses heures aux buildcops et aux développeurs. Ils sont difficiles à détecter et à déboguer. Même s'il existe des systèmes automatisés pour détecter les flocons, ils doivent accumuler des centaines de tests avant de pouvoir dénicher avec précision les tests. Même lorsqu'ils détectent, ils refusent la liste de vos tests et la couverture des tests est perdue. Par conséquent, les auteurs de tests doivent vérifier si leurs tests sont irréguliers lors de l'écriture des tests. Cela peut être facilement fait en exécutant votre test avec l'indicateur: --runs_per_test=1000

Utiliser TensorFlowTestCase

TensorFlowTestCase prend les précautions nécessaires telles que l'ensemencement de tous les générateurs de nombres aléatoires utilisés pour réduire autant que possible la formation de flocons. Au fur et à mesure que nous découvrons et corrigeons davantage de sources de flakiness, celles-ci seront toutes ajoutées à TensorFlowTestCase. Par conséquent, vous devez utiliser TensorFlowTestCase lors de l'écriture de tests pour tensorflow. TensorFlowTestCase est défini ici: tensorflow/python/framework/test_util.py

Rédiger des tests hermétiques

Les tests hermétiques ne nécessitent aucune ressource extérieure. Ils sont emballés avec tout ce dont ils ont besoin et ils ne font que démarrer les faux services dont ils pourraient avoir besoin. Tous les services autres que vos tests sont des sources de non-déterminisme. Même avec une disponibilité de 99% des autres services, le réseau peut s'effondrer, la réponse RPC peut être retardée et vous pourriez vous retrouver avec un message d'erreur inexplicable. Les services externes peuvent être, mais sans s'y limiter, GCS, S3 ou tout autre site Web.