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:
- Permitir generar
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) - Utilizado en las utilidades de visualización
tfds.show_examples
,tfds.as_dataframe
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 estándar. El uso de .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