Scrittura di set di dati personalizzati

Segui questa guida per creare un nuovo set di dati (in TFDS o nel tuo repository).

Controlla il nostro elenco di set di dati per vedere se il set di dati che desideri è già presente.

TL;DR

Il modo più semplice per scrivere un nuovo set di dati è utilizzare la CLI 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_dataset_builder.py` to implement your dataset.
cd my_dataset/
tfds build  # Download and prepare the dataset to `~/tensorflow_datasets/`

Per utilizzare il nuovo set di dati con tfds.load('my_dataset') :

  • tfds.load rileverà e caricherà automaticamente il set di dati generato in ~/tensorflow_datasets/my_dataset/ (ad esempio da tfds build ).
  • In alternativa, puoi import my.project.datasets.my_dataset per registrare il tuo set di dati:
import my.project.datasets.my_dataset  # Register `my_dataset`

ds = tfds.load('my_dataset')  # `my_dataset` registered

Panoramica

I set di dati sono distribuiti in tutti i tipi di formati e in tutti i tipi di luoghi e non sono sempre archiviati in un formato pronto per essere inserito in una pipeline di machine learning. Inserisci TFDS.

TFDS elabora questi set di dati in un formato standard (dati esterni -> file serializzati), che può quindi essere caricato come pipeline di machine learning (file serializzati -> tf.data.Dataset ). La serializzazione viene eseguita una sola volta. L'accesso successivo leggerà direttamente da quei file pre-elaborati.

La maggior parte della preelaborazione viene eseguita automaticamente. Ogni set di dati implementa una sottoclasse di tfds.core.DatasetBuilder , che specifica:

  • Da dove provengono i dati (ovvero i loro URL);
  • Come si presenta il set di dati (ovvero le sue caratteristiche);
  • Come devono essere suddivisi i dati (es. TRAIN e TEST );
  • e i singoli esempi nel set di dati.

Scrivi il tuo set di dati

Modello predefinito: tfds new

Utilizza la CLI di TFDS per generare i file Python modello richiesti.

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

Questo comando genererà una nuova cartella my_dataset/ con la seguente struttura:

my_dataset/
    __init__.py
    README.md # Markdown description of the dataset.
    CITATIONS.bib # Bibtex citation for the dataset.
    TAGS.txt # List of tags describing the dataset.
    my_dataset_dataset_builder.py # Dataset definition
    my_dataset_dataset_builder_test.py # Test
    dummy_data/ # (optional) Fake data (used for testing)
    checksum.tsv # (optional) URL checksums (see `checksums` section).

Cerca TODO(my_dataset) qui e modificalo di conseguenza.

Esempio di set di dati

Tutti i set di dati sono sottoclassi implementate di tfds.core.DatasetBuilder , che si occupa della maggior parte dei boilerplate. Supporta:

  • Set di dati di piccole/medie dimensioni che possono essere generati su una singola macchina (questo tutorial).
  • Set di dati molto grandi che richiedono la generazione distribuita (utilizzando Apache Beam , consulta la nostra guida sui set di dati di grandi dimensioni )

Ecco un esempio minimo di un generatore di set di dati basato su tfds.core.GeneratorBasedBuilder :

class Builder(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 self.dataset_info_from_configs(
        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',
      }

Tieni presente che, per alcuni formati di dati specifici, forniamo generatori di set di dati pronti all'uso per occuparsi della maggior parte dell'elaborazione dei dati.

Vediamo nel dettaglio i 3 metodi astratti per sovrascrivere.

_info : metadati del set di dati

_info restituisce tfds.core.DatasetInfo contenente i metadati del set di dati .

def _info(self):
  # The `dataset_info_from_configs` base method will construct the
  # `tfds.core.DatasetInfo` object using the passed-in parameters and
  # adding: builder (self), description/citations/tags from the config
  # files located in the same package.
  return self.dataset_info_from_configs(
      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,
  )

La maggior parte dei campi dovrebbe essere autoesplicativa. Alcune precisazioni:

Scrittura del file BibText CITATIONS.bib :

  • Cerca nel sito Web del set di dati istruzioni per la citazione (utilizzalo in formato BibTex).
  • Per i documenti arXiv : trova il documento e fai clic sul collegamento BibText sul lato destro.
  • Trova l'articolo su Google Scholar e fai clic sulle virgolette doppie sotto il titolo e nel popup fai clic su BibTeX .
  • Se non è presente alcun documento associato (ad esempio, c'è solo un sito Web), puoi utilizzare l' editor BibTeX Online per creare una voce BibTeX personalizzata (il menu a discesa ha un tipo di voce Online ).

Aggiornamento del file TAGS.txt :

  • Tutti i tag consentiti sono precompilati nel file generato.
  • Rimuovi tutti i tag che non si applicano al set di dati.
  • I tag validi sono elencati in tensorflow_datasets/core/valid_tags.txt .
  • Per aggiungere un tag a quell'elenco, invia un PR.

Mantieni l'ordine del set di dati

Per impostazione predefinita, i record dei dataset vengono mescolati quando vengono archiviati per rendere la distribuzione delle classi più uniforme nel dataset, poiché spesso i record appartenenti alla stessa classe sono contigui. Per specificare che il set di dati deve essere ordinato in base alla chiave generata fornita da _generate_examples , il campo disable_shuffling deve essere impostato su True . Per impostazione predefinita è impostato su False .

def _info(self):
  return self.dataset_info_from_configs(
    # [...]
    disable_shuffling=True,
    # [...]
  )

Tieni presente che la disattivazione dello spostamento casuale ha un impatto sulle prestazioni poiché gli shard non possono più essere letti in parallelo.

_split_generators : scarica e divide i dati

Download ed estrazione dei dati di origine

La maggior parte dei set di dati deve scaricare dati dal Web. Questo viene fatto utilizzando l'argomento di input tfds.download.DownloadManager di _split_generators . dl_manager ha i seguenti metodi:

  • download : supporta http(s):// , ftp(s)://
  • extract : attualmente supporta .zip , .gz e .tar .
  • download_and_extract : uguale a dl_manager.extract(dl_manager.download(urls))

Tutti questi metodi restituiscono tfds.core.Path (alias per epath.Path ), che sono oggetti simili a pathlib.Path .

Questi metodi supportano strutture nidificate arbitrarie ( list , dict ), come:

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 ed estrazione manuale

Alcuni dati non possono essere scaricati automaticamente (ad esempio richiedono un accesso), in questo caso l'utente scaricherà manualmente i dati di origine e li inserirà in manual_dir/ (il valore predefinito è ~/tensorflow_datasets/downloads/manual/ ).

È quindi possibile accedere ai file tramite 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)
    ...

La posizione manual_dir può essere personalizzata con tfds build --manual_dir= o utilizzando tfds.download.DownloadConfig .

Leggi direttamente l'archivio

dl_manager.iter_archive legge un archivio in modo sequenziale senza estrarlo. Ciò può risparmiare spazio di archiviazione e migliorare le prestazioni su alcuni file system.

for filename, fobj in dl_manager.iter_archive('path/to/archive.zip'):
  ...

fobj ha gli stessi metodi di with open('rb') as fobj: (ad esempio fobj.read() )

Specifica delle suddivisioni del set di dati

Se il set di dati viene fornito con suddivisioni predefinite (ad esempio MNIST ha suddivisioni train e test ), mantieni quelle. Altrimenti, specificare solo una singola suddivisione all . Gli utenti possono creare dinamicamente i propri subsplit con l' API subsplit (ad esempio split='train[80%:]' ). Si noti che qualsiasi stringa alfabetica può essere utilizzata come nome diviso, ad eccezione del già citato all .

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 : generatore di esempi

_generate_examples genera gli esempi per ogni suddivisione dai dati di origine.

Questo metodo in genere legge gli artefatti del set di dati di origine (ad esempio un file CSV) e produce tuple (key, feature_dict) :

  • key : identificatore di esempio. Utilizzato per mescolare deterministicamente gli esempi utilizzando hash(key) o per ordinare per chiave quando la mescolanza è disabilitata (vedere la sezione Mantenere l'ordine del set di dati ). Dovrebbe essere:
    • unique : se due esempi utilizzano la stessa chiave, verrà sollevata un'eccezione.
    • deterministico : non dovrebbe dipendere da download_dir , os.path.listdir order,... La generazione dei dati due volte dovrebbe produrre la stessa chiave.
    • comparabile : se la riproduzione casuale è disabilitata, la chiave verrà utilizzata per ordinare il set di dati.
  • feature_dict : un dict contenente i valori di esempio.
    • La struttura deve corrispondere alla struttura features= definita in tfds.core.DatasetInfo .
    • I tipi di dati complessi (immagine, video, audio,...) verranno codificati automaticamente.
    • Ciascuna funzionalità spesso accetta più tipi di input (ad esempio video accetta /path/to/vid.mp4 , np.array(shape=(l, h, w, c)) , List[paths] , List[np.array(shape=(h, w, c)] , List[img_bytes] ,...)
    • Per ulteriori informazioni, consultare la guida al connettore delle funzionalità .
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'],
      }

Accesso ai file e tf.io.gfile

Per supportare i sistemi di archiviazione cloud, evitare l'uso delle operazioni di I/O integrate in Python.

Invece, dl_manager restituisce oggetti simili a pathlib direttamente compatibili con l'archiviazione 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())

In alternativa, utilizza l'API tf.io.gfile anziché quella integrata per le operazioni sui file:

Pathlib dovrebbe essere preferito a tf.io.gfile (vedere razionale .

Dipendenze aggiuntive

Alcuni set di dati richiedono dipendenze Python aggiuntive solo durante la generazione. Ad esempio, il set di dati SVHN utilizza scipy per caricare alcuni dati.

Se stai aggiungendo il set di dati al repository TFDS, utilizza tfds.core.lazy_imports per mantenere piccolo il pacchetto tensorflow-datasets . Gli utenti installeranno dipendenze aggiuntive solo se necessario.

Per utilizzare lazy_imports :

  • Aggiungi una voce per il tuo set di dati in DATASET_EXTRAS in setup.py . Ciò fa sì che gli utenti possano eseguire, ad esempio, pip install 'tensorflow-datasets[svhn]' per installare le dipendenze aggiuntive.
  • Aggiungi una voce per l'importazione in LazyImporter e in LazyImportsTest .
  • Utilizza tfds.core.lazy_imports per accedere alla dipendenza (ad esempio, tfds.core.lazy_imports.scipy ) nel tuo DatasetBuilder .

Dati danneggiati

Alcuni set di dati non sono perfettamente puliti e contengono dati corrotti (ad esempio, le immagini sono in file JPEG ma alcune sono JPEG non valide). Questi esempi dovrebbero essere saltati, ma lascia una nota nella descrizione del set di dati quanti esempi sono stati eliminati e perché.

Configurazione/varianti del set di dati (tfds.core.BuilderConfig)

Alcuni set di dati possono avere più varianti o opzioni su come i dati vengono preelaborati e scritti su disco. Ad esempio, cycle_gan ha una configurazione per coppia di oggetti ( cycle_gan/horse2zebra , cycle_gan/monet2photo ,...).

Questo viene fatto tramite tfds.core.BuilderConfig s:

  1. Definisci il tuo oggetto di configurazione come una sottoclasse di tfds.core.BuilderConfig . Ad esempio, MyDatasetConfig .

    @dataclasses.dataclass
    class MyDatasetConfig(tfds.core.BuilderConfig):
      img_size: Tuple[int, int] = (0, 0)
    
  2. Definire il membro della classe BUILDER_CONFIGS = [] in MyDataset che elenca i MyDatasetConfig esposti dal set di dati.

    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
    
  3. Utilizzare self.builder_config in MyDataset per configurare la generazione dei dati (ad esempio shape=self.builder_config.img_size ). Ciò potrebbe includere l'impostazione di valori diversi in _info() o la modifica dell'accesso ai dati di download.

Appunti:

  • Ogni configurazione ha un nome univoco. Il nome completo di una configurazione è dataset_name/config_name (ad esempio coco/2017 ).
  • Se non specificato, verrà utilizzata la prima configurazione in BUILDER_CONFIGS (ad esempio tfds.load('c4') default to c4/en )

Vedi anli per un esempio di un set di dati che utilizza BuilderConfig s.

Versione

La versione può avere due significati diversi:

  • La versione dei dati originali "esterna": ad esempio COCO v2019, v2017,...
  • La versione "interna" del codice TFDS: ad esempio rinomina una funzionalità in tfds.features.FeaturesDict , correggi un bug in _generate_examples

Per aggiornare un set di dati:

  • Per l'aggiornamento dei dati "esterni": più utenti potrebbero voler accedere contemporaneamente a un anno/versione specifica. Questo viene fatto utilizzando un tfds.core.BuilderConfig per versione (ad esempio coco/2017 , coco/2019 ) o una classe per versione (ad esempio Voc2007 , Voc2012 ).
  • Per l'aggiornamento del codice "interno": gli utenti scaricano solo la versione più recente. Qualsiasi aggiornamento del codice dovrebbe aumentare l'attributo della classe VERSION (ad esempio da 1.0.0 a VERSION = tfds.core.Version('2.0.0') ) seguendo il controllo delle versioni semantico .

Aggiungi un'importazione per la registrazione

Non dimenticare di importare il modulo del set di dati nel tuo progetto __init__ per essere registrato automaticamente in tfds.load , tfds.builder .

import my_project.datasets.my_dataset  # Register MyDataset

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

Ad esempio, se stai contribuendo a tensorflow/datasets , aggiungi l'importazione del modulo al __init__.py della sua sottodirectory (ad esempio image/__init__.py .

Verificare la presenza di trucchi di implementazione comuni

Si prega di verificare i trucchi di implementazione comuni .

Metti alla prova il tuo set di dati

Scarica e prepara: tfds build

Per generare il set di dati, esegui tfds build dalla directory my_dataset/ :

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

Alcuni flag utili per lo sviluppo:

  • --pdb : entra in modalità debug se viene sollevata un'eccezione.
  • --overwrite : elimina i file esistenti se il set di dati è già stato generato.
  • --max_examples_per_split : genera solo i primi X esempi (il valore predefinito è 1), anziché il set di dati completo.
  • --register_checksums : registra i checksum degli URL scaricati. Dovrebbe essere utilizzato solo durante lo sviluppo.

Consulta la documentazione della CLI per l'elenco completo dei flag.

Checksum

Si consiglia di registrare i checksum dei propri set di dati per garantire il determinismo, aiutare con la documentazione,... Questo viene fatto generando il set di dati con --register_checksums (vedere la sezione precedente).

Se rilasci i tuoi set di dati tramite PyPI, non dimenticare di esportare i file checksums.tsv (ad esempio nel package_data del tuo setup.py ).

Esegui test unitari del tuo set di dati

tfds.testing.DatasetBuilderTestCase è un TestCase di base per esercitare completamente un set di dati. Utilizza "dati fittizi" come dati di test che imitano la struttura del set di dati di origine.

  • I dati di test devono essere inseriti nella directory my_dataset/dummy_data/ e devono imitare gli artefatti del set di dati di origine scaricati ed estratti. Può essere creato manualmente o automaticamente con uno script ( script di esempio ).
  • Assicurati di utilizzare dati diversi nelle suddivisioni dei dati del test, poiché il test fallirà se le suddivisioni del set di dati si sovrappongono.
  • I dati del test non devono contenere materiale protetto da copyright . In caso di dubbio, non creare i dati utilizzando materiale dal set di dati originale.
import tensorflow_datasets as tfds
from . import my_dataset_dataset_builder


class MyDatasetTest(tfds.testing.DatasetBuilderTestCase):
  """Tests for my_dataset dataset."""
  DATASET_CLASS = my_dataset_dataset_builder.Builder
  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 my_dataset/dummy_data dir.
      'name2': 'file2',
  }


if __name__ == '__main__':
  tfds.testing.test_main()

Eseguire il comando seguente per testare il set di dati.

python my_dataset_test.py

Inviaci un feedback

Cerchiamo continuamente di migliorare il flusso di lavoro di creazione del set di dati, ma possiamo farlo solo se siamo consapevoli dei problemi. Quali problemi o errori hai riscontrato durante la creazione del set di dati? C'era una parte che creava confusione o non funzionava la prima volta?

Per favore condividi il tuo feedback su GitHub .