Escribir conjuntos de datos personalizados

Siga esta guía para crear un nuevo conjunto de datos (ya sea en TFDS o en su propio repositorio).

Consulte nuestra lista de conjuntos de datos para ver si el conjunto de datos que desea ya está presente.

TL;DR

La forma más fácil de escribir un nuevo conjunto de datos es usar la CLI de 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/`

Para usar el nuevo conjunto de datos con tfds.load('my_dataset') :

  • tfds.load detectará y cargará automáticamente el conjunto de datos generado en ~/tensorflow_datasets/my_dataset/ (por ejemplo, por tfds build ).
  • Alternativamente, puede import my.project.datasets.my_dataset para registrar su conjunto de datos:
import my.project.datasets.my_dataset  # Register `my_dataset`

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

Descripción general

Los conjuntos de datos se distribuyen en todo tipo de formatos y en todo tipo de lugares, y no siempre se almacenan en un formato que esté listo para alimentar una canalización de aprendizaje automático. Introduzca TFDS.

TFDS procesa esos conjuntos de datos en un formato estándar (datos externos -> archivos serializados), que luego se pueden cargar como canalización de aprendizaje automático (archivos serializados -> tf.data.Dataset ). La serialización se realiza una sola vez. El acceso posterior leerá directamente esos archivos preprocesados.

La mayor parte del preprocesamiento se realiza automáticamente. Cada conjunto de datos implementa una subclase de tfds.core.DatasetBuilder , que especifica:

  • De dónde provienen los datos (es decir, sus URL);
  • Aspecto del conjunto de datos (es decir, sus características);
  • Cómo se deben dividir los datos (por ejemplo TRAIN y TEST );
  • y los ejemplos individuales en el conjunto de datos.

Escribe tu conjunto de datos

Plantilla predeterminada: tfds new

Utilice la CLI de TFDS para generar los archivos de plantilla de Python necesarios.

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

Este comando generará una nueva carpeta my_dataset/ con la siguiente estructura:

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

Busque TODO(my_dataset) aquí y modifíquelo en consecuencia.

Ejemplo de conjunto de datos

Todos los conjuntos de datos son subclases implementadas de tfds.core.DatasetBuilder , que se encarga de la mayoría de los repetitivos. Es compatible con:

  • Conjuntos de datos pequeños/medianos que se pueden generar en una sola máquina (este tutorial).
  • Conjuntos de datos muy grandes que requieren generación distribuida (usando Apache Beam , consulte nuestra guía de conjunto de datos enorme )

Aquí hay un ejemplo mínimo de un generador de conjuntos de datos que se basa en 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',
      }

Tenga en cuenta que, para algunos formatos de datos específicos, proporcionamos creadores de conjuntos de datos listos para usar para encargarse de la mayoría del procesamiento de datos.

Veamos en detalle los 3 métodos abstractos para sobrescribir.

_info : metadatos del conjunto de datos

_info devuelve tfds.core.DatasetInfo que contiene los metadatos del conjunto de datos .

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 mayoría de los campos deben explicarse por sí mismos. Algunas precisiones:

Escribiendo el archivo BibText CITATIONS.bib :

  • Busque en el sitio web del conjunto de datos las instrucciones de citas (utilícelas en formato BibTex).
  • Para artículos de arXiv : busque el artículo y haga clic en el enlace BibText en el lado derecho.
  • Busque el documento en Google Scholar y haga clic en las comillas dobles debajo del título y en la ventana emergente, haga clic en BibTeX .
  • Si no hay un artículo asociado (por ejemplo, solo hay un sitio web), puede usar el editor en línea de BibTeX para crear una entrada BibTeX personalizada (el menú desplegable tiene un tipo de entrada Online ).

Actualización del archivo TAGS.txt :

  • Todas las etiquetas permitidas se completan previamente en el archivo generado.
  • Quite todas las etiquetas que no se aplican al conjunto de datos.
  • Las etiquetas válidas se enumeran en tensorflow_datasets/core/valid_tags.txt .
  • Para agregar una etiqueta a esa lista, envíe un PR.

Mantener el orden del conjunto de datos

De forma predeterminada, los registros de los conjuntos de datos se mezclan cuando se almacenan para que la distribución de clases sea más uniforme en el conjunto de datos, ya que a menudo los registros que pertenecen a la misma clase son contiguos. Para especificar que el conjunto de datos debe ordenarse por la clave generada proporcionada por _generate_examples , el campo disable_shuffling debe establecerse en True . De forma predeterminada, se establece en False .

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

Tenga en cuenta que deshabilitar la reproducción aleatoria tiene un impacto en el rendimiento, ya que los fragmentos ya no se pueden leer en paralelo.

_split_generators : descarga y divide datos

Descarga y extracción de datos de origen

La mayoría de los conjuntos de datos necesitan descargar datos de la web. Esto se hace usando el argumento de entrada tfds.download.DownloadManager de _split_generators . dl_manager tiene los siguientes métodos:

  • download : compatible con http(s):// , ftp(s)://
  • extract : actualmente admite archivos .zip , .gz y .tar .
  • download_and_extract : Igual que dl_manager.extract(dl_manager.download(urls))

Todos esos métodos devuelven tfds.core.Path (alias para epath.Path ), que son objetos similares a pathlib.Path .

Esos métodos admiten una estructura anidada arbitraria ( 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/'),
}

Descarga y extracción manual

Algunos datos no se pueden descargar automáticamente (por ejemplo, requieren un inicio de sesión); en este caso, el usuario descargará manualmente los datos de origen y los colocará en manual_dir/ (el valor predeterminado es ~/tensorflow_datasets/downloads/manual/ ).

Luego se puede acceder a los archivos a través 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)
    ...

La ubicación manual_dir se puede personalizar con tfds build --manual_dir= o usando tfds.download.DownloadConfig .

Leer archivo directamente

dl_manager.iter_archive lee archivos secuencialmente sin extraerlos. Esto puede ahorrar espacio de almacenamiento y mejorar el rendimiento en algunos sistemas de archivos.

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

fobj tiene los mismos métodos que with open('rb') as fobj: (por ejemplo, fobj.read() )

Especificar divisiones de conjuntos de datos

Si el conjunto de datos viene con divisiones predefinidas (p. ej., MNIST tiene divisiones train y test ), consérvelas. De lo contrario, solo especifique una única división all . Los usuarios pueden crear dinámicamente sus propias subdivisiones con la API de subdivisiones (por ejemplo, split='train[80%:]' ). Tenga en cuenta que cualquier cadena alfabética se puede utilizar como nombre dividido, además de la mencionada 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 : generador de ejemplo

_generate_examples genera los ejemplos para cada división a partir de los datos de origen.

Este método normalmente leerá artefactos del conjunto de datos de origen (p. ej., un archivo CSV) y producirá tuplas (key, feature_dict) :

  • key : Ejemplo de identificador. Se utiliza para mezclar de forma determinista los ejemplos usando hash(key) o para ordenar por clave cuando la reproducción aleatoria está deshabilitada (consulte la sección Mantener el orden del conjunto de datos ). Debiera ser:
    • único : si dos ejemplos usan la misma clave, se generará una excepción.
    • determinista : no debería depender de download_dir , os.path.listdir order,... Generar los datos dos veces debería generar la misma clave.
    • comparable : si la reproducción aleatoria está deshabilitada, la tecla se usará para ordenar el conjunto de datos.
  • feature_dict : un dict que contiene los valores de ejemplo.
    • La estructura debe coincidir con la estructura features= definida en tfds.core.DatasetInfo .
    • Los tipos de datos complejos (imagen, video, audio,...) se codificarán automáticamente.
    • Cada característica a menudo acepta múltiples tipos de entrada (por ejemplo, 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 la guía del conector de funciones para obtener más información.
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'],
      }

Acceso a archivos y tf.io.gfile

Para admitir sistemas de almacenamiento en la nube, evite el uso de las operaciones de E/S integradas de Python.

En cambio, dl_manager devuelve objetos similares a pathlib directamente compatibles con el almacenamiento de 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())

Alternativamente, use la API tf.io.gfile en lugar de la integrada para operaciones con archivos:

Se debe preferir Pathlib a tf.io.gfile ( ver .

Dependencias adicionales

Algunos conjuntos de datos requieren dependencias de Python adicionales solo durante la generación. Por ejemplo, el conjunto de datos SVHN usa scipy para cargar algunos datos.

Si está agregando un conjunto de datos al repositorio de TFDS, use tfds.core.lazy_imports para mantener pequeño el paquete tensorflow-datasets . Los usuarios instalarán dependencias adicionales solo según sea necesario.

Para usar lazy_imports :

  • Agregue una entrada para su conjunto de datos en DATASET_EXTRAS en setup.py . Esto hace que los usuarios puedan hacer, por ejemplo, pip install 'tensorflow-datasets[svhn]' para instalar las dependencias adicionales.
  • Agregue una entrada para su importación a LazyImporter y a LazyImportsTest .
  • Use tfds.core.lazy_imports para acceder a la dependencia (por ejemplo, tfds.core.lazy_imports.scipy ) en su DatasetBuilder .

Datos corrompidos

Algunos conjuntos de datos no están perfectamente limpios y contienen algunos datos corruptos (por ejemplo, las imágenes están en archivos JPEG pero algunos son JPEG no válidos). Estos ejemplos deben omitirse, pero deje una nota en la descripción del conjunto de datos sobre cuántos ejemplos se eliminaron y por qué.

Configuración/variantes del conjunto de datos (tfds.core.BuilderConfig)

Algunos conjuntos de datos pueden tener múltiples variantes u opciones sobre cómo los datos se procesan previamente y se escriben en el disco. Por ejemplo, cycle_gan tiene una configuración por pares de objetos ( cycle_gan/horse2zebra , cycle_gan/monet2photo ,...).

Esto se hace a través de tfds.core.BuilderConfig s:

  1. Defina su objeto de configuración como una subclase de tfds.core.BuilderConfig . Por ejemplo, MyDatasetConfig .

    @dataclasses.dataclass
    class MyDatasetConfig(tfds.core.BuilderConfig):
      img_size: Tuple[int, int] = (0, 0)
    
  2. Defina el miembro de clase BUILDER_CONFIGS = [] en MyDataset que enumera MyDatasetConfig s que expone el conjunto de datos.

    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. Utilice self.builder_config en MyDataset para configurar la generación de datos (p. ej. shape=self.builder_config.img_size ). Esto puede incluir establecer diferentes valores en _info() o cambiar el acceso a los datos de descarga.

Notas:

  • Cada configuración tiene un nombre único. El nombre completo de una configuración es dataset_name/config_name (por ejemplo, coco/2017 ).
  • Si no se especifica, se usará la primera configuración en BUILDER_CONFIGS (por ejemplo tfds.load('c4') por defecto es c4/en )

Consulte anli para ver un ejemplo de un conjunto de datos que usa BuilderConfig s.

Versión

La versión puede referirse a dos significados diferentes:

  • La versión de datos original "externa": por ejemplo, COCO v2019, v2017,...
  • La versión de código TFDS "interna": por ejemplo, cambie el nombre de una función en tfds.features.FeaturesDict , corrija un error en _generate_examples

Para actualizar un conjunto de datos:

  • Para actualización de datos "externos": Múltiples usuarios pueden querer acceder a un año/versión específico simultáneamente. Esto se hace usando un tfds.core.BuilderConfig por versión (p. ej. coco/2017 , coco/2019 ) o una clase por versión (p. ej Voc2007 , Voc2012 ).
  • Para actualización de código "interna": los usuarios solo descargan la versión más reciente. Cualquier actualización de código debe aumentar el atributo de clase VERSION (por ejemplo, de 1.0.0 a VERSION = tfds.core.Version('2.0.0') ) siguiendo el control de versiones semántico .

Agregar una importación para el registro

No olvide importar el módulo del conjunto de datos a su proyecto __init__ para que se registre automáticamente en tfds.load , tfds.builder .

import my_project.datasets.my_dataset  # Register MyDataset

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

Por ejemplo, si está contribuyendo a tensorflow/datasets , agregue la importación del módulo __init__.py de su subdirectorio (por ejemplo image/__init__.py .

Compruebe si hay problemas comunes de implementación

Verifique los problemas comunes de implementación .

Pruebe su conjunto de datos

Descargar y preparar: tfds build

Para generar el conjunto de datos, ejecute tfds build desde el directorio my_dataset/ :

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

Algunas banderas útiles para el desarrollo:

  • --pdb : ingresa al modo de depuración si se genera una excepción.
  • --overwrite : elimina los archivos existentes si el conjunto de datos ya se generó.
  • --max_examples_per_split : genera solo los primeros X ejemplos (predeterminado en 1), en lugar del conjunto de datos completo.
  • --register_checksums : registra las sumas de verificación de las URL descargadas. Solo debe usarse durante el desarrollo.

Consulte la documentación de CLI para obtener una lista completa de indicadores.

Sumas de comprobación

Se recomienda registrar las sumas de verificación de sus conjuntos de datos para garantizar el determinismo, ayuda con la documentación,... Esto se hace generando el conjunto de datos con --register_checksums (ver sección anterior).

Si está publicando sus conjuntos de datos a través de PyPI, no olvide exportar los archivos checksums.tsv (por ejemplo, en el package_data de su setup.py ).

Prueba unitaria de tu conjunto de datos

tfds.testing.DatasetBuilderTestCase es un TestCase base para ejercitar completamente un conjunto de datos. Utiliza "datos ficticios" como datos de prueba que imitan la estructura del conjunto de datos de origen.

  • Los datos de prueba deben colocarse en el directorio my_dataset/dummy_data/ y deben imitar los artefactos del conjunto de datos de origen tal como se descargaron y extrajeron. Se puede crear de forma manual o automática con un script ( script de ejemplo ).
  • Asegúrese de usar datos diferentes en sus divisiones de datos de prueba, ya que la prueba fallará si las divisiones de su conjunto de datos se superponen.
  • Los datos de prueba no deben contener ningún material protegido por derechos de autor . En caso de duda, no cree los datos utilizando material del conjunto de datos original.
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()

Ejecute el siguiente comando para probar el conjunto de datos.

python my_dataset_test.py

Envianos tus comentarios

Continuamente intentamos mejorar el flujo de trabajo de creación de conjuntos de datos, pero solo podemos hacerlo si somos conscientes de los problemas. ¿Qué problemas o errores encontró al crear el conjunto de datos? ¿Había una parte que era confusa o no funcionaba la primera vez?

Comparta sus comentarios en GitHub .