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 de jeux de données pour voir si le jeu de données que vous souhaitez est déjà présent.
TL; DR
Le moyen le plus simple d'écrire un nouveau jeu de données consiste à utiliser la CLI TFDS :
cd path/to/my/project/datasets/
tfds new my_dataset # Create `my_dataset/my_dataset.py` template files
# [...] Manually modify `my_dataset/my_dataset_dataset_builder.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étectera et chargera automatiquement le jeu de données généré dans~/tensorflow_datasets/my_dataset/
(par exemple partfds build
). - Vous pouvez également
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 à alimenter un pipeline d'apprentissage automatique. Entrez TFDS.
TFDS traite ces ensembles de données dans un format standard (données externes -> fichiers sérialisés), qui peuvent ensuite être chargés en tant que pipeline d'apprentissage automatique (fichiers sérialisés -> tf.data.Dataset
). La sérialisation n'est effectuée qu'une seule fois. Un accès ultérieur lira directement ces fichiers prétraités.
La majeure partie du prétraitement est effectuée automatiquement. Chaque jeu de données implémente une sous-classe de tfds.core.DatasetBuilder
, qui spécifie :
- D'où proviennent les données (c'est-à-dire leurs URL) ;
- À quoi ressemble l'ensemble de données (c'est-à-dire ses caractéristiques) ;
- Comment les données doivent être divisées (par exemple
TRAIN
etTEST
); - et les exemples individuels dans l'ensemble de données.
Écrivez votre jeu de données
Modèle par défaut : tfds new
Utilisez l'interface de ligne de commande TFDS pour générer les fichiers python de modèle requis.
cd path/to/project/datasets/ # Or use `--dir=path/to/project/datasets/` below
tfds new my_dataset
Cette commande générera un nouveau dossier my_dataset/
avec la structure suivante :
my_dataset/
__init__.py
README.md # Markdown description of the dataset.
CITATIONS.bib # Bibtex citation for the dataset.
TAGS.txt # List of tags describing the dataset.
my_dataset_dataset_builder.py # Dataset definition
my_dataset_dataset_builder_test.py # Test
dummy_data/ # (optional) Fake data (used for testing)
checksum.tsv # (optional) URL checksums (see `checksums` section).
Recherchez TODO(my_dataset)
ici et modifiez-le en conséquence.
Exemple de jeu de données
Tous les jeux de données sont des sous-classes implémentées de tfds.core.DatasetBuilder
, qui prend en charge la plupart des passe-partout. Elle supporte:
- Ensembles de données petits/moyens pouvant être générés sur une seule machine (ce tutoriel).
- Ensembles de données très volumineux nécessitant une génération distribuée (en utilisant Apache Beam , consultez notre énorme guide d'ensemble de données )
Voici un exemple minimal d'un générateur de jeu de données basé sur tfds.core.GeneratorBasedBuilder
:
class Builder(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 self.dataset_info_from_configs(
features=tfds.features.FeaturesDict({
'image': tfds.features.Image(shape=(256, 256, 3)),
'label': tfds.features.ClassLabel(
names=['no', 'yes'],
doc='Whether this is a picture of a cat'),
}),
)
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',
}
Notez que, pour certains formats de données spécifiques, nous fournissons des générateurs d'ensembles de données prêts à l'emploi pour prendre en charge la plupart des traitements de données.
Voyons en détail les 3 méthodes abstraites à écraser.
_info
: métadonnées du jeu de données
_info
renvoie le tfds.core.DatasetInfo
contenant les métadonnées de l'ensemble de données .
def _info(self):
# The `dataset_info_from_configs` base method will construct the
# `tfds.core.DatasetInfo` object using the passed-in parameters and
# adding: builder (self), description/citations/tags from the config
# files located in the same package.
return self.dataset_info_from_configs(
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,
)
La plupart des champs doivent être explicites. Quelques précisions :
-
features
: Cela spécifie la structure, la forme,... du jeu de données. Prend en charge les types de données complexes (audio, vidéo, séquences imbriquées,...). Consultez les fonctionnalités disponibles ou le guide des connecteurs de fonctionnalités pour plus d'informations. -
disable_shuffling
: Voir la section Maintenir l'ordre des jeux de données .
Ecriture du fichier BibText
CITATIONS.bib
:
- Recherchez sur le site Web de l'ensemble de données les instructions de citation (utilisez-les au format BibTex).
- Pour les articles arXiv : trouvez l'article et cliquez sur le lien
BibText
sur le côté droit. - Recherchez l'article sur Google Scholar et cliquez sur le guillemet double sous le titre et dans la fenêtre contextuelle, cliquez sur
BibTeX
. - S'il n'y a pas d'article associé (par exemple, il n'y a qu'un site Web), vous pouvez utiliser l' éditeur en ligne BibTeX pour créer une entrée BibTeX personnalisée (le menu déroulant a un type d'entrée
Online
).
Mise à jour du fichier TAGS.txt
:
- Toutes les balises autorisées sont pré-remplies dans le fichier généré.
- Supprimez toutes les balises qui ne s'appliquent pas au jeu de données.
- Les balises valides sont répertoriées dans tensorflow_datasets/core/valid_tags.txt .
- Pour ajouter un tag à cette liste, veuillez envoyer un PR.
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 spécifier que l'ensemble de données doit être trié par la clé générée fournie par _generate_examples
, le champ disable_shuffling
doit être défini sur True
. Par défaut, il est défini sur False
.
def _info(self):
return self.dataset_info_from_configs(
# [...]
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écharge et divise les données
Téléchargement et extraction des données source
La plupart des ensembles de données doivent télécharger des données à partir du Web. Cela se fait à l'aide de l'argument d'entrée tfds.download.DownloadManager
de _split_generators
. dl_manager
a les méthodes suivantes :
-
download
: prend en chargehttp(s)://
,ftp(s)://
-
extract
: prend actuellement en charge les fichiers.zip
,.gz
et.tar
. -
download_and_extract
: Identique àdl_manager.extract(dl_manager.download(urls))
Toutes ces méthodes renvoient tfds.core.Path
(alias pour epath.Path
), qui sont des objets de type pathlib.Path .
Ces méthodes prennent en charge une structure imbriquée arbitraire ( 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 une connexion), dans ce cas, l'utilisateur téléchargera manuellement les données sources et les placera dans manual_dir/
(par défaut à ~/tensorflow_datasets/downloads/manual/
).
Les fichiers sont alors accessibles via 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)
...
L'emplacement manual_dir
peut être personnalisé avec tfds build --manual_dir=
ou en utilisant tfds.download.DownloadConfig
.
Lire l'archive directement
dl_manager.iter_archive
lit une archive séquentiellement 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 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 fourni avec des divisions prédéfinies (par exemple, MNIST
a des divisions train
et test
), conservez-les. Sinon, ne spécifiez qu'un seul partage all
. Les utilisateurs peuvent créer dynamiquement leurs propres sous-splits avec l' API de sous-split (par exemple, split='train[80%:]'
). Notez que n'importe quelle chaîne alphabétique peut être utilisée comme nom partagé, à l'exception de all
.
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 d'exemples
_generate_examples
génère les exemples pour chaque fractionnement à partir des données source.
Cette méthode lira généralement les artefacts de l'ensemble de données source (par exemple, un fichier CSV) et produira des tuples (key, feature_dict)
:
-
key
: Exemple d'identifiant. Utilisé pour mélanger de manière déterministe les exemples en utilisanthash(key)
ou pour trier par clé lorsque le shuffling est désactivé (voir section Maintenir l'ordre des jeux de données ). Devrait être:- unique : Si deux exemples utilisent la même clé, une exception sera levée.
- déterministe : ne doit pas dépendre de l'ordre
download_dir
,os.path.listdir
,... Générer les données deux fois devrait donner la même clé. - comparable : si le brassage est désactivé, la clé sera utilisée pour trier le jeu de données.
-
feature_dict
: undict
contenant les exemples de valeurs.- La structure doit correspondre à la structure
features=
définie danstfds.core.DatasetInfo
. - Les types de données complexes (image, vidéo, audio,...) seront automatiquement encodés.
- Chaque fonctionnalité accepte souvent plusieurs types d'entrée (par exemple, video accept
/path/to/vid.mp4
,np.array(shape=(l, h, w, c))
,List[paths]
,List[np.array(shape=(h, w, c)]
,List[img_bytes]
,...) - Consultez le guide des connecteurs de fonctions pour plus d'informations.
- La structure doit correspondre à la structure
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 au fichier 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 à Python.
Au lieu de cela, le dl_manager
renvoie des objets de type pathlib 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 l'API tf.io.gfile
au lieu de l'API intégrée pour les opérations sur les fichiers :
-
open
->tf.io.gfile.GFile
-
os.rename
->tf.io.gfile.rename
- ...
Pathlib doit être préféré à tf.io.gfile
(voir rational .
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, le jeu de données SVHN utilise scipy
pour charger certaines données.
Si vous ajoutez un jeu de données dans le référentiel TFDS, veuillez utiliser tfds.core.lazy_imports
pour garder le package tensorflow-datasets
petit. Les utilisateurs installeront des dépendances supplémentaires uniquement si nécessaire.
Pour utiliser lazy_imports
:
- Ajoutez une entrée pour votre ensemble de données dans
DATASET_EXTRAS
danssetup.py
. Cela permet aux utilisateurs de faire, par exemple,pip install 'tensorflow-datasets[svhn]'
pour installer les dépendances supplémentaires. - Ajoutez une entrée pour votre importation à
LazyImporter
et àLazyImportsTest
. - Utilisez
tfds.core.lazy_imports
pour accéder à la dépendance (par exemple,tfds.core.lazy_imports.scipy
) dans votreDatasetBuilder
.
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 invalides). Ces exemples doivent être ignorés, mais laissez une note dans la description du jeu de données indiquant 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 paire d'objets ( cycle_gan/horse2zebra
, cycle_gan/monet2photo
,...).
Cela se fait via tfds.core.BuilderConfig
s :
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)
Définissez le membre de classe
BUILDER_CONFIGS = []
dansMyDataset
qui répertorie lesMyDatasetConfig
que l'ensemble de données 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
Utilisez
self.builder_config
dansMyDataset
pour configurer la génération de données (par exempleshape=self.builder_config.img_size
). Cela peut inclure la définition de différentes valeurs dans_info()
ou la modification de 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 exemplecoco/2017
). - S'il n'est pas spécifié, la première configuration dans
BUILDER_CONFIGS
sera utilisée (par exempletfds.load('c4')
par défaut surc4/en
)
Voir anli
pour un exemple d'ensemble de données qui utilise BuilderConfig
s.
Version
La version peut faire référence à deux significations différentes :
- La version originale "externe" des données : par exemple COCO v2019, v2017,...
- La version "interne" du code TFDS : par exemple, renommer une fonctionnalité dans
tfds.features.FeaturesDict
, corriger un bug 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 en utilisant un
tfds.core.BuilderConfig
par version (par exemplecoco/2017
,coco/2019
) ou une classe par version (par exempleVoc2007
,Voc2012
). - Pour la mise à jour "interne" du code : les utilisateurs ne téléchargent que la version la plus récente. Toute mise à jour du code doit augmenter l'attribut de classe
VERSION
(par exemple de1.0.0
àVERSION = tfds.core.Version('2.0.0')
) suite au versioning sémantique .
Ajouter une importation pour l'enregistrement
N'oubliez pas d'importer le module dataset dans votre projet __init__
pour ê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
, ajoutez le module import au __init__.py
de son sous-répertoire (par exemple image/__init__.py
.
Vérifiez les pièges de mise en œuvre courants
Veuillez vérifier les problèmes d'implémentation courants .
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 répertoire my_dataset/
:
cd path/to/datasets/my_dataset/
tfds build --register_checksums
Quelques drapeaux utiles pour le développement :
-
--pdb
: passe en mode débogage si une exception est déclenchée. -
--overwrite
: supprime les fichiers existants si le jeu de données a déjà été généré. -
--max_examples_per_split
: ne génère que les X premiers exemples (par défaut à 1), plutôt que l'ensemble de données complet. -
--register_checksums
: enregistre les sommes de contrôle des URL téléchargées. Ne doit être utilisé qu'en cours de développement.
Voir la documentation CLI pour la liste complète des drapeaux.
Sommes de contrôle
Il est recommandé d'enregistrer les sommes de contrôle de vos jeux de données pour garantir le déterminisme, aide à la documentation,... Cela se fait en générant le jeu de données avec le --register_checksums
(voir section précédente).
Si vous publiez vos ensembles de données via PyPI, n'oubliez pas d'exporter les fichiers checksums.tsv
(par exemple dans le package_data
de votre setup.py
).
Testez unitairement votre jeu de données
tfds.testing.DatasetBuilderTestCase
est un TestCase
de base pour exercer pleinement un jeu 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 de test doivent être placées dans le répertoire
my_dataset/dummy_data/
et doivent imiter les artefacts de l'ensemble de données source tels qu'ils ont été téléchargés et extraits. Il peut être créé manuellement ou automatiquement avec un script ( exemple de script ). - Assurez-vous d'utiliser des données différentes dans vos fractionnements de données de test, car le test échouera si les fractionnements de votre ensemble de données se chevauchent.
- Les données de test ne doivent contenir aucun élément protégé par des droits d'auteur . En cas de doute, ne créez pas les données à l'aide du matériel de l'ensemble de données d'origine.
import tensorflow_datasets as tfds
from . import my_dataset_dataset_builder
class MyDatasetTest(tfds.testing.DatasetBuilderTestCase):
"""Tests for my_dataset dataset."""
DATASET_CLASS = my_dataset_dataset_builder.Builder
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 my_dataset/dummy_data 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 nous ne pouvons le faire que si nous sommes conscients des problèmes. Quels problèmes ou erreurs avez-vous rencontrés lors de la création de l'ensemble de données ? Y avait-il une partie qui prêtait à confusion ou qui ne fonctionnait pas la première fois ?
Veuillez partager vos commentaires sur GitHub .