Dicas comuns de implementação

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

Legado SplitGenerator 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 autocontido em uma pasta).

  • Conjuntos de dados antigos (ruins): <category>/<ds_name>.py
  • Novos conjuntos de dados (bom): <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 estava distribuindo os arquivos do conjunto de dados em muitos lugares. Estava dificultando a implementação de conjuntos de dados fora do repositório TFDS. Para consistência, a nova estrutura deve ser usada em todos os lugares agora.

As listas de descrição devem ser formatadas como remarcações

O str DatasetInfo.description é formatado como markdown. As listas de descontos 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 nomes de 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 : Etiquetas legíveis por humanos são usadas em muitos lugares:

  • Permite render str diretamente em _generate_examples : yield {'label': 'dog'}
  • Exposto nos usuários como info.features['label'].names (método de conversão .str2int('dog') ,... também disponível)
  • Usado nos utilitários de visualização tfds.show_examples , tfds.as_dataframe

Esqueci o formato da imagem

Ao usar tfds.features.Image , tfds.features.Video , se as imagens tiverem forma estática, elas devem ser especificadas explicitamente:

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

Justificativa : Ele permite a inferência de forma estática (por exemplo ds.element_spec['image'].shape ), que é necessária para o agrupamento (o agrupamento de imagens de formato desconhecido exigiria o redimensionamento delas 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 corretos semanticamente, 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 chamadas 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 : O uso de importações preguiçosas no escopo global importaria o módulo para todos os usuários do tfds, anulando o objetivo das importações preguiçosas.

Calculando dinamicamente as 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 : o 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 desejam:

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 moderna orientada a objetos 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 , deve ser uma função

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

Justificativa : Deixa explícito ao leitor que a função não possui 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 preguiçosas em Python

Nós importamos preguiçosamente grandes módulos como o TensorFlow. As importações preguiçosas adiam a importação real do módulo para o primeiro uso do módulo. Portanto, os usuários que não precisam desse grande módulo nunca o importarão.

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 realmente só importará o módulo quando um atributo for acessado ( __getattr__ ).

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

from tensorflow_datasets.core.utils.lazy_imports_utils import lazy_imports

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