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 d'ensembles de données pour voir si l'ensemble de données que vous souhaitez est déjà présent.
TL;DR
Le moyen le plus simple d'écrire un nouvel ensemble 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 nouvel ensemble de données avec tfds.load('my_dataset')
:
-
tfds.load
détectera et chargera automatiquement l'ensemble 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. L'accès ultérieur lira directement ces fichiers prétraités.
La plupart du prétraitement est effectué automatiquement. Chaque ensemble 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 ensemble de données
Modèle par défaut : tfds new
Utilisez TFDS CLI 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 en conséquence.
Exemple d'ensemble de données
Tous les ensembles de données sont des sous-classes implémentées de tfds.core.DatasetBuilder
, qui prend en charge la plupart des passe-partout. Il prend en charge :
- Ensembles de données petits/moyens pouvant être générés sur une seule machine (ce tutoriel).
- Très grands ensembles de données qui nécessitent une génération distribuée (en utilisant Apache Beam , voir notre guide sur les énormes ensembles de données )
Voici un exemple minimal de générateur d'ensemble 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 proposons des générateurs de jeux de données prêts à l'emploi pour prendre en charge la plupart du traitement des données.
Voyons en détail les 3 méthodes abstraites pour écraser.
_info
: métadonnées de l'ensemble 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
: Ceci spécifie la structure, la forme,... 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 .
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 : recherchez 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 y a juste 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 à l'ensemble de données.
- Les balises valides sont répertoriées dans tensorflow_datasets/core/valid_tags.txt .
- Pour ajouter une balise à 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 lors du stockage afin de rendre la répartition des classes plus uniforme dans l'ensemble de données, car les enregistrements appartenant à la même classe sont souvent contigus. Afin de spécifier que l'ensemble de données doit être trié en fonction de 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 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 ensembles de données doivent télécharger des données à partir du Web. Cela se fait en utilisant 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 les 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 l'archive directement
dl_manager.iter_archive
lit les archives 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 qu'avec 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 des répartitions prédéfinies (par exemple, MNIST
a des répartitions train
et test
), conservez-les. Sinon, spécifiez uniquement une seule répartition all
. Les utilisateurs peuvent créer dynamiquement leurs propres sous-splits avec l' API subsplit (par exemple split='train[80%:]'
). Notez que n'importe quelle chaîne alphabétique peut être utilisée comme nom de partage, à 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
: Exemple de générateur
_generate_examples
génère les exemples pour chaque division à 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 les exemples de manière déterministe à l'aidehash(key)
ou pour trier par clé lorsque la lecture aléatoire est désactivée (voir la section Maintenir l'ordre des ensembles de données ). Devrait ê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 fois devrait donner la même clé. - comparable : Si la lecture aléatoire est désactivée, la clé sera utilisée pour trier l'ensemble 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 du connecteur 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
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, 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, l'ensemble de données SVHN utilise scipy
pour charger certaines données.
Si vous ajoutez un ensemble de données dans le référentiel TFDS, veuillez utiliser tfds.core.lazy_imports
pour que le package tensorflow-datasets
reste 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 dans
LazyImporter
et dansLazyImportsTest
. - 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 fichiers JPEG invalides). 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 de l'ensemble de données (tfds.core.BuilderConfig)
Certains ensembles de données peuvent avoir plusieurs variantes ou options sur la manière 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
exposé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-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 valeurs différentes 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
). - Si elle n'est pas spécifiée, la première configuration dans
BUILDER_CONFIGS
sera utilisée (par exemple,tfds.load('c4')
par défautc4/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 des données originales "externes" : 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 du code « interne » : les utilisateurs téléchargent uniquement la version la plus récente. Toute mise à jour de code doit augmenter l'attribut de classe
VERSION
(par exemple de1.0.0
àVERSION = tfds.core.Version('2.0.0')
) après le versionnement 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 courants de la mise en œuvre
Veuillez vérifier les pièges courants de 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 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
: Entrez 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
: génère uniquement les X premiers exemples (1 par défaut), 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.
Consultez la documentation CLI pour la liste complète des indicateurs.
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, l'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 ensemble de données
tfds.testing.DatasetBuilderTestCase
est un TestCase
de base pour 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 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 que 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 répartitions de données de test, car le test échouera si les répartitions de votre ensemble 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, ne créez pas les données en utilisant des éléments 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 du premier coup ?
Veuillez partager vos commentaires sur GitHub .