Problemas comunes de implementación

Esta página describe el error común de implementación al implementar un nuevo conjunto de datos.

Debe evitarse SplitGenerator heredado

La antigua API tfds.core.SplitGenerator está obsoleta.

def _split_generator(...):
  return [
      tfds.core.SplitGenerator(name='train', gen_kwargs={'path': train_path}),
      tfds.core.SplitGenerator(name='test', gen_kwargs={'path': test_path}),
  ]

Debe ser reemplazado por:

def _split_generator(...):
  return {
      'train': self._generate_examples(path=train_path),
      'test': self._generate_examples(path=test_path),
  }

Justificación : la nueva API es menos detallada y más explícita. La antigua API se eliminará en una versión futura.

Los nuevos conjuntos de datos deben ser autónomos en una carpeta

Al agregar un conjunto de datos dentro del repositorio tensorflow_datasets/ , asegúrese de seguir la estructura del conjunto de datos como carpeta (todas las sumas de verificación, datos ficticios, código de implementación autónomo en una carpeta).

  • Conjuntos de datos antiguos (malos): <category>/<ds_name>.py
  • Nuevos conjuntos de datos (buenos): <category>/<ds_name>/<ds_name>.py

Utilice la CLI de TFDS ( tfds new o gtfds new para googlers) para generar la plantilla.

Justificación : la estructura anterior requería rutas absolutas para sumas de verificación, datos falsos y estaba distribuyendo los archivos del conjunto de datos en muchos lugares. Estaba dificultando la implementación de conjuntos de datos fuera del repositorio de TFDS. Por consistencia, la nueva estructura debe usarse en todas partes ahora.

Las listas de descripción deben formatearse como Markdown

La str DatasetInfo.description tiene el formato de descuento. Las listas de rebajas requieren una línea vacía antes del primer elemento:

_DESCRIPTION = """
Some text.
                      # << Empty line here !!!
1. Item 1
2. Item 1
3. Item 1
                      # << Empty line here !!!
Some other text.
"""

Justificación : la descripción mal formateada crea artefactos visuales en la documentación de nuestro catálogo. Sin las líneas vacías, el texto anterior se representaría como:

Algún texto. 1. Punto 1 2. Punto 1 3. Punto 1 Otro texto

Olvidé los nombres de ClassLabel

Cuando utilice tfds.features.ClassLabel , intente proporcionar las etiquetas legibles por humanos str con names= o names_file= (en lugar de num_classes=10 ).

features = {
    'label': tfds.features.ClassLabel(names=['dog', 'cat', ...]),
}

Justificación : las etiquetas legibles por humanos se utilizan en muchos lugares:

  • Permita producir str directamente en _generate_examples : yield {'label': 'dog'}
  • Expuesto en los usuarios como info.features['label'].names (método de conversión .str2int('dog') ,... también disponible)
  • Usado en las utilidades de visualización tfds.show_examples , tfds.as_dataframe

Olvidé la forma de la imagen

Al usar tfds.features.Image , tfds.features.Video , si las imágenes tienen forma estática, deben especificarse explícitamente:

features = {
    'image': tfds.features.Image(shape=(256, 256, 3)),
}

Justificación : permite la inferencia de forma estática (p. ej ds.element_spec['image'].shape ), que es necesaria para el procesamiento por lotes (el procesamiento por lotes de imágenes de forma desconocida requeriría cambiar su tamaño primero).

Preferir un tipo más específico en lugar de tfds.features.Tensor

Cuando sea posible, prefiera los tipos más específicos tfds.features.ClassLabel , tfds.features.BBoxFeatures ,... en lugar del genérico tfds.features.Tensor .

Justificación : además de ser más correctas desde el punto de vista semántico, las funciones específicas proporcionan metadatos adicionales a los usuarios y las herramientas las detectan.

Importaciones perezosas en el espacio global

Las importaciones perezosas no deben llamarse desde el espacio global. Por ejemplo, lo siguiente es incorrecto:

tfds.lazy_imports.apache_beam # << Error: Import beam in the global scope

def f() -> beam.Map:
  ...

Justificación : el uso de importaciones diferidas en el ámbito global importaría el módulo para todos los usuarios de tfds, anulando el propósito de las importaciones diferidas.

Cálculo dinámico de divisiones de tren/prueba

Si el conjunto de datos no proporciona divisiones oficiales, tampoco debería hacerlo TFDS. Se debe evitar lo siguiente:

_TRAIN_TEST_RATIO = 0.7

def _split_generator():
  ids = list(range(num_examples))
  np.random.RandomState(seed).shuffle(ids)

  # Split train/test
  train_ids = ids[_TRAIN_TEST_RATIO * num_examples:]
  test_ids = ids[:_TRAIN_TEST_RATIO * num_examples]
  return {
      'train': self._generate_examples(train_ids),
      'test': self._generate_examples(test_ids),
  }

Justificación : TFDS intenta proporcionar conjuntos de datos tan parecidos como los datos originales. La API de subdivisión debe usarse en su lugar para permitir que los usuarios creen dinámicamente las subdivisiones que desean:

ds_train, ds_test = tfds.load(..., split=['train[:80%]', 'train[80%:]'])

Guía de estilo de Python

Prefiero usar la API pathlib

En lugar de la API tf.io.gfile , es preferible utilizar la API pathlib . Todos los métodos dl_manager devuelven objetos similares a pathlib compatibles con GCS, S3,...

path = dl_manager.download_and_extract('http://some-website/my_data.zip')

json_path = path / 'data/file.json'

json.loads(json_path.read_text())

Justificación : la API pathlib es una API de archivo moderna orientada a objetos que elimina el modelo estándar. El uso .read_text() / .read_bytes() también garantiza que los archivos se cierren correctamente.

Si el método no se usa a self , debería ser una función.

Si un método de clase no usa self , debería ser una función simple (definida fuera de la clase).

Justificación : Hace explícito al lector que la función no tiene efectos secundarios, ni entrada/salida oculta:

x = f(y)  # Clear inputs/outputs

x = self.f(y)  # Does f depend on additional hidden variables ? Is it stateful ?

Importaciones perezosas en Python

Importamos perezosamente grandes módulos como TensorFlow. Las importaciones diferidas difieren la importación real del módulo al primer uso del módulo. Entonces, los usuarios que no necesitan este gran módulo nunca lo importarán.

from tensorflow_datasets.core.utils.lazy_imports_utils import tensorflow as tf
# After this statement, TensorFlow is not imported yet

...

features = tfds.features.Image(dtype=tf.uint8)
# After using it (`tf.uint8`), TensorFlow is now imported

Debajo del capó, la clase LazyModule actúa como una fábrica, que solo importará el módulo cuando se acceda a un atributo ( __getattr__ ).

También puede usarlo convenientemente con un administrador de contexto:

from tensorflow_datasets.core.utils.lazy_imports_utils import lazy_imports

with lazy_imports(error_callback=..., success_callback=...):
  import some_big_module