Suivez ce guide pour créer un nouveau jeu de données (soit dans TFDS, soit dans votre propre dépôt).
Consultez notre liste d'ensembles de données pour vérifier si celui que vous recherchez y figure déjà.
TL;DR
La méthode la plus simple pour créer un nouveau jeu de données consiste à utiliser l' interface de ligne de commande 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.loaddé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_datasetpour enregistrer votre jeu de données :
import my.project.datasets.my_dataset # Register `my_dataset`
ds = tfds.load('my_dataset') # `my_dataset` registered
Aperçu
Les jeux de données sont distribués sous toutes sortes de formats et à tous les endroits, et ils ne sont pas toujours stockés dans un format directement exploitable par un pipeline d'apprentissage automatique. C'est là qu'intervient TFDS.
TFDS convertit ces jeux de données en un format standard (données externes → fichiers sérialisés), qui peuvent ensuite être chargés dans un pipeline d'apprentissage automatique (fichiers sérialisés → tf.data.Dataset ). La sérialisation n'est effectuée qu'une seule fois. Les accès suivants se feront directement à partir de 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,
TRAINetTEST) ; - et les exemples individuels de l'ensemble de données.
Écrivez votre ensemble 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 modèles 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 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 gère la majeure partie du code répétitif. Il prend en charge :
- Des ensembles de données de petite/moyenne taille qui peuvent être générés sur une seule machine (ce tutoriel).
- Les très grands ensembles de données qui nécessitent une génération distribuée (à l'aide d' Apache Beam , voir notre guide sur les très grands ensembles de données )
Voici un exemple minimal de générateur de jeux 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 outils de création de jeux de données prêts à l'emploi qui prennent en charge la majeure partie du traitement des données.
Examinons en détail les 3 méthodes abstraites à surcharger.
_info : métadonnées du jeu de données
_info renvoie le tfds.core.DatasetInfo contenant les métadonnées du jeu 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 devraient être explicites. Quelques précisions :
-
features: Spécifiez la structure et la forme de l’ensemble 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 du connecteur de fonctionnalités pour plus d’informations. -
disable_shuffling: Voir la section Maintenir l'ordre des ensembles de données .
Écriture du fichier BibText CITATIONS.bib :
- Consultez le site web du jeu de données pour obtenir les instructions de citation (utilisez-les au format BibTex).
- Pour les articles arXiv : trouvez l’article et cliquez sur le lien
BibTextà droite. - Trouvez l'article sur Google Scholar et cliquez sur les guillemets doubles sous le titre, puis dans la fenêtre contextuelle, cliquez sur
BibTeX. - S'il n'y a pas de document associé (par exemple, il n'y a qu'un site web), vous pouvez utiliser l' éditeur BibTeX en ligne pour créer une entrée BibTeX personnalisée (le menu déroulant propose 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 étiquettes qui ne s'appliquent pas à l'ensemble de données.
- Les étiquettes valides sont répertoriées dans tensorflow_datasets/core/valid_tags.txt .
- Pour ajouter une étiquette à cette liste, veuillez envoyer une PR.
Conserver l'ordre des ensembles de données
Par défaut, les enregistrements des jeux de données sont mélangés lors de leur stockage afin d'uniformiser la répartition des classes, car les enregistrements d'une même classe sont souvent contigus. Pour que le jeu de données soit trié selon la clé générée 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,
# [...]
)
N'oubliez pas que la désactivation du brassage a un impact sur les performances, car les fragments ne peuvent plus être lus en parallèle.
_split_generators : télécharge et divise les données
Téléchargement et extraction des données sources
La plupart des jeux de données nécessitent le téléchargement de données depuis le web. Ceci est réalisé à l'aide de l'argument d'entrée tfds.download.DownloadManager de _split_generators . dl_manager possède les méthodes suivantes :
-
download: prend en chargehttp(s)://,ftp(s):// -
extract: prend actuellement en charge les fichiers.zip,.gzet.tar. -
download_and_extract: Identique àdl_manager.extract(dl_manager.download(urls))
Toutes ces méthodes renvoient des objets tfds.core.Path (alias de epath.Path ), qui sont des objets de type pathlib.Path .
Ces méthodes prennent en charge des structures imbriquées arbitraires ( 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 ensuite 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 directement les archives
dl_manager.iter_archive lit les archives séquentiellement sans les extraire. Cela permet d'économiser de l'espace de stockage et d'améliorer les performances sur certains systèmes de fichiers.
for filename, fobj in dl_manager.iter_archive('path/to/archive.zip'):
...
fobj possède les mêmes méthodes que with open('rb') as fobj: (par exemple fobj.read() )
Spécification des divisions de l'ensemble de données
Si le jeu de données comporte des divisions prédéfinies (par exemple, MNIST propose des ensembles train et test ), conservez-les. Sinon, spécifiez uniquement une division « all . Les utilisateurs peuvent créer dynamiquement leurs propres sous-divisions grâce à l' API `subsplit` (par exemple split='train[80%:]' ). Notez que toute chaîne alphabétique peut être utilisée comme nom de division, à l'exception de all mentionné précédemment.
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 division à partir des données sources.
Cette méthode lira généralement les artefacts du jeu de données source (par exemple un fichier CSV) et produira des tuples (key, feature_dict) :
-
key: Identifiant de l’exemple. Utilisée pour mélanger les exemples de manière déterministe à l’aidehash(key)ou pour trier par clé lorsque le mélange est désactivé (voir la section « Conserver l’ordre du jeu de données » ). Doit être :- unique : Si deux exemples utilisent la même clé, une exception sera levée.
- déterministe : ne devrait pas dépendre de l'ordre
download_dir,os.path.listdir,... La génération des données à deux reprises devrait donner la même clé. - comparable : Si le brassage est désactivé, la clé sera utilisée pour trier l'ensemble de données.
-
feature_dict: Undictcontenant les valeurs d'exemple.- 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, la 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],...) - Consultez le guide des connecteurs de fonctionnalités 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 aux fichiers et tf.io.gfile
Pour assurer la compatibilité avec les systèmes de stockage Cloud, évitez d'utiliser les opérations d'E/S intégrées de Python.
En revanche, 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 devrait être préféré à tf.io.gfile (voir rational .
Dépendances supplémentaires
Certains jeux de données nécessitent des dépendances Python supplémentaires uniquement lors de leur 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 au dépôt TFDS, veuillez utiliser tfds.core.lazy_imports afin de limiter la taille du package tensorflow-datasets . Les utilisateurs n'installeront les dépendances supplémentaires qu'en cas de besoin.
Pour utiliser lazy_imports :
- Ajoutez une entrée pour votre jeu de données dans
DATASET_EXTRASdanssetup.py. Cela permet aux utilisateurs d'exécuter, par exemple,pip install 'tensorflow-datasets[svhn]'pour installer les dépendances supplémentaires. - Ajoutez une entrée pour votre importation à
LazyImporteret àLazyImportsTest. - Utilisez
tfds.core.lazy_importspour accéder à la dépendance (par exemple,tfds.core.lazy_imports.scipy) dans votreDatasetBuilder.
Données corrompues
Certains jeux de données ne sont pas parfaitement propres et contiennent des données corrompues (par exemple, des images au format JPEG, mais certaines sont invalides). Il convient d'ignorer ces exemples, en indiquant dans la description du jeu de données le nombre d'exemples supprimés et la raison de cette suppression.
Configuration/variantes du jeu de données (tfds.core.BuilderConfig)
Certains jeux de données peuvent comporter plusieurs variantes, ou options de prétraitement et d'écriture sur disque. Par exemple, cycle_gan possède une configuration par paire d'objets ( cycle_gan/horse2zebra , cycle_gan/monet2photo , etc.).
Cela se fait via tfds.core.BuilderConfig :
Définissez votre objet de configuration comme une 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 = []dansMyDatasetqui liste lesMyDatasetConfigexposés par l'ensemble de données.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-argsUtilisez
self.builder_configdansMyDatasetpour configurer la génération des données (par exemple :shape=self.builder_config.img_size). Cela peut inclure la définition de valeurs différentes dans_info()ou la modification de l'accès aux données de téléchargement.
Remarques :
- Chaque configuration possède un nom unique. Le nom complet d'une configuration est
dataset_name/config_name(par exemplecoco/2017). - Si aucune configuration n'est spécifiée, la première configuration dans
BUILDER_CONFIGSsera utilisée (par exempletfds.load('c4')par défautc4/en).
Voir anli pour un exemple d'ensemble de données utilisant BuilderConfig .
Version
Le terme « version » peut avoir deux significations différentes :
- La version des données originales « externes » : par exemple COCO v2019, v2017,...
- Version interne du code TFDS : par exemple, renommer une fonctionnalité dans
tfds.features.FeaturesDict, corriger un bogue dans_generate_examples
Pour mettre à jour un ensemble de données :
- Pour la mise à jour de données « externes » : plusieurs utilisateurs peuvent souhaiter accéder simultanément à une année/version spécifique. Ceci est réalisé en utilisant un
tfds.core.BuilderConfigpar version (par exemple,coco/2017,coco/2019) ou une classe par version (par exemple,Voc2007,Voc2012). - Pour les mises à jour de code « internes » : les utilisateurs téléchargent uniquement la version la plus récente. Toute mise à jour de code doit incrémenter l’attribut de classe
VERSION(par exemple, de1.0.0àVERSION = tfds.core.Version('2.0.0')) conformément au versionnage sémantique .
Ajouter une importation pour l'enregistrement
N'oubliez pas d'importer le module de jeu de données dans votre projet __init__ pour qu'il soit 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 l'importation du module au fichier __init__.py de son sous-répertoire (par exemple image/__init__.py .
Vérifiez les pièges courants liés à la mise en œuvre.
Veuillez vérifier les pièges courants liés à la mise en œuvre .
Testez votre ensemble de données
Téléchargez et préparez : tfds build
Pour générer l'ensemble de données, exécutez la tfds build depuis le répertoire my_dataset/ :
cd path/to/datasets/my_dataset/
tfds build --register_checksums
Quelques options utiles pour le développement :
-
--pdb: Passer en mode débogage si une exception est levée. -
--overwrite: Supprime les fichiers existants si l'ensemble de données a déjà été généré. -
--max_examples_per_split: Ne générer que les X premiers exemples (par défaut 1), au lieu de l'ensemble de données complet. -
--register_checksums: Enregistre les sommes de contrôle des URL téléchargées. À utiliser uniquement en phase de développement.
Consultez la documentation de l'interface de ligne de commande pour obtenir la liste complète des options.
Sommes de contrôle
Il est recommandé d'enregistrer les sommes de contrôle de vos ensembles de données pour garantir le déterminisme, faciliter la documentation, etc. Cela se fait en générant l'ensemble de données avec --register_checksums (voir section précédente).
Si vous publiez vos jeux 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 ensemble de données
tfds.testing.DatasetBuilderTestCase est un TestCase de base permettant de tester intégralement un jeu de données. Il utilise des « données factices » comme données de test, imitant la structure du jeu de données source.
- Les données de test doivent être placées dans le répertoire
my_dataset/dummy_data/et reproduire les artefacts du jeu de données source tels que téléchargés et extraits. Elles peuvent être créées manuellement ou automatiquement à l'aide d'un script ( exemple de script ). - Veillez à utiliser des données différentes dans vos divisions de données de test, car le test échouera si les divisions de vos ensembles de données se chevauchent.
- Les données de test ne doivent contenir aucun élément protégé par le droit d'auteur . En cas de doute, n'utilisez pas d'éléments provenant du jeu de données original pour créer les données.
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 nous efforçons constamment d'améliorer le processus de création des jeux de données, mais nous ne pouvons y parvenir que si nous sommes informés des problèmes rencontrés. Quels problèmes ou erreurs avez-vous rencontrés lors de la création du jeu de données ? Y a-t-il eu une étape qui vous a paru confuse ou qui n'a pas fonctionné du premier coup ?
Veuillez partager vos commentaires sur GitHub .