Pegadinhas de implementação comuns

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

Legado SplitGenerator deve ser evitado

O velho tfds.core.SplitGenerator API 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 detalhado e mais explícita. A API antiga será removida na versão futura.

Novos conjuntos de dados devem ser autocontidos em uma pasta

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

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

Use o TFDS CLI ( tfds new , ou gtfds new para Googlers) para gerar o modelo.

Justificativa: Estrutura velha necessário caminhos absolutos para checksums, 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.

Listas de descrição devem ser formatadas como markdown

O DatasetInfo.description str é formatado como remarcação. As listas de remarcação exigem 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 formatadas criar artefatos visuais em nossa documentação 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 do ClassLabel

Ao usar tfds.features.ClassLabel , tentar fornecer as etiquetas legíveis str com names= ou names_file= (em vez de num_classes=10 ).

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

Justificativa: etiquetas legíveis humanos são usados em muitos lugares:

  • Permitir para produzir str diretamente no _generate_examples : yield {'label': 'dog'}
  • Expostas nos usuários como info.features['label'].names (método de conversão .str2int('dog') , ... também disponível)
  • Usado na visualização utils tfds.show_examples , tfds.as_dataframe

Esqueci o formato da imagem

Ao usar tfds.features.Image , tfds.features.Video , se as imagens têm forma estática, eles devem ser expliclty especificado:

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

Justificativa: É permitir inferência forma estática (por exemplo ds.element_spec['image'].shape ), Que é necessário para lotes (de porção imagens de forma desconhecida exigiria redimensionando-as em primeiro lugar).

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

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

Justificativa: Além de ser mais semanticamente correta, recursos específicos fornece metadados adicionais para os usuários e são detectados por ferramentas.

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

As importações lentas 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: Usando importações preguiçosos no escopo global iria importar o módulo para todos os usuários TFDS, derrotando o propósito de importações preguiçosos.

Computando dinamicamente divisões de trem / teste

Se o conjunto de dados não fornece divisões oficiais, o TFDS também não deve. 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 tentar fornecer conjuntos de dados tão perto como os dados originais. A API sub-split deve ser usada para permitir aos usuários criar dinamicamente os subsplits que eles querem:

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

Guia de estilo Python

Prefira usar a API pathlib

Em vez do tf.io.gfile API, é preferível usar a API pathlib . Todos os dl_manager métodos retornos pathlib-como objetos compatíveis com o 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: pathlib API é uma moderna orientada objeto de arquivo API que remove clichê. Usando .read_text() / .read_bytes() também garantir que os arquivos são fechados corretamente.

Se o método não está usando self , ele deve ser uma função

Se um método de classe não está a utilizar self , ele deve ser uma função simples (definida fora da classe).

Justificativa: Isso torna mais explícito ao leitor que a função não tem efeitos colaterais, nem de entrada escondida / saída:

x = f(y)  # Clear inputs/outputs

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