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:
- Permitir 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 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 moderna de arquivo 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
, 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