Merci de vous être connecté à Google I/O. Voir toutes les sessions à la demande Regarder à la demande

Problèmes de mise en œuvre courants

Cette page décrit le problème de mise en œuvre courant lors de la mise en œuvre d'un nouvel ensemble de données.

SplitGenerator doit être évité

L'ancienne API tfds.core.SplitGenerator est obsolète.

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

Doit être remplacé par :

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

Justification : La nouvelle API est moins verbeuse et plus explicite. L'ancienne API sera supprimée dans la future version.

Les nouveaux ensembles de données doivent être autonomes dans un dossier

Lors de l'ajout d'un ensemble de données dans le référentiel tensorflow_datasets/ , assurez-vous de suivre la structure de l'ensemble de données en tant que dossier (toutes les sommes de contrôle, données factices, code d'implémentation autonome dans un dossier).

  • Anciens ensembles de données (mauvais) : <category>/<ds_name>.py
  • Nouveaux ensembles de données (bon) : <category>/<ds_name>/<ds_name>.py

Utilisez l'interface de ligne de commande TFDS ( tfds new ou gtfds new pour les googleurs) pour générer le modèle.

Justification : L'ancienne structure nécessitait des chemins absolus pour les sommes de contrôle, les fausses données et distribuait les fichiers de l'ensemble de données à de nombreux endroits. Cela rendait plus difficile la mise en œuvre d'ensembles de données en dehors du référentiel TFDS. Par souci de cohérence, la nouvelle structure devrait désormais être utilisée partout.

Les listes de description doivent être formatées comme Markdown

La DatasetInfo.description est au format str . Les listes Markdown nécessitent une ligne vide avant le premier élément :

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

Justification : Une description mal formatée crée des artefacts visuels dans la documentation de notre catalogue. Sans les lignes vides, le texte ci-dessus serait rendu comme suit :

Du texte. 1. Rubrique 1 2. Rubrique 1 3. Rubrique 1 Autre texte

Noms de ClassLabel oubliés

Lorsque vous utilisez tfds.features.ClassLabel , essayez de fournir les étiquettes lisibles par l'homme str avec names= ou names_file= (au lieu de num_classes=10 ).

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

Justification : Des étiquettes lisibles par l'homme sont utilisées dans de nombreux endroits :

  • Autoriser à produire str directement dans _generate_examples : yield {'label': 'dog'}
  • Exposé dans les utilisateurs comme info.features['label'].names (méthode de conversion .str2int('dog') ,... également disponible)
  • Utilisé dans les utilitaires de visualisation tfds.show_examples , tfds.as_dataframe

Forme d'image oubliée

Lors de l'utilisation tfds.features.Image , tfds.features.Video , si les images ont une forme statique, elles doivent être explicitement spécifiées :

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

Justification : Il permet l'inférence de forme statique (par exemple ds.element_spec['image'].shape ), qui est nécessaire pour le traitement par lots (le traitement par lots d'images de forme inconnue nécessiterait d'abord de les redimensionner).

Préférez un type plus spécifique au lieu de tfds.features.Tensor

Lorsque cela est possible, préférez les types plus spécifiques tfds.features.ClassLabel , tfds.features.BBoxFeatures ,... au lieu du générique tfds.features.Tensor .

Justification : En plus d'être sémantiquement plus correctes, les fonctionnalités spécifiques fournissent des métadonnées supplémentaires aux utilisateurs et sont détectées par les outils.

Importations paresseuses dans l'espace mondial

Les importations différées ne doivent pas être appelées depuis l'espace global. Par exemple, ce qui suit est faux :

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

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

Justification : L'utilisation d'importations différées dans la portée globale importerait le module pour tous les utilisateurs de tfds, ce qui irait à l'encontre de l'objectif des importations différées.

Calcul dynamique des fractionnements train/test

Si l'ensemble de données ne fournit pas de divisions officielles, TFDS ne le devrait pas non plus. Les éléments suivants doivent être évités :

_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),
  }

Justification : TFDS essaie de fournir des ensembles de données aussi proches que les données d'origine. L' API de sous-split doit être utilisée à la place pour permettre aux utilisateurs de créer dynamiquement les sous-splits qu'ils souhaitent :

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

Guide de style Python

Préférez utiliser l'API pathlib

Au lieu de l'API tf.io.gfile , il est préférable d'utiliser l' API pathlib . Toutes les méthodes dl_manager renvoient des objets de type pathlib compatibles avec 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())

Justification : l'API pathlib est une API de fichier orientée objet moderne qui supprime le passe-partout. L'utilisation .read_text() / .read_bytes() garantit également que les fichiers sont correctement fermés.

Si la méthode n'utilise pas self , cela devrait être une fonction

Si une méthode de classe n'utilise pas self , il doit s'agir d'une fonction simple (définie en dehors de la classe).

Raisonnement : Il est explicite pour le lecteur que la fonction n'a pas d'effets secondaires, ni d'entrée/sortie cachée :

x = f(y)  # Clear inputs/outputs

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

Importations paresseuses en Python

Nous importons paresseusement de gros modules comme TensorFlow. Les importations différées reportent l'importation réelle du module à la première utilisation du module. Ainsi, les utilisateurs qui n'ont pas besoin de ce gros module ne l'importeront jamais.

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

Sous le capot, la classe LazyModule agit comme une fabrique, qui n'importera réellement le module que lorsqu'un attribut sera accédé ( __getattr__ ).

Vous pouvez également l'utiliser facilement avec un gestionnaire de contexte :

from tensorflow_datasets.core.utils.lazy_imports_utils import lazy_imports

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