Siga este guia para criar um novo conjunto de dados (no TFDS ou em seu próprio repositório).
Verifique nossa lista de conjuntos de dados para ver se o conjunto de dados desejado já está presente.
TL;DR
A maneira mais fácil de escrever um novo conjunto de dados é usar a CLI do TFDS :
cd path/to/my/project/datasets/
tfds new my_dataset # Create `my_dataset/my_dataset.py` template files
# [...] Manually modify `my_dataset/my_dataset.py` to implement your dataset.
cd my_dataset/
tfds build # Download and prepare the dataset to `~/tensorflow_datasets/`
Para usar o novo conjunto de dados com tfds.load('my_dataset')
:
-
tfds.load
detectará e carregará automaticamente o conjunto de dados gerado em~/tensorflow_datasets/my_dataset/
(por exemplo, portfds build
). - Como alternativa, você pode
import my.project.datasets.my_dataset
para registrar seu conjunto de dados:
import my.project.datasets.my_dataset # Register `my_dataset`
ds = tfds.load('my_dataset') # `my_dataset` registered
Visão geral
Os conjuntos de dados são distribuídos em todos os tipos de formatos e em todos os tipos de lugares, e nem sempre são armazenados em um formato pronto para alimentar um pipeline de aprendizado de máquina. Digite TFDS.
O TFDS processa esses conjuntos de dados em um formato padrão (dados externos -> arquivos serializados), que podem ser carregados como pipeline de aprendizado de máquina (arquivos serializados -> tf.data.Dataset
). A serialização é feita apenas uma vez. O acesso subsequente lerá diretamente desses arquivos pré-processados.
A maior parte do pré-processamento é feito automaticamente. Cada conjunto de dados implementa uma subclasse de tfds.core.DatasetBuilder
, que especifica:
- De onde vêm os dados (ou seja, seus URLs);
- Qual é a aparência do conjunto de dados (ou seja, seus recursos);
- Como os dados devem ser divididos (por exemplo,
TRAIN
eTEST
); - e os exemplos individuais no conjunto de dados.
Escreva seu conjunto de dados
Modelo padrão: tfds new
Use a CLI do TFDS para gerar os arquivos python de modelo necessários.
cd path/to/project/datasets/ # Or use `--dir=path/to/project/datasets/` below
tfds new my_dataset
Este comando irá gerar uma nova pasta my_dataset/
com a seguinte estrutura:
my_dataset/
__init__.py
my_dataset.py # Dataset definition
my_dataset_test.py # (optional) Test
dummy_data/ # (optional) Fake data (used for testing)
checksum.tsv # (optional) URL checksums (see `checksums` section).
Procure por TODO(my_dataset)
aqui e modifique de acordo.
Exemplo de conjunto de dados
Todos os conjuntos de dados são implementados como tfds.core.GeneratorBasedBuilder
, uma subclasse de tfds.core.DatasetBuilder
que cuida da maioria dos clichês. Ele suporta:
- Conjuntos de dados pequenos/médios que podem ser gerados em uma única máquina (este tutorial).
- Conjuntos de dados muito grandes que exigem geração distribuída (usando Apache Beam , consulte nosso guia de conjunto de dados enorme )
Aqui está um exemplo mínimo de classe de conjunto de dados:
class MyDataset(tfds.core.GeneratorBasedBuilder):
"""DatasetBuilder for my_dataset dataset."""
VERSION = tfds.core.Version('1.0.0')
RELEASE_NOTES = {
'1.0.0': 'Initial release.',
}
def _info(self) -> tfds.core.DatasetInfo:
"""Dataset metadata (homepage, citation,...)."""
return tfds.core.DatasetInfo(
builder=self,
features=tfds.features.FeaturesDict({
'image': tfds.features.Image(shape=(256, 256, 3)),
'label': tfds.features.ClassLabel(
names=['no', 'yes'],
doc='Whether this is a picture of a cat'),
}),
)
def _split_generators(self, dl_manager: tfds.download.DownloadManager):
"""Download the data and define splits."""
extracted_path = dl_manager.download_and_extract('http://data.org/data.zip')
# dl_manager returns pathlib-like objects with `path.read_text()`,
# `path.iterdir()`,...
return {
'train': self._generate_examples(path=extracted_path / 'train_images'),
'test': self._generate_examples(path=extracted_path / 'test_images'),
}
def _generate_examples(self, path) -> Iterator[Tuple[Key, Example]]:
"""Generator of examples for each split."""
for img_path in path.glob('*.jpeg'):
# Yields (key, example)
yield img_path.name, {
'image': img_path,
'label': 'yes' if img_path.name.startswith('yes_') else 'no',
}
Vamos ver em detalhes os 3 métodos abstratos para sobrescrever.
_info
: metadados do conjunto de dados
_info
retorna o tfds.core.DatasetInfo
contendo os metadados do conjunto de dados.
def _info(self):
return tfds.core.DatasetInfo(
builder=self,
# Description and homepage used for documentation
description="""
Markdown description of the dataset. The text will be automatically
stripped and dedent.
""",
homepage='https://dataset-homepage.org',
features=tfds.features.FeaturesDict({
'image_description': tfds.features.Text(),
'image': tfds.features.Image(),
# Here, 'label' can be 0-4.
'label': tfds.features.ClassLabel(num_classes=5),
}),
# If there's a common `(input, target)` tuple from the features,
# specify them here. They'll be used if as_supervised=True in
# builder.as_dataset.
supervised_keys=('image', 'label'),
# Specify whether to disable shuffling on the examples. Set to False by default.
disable_shuffling=False,
# Bibtex citation for the dataset
citation=r"""
@article{my-awesome-dataset-2020,
author = {Smith, John},}
""",
)
A maioria dos campos deve ser autoexplicativa. Algumas precisões:
-
features
: Isso especifica a estrutura do conjunto de dados, forma,... Suporta tipos de dados complexos (áudio, vídeo, sequências aninhadas,...). Consulte os recursos disponíveis ou o guia do conector de recursos para obter mais informações. -
disable_shuffling
: Consulte a seção Manter ordem do conjunto de dados. -
citation
: Para encontrar a citaçãoBibText
:- Pesquise no site do conjunto de dados para obter instruções de citação (use isso no formato BibTex).
- Para artigos arXiv : encontre o artigo e clique no link
BibText
no lado direito. - Encontre o artigo no Google Scholar e clique na aspa dupla abaixo do título e no pop-up, clique em
BibTeX
. - Se não houver nenhum artigo associado (por exemplo, há apenas um site), você pode usar o BibTeX Online Editor para criar uma entrada BibTeX personalizada (o menu suspenso tem um tipo de entrada
Online
).
Manter a ordem do conjunto de dados
Por padrão, os registros dos conjuntos de dados são embaralhados quando armazenados para tornar a distribuição das classes mais uniforme em todo o conjunto de dados, pois muitas vezes os registros pertencentes à mesma classe são contíguos. Para especificar que o conjunto de dados deve ser classificado pela chave gerada fornecida por _generate_examples
, o campo disable_shuffling
deve ser definido como True
. Por padrão, é definido como False
.
def _info(self):
return tfds.core.DatasetInfo(
# [...]
disable_shuffling=True,
# [...]
)
Lembre-se de que desabilitar o embaralhamento tem um impacto no desempenho, pois os fragmentos não podem mais ser lidos em paralelo.
_split_generators
: baixa e divide dados
Download e extração de dados de origem
A maioria dos conjuntos de dados precisa baixar dados da web. Isso é feito usando o argumento de entrada tfds.download.DownloadManager
de _split_generators
. dl_manager
tem os seguintes métodos:
-
download
: suportahttp(s)://
,ftp(s)://
-
extract
: atualmente suporta arquivos.zip
,.gz
e.tar
. -
download_and_extract
: O mesmo quedl_manager.extract(dl_manager.download(urls))
Todos esses métodos retornam tfds.core.Path
(aliases para epath.Path
), que são objetos do tipo pathlib.Path .
Esses métodos suportam estrutura aninhada arbitrária ( list
, dict
), como:
extracted_paths = dl_manager.download_and_extract({
'foo': 'https://example.com/foo.zip',
'bar': 'https://example.com/bar.zip',
})
# This returns:
assert extracted_paths == {
'foo': Path('/path/to/extracted_foo/'),
'bar': Path('/path/extracted_bar/'),
}
Download e extração manual
Alguns dados não podem ser baixados automaticamente (por exemplo, exigem um login), neste caso, o usuário baixará manualmente os dados de origem e os colocará em manual_dir/
(o padrão é ~/tensorflow_datasets/downloads/manual/
).
Os arquivos podem ser acessados através dl_manager.manual_dir
:
class MyDataset(tfds.core.GeneratorBasedBuilder):
MANUAL_DOWNLOAD_INSTRUCTIONS = """
Register into https://example.org/login to get the data. Place the `data.zip`
file in the `manual_dir/`.
"""
def _split_generators(self, dl_manager):
# data_path is a pathlib-like `Path('<manual_dir>/data.zip')`
archive_path = dl_manager.manual_dir / 'data.zip'
# Extract the manually downloaded `data.zip`
extracted_path = dl_manager.extract(archive_path)
...
A localização manual_dir
pode ser personalizada com tfds build --manual_dir=
ou usando tfds.download.DownloadConfig
.
Ler arquivo diretamente
dl_manager.iter_archive
lê os arquivos sequencialmente sem extraí-los. Isso pode economizar espaço de armazenamento e melhorar o desempenho em alguns sistemas de arquivos.
for filename, fobj in dl_manager.iter_archive('path/to/archive.zip'):
...
fobj
tem os mesmos métodos que with open('rb') as fobj:
(por exemplo fobj.read()
)
Especificando divisões do conjunto de dados
Se o conjunto de dados vier com divisões pré-definidas (por exemplo, MNIST
tem divisões de train
e test
), mantenha-as. Caso contrário, especifique apenas uma única divisão tfds.Split.TRAIN
. Os usuários podem criar dinamicamente seus próprios subsplits com a API de subsplit (por exemplo split='train[80%:]'
).
def _split_generators(self, dl_manager):
# Download source data
extracted_path = dl_manager.download_and_extract(...)
# Specify the splits
return {
'train': self._generate_examples(
images_path=extracted_path / 'train_imgs',
label_path=extracted_path / 'train_labels.csv',
),
'test': self._generate_examples(
images_path=extracted_path / 'test_imgs',
label_path=extracted_path / 'test_labels.csv',
),
}
_generate_examples
: gerador de exemplo
_generate_examples
gera os exemplos para cada divisão dos dados de origem.
Esse método normalmente lerá artefatos do conjunto de dados de origem (por exemplo, um arquivo CSV) e produzirá tuplas (key, feature_dict)
:
-
key
: Identificador de exemplo. Usado para embaralhar deterministicamente os exemplos usandohash(key)
ou para classificar por chave quando o embaralhamento está desabilitado (consulte a seção Manter a ordem do conjunto de dados ). Deveria estar:- unique : Se dois exemplos usarem a mesma chave, uma exceção será gerada.
- deterministic : Não deve depender de
download_dir
, ordemos.path.listdir
,... Gerar os dados duas vezes deve gerar a mesma chave. - comparável : Se o embaralhamento estiver desabilitado, a chave será usada para classificar o conjunto de dados.
-
feature_dict
: Umdict
contendo os valores de exemplo.- A estrutura deve corresponder à estrutura
features=
definida emtfds.core.DatasetInfo
. - Tipos de dados complexos (imagem, vídeo, áudio,...) serão codificados automaticamente.
- Cada recurso geralmente aceita vários tipos de entrada (por exemplo, video accept
/path/to/vid.mp4
,np.array(shape=(l, h, w, c))
,List[paths]
,List[np.array(shape=(h, w, c)]
,List[img_bytes]
,...) - Consulte o guia do conector de recursos para obter mais informações.
- A estrutura deve corresponder à estrutura
def _generate_examples(self, images_path, label_path):
# Read the input data out of the source files
with label_path.open() as f:
for row in csv.DictReader(f):
image_id = row['image_id']
# And yield (key, feature_dict)
yield image_id, {
'image_description': row['description'],
'image': images_path / f'{image_id}.jpeg',
'label': row['label'],
}
Acesso ao arquivo e tf.io.gfile
Para oferecer suporte a sistemas de armazenamento em nuvem, evite o uso das operações de E/S integradas do Python.
Em vez disso, o dl_manager
retorna objetos semelhantes a pathlib diretamente compatíveis com o armazenamento do Google Cloud:
path = dl_manager.download_and_extract('http://some-website/my_data.zip')
json_path = path / 'data/file.json'
json.loads(json_path.read_text())
Como alternativa, use a API tf.io.gfile
em vez da built-in para operações de arquivo:
-
open
->tf.io.gfile.GFile
-
os.rename
->tf.io.gfile.rename
- ...
Pathlib deve ser preferido a tf.io.gfile
(veja .
Dependências extras
Alguns conjuntos de dados exigem dependências adicionais do Python apenas durante a geração. Por exemplo, o conjunto de dados SVHN usa scipy
para carregar alguns dados.
Se você estiver adicionando um conjunto de dados ao repositório TFDS, use tfds.core.lazy_imports
para manter o pacote tensorflow-datasets
pequeno. Os usuários instalarão dependências adicionais somente conforme necessário.
Para usar lazy_imports
:
- Adicione uma entrada para seu conjunto de dados em
DATASET_EXTRAS
emsetup.py
. Isso faz com que os usuários possam fazer, por exemplo,pip install 'tensorflow-datasets[svhn]'
para instalar as dependências extras. - Adicione uma entrada para sua importação ao
LazyImporter
e aoLazyImportsTest
. - Use
tfds.core.lazy_imports
para acessar a dependência (por exemplo,tfds.core.lazy_imports.scipy
) em seuDatasetBuilder
.
Dados corrompidos
Alguns conjuntos de dados não são perfeitamente limpos e contêm alguns dados corrompidos (por exemplo, as imagens estão em arquivos JPEG, mas alguns são JPEG inválidos). Esses exemplos devem ser ignorados, mas deixe uma nota na descrição do conjunto de dados quantos exemplos foram descartados e por quê.
Configuração/variantes do conjunto de dados (tfds.core.BuilderConfig)
Alguns conjuntos de dados podem ter várias variantes ou opções de como os dados são pré-processados e gravados em disco. Por exemplo, cycle_gan tem uma configuração por pares de objetos ( cycle_gan/horse2zebra
, cycle_gan/monet2photo
,...).
Isso é feito através tfds.core.BuilderConfig
s:
Defina seu objeto de configuração como uma subclasse de
tfds.core.BuilderConfig
. Por exemplo,MyDatasetConfig
.@dataclasses.dataclass class MyDatasetConfig(tfds.core.BuilderConfig): img_size: Tuple[int, int] = (0, 0)
Defina o membro da classe
BUILDER_CONFIGS = []
emMyDataset
que listaMyDatasetConfig
s que o conjunto de dados expõe.class MyDataset(tfds.core.GeneratorBasedBuilder): VERSION = tfds.core.Version('1.0.0') # pytype: disable=wrong-keyword-args BUILDER_CONFIGS = [ # `name` (and optionally `description`) are required for each config MyDatasetConfig(name='small', description='Small ...', img_size=(8, 8)), MyDatasetConfig(name='big', description='Big ...', img_size=(32, 32)), ] # pytype: enable=wrong-keyword-args
Use
self.builder_config
emMyDataset
para configurar a geração de dados (por exemploshape=self.builder_config.img_size
). Isso pode incluir definir valores diferentes em_info()
ou alterar o acesso aos dados de download.
Notas:
- Cada configuração tem um nome exclusivo. O nome totalmente qualificado de uma configuração é
dataset_name/config_name
(por exemplo,coco/2017
). - Se não for especificado, a primeira configuração em
BUILDER_CONFIGS
será usada (por exemplotfds.load('c4')
padrão parac4/en
)
Consulte anli
para obter um exemplo de um conjunto de dados que usa BuilderConfig
s.
Versão
Versão pode se referir a dois significados diferentes:
- A versão de dados original "externa": por exemplo, COCO v2019, v2017,...
- A versão "interna" do código TFDS: por exemplo, renomeie um recurso em
tfds.features.FeaturesDict
, corrija um bug em_generate_examples
Para atualizar um conjunto de dados:
- Para atualização de dados "externos": Vários usuários podem querer acessar um ano/versão específico simultaneamente. Isso é feito usando um
tfds.core.BuilderConfig
por versão (por exemplo,coco/2017
,coco/2019
) ou uma classe por versão (por exemplo,Voc2007
,Voc2012
). - Para atualização de código "interno": Os usuários baixam apenas a versão mais recente. Qualquer atualização de código deve aumentar o atributo da classe
VERSION
(por exemplo, de1.0.0
paraVERSION = tfds.core.Version('2.0.0')
) seguindo o versionamento semântico .
Adicionar uma importação para registro
Não esqueça de importar o módulo dataset para seu projeto __init__
para ser registrado automaticamente em tfds.load
, tfds.builder
.
import my_project.datasets.my_dataset # Register MyDataset
ds = tfds.load('my_dataset') # MyDataset available
Por exemplo, se você estiver contribuindo para tensorflow/datasets
, adicione a importação do módulo ao __init__.py
de seu subdiretório (por exemplo, image/__init__.py
.
Verifique se há pegadinhas de implementação comuns
Por favor, verifique as pegadinhas de implementação comuns .
Teste seu conjunto de dados
Baixe e prepare: tfds build
Para gerar o conjunto de dados, execute tfds build
do diretório my_dataset/
:
cd path/to/datasets/my_dataset/
tfds build --register_checksums
Alguns sinalizadores úteis para desenvolvimento:
-
--pdb
: Entra no modo de depuração se uma exceção for levantada. -
--overwrite
: Excluir arquivos existentes se o conjunto de dados já foi gerado. -
--max_examples_per_split
: gera apenas os primeiros exemplos X (padrão para 1), em vez do conjunto de dados completo. -
--register_checksums
: Grava as somas de verificação dos URLs baixados. Só deve ser usado durante o desenvolvimento.
Consulte a documentação da CLI para obter a lista completa de sinalizadores.
Somas de verificação
Recomenda-se registrar as somas de verificação de seus conjuntos de dados para garantir o determinismo, ajudar na documentação,... Isso é feito gerando o conjunto de dados com o --register_checksums
(consulte a seção anterior).
Se você estiver liberando seus conjuntos de dados através do PyPI, não se esqueça de exportar os arquivos checksums.tsv
(por exemplo, no package_data
do seu setup.py
).
Teste de unidade seu conjunto de dados
tfds.testing.DatasetBuilderTestCase
é um TestCase
básico para exercitar totalmente um conjunto de dados. Ele usa "dados fictícios" como dados de teste que imitam a estrutura do conjunto de dados de origem.
- Os dados de teste devem ser colocados no
my_dataset/dummy_data/
e devem imitar os artefatos do conjunto de dados de origem conforme baixados e extraídos. Ele pode ser criado manualmente ou automaticamente com um script ( script de exemplo ). - Certifique-se de usar dados diferentes nas divisões de dados de teste, pois o teste falhará se as divisões do conjunto de dados se sobrepuserem.
- Os dados de teste não devem conter nenhum material protegido por direitos autorais . Em caso de dúvida, não crie os dados usando material do conjunto de dados original.
import tensorflow_datasets as tfds
from . import my_dataset
class MyDatasetTest(tfds.testing.DatasetBuilderTestCase):
"""Tests for my_dataset dataset."""
DATASET_CLASS = my_dataset.MyDataset
SPLITS = {
'train': 3, # Number of fake train example
'test': 1, # Number of fake test example
}
# If you are calling `download/download_and_extract` with a dict, like:
# dl_manager.download({'some_key': 'http://a.org/out.txt', ...})
# then the tests needs to provide the fake output paths relative to the
# fake data directory
DL_EXTRACT_RESULT = {
'name1': 'path/to/file1', # Relative to dummy_data/my_dataset dir.
'name2': 'file2',
}
if __name__ == '__main__':
tfds.testing.test_main()
Execute o comando a seguir para testar o conjunto de dados.
python my_dataset_test.py
Envie-nos comentários
Estamos continuamente tentando melhorar o fluxo de trabalho de criação do conjunto de dados, mas só podemos fazê-lo se estivermos cientes dos problemas. Quais problemas, erros você encontrou ao criar o conjunto de dados? Havia uma parte que estava confusa, clichê ou não estava funcionando na primeira vez? Por favor, compartilhe seus comentários no github .