Gravando conjuntos de dados personalizados

Siga este guia para criar um novo conjunto de dados (em TFDS ou em seu próprio repositório).

Confira nossa lista de conjuntos de dados para ver se o conjunto de dados que você deseja já está presente.

TL; DR

A maneira mais fácil de escrever um novo conjunto de dados é usar o TFDS CLI :

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 irá detectar automaticamente e carregar o conjunto de dados gerados em ~/tensorflow_datasets/my_dataset/ (por exemplo, por tfds build ).
  • Alternativamente, você pode explicitamente import my.project.datasets.my_dataset para registrar o 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.

TFDS processar os conjuntos de dados em um formato padrão (dados externos -> arquivos serializados), que podem então ser carregados como pipeline de aprendizagem de máquina (arquivos serializados -> tf.data.Dataset ). A serialização é feita apenas uma vez. O acesso subsequente lerá diretamente esses arquivos pré-processados.

A maior parte do pré-processamento é feita 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 dividido (por exemplo TRAIN e TEST );
  • e os exemplos individuais no conjunto de dados.

Escreva seu conjunto de dados

Modelo padrão: tfds new

Use TFDS CLI para gerar os arquivos python modelo necessários.

cd path/to/project/datasets/  # Or use `--dir=path/to/project/datasets/` below
tfds new my_dataset

Este comando irá gerar um novo my_dataset/ pasta 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).

Procurar TODO(my_dataset) aqui e modificar em conformidade.

Exemplo de conjunto de dados

Todos os conjuntos de dados são implementados como tfds.core.GeneratorBasedBuilder , uma subclasse do tfds.core.DatasetBuilder que cuida da maior clichê. Suporta:

  • Conjuntos de dados pequenos / médios que podem ser gerados em uma única máquina (este tutorial).
  • Muito grandes conjuntos de dados que necessitam de geração distribuída (usando Apache feixe , consulte o 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']),
        }),
    )

  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 : conjunto de dados de metadados

_info retorna o tfds.core.DatasetInfo contendo o metadados 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 : Esta especificar a estrutura do conjunto de dados, forma, ... Apoio tipos complexos de dados (áudio, vídeo, sequências aninhadas, ...). Veja as funcionalidades disponíveis ou a guia conector de recurso para mais informações.
  • disable_shuffling : Ver secção Manter dataset fim .
  • citation : Para encontrar o BibText citação:
    • Pesquise o site do conjunto de dados para obter instruções de citação (use no formato BibTex).
    • Para arXiv papéis: encontrar o papel e clique no BibText link no lado direito.
    • Encontrar o papel no Google Scholar e clique na marca de dupla cotação abaixo do título e no pop-up, clique em BibTeX .
    • Se houver papel não associado (por exemplo, há apenas um site), você pode usar o editor online BibTeX para criar uma entrada BibTeX personalizado (o menu drop-down tem uma Online tipo de entrada).

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, uma vez que muitas vezes os registros pertencentes à mesma classe são contíguos. A fim de especificar que o conjunto de dados devem ser ordenados pela chave gerada fornecida pelo _generate_examples campo disable_shuffling deve ser definido para True . Por padrão, ela é definida para False .

def _info(self):
  return tfds.core.DatasetInfo(
    # [...]
    disable_shuffling=True,
    # [...]
  )

Lembre-se de que desativar o embaralhamento tem um impacto no desempenho, pois os shards não podem mais ser lidos em paralelo.

_split_generators : downloads e divide os dados

Baixar e extrair dados de origem

A maioria dos conjuntos de dados precisa baixar dados da web. Isso é feito usando o tfds.download.DownloadManager argumento de entrada do _split_generators . dl_manager tem os seguintes métodos:

  • download : suportes http(s):// , ftp(s)://
  • extract : atualmente suporta .zip , .gz , e .tar arquivos.
  • download_and_extract : O mesmo que dl_manager.extract(dl_manager.download(urls))

Todos esses métodos retorna tfds.core.ReadOnlyPath , que são pathlib.Path-como objetos.

Esses métodos suportes arbitrário estrutura aninhada ( 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 manual e extração

Alguns dados não podem ser transferidos automaticamente (por exemplo, exigir um login), neste caso, o usuário irá baixar manualmente os dados de origem e colocá-lo em manual_dir/ (o padrão é ~/tensorflow_datasets/downloads/manual/ ).

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)
    ...

O manual_dir localização pode ser personalizado com tfds build --manual_dir= ou usando tfds.download.DownloadConfig .

Leia o arquivo diretamente

dl_manager.iter_archive lê um arquivo 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 como with open('rb') as fobj: (por exemplo fobj.read() )

Especificando as divisões do conjunto de dados

Se o conjunto de dados vem com splits pré-definidos (por exemplo MNIST tem train e test splits), manter aqueles. Caso contrário, especifique apenas um único tfds.Split.TRAIN divisão. Os usuários podem criar dinamicamente os seus próprios subsplits com a API 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 : Exemplo gerador

_generate_examples gera os exemplos para cada grupo a partir da fonte de dados.

Este método normalmente ler artefatos conjunto de dados de origem (por exemplo, um arquivo CSV) e rendimento (key, feature_dict) tuplas:

  • key : Exemplo identificador. Usado para deterministically embaralhar os exemplos usando hash(key) ou para ordenar por chave quando baralhar está desativada (ver secção Manter dataset fim ). Deveria estar:
    • único: Se dois exemplos usam a mesma chave, uma exceção será levantada.
    • determinista: não deve depender de download_dir , os.path.listdir fim, ... gerando os dados duas vezes deve render a mesma chave.
    • comparável: Se baralhar está desativado a chave será usado para classificar o conjunto de dados.
  • feature_dict : Um dict contendo os valores de exemplo.
    • A estrutura deve corresponder ao features= estrutura definida no tfds.core.DatasetInfo .
    • Tipos de dados complexos (imagem, vídeo, áudio, ...) serão codificados automaticamente.
    • Cada característica muitas vezes aceitar vários tipos de entradas (por exemplo, vídeo aceitar /path/to/vid.mp4 , np.array(shape=(l, h, w, c)) , List[paths] , List[np.array(shape=(h, w, c)] , List[img_bytes] , ...)
    • Veja o guia de conector de recurso para mais informações.
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 a arquivos e tf.io.gfile

Para oferecer suporte aos sistemas de armazenamento em nuvem, evite o uso dos ops de E / S integrados do Python.

Em vez disso, os dl_manager retornos pathlib-como objetos diretamente compatíveis com o armazenamento 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 tf.io.gfile API em vez de built-in para operações de arquivo:

Pathlib deve ser preferido para tf.io.gfile (veja racional .

Dependências extras

Alguns conjuntos de dados requerem dependências Python adicionais apenas durante a geração. Por exemplo, os usos SVHN conjunto de dados scipy para carregar alguns dados.

Se você está adicionando conjunto de dados para o repositório TFDS, utilize tfds.core.lazy_imports para manter os tensorflow-datasets de pequenos pacotes. Os usuários instalarão dependências adicionais apenas conforme necessário.

Para usar lazy_imports :

  • Adicione uma entrada para o conjunto de dados em DATASET_EXTRAS em setup.py . Isso faz com que os usuários podem fazer, por exemplo, pip install 'tensorflow-datasets[svhn]' para instalar as dependências extras.
  • Adicione uma entrada para a sua importação para LazyImporter e ao LazyImportsTest .
  • Use tfds.core.lazy_imports para acessar a dependência (por exemplo, tfds.core.lazy_imports.scipy ) no seu DatasetBuilder .

Dados corrompidos

Alguns conjuntos de dados não estã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 no disco. Por exemplo, cycle_gan tem uma configuração por pares de objetos ( cycle_gan/horse2zebra , cycle_gan/monet2photo , ...).

Isto é feito através tfds.core.BuilderConfig s:

  1. Definir o 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)
    
  2. Definir o BUILDER_CONFIGS = [] membro da classe em MyDataset que as listas MyDatasetConfig s que os expõe conjunto de dados.

    class MyDataset(tfds.core.GeneratorBasedBuilder):
      VERSION = tfds.core.Version('1.0.0')
      # pytype: disable=wrong-keyword-args
      BUILDER_CONFIG = [
          # `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
    
  3. Uso self.builder_config em MyDataset a geração de dados de configuração (por exemplo, shape=self.builder_config.img_size ). Isso pode incluir a definição de valores diferentes em _info() ou alterar acesso de download de dados.

Notas:

  • Cada configuração possui um nome exclusivo. O nome completo de uma configuração é dataset_name/config_name (por exemplo, coco/2017 ).
  • Se não for especificado, a primeira configuração em BUILDER_CONFIGS vai ser utilizado (por exemplo, tfds.load('c4') padrão para c4/en )

Veja anli para um exemplo de um conjunto de dados que usa BuilderConfig s.

Versão

A versão pode se referir a dois significados diferentes:

  • A versão de dados original "externa": por exemplo, COCO v2019, v2017, ...
  • O TFDS versão do código "interna": por exemplo recurso renomear um em tfds.features.FeaturesDict , corrigir um bug no _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. Isto é feito usando uma 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 do código deve aumentar a VERSION atributo de classe (por exemplo, a partir de 1.0.0 para VERSION = tfds.core.Version('2.0.0') ) após versionamento semântico .

Adicionar uma importação para registro

Não se esqueça de importar o módulo conjunto de dados ao seu projeto __init__ seja automaticamente registrado em tfds.load , tfds.builder .

import my_project.datasets.my_dataset  # Register MyDataset

ds = tfds.load('my_dataset')  # MyDataset available

Por exemplo, se você está contribuindo para tensorflow/datasets , adicione a importação módulo para o seu subdiretório __init__.py (por exemplo, image/__init__.py .

Verifique se há pegadinhas de implementação comuns

Por favor verifique as armadilhas comuns de implementação .

Teste seu conjunto de dados

Download e preparação: tfds build

Para gerar o conjunto de dados, executar tfds build a partir do my_dataset/ diretório:

cd path/to/datasets/my_dataset/
tfds build --register_checksums

Alguns sinalizadores úteis para desenvolvimento:

  • --pdb : Entre no modo de depuração se uma exceção é levantada.
  • --overwrite : Excluir arquivos existentes, se o conjunto de dados já foi gerado.
  • --max_examples_per_split : gerar apenas os primeiros exemplos X (padrão para 1), ao invés do conjunto de dados completo.
  • --register_checksums : Grave as somas de verificação de urls baixado. Deve ser usado apenas durante o desenvolvimento.

Veja a documentação CLI para a lista completa de sinalizadores.

Checksums

Recomenda-se para gravar as somas de verificação de seus conjuntos de dados para o determinismo garantia, ajuda com documentação, ... Isto é feito através da geração do conjunto de dados com as --register_checksums (ver secção anterior).

Se você está liberando seus conjuntos de dados através PyPI, não se esqueça de exportar os checksums.tsv arquivos (por exemplo, no package_data do seu setup.py ).

Teste de unidade seu conjunto de dados

tfds.testing.DatasetBuilderTestCase é uma base TestCase exercer plenamente 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 em my_dataset/dummy_data/ diretório e deve imitar os artefatos conjunto de dados como fonte baixados e extraídos. Ele pode ser criado manualmente ou automaticamente com um (roteiro script de exemplo ).
  • Certifique-se de usar dados diferentes em suas divisões de dados de teste, pois o teste falhará se as divisões do conjunto de dados se sobreporem.
  • Os dados do teste não deve conter qualquer material com 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 seguinte comando 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 fazer isso se estivermos cientes dos problemas. Quais problemas ou erros você encontrou ao criar o conjunto de dados? Havia uma parte que era confusa, clichê ou não estava funcionando da primeira vez? Por favor, compartilhe seu feedback sobre github .