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