Problemas comunes de implementación

Esta página describe los problemas de implementación comunes al implementar un nuevo conjunto de datos.

Se debe evitar SplitGenerator heredado

La antigua API tfds.core.SplitGenerator está en desuso.

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 versiones futuras.

Los nuevos conjuntos de datos deben estar contenidos 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 antigua requería rutas absolutas para las sumas de verificación, datos falsos y distribuía los archivos del conjunto de datos en muchos lugares. Estaba dificultando la implementación de conjuntos de datos fuera del repositorio TFDS. Para mantener la coherencia, la nueva estructura debería utilizarse ahora en todas partes.

Las listas de descripciones deben tener formato de rebajas.

La str DatasetInfo.description tiene el formato Markdown. 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 Algún otro texto

Olvidé los nombres de ClassLabel

Cuando utilice tfds.features.ClassLabel , intente proporcionar las etiquetas str legibles por humanos 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:

Olvidé la forma de la imagen

Cuando se utilizan tfds.features.Image , tfds.features.Video , si las imágenes tienen forma estática, se deben especificar explícitamente:

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

Justificación : permite la inferencia de formas estáticas (por ejemplo, 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).

Prefiero 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 tfds.features.Tensor genérico.

Justificación : además de ser más semánticamente correctas, las funciones específicas proporcionan metadatos adicionales a los usuarios y son detectadas por herramientas.

Importaciones perezosas en el espacio global

Las importaciones perezosas no deberían 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.

Computación dinámica de divisiones de tren/prueba

Si el conjunto de datos no proporciona divisiones oficiales, TFDS tampoco debería hacerlo. 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 lo más parecidos a los datos originales. En su lugar, se debe utilizar la API de subdivisión para permitir a los usuarios crear dinámicamente las subdivisiones que deseen:

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 archivos moderna orientada a objetos que elimina el texto repetitivo. El uso .read_text() / .read_bytes() también garantiza que los archivos se cierren correctamente.

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

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

Justificación : deja explícito al lector que la función no tiene efectos secundarios ni entradas/salidas ocultas:

x = f(y)  # Clear inputs/outputs

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

Importaciones diferidas en Python

Importamos perezosamente módulos grandes como TensorFlow. Las importaciones diferidas difieren la importación real del módulo hasta el primer uso del módulo. Por lo tanto, los usuarios que no necesiten este gran módulo nunca lo importarán. Usamos etils.epy.lazy_imports .

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

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

También puedes usarlo cómodamente con un administrador de contexto:

from etils import epy

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