Dicas comuns de implementação

Esta página descreve o problema comum de implementação ao implementar um novo conjunto de dados.

SplitGenerator legado deve ser evitado

A antiga API tfds.core.SplitGenerator está obsoleta.

def _split_generator(...):
  return [
      tfds.core.SplitGenerator(name='train', gen_kwargs={'path': train_path}),
      tfds.core.SplitGenerator(name='test', gen_kwargs={'path': test_path}),
  ]

Deve ser substituído por:

def _split_generator(...):
  return {
      'train': self._generate_examples(path=train_path),
      'test': self._generate_examples(path=test_path),
  }

Justificativa : A nova API é menos detalhada e mais explícita. A API antiga será removida na versão futura.

Novos conjuntos de dados devem ser independentes em uma pasta

Ao adicionar um conjunto de dados dentro do repositório tensorflow_datasets/ , certifique-se de seguir a estrutura do conjunto de dados como pasta (todas as somas de verificação, dados fictícios, código de implementação contido em uma pasta).

  • Conjuntos de dados antigos (ruins): <category>/<ds_name>.py
  • Novos conjuntos de dados (bons): <category>/<ds_name>/<ds_name>.py

Use a CLI do TFDS ( tfds new ou gtfds new para googlers) para gerar o modelo.

Justificativa : A estrutura antiga exigia caminhos absolutos para somas de verificação, dados falsos e distribuía os arquivos do conjunto de dados em muitos lugares. Estava dificultando a implementação de conjuntos de dados fora do repositório TFDS. Para manter a consistência, a nova estrutura deve ser usada em todos os lugares agora.

As listas de descrição devem ser formatadas como markdown

A str DatasetInfo.description é formatada como markdown. As listas Markdown requerem uma linha vazia antes do primeiro item:

_DESCRIPTION = """
Some text.
                      # << Empty line here !!!
1. Item 1
2. Item 1
3. Item 1
                      # << Empty line here !!!
Some other text.
"""

Justificativa : Descrição mal formatada cria artefatos visuais em nossa documentação de catálogo. Sem as linhas vazias, o texto acima seria renderizado como:

Algum texto. 1. Item 1 2. Item 1 3. Item 1 Algum outro texto

Esqueci os nomes ClassLabel

Ao usar tfds.features.ClassLabel , tente fornecer os rótulos legíveis por humanos str com names= ou names_file= (em vez de num_classes=10 ).

features = {
    'label': tfds.features.ClassLabel(names=['dog', 'cat', ...]),
}

Justificativa : Rótulos legíveis por humanos são usados ​​em muitos lugares:

Esqueci o formato da imagem

Ao usar tfds.features.Image , tfds.features.Video , se as imagens tiverem formato estático, elas deverão ser especificadas explicitamente:

features = {
    'image': tfds.features.Image(shape=(256, 256, 3)),
}

Justificativa : permite inferência de forma estática (por exemplo, ds.element_spec['image'].shape ), que é necessária para lote (o lote de imagens de formato desconhecido exigiria redimensioná-las primeiro).

Prefira um tipo mais específico em vez de tfds.features.Tensor

Quando possível, prefira os tipos mais específicos tfds.features.ClassLabel , tfds.features.BBoxFeatures ,... em vez do genérico tfds.features.Tensor .

Justificativa : Além de serem mais semanticamente corretos, recursos específicos fornecem metadados adicionais aos usuários e são detectados por ferramentas.

Importações preguiçosas no espaço global

As importações preguiçosas não devem ser retiradas do espaço global. Por exemplo, o seguinte está errado:

tfds.lazy_imports.apache_beam # << Error: Import beam in the global scope

def f() -> beam.Map:
  ...

Justificativa : Usar importações lentas no escopo global importaria o módulo para todos os usuários do tfds, anulando o propósito das importações lentas.

Computação dinâmica de divisões de treinamento/teste

Se o conjunto de dados não fornecer divisões oficiais, o TFDS também não deveria. O seguinte deve ser evitado:

_TRAIN_TEST_RATIO = 0.7

def _split_generator():
  ids = list(range(num_examples))
  np.random.RandomState(seed).shuffle(ids)

  # Split train/test
  train_ids = ids[_TRAIN_TEST_RATIO * num_examples:]
  test_ids = ids[:_TRAIN_TEST_RATIO * num_examples]
  return {
      'train': self._generate_examples(train_ids),
      'test': self._generate_examples(test_ids),
  }

Justificativa : TFDS tenta fornecer conjuntos de dados tão próximos quanto os dados originais. A API de subdivisão deve ser usada para permitir que os usuários criem dinamicamente as subdivisões que desejarem:

ds_train, ds_test = tfds.load(..., split=['train[:80%]', 'train[80%:]'])

Guia de estilo Python

Prefira usar a API pathlib

Em vez da API tf.io.gfile , é preferível usar a API pathlib . Todos os métodos dl_manager retornam objetos do tipo pathlib compatíveis com GCS, S3,...

path = dl_manager.download_and_extract('http://some-website/my_data.zip')

json_path = path / 'data/file.json'

json.loads(json_path.read_text())

Justificativa : a API pathlib é uma API de arquivo orientada a objetos moderna que remove clichês. Usar .read_text() / .read_bytes() também garante que os arquivos sejam fechados corretamente.

Se o método não estiver usando self , deverá ser uma função

Se um método de classe não estiver usando self , deverá ser uma função simples (definida fora da classe).

Justificativa : Deixa explícito ao leitor que a função não tem efeitos colaterais, nem entrada/saída oculta:

x = f(y)  # Clear inputs/outputs

x = self.f(y)  # Does f depend on additional hidden variables ? Is it stateful ?

Importações lentas em Python

Importamos preguiçosamente grandes módulos como o TensorFlow. As importações lentas adiam a importação real do módulo para o primeiro uso do módulo. Portanto, os usuários que não precisam deste grande módulo nunca irão importá-lo. Usamos etils.epy.lazy_imports .

from tensorflow_datasets.core.utils.lazy_imports_utils import tensorflow as tf
# After this statement, TensorFlow is not imported yet

...

features = tfds.features.Image(dtype=tf.uint8)
# After using it (`tf.uint8`), TensorFlow is now imported

Nos bastidores, a classe LazyModule atua como uma fábrica, que só importará o módulo quando um atributo for acessado ( __getattr__ ).

Você também pode usá-lo convenientemente com um gerenciador de contexto:

from etils import epy

with epy.lazy_imports(error_callback=..., success_callback=...):
  import some_big_module