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