Agradecemos por acompanhar o Google I/O. Assista todas as sessões on demand.Assista on demand

Práticas recomendadas de teste do TensorFlow

Estas são as práticas recomendadas para testar o código no repositório do TensorFlow .

Antes de começar

Antes de contribuir com o código-fonte para um projeto do TensorFlow, analise o arquivo CONTRIBUTING.md no repositório GitHub do projeto. (Por exemplo, consulte o arquivo CONTRIBUTING.md para o repositório TensorFlow principal .) Todos os contribuidores de código devem assinar um Contrato de Licença de Contribuidor (CLA).

Princípios gerais

Depende apenas do que você usa em suas regras de CONSTRUÇÃO

TensorFlow é uma grande biblioteca e, dependendo do pacote completo, ao escrever um teste de unidade para seus submódulos tem sido uma prática comum. No entanto, isso desativa a análise baseada na dependência do bazel . Isso significa que os sistemas de integração contínua não podem eliminar de maneira inteligente os testes não relacionados para execuções de pré-envio / pós-envio. Se você depender apenas dos submódulos que está testando em seu arquivo BUILD , economizará tempo para todos os desenvolvedores do TensorFlow e muito poder de computação valioso.

No entanto, modificar sua dependência de construção para omitir os destinos TF completos traz algumas limitações para o que você pode importar em seu código Python. Você não poderá mais usar o import tensorflow as tf instrução import tensorflow as tf em seus testes de unidade. Mas isso é uma troca que vale a pena, pois evita que todos os desenvolvedores executem milhares de testes desnecessários.

Todo código deve ter testes de unidade

Para qualquer código que você escrever, você também deve escrever seus testes de unidade. Se você escrever um novo arquivo foo.py , você deve colocar seus testes de unidade em foo_test.py e enviá-lo dentro da mesma mudança. Almeje uma cobertura de teste incremental> 90% para todo o seu código.

Evite usar regras de teste de bazel nativas no TF

TF tem muitas sutilezas ao executar testes. Trabalhamos para ocultar todas essas complexidades em nossas macros de bazel. Para evitar ter que lidar com isso, use o seguinte em vez das regras de teste nativas. Observe que todos eles são definidos em tensorflow/tensorflow.bzl Para testes CC, use tf_cc_test , tf_gpu_cc_test , tf_gpu_only_cc_test . Para testes Python, use tf_py_test ou gpu_py_test . Se você precisar de algo realmente próximo à regra nativa py_test , use aquela definida em tensorflow.bzl. Você só precisa adicionar a seguinte linha no topo do arquivo BUILD: load(“tensorflow/tensorflow.bzl”, “py_test”)

Esteja ciente de onde o teste é executado

Quando você escreve um teste, nossa infra de teste pode cuidar da execução de seus testes na CPU, GPU e aceleradores se você os escrever de acordo. Temos testes automatizados que rodam em Linux, macos, windows, que possuem sistemas com ou sem GPUs. Você simplesmente precisa escolher uma das macros listadas acima e, em seguida, usar tags para limitar onde elas são executadas.

  • manual tag manual impedirá que seu teste seja executado em qualquer lugar. Isso inclui execuções manuais de teste que usam padrões como bazel test tensorflow/…

  • no_oss irá excluir o seu teste da execução na infraestrutura de teste oficial TF OSS.

  • no_mac tags no_mac ou no_windows podem ser usadas para excluir seu teste de suítes de teste de sistema operacional relevantes.

  • no_gpu tag no_gpu pode ser usada para impedir que seu teste seja executado em suítes de teste de GPU.

Verifique os testes executados em suítes de teste esperadas

TF tem algumas suítes de teste. Às vezes, eles podem ser confusos para configurar. Pode haver problemas diferentes que fazem com que seus testes sejam omitidos das compilações contínuas. Portanto, você deve verificar se seus testes estão sendo executados conforme o esperado. Para fazer isso:

  • Aguarde até que seus pré-envios em sua solicitação de pull (PR) sejam concluídos.
  • Role até o final do seu PR para ver as verificações de status.
  • Clique no link “Detalhes” no lado direito de qualquer cheque Kokoro.
  • Verifique a lista “Alvos” para encontrar seus alvos recém-adicionados.

Cada classe / unidade deve ter seu próprio arquivo de teste de unidade

Classes de teste separadas nos ajudam a isolar melhor as falhas e recursos. Eles tornam os arquivos de teste muito mais curtos e fáceis de ler. Portanto, todos os seus arquivos Python devem ter pelo menos um arquivo de teste correspondente (para cada foo.py , ele deve ter foo_test.py ). Para testes mais elaborados, como testes de integração que requerem configurações diferentes, é bom adicionar mais arquivos de teste.

Velocidade e tempos de execução

A fragmentação deve ser usada o mínimo possível

Em vez de fragmentar, considere:

  • Tornando seus testes menores
  • Se o acima não for possível, divida os testes

A fragmentação ajuda a reduzir a latência geral de um teste, mas o mesmo pode ser alcançado dividindo os testes em alvos menores. A divisão de testes nos dá um nível mais preciso de controle em cada teste, minimizando execuções de pré-envio desnecessárias e reduzindo a perda de cobertura de um buildcop desabilitando um alvo inteiro devido a um caso de teste com comportamento incorreto. Além disso, a fragmentação incorre em custos ocultos que não são tão óbvios, como a execução de todos os códigos de inicialização de teste para todos os fragmentos. Este problema foi escalado para nós por equipes de infra-estrutura como uma fonte que cria carga extra.

Testes menores são melhores

Quanto mais rápido seus testes forem executados, maior será a probabilidade de as pessoas executá-los. Um segundo extra para seu teste pode se acumular em horas extras gastas executando seu teste por desenvolvedores e nossa infraestrutura. Tente fazer seus testes rodarem em menos de 30 segundos (no modo não opt!), E diminua-os. Apenas marque seus testes como médios como último recurso. O infra não executa nenhum grande teste como pré-envio ou pós-envio! Portanto, escreva um teste grande apenas se for determinar onde ele será executado. Algumas dicas para tornar os testes mais rápidos:

  • Execute menos iterações de treinamento em seu teste
  • Considere o uso de injeção de dependência para substituir dependências pesadas do sistema em teste por falsificações simples.
  • Considere o uso de dados de entrada menores em testes de unidade
  • Se nada mais funcionar, tente dividir o arquivo de teste.

Os tempos de teste devem ter como objetivo a metade do tempo limite do tamanho do teste para evitar flocos

Com alvos de teste bazel , pequenos testes têm tempos limite de 1 minuto. O tempo limite médio de teste é de 5 minutos. Testes grandes simplesmente não são executados pelo infra de teste do TensorFlow. No entanto, muitos testes não são determinísticos quanto ao tempo que levam. Por vários motivos, seus testes podem levar mais tempo de vez em quando. E, se você marcar um teste que é executado por 50 segundos em média como pequeno, seu teste irá falhar se for programado em uma máquina com uma CPU antiga. Portanto, planeje um tempo médio de execução de 30 segundos para pequenos testes. Aponte para 2 minutos e 30 segundos de tempo médio de execução para testes médios.

Reduza o número de amostras e aumente as tolerâncias para o treinamento

Testes de execução lenta impedem os contribuidores. Executar o treinamento em testes pode ser muito lento. Prefira tolerâncias mais altas para poder usar menos amostras em seus testes para mantê-los suficientemente rápidos (máximo de 2,5 minutos).

Elimine o não determinismo e os flocos

Escreva testes determinísticos

Os testes de unidade devem ser sempre determinísticos. Todos os testes executados no TAP e na guitarra devem ser executados da mesma forma todas as vezes, se não houver nenhuma alteração no código que os afete. Para garantir isso, a seguir estão alguns pontos a serem considerados.

Sempre semeie qualquer fonte de estocasticidade

Qualquer gerador de número aleatório ou qualquer outra fonte de estocasticidade pode causar descamação. Portanto, cada um deles deve ser semeado. Além de tornar os testes menos fragmentados, isso torna todos os testes reproduzíveis. Diferentes maneiras de definir algumas sementes que você pode precisar definir em testes TF são:

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

Evite usar o sleep em testes multithread

Usar a função de sleep em testes pode ser uma das principais causas de descamação. Especialmente ao usar vários encadeamentos, o uso de sleep para esperar por outro encadeamento nunca será determinístico. Isso ocorre porque o sistema não é capaz de garantir qualquer ordem de execução de diferentes threads ou processos. Portanto, prefira construções de sincronização determinísticas, como mutexes.

Verifique se o teste está fragmentado

Flakes fazem com que buildcops e desenvolvedores percam muitas horas. Eles são difíceis de detectar e de depurar. Mesmo que existam sistemas automatizados para detectar descamação, eles precisam acumular centenas de execuções de teste antes de poderem denylist com precisão os testes. Mesmo quando detectam, eles denylist seus testes e a cobertura do teste é perdida. Portanto, os autores dos testes devem verificar se seus testes são instáveis ​​ao escrevê-los. Isso pode ser feito facilmente executando seu teste com a sinalização: --runs_per_test=1000

Use TensorFlowTestCase

O TensorFlowTestCase toma os cuidados necessários, como a propagação de todos os geradores de números aleatórios usados ​​para reduzir ao máximo a flacidez. Conforme descobrimos e corrigimos mais fontes de instabilidade, tudo isso será adicionado ao TensorFlowTestCase. Portanto, você deve usar TensorFlowTestCase ao escrever testes para tensorflow. TensorFlowTestCase é definido aqui: tensorflow/python/framework/test_util.py

Escreva testes herméticos

Os testes herméticos não precisam de recursos externos. Eles vêm com tudo de que precisam e apenas iniciam quaisquer serviços falsos de que possam precisar. Quaisquer serviços que não sejam seus testes são fontes de não determinismo. Mesmo com 99% de disponibilidade de outros serviços, a rede pode falhar, a resposta rpc pode ser atrasada e você pode acabar com uma mensagem de erro inexplicável. Os serviços externos podem ser, mas não limitados a, GCS, S3 ou qualquer site.