O Google I / O retorna de 18 a 20 de maio! Reserve espaço e monte sua agenda Cadastre-se agora
Esta página foi traduzida pela API Cloud Translation.
Switch to English

Gravando conjuntos de dados personalizados

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

Verifique 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 e carregar automaticamente o conjunto de dados gerado em ~/tensorflow_datasets/my_dataset/ (por exemplo, por tfds build ).
  • Como alternativa, você pode import my.project.datasets.my_dataset explicitamente 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.

TFDS processa esses conjuntos de dados em um formato padrão (dados externos -> arquivos serializados), que podem então 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 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 divididos (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 de modelo python necessários.

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

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

Pesquise 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 tfds.core.DatasetBuilder . Suporta:

  • Conjuntos de dados pequenos / médios que podem ser gerados em uma única máquina (este tutorial).
  • Conjuntos de dados muito grandes que requerem geração distribuída (usando Apache Beam ). Veja nosso enorme guia de conjunto de dados )

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 : 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 : Isto 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 : Veja a seção Manter a ordem do conjunto de dados .
  • citation : Para encontrar a citação BibText :
    • Pesquise o site do conjunto de dados para obter instruções de citação (use no formato BibTex).
    • Para papéis arXiv : encontre o papel e clique no link BibText no lado direito.
    • Encontre o artigo no Google Scholar e clique nas aspas duplas abaixo do título e, no pop-up, clique em BibTeX .
    • Se não houver papel associado (por exemplo, há apenas um site), você pode usar o Editor BibTeX Online 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, uma vez que 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, ele é definido como 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 : baixa e divide 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 : suporta http(s):// , ftp(s)://
  • extract : atualmente suporta arquivos .zip , .gz e .tar .
  • download_and_extract : igual a dl_manager.extract(dl_manager.download(urls))

Todos esses métodos retornam tfds.core.ReadOnlyPath , 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 manual e extração

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

Os arquivos podem ser acessados ​​por meio de 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 local do manual_dir pode ser personalizado com tfds build --manual_dir= ou usando tfds.download.DownloadConfig .

Leia o arquivo diretamente

dl_manager.iter_archive lê 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 as divisões do conjunto de dados

Se o conjunto de dados vier com divisões predefinidas (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.

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

  • key : Identificador de exemplo. Usado para embaralhar deterministicamente os exemplos usando hash(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 usam a mesma chave, uma exceção será levantada.
    • determinística : não deve depender da ordem download_dir , os.path.listdir , ... Gerar os dados duas vezes deve os.path.listdir na mesma chave.
    • comparável : se o embaralhamento estiver desativado, a chave será usada para classificar o conjunto de dados.
  • feature_dict : Um dict contendo os valores de exemplo.
    • A estrutura deve corresponder à estrutura features= definida em 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] , ...)
    • Consulte o guia do conector de recursos para obter 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 ao arquivo e tf.io.gfile

Para oferecer suporte aos sistemas de armazenamento em nuvem, use a API tf.io.gfile vez da integrada para operações de arquivo. Ex:

Dependências extras

Alguns conjuntos de dados requerem dependências Python adicionais 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 apenas conforme necessário.

Para usar lazy_imports :

  • Adicione uma entrada para seu conjunto de dados em DATASET_EXTRAS em setup.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 ao LazyImportsTest .
  • Use tfds.core.lazy_imports para acessar a dependência (por exemplo, tfds.core.lazy_imports.scipy ) em 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 , ...).

Isso é feito por meio de tfds.core.BuilderConfig s:

  1. 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)
    
  2. Defina o membro da classe BUILDER_CONFIGS = [] em MyDataset que lista MyDatasetConfig 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_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. Use self.builder_config em MyDataset para configurar a geração de dados (por exemplo, shape=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 possui um nome exclusivo. O nome totalmente qualificado de um config é dataset_name/config_name (por exemplo, coco/2017 ).
  • Se não for especificado, a primeira configuração em BUILDER_CONFIGS será usada (por exemplo, tfds.load('c4') padrão para c4/en )

Consulte anli para um exemplo de 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, ...
  • A versão do código TFDS "interno": por exemplo, renomear um recurso em tfds.features.FeaturesDict , corrigir 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. 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 de código deve aumentar o atributo de classe VERSION (por exemplo, de 1.0.0 para VERSION = tfds.core.Version('2.0.0') ) seguindo o controle de versão semântico .

Adicionar uma importação para registro

Não se esqueça de importar o módulo dataset para seu projeto __init__ para ser 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ê estiver contribuindo com tensorflow/datasets , adicione o módulo import ao seu subdiretório __init__.py (por exemplo, image/__init__.py .

Teste seu conjunto de dados

Baixe e prepare: tfds build

Para gerar o conjunto de dados, execute tfds build partir 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 : Exclui arquivos existentes se o conjunto de dados já foi gerado.
  • --max_examples_per_split : gere apenas os primeiros exemplos X (padrão para 1), ao invés do conjunto de dados completo.
  • --register_checksums : registra os checksums dos urls baixados. Deve ser usado apenas durante o desenvolvimento.

Consulte a documentação da CLI para obter uma lista completa de sinalizadores.

Checksums

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 --register_checksums (consulte a seção anterior).

Se você estiver liberando seus conjuntos de dados por meio 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 base 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 baixados e extraídos. Ele pode ser criado manualmente ou automaticamente com um script ( 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 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 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 seus comentários no github .