Aide à protéger la Grande barrière de corail avec tensorflow sur Kaggle Rejoignez Défi

Écriture de jeux de données personnalisés

Suivez ce guide pour créer un nouvel ensemble de données (soit dans TFDS, soit dans votre propre référentiel).

Consultez notre liste des ensembles de données pour voir si l'ensemble de données que vous voulez est déjà présent.

TL;DR

La meilleure façon d'écrire un nouvel ensemble de données est d'utiliser la TFDS CLI :

cd path/to/my/project/datasets/
tfds new my_dataset  # Create `my_dataset/my_dataset.py` template files
# [...] Manually modify `my_dataset/my_dataset.py` to implement your dataset.
cd my_dataset/
tfds build  # Download and prepare the dataset to `~/tensorflow_datasets/`

Pour utiliser le nouveau jeu de données avec tfds.load('my_dataset') :

  • tfds.load détecte automatiquement et charger le jeu de données généré dans ~/tensorflow_datasets/my_dataset/ (par exemple par tfds build ).
  • Vous pouvez explicitement import my.project.datasets.my_dataset pour enregistrer votre ensemble de données:
import my.project.datasets.my_dataset  # Register `my_dataset`

ds = tfds.load('my_dataset')  # `my_dataset` registered

Aperçu

Les ensembles de données sont distribués dans toutes sortes de formats et dans toutes sortes d'endroits, et ils ne sont pas toujours stockés dans un format prêt à être intégré à un pipeline d'apprentissage automatique. Entrez TFDS.

TFDS traiter ces ensembles de données dans un format standard (données externes -> fichiers sérialisés), qui peuvent ensuite être chargés comme pipeline d'apprentissage de la machine (fichiers sérialisés -> tf.data.Dataset ). La sérialisation n'est effectuée qu'une seule fois. L'accès ultérieur sera lu directement à partir de ces fichiers pré-traités.

La plupart des prétraitements sont effectués automatiquement. Chaque jeu de données met en œuvre une sous - classe de tfds.core.DatasetBuilder , qui précise:

  • D'où proviennent les données (c'est-à-dire ses URL) ;
  • À quoi ressemble l'ensemble de données (c'est-à-dire ses caractéristiques) ;
  • Comment les données doivent être divisés (par exemple TRAIN et TEST );
  • et les exemples individuels dans l'ensemble de données.

Écrivez votre jeu de données

Modèle par défaut: tfds new

Utilisez TFDS CLI pour générer les fichiers de python de modèle requis.

cd path/to/project/datasets/  # Or use `--dir=path/to/project/datasets/` below
tfds new my_dataset

Cette commande va générer un nouveau my_dataset/ dossier avec la structure suivante:

my_dataset/
    __init__.py
    my_dataset.py # Dataset definition
    my_dataset_test.py # (optional) Test
    dummy_data/ # (optional) Fake data (used for testing)
    checksum.tsv # (optional) URL checksums (see `checksums` section).

Recherche de TODO(my_dataset) ici et modifier en conséquence.

Exemple de jeu de données

Tous les ensembles de données sont mises en œuvre tfds.core.GeneratorBasedBuilder , un sous - classes de tfds.core.DatasetBuilder qui se charge de la plupart passe- partout. Elle supporte:

  • Petits/moyens jeux de données pouvant être générés sur une seule machine (ce tutoriel).
  • Très grands ensembles de données qui nécessitent la production distribuée ( en utilisant Apache faisceau , consultez notre grand guide de jeu de données )

Voici un exemple minimal de classe de jeu de données :

class MyDataset(tfds.core.GeneratorBasedBuilder):
  """DatasetBuilder for my_dataset dataset."""

  VERSION = tfds.core.Version('1.0.0')
  RELEASE_NOTES = {
      '1.0.0': 'Initial release.',
  }

  def _info(self) -> tfds.core.DatasetInfo:
    """Dataset metadata (homepage, citation,...)."""
    return tfds.core.DatasetInfo(
        builder=self,
        features=tfds.features.FeaturesDict({
            'image': tfds.features.Image(shape=(256, 256, 3)),
            'label': tfds.features.ClassLabel(names=['no', 'yes']),
        }),
    )

  def _split_generators(self, dl_manager: tfds.download.DownloadManager):
    """Download the data and define splits."""
    extracted_path = dl_manager.download_and_extract('http://data.org/data.zip')
    # dl_manager returns pathlib-like objects with `path.read_text()`,
    # `path.iterdir()`,...
    return {
        'train': self._generate_examples(path=extracted_path / 'train_images'),
        'test': self._generate_examples(path=extracted_path / 'test_images'),
    }

  def _generate_examples(self, path) -> Iterator[Tuple[Key, Example]]:
    """Generator of examples for each split."""
    for img_path in path.glob('*.jpeg'):
      # Yields (key, example)
      yield img_path.name, {
          'image': img_path,
          'label': 'yes' if img_path.name.startswith('yes_') else 'no',
      }

Voyons en détail les 3 méthodes abstraites à écraser.

_info : ensemble de données de métadonnées

_info renvoie le tfds.core.DatasetInfo contenant le méta - données du jeu de données .

def _info(self):
  return tfds.core.DatasetInfo(
      builder=self,
      # Description and homepage used for documentation
      description="""
      Markdown description of the dataset. The text will be automatically
      stripped and dedent.
      """,
      homepage='https://dataset-homepage.org',
      features=tfds.features.FeaturesDict({
          'image_description': tfds.features.Text(),
          'image': tfds.features.Image(),
          # Here, 'label' can be 0-4.
          'label': tfds.features.ClassLabel(num_classes=5),
      }),
      # If there's a common `(input, target)` tuple from the features,
      # specify them here. They'll be used if as_supervised=True in
      # builder.as_dataset.
      supervised_keys=('image', 'label'),
      # Specify whether to disable shuffling on the examples. Set to False by default.
      disable_shuffling=False,
      # Bibtex citation for the dataset
      citation=r"""
      @article{my-awesome-dataset-2020,
               author = {Smith, John},}
      """,
  )

La plupart des champs doivent être explicites. Quelques précisions :

  • features : Cette préciser la structure du jeu de données, la forme, ... en charge les types de données complexes (audio, vidéo, séquences imbriquées, ...). Voir les fonctionnalités disponibles ou le guide de connecteur fonction pour plus d' informations.
  • disable_shuffling : Voir la section Maintenir ensemble de données pour .
  • citation : Pour trouver la BibText citation:
    • Recherchez les instructions de citation sur le site Web de l'ensemble de données (utilisez-les au format BibTex).
    • Pour arXiv papiers: trouver le papier et cliquez sur le BibText lien sur la droite.
    • Trouver le document sur Google Scholar et cliquez sur la marque double cotation sous le titre et le menu contextuel, cliquez sur BibTeX .
    • S'il n'y a pas de papier associée (par exemple, il y a juste un site Web), vous pouvez utiliser l' BibTeX éditeur en Online ligne pour créer une entrée BibTeX personnalisée (le menu déroulant a une Online type d'entrée).

Maintenir l'ordre des ensembles de données

Par défaut, les enregistrements des ensembles de données sont mélangés lorsqu'ils sont stockés afin de rendre la distribution des classes plus uniforme dans l'ensemble de données, car souvent les enregistrements appartenant à la même classe sont contigus. Afin de préciser que l'ensemble de données doit être triée par la clé générée fournie par _generate_examples le champ disable_shuffling doit être réglé sur True . Par défaut , il est réglé sur False .

def _info(self):
  return tfds.core.DatasetInfo(
    # [...]
    disable_shuffling=True,
    # [...]
  )

Gardez à l'esprit que la désactivation de la lecture aléatoire a un impact sur les performances car les partitions ne peuvent plus être lues en parallèle.

_split_generators : téléchargements et splits données

Téléchargement et extraction des données sources

La plupart des ensembles de données doivent télécharger des données à partir du Web. Cela se fait à l' aide du tfds.download.DownloadManager argument d'entrée de _split_generators . dl_manager a les méthodes suivantes:

  • download : supports http(s):// , ftp(s)://
  • extract : prend actuellement en charge .zip , .gz , et .tar fichiers.
  • download_and_extract : Identique à dl_manager.extract(dl_manager.download(urls))

Toutes ces méthodes retourne tfds.core.ReadOnlyPath , qui sont pathlib.Path comme des objets.

Ces méthodes arbitraires supports structure imbriquée ( list , dict ), comme:

extracted_paths = dl_manager.download_and_extract({
    'foo': 'https://example.com/foo.zip',
    'bar': 'https://example.com/bar.zip',
})
# This returns:
assert extracted_paths == {
    'foo': Path('/path/to/extracted_foo/'),
    'bar': Path('/path/extracted_bar/'),
}

Téléchargement et extraction manuels

Certaines données ne peuvent pas être téléchargées automatiquement (par exemple , nécessitent un login), dans ce cas, l' utilisateur télécharger manuellement les données source et le placer dans manual_dir/ (par défaut ~/tensorflow_datasets/downloads/manual/ ).

Les fichiers peuvent alors être accessible par dl_manager.manual_dir :

class MyDataset(tfds.core.GeneratorBasedBuilder):

  MANUAL_DOWNLOAD_INSTRUCTIONS = """
  Register into https://example.org/login to get the data. Place the `data.zip`
  file in the `manual_dir/`.
  """

  def _split_generators(self, dl_manager):
    # data_path is a pathlib-like `Path('<manual_dir>/data.zip')`
    archive_path = dl_manager.manual_dir / 'data.zip'
    # Extract the manually downloaded `data.zip`
    extracted_path = dl_manager.extract(archive_path)
    ...

Le manual_dir emplacement peut être personnalisé avec tfds build --manual_dir= ou en utilisant tfds.download.DownloadConfig .

Lire l'archive directement

dl_manager.iter_archive lit une séquence d' archives sans les extraire. Cela peut économiser de l'espace de stockage et améliorer les performances sur certains systèmes de fichiers.

for filename, fobj in dl_manager.iter_archive('path/to/archive.zip'):
  ...

fobj a les mêmes méthodes que with open('rb') as fobj: (par exemple fobj.read() )

Spécification des fractionnements d'ensembles de données

Si l'ensemble de données est livré avec splits prédéfinies (par exemple MNIST a le train et test divise), garder ceux -ci . Dans le cas contraire, spécifiez un seul tfds.Split.TRAIN divisé. Les utilisateurs peuvent créer dynamiquement leurs propres subsplits avec l' API subsplit (par exemple split='train[80%:]' ).

def _split_generators(self, dl_manager):
  # Download source data
  extracted_path = dl_manager.download_and_extract(...)

  # Specify the splits
  return {
      'train': self._generate_examples(
          images_path=extracted_path / 'train_imgs',
          label_path=extracted_path / 'train_labels.csv',
      ),
      'test': self._generate_examples(
          images_path=extracted_path / 'test_imgs',
          label_path=extracted_path / 'test_labels.csv',
      ),
  }

_generate_examples : générateur Exemple

_generate_examples génère les exemples pour chaque groupe à partir de la source de données.

Cette méthode va lire généralement des artefacts de l' ensemble de données source (par exemple un fichier CSV) et de rendement (key, feature_dict) tuples:

  • key : identifier l' exemple. Utilisé pour mélanger les exemples en utilisant déterministe hash(key) ou pour trier par clé lors de brassage est désactivé (voir la section Maintenir ensemble de données pour ). Devrait être:
    • unique: Si deux exemples utilisent la même clé, sera soulevé une exception.
    • déterministe: Faut - il dépend pas download_dir , os.path.listdir commande, ... Génération des données devraient produire les mêmes deux fois.
    • comparable: Si brassage est désactivé la clé sera utilisée pour trier l'ensemble de données.
  • feature_dict : A dict contenant les valeurs d'exemple.
    • La structure doit correspondre à la features= structure définie dans tfds.core.DatasetInfo .
    • Les types de données complexes (image, vidéo, audio,...) seront automatiquement encodés.
    • Chaque fonction accepte souvent plusieurs types d'entrée (vidéo accepte /path/to/vid.mp4 , np.array(shape=(l, h, w, c)) , List[paths] , List[np.array(shape=(h, w, c)] , List[img_bytes] , ...)
    • Voir le guide de connecteur fonction pour plus d' informations.
def _generate_examples(self, images_path, label_path):
  # Read the input data out of the source files
  with label_path.open() as f:
    for row in csv.DictReader(f):
      image_id = row['image_id']
      # And yield (key, feature_dict)
      yield image_id, {
          'image_description': row['description'],
          'image': images_path / f'{image_id}.jpeg',
          'label': row['label'],
      }

Accès aux fichiers et tf.io.gfile

Afin de prendre en charge les systèmes de stockage Cloud, évitez d'utiliser les opérations d'E/S intégrées de Python.

Au lieu de cela, les dl_manager retours pathlib comme des objets directement compatibles avec le stockage Google Cloud:

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

json_path = path / 'data/file.json'

json.loads(json_path.read_text())

Vous pouvez également utiliser tf.io.gfile API plutôt que pour les opérations de fichier intégré:

Pathlib devrait être a préféré tf.io.gfile (voir rationnelle .

Dépendances supplémentaires

Certains ensembles de données nécessitent des dépendances Python supplémentaires uniquement lors de la génération. Par exemple, l'ensemble de données SVHN utilisations scipy pour charger des données.

Si vous ajoutez ensemble de données dans le référentiel TFDS, s'il vous plaît utiliser tfds.core.lazy_imports pour garder les tensorflow-datasets de tensorflow-datasets petit paquet. Les utilisateurs n'installeront des dépendances supplémentaires que si nécessaire.

Pour utiliser lazy_imports :

  • Ajouter une entrée pour votre ensemble de données en DATASET_EXTRAS dans setup.py . Cela fait en sorte que les utilisateurs peuvent faire, par exemple, pip install 'tensorflow-datasets[svhn]' de pip install 'tensorflow-datasets[svhn]' installer les dépendances supplémentaires.
  • Ajouter une entrée pour votre importation LazyImporter et à la LazyImportsTest .
  • Utilisez tfds.core.lazy_imports pour accéder à la dépendance (par exemple, tfds.core.lazy_imports.scipy ) dans votre DatasetBuilder .

Données corrompues

Certains ensembles de données ne sont pas parfaitement propres et contiennent des données corrompues (par exemple, les images sont dans des fichiers JPEG mais certaines sont des JPEG non valides). Ces exemples doivent être ignorés, mais laissez une note dans la description de l'ensemble de données combien d'exemples ont été supprimés et pourquoi.

Configuration/variantes du jeu de données (tfds.core.BuilderConfig)

Certains ensembles de données peuvent avoir plusieurs variantes ou options sur la façon dont les données sont prétraitées et écrites sur le disque. Par exemple, cycle_gan a une configuration par paires d'objets ( cycle_gan/horse2zebra , cycle_gan/monet2photo , ...).

Cela se fait par tfds.core.BuilderConfig s:

  1. Définissez votre objet de configuration en tant que sous - classe de tfds.core.BuilderConfig . Par exemple, MyDatasetConfig .

    @dataclasses.dataclass
    class MyDatasetConfig(tfds.core.BuilderConfig):
      img_size: Tuple[int, int] = (0, 0)
    
  2. Définir le BUILDER_CONFIGS = [] membre de la classe dans MyDataset que les listes MyDatasetConfig s que les jeux de données de Les expose.

    class MyDataset(tfds.core.GeneratorBasedBuilder):
      VERSION = tfds.core.Version('1.0.0')
      # pytype: disable=wrong-keyword-args
      BUILDER_CONFIGS = [
          # `name` (and optionally `description`) are required for each config
          MyDatasetConfig(name='small', description='Small ...', img_size=(8, 8)),
          MyDatasetConfig(name='big', description='Big ...', img_size=(32, 32)),
      ]
      # pytype: enable=wrong-keyword-args
    
  3. Utilisation self.builder_config dans MyDataset pour configurer la génération de données (par exemple , shape=self.builder_config.img_size ). Cela peut inclure la définition des valeurs différentes dans _info() ou modifier l' accès aux données de téléchargement.

Remarques:

  • Chaque configuration a un nom unique. Le nom complet d'une configuration est dataset_name/config_name (par exemple coco/2017 ).
  • Si non spécifié, la première configuration en BUILDER_CONFIGS sera utilisé (par exemple tfds.load('c4') par défaut à c4/en )

Voir anli pour un exemple d'un ensemble de données qui utilise BuilderConfig s.

Version

La version peut faire référence à deux sens différents :

  • La version "externe" des données d'origine : ex COCO v2019, v2017,...
  • La « interne » version du code TFDS: par exemple renommer une fonction dans tfds.features.FeaturesDict , corriger un bogue dans _generate_examples

Pour mettre à jour un ensemble de données :

  • Pour la mise à jour des données « externes » : plusieurs utilisateurs peuvent souhaiter accéder simultanément à une année/version spécifique. Cela se fait à l'aide d' une tfds.core.BuilderConfig par version (par exemple coco/2017 , coco/2019 ) ou une classe par version (par exemple Voc2007 , Voc2012 ).
  • Pour la mise à jour du code "interne": Les utilisateurs ne téléchargent que la version la plus récente. Toute mise à jour de code devrait augmenter la VERSION attribut de classe (par exemple de 1.0.0 à VERSION = tfds.core.Version('2.0.0') ) suivant versioning sémantique .

Ajouter une importation pour l'enregistrement

Ne pas oublier d'importer le module jeu de données à votre projet __init__ être automatiquement enregistré dans tfds.load , tfds.builder .

import my_project.datasets.my_dataset  # Register MyDataset

ds = tfds.load('my_dataset')  # MyDataset available

Par exemple, si vous contribuez à tensorflow/datasets de __init__.py image/__init__.py tensorflow/datasets , ajouter le module importation de son sous - répertoire __init__.py (par exemple , image/__init__.py .

Vérifiez les pièges de mise en œuvre courants

S'il vous plaît vérifier les gotchas de mise en œuvre commune .

Testez votre jeu de données

Télécharger et préparer: tfds build

Pour générer l'ensemble de données, exécutez tfds build à partir du my_dataset/ répertoire:

cd path/to/datasets/my_dataset/
tfds build --register_checksums

Quelques indicateurs utiles pour le développement :

  • --pdb : Entrer en mode débogage si une exception est levée.
  • --overwrite : Supprimer les fichiers existants si l'ensemble de données a déjà été généré.
  • --max_examples_per_split : Seulement générer les premiers exemples de X (par défaut à 1), plutôt que l'ensemble des données.
  • --register_checksums : Enregistrez les checksums des urls téléchargées. Ne doit être utilisé que pendant le développement.

Consultez la documentation de CLI pour la liste complète des drapeaux.

Sommes de contrôle

Il est recommandé d'enregistrer les checksums de vos ensembles de données au déterminisme de la garantie, l' aide de la documentation, ... Ceci est fait en générant l'ensemble de données avec les --register_checksums (voir la section précédente).

Si vous publiez vos ensembles de données à travers PyPI, ne pas oublier d'exporter les checksums.tsv fichiers (par exemple dans le package_data de votre setup.py ).

Testez votre ensemble de données

tfds.testing.DatasetBuilderTestCase est une base TestCase d'exercer pleinement un ensemble de données. Il utilise des "données factices" comme données de test qui imitent la structure de l'ensemble de données source.

  • Les données d'essai doivent être mises en my_dataset/dummy_data/ répertoire et doivent imiter les artefacts de jeu de données comme source téléchargé et extrait. Il peut être créé manuellement ou automatiquement avec un script ( exemple de script ).
  • Assurez-vous d'utiliser des données différentes dans vos divisions de données de test, car le test échouera si vos divisions de jeu de données se chevauchent.
  • Les données d'essai ne doit contenir aucun matériel protégé par copyright. En cas de doute, ne créez pas les données en utilisant le matériel de l'ensemble de données d'origine.
import tensorflow_datasets as tfds
from . import my_dataset


class MyDatasetTest(tfds.testing.DatasetBuilderTestCase):
  """Tests for my_dataset dataset."""
  DATASET_CLASS = my_dataset.MyDataset
  SPLITS = {
      'train': 3,  # Number of fake train example
      'test': 1,  # Number of fake test example
  }

  # If you are calling `download/download_and_extract` with a dict, like:
  #   dl_manager.download({'some_key': 'http://a.org/out.txt', ...})
  # then the tests needs to provide the fake output paths relative to the
  # fake data directory
  DL_EXTRACT_RESULT = {
      'name1': 'path/to/file1',  # Relative to dummy_data/my_dataset dir.
      'name2': 'file2',
  }


if __name__ == '__main__':
  tfds.testing.test_main()

Exécutez la commande suivante pour tester l'ensemble de données.

python my_dataset_test.py

Envoyez-nous vos commentaires

Nous essayons continuellement d'améliorer le flux de travail de création d'ensembles de données, mais ne pouvons le faire que si nous sommes conscients des problèmes. Quels problèmes, erreurs avez-vous rencontrés lors de la création du jeu de données ? Y avait-il une partie qui était déroutante, passe-partout ou ne fonctionnait pas la première fois ? S'il vous plaît partager vos commentaires sur GitHub .