Charger un tfrecord externe avec TFDS

Si vous avez un proto tf.train.Example (à l'intérieur .tfrecord , .riegeli ,...), qui a été généré par des outils tiers, que vous souhaitez charger directement avec l'API tfds, alors cette page est pour vous.

Afin de charger vos fichiers .tfrecord , il vous suffit de :

  • Suivez la convention de dénomination TFDS.
  • Ajoutez des fichiers de métadonnées ( dataset_info.json , features.json ) avec vos fichiers tfrecord.

Limites:

Convention de dénomination des fichiers

TFDS prend en charge la définition d'un modèle pour les noms de fichiers, ce qui offre la flexibilité d'utiliser différents schémas de dénomination de fichiers. Le modèle est représenté par un tfds.core.ShardedFileTemplate et prend en charge les variables suivantes : {DATASET} , {SPLIT} , {FILEFORMAT} , {SHARD_INDEX} , {NUM_SHARDS} et {SHARD_X_OF_Y} . Par exemple, le schéma de dénomination de fichier par défaut de TFDS est : {DATASET}-{SPLIT}.{FILEFORMAT}-{SHARD_X_OF_Y} . Pour MNIST, cela signifie que les noms de fichiers se présentent comme suit :

  • mnist-test.tfrecord-00000-of-00001
  • mnist-train.tfrecord-00000-of-00001

Ajouter des métadonnées

Fournir la structure des fonctionnalités

Pour que TFDS puisse décoder le proto tf.train.Example , vous devez fournir la structure tfds.features correspondant à vos spécifications. Par exemple:

features = tfds.features.FeaturesDict({
    'image':
        tfds.features.Image(
            shape=(256, 256, 3),
            doc='Picture taken by smartphone, downscaled.'),
    'label':
        tfds.features.ClassLabel(names=['dog', 'cat']),
    'objects':
        tfds.features.Sequence({
            'camera/K': tfds.features.Tensor(shape=(3,), dtype=tf.float32),
        }),
})

Correspond aux spécifications tf.train.Example suivantes :

{
    'image': tf.io.FixedLenFeature(shape=(), dtype=tf.string),
    'label': tf.io.FixedLenFeature(shape=(), dtype=tf.int64),
    'objects/camera/K': tf.io.FixedLenSequenceFeature(shape=(3,), dtype=tf.int64),
}

La spécification des fonctionnalités permet à TFDS de décoder automatiquement les images, les vidéos,... Comme tout autre ensemble de données TFDS, les métadonnées des fonctionnalités (par exemple les noms d'étiquettes,...) seront exposées à l'utilisateur (par exemple info.features['label'].names ).

Si vous contrôlez le pipeline de génération

Si vous générez des ensembles de données en dehors de TFDS mais contrôlez toujours le pipeline de génération, vous pouvez utiliser tfds.features.FeatureConnector.serialize_example pour encoder vos données de dict[np.ndarray] en tf.train.Example proto bytes :

with tf.io.TFRecordWriter('path/to/file.tfrecord') as writer:
  for ex in all_exs:
    ex_bytes = features.serialize_example(data)
    writer.write(ex_bytes)

Cela garantira la compatibilité des fonctionnalités avec TFDS.

De même, un feature.deserialize_example existe pour décoder le proto ( exemple )

Si vous ne contrôlez pas le pipeline de génération

Si vous voulez voir comment tfds.features sont représentés dans un tf.train.Example , vous pouvez examiner ceci dans colab :

  • Pour traduire tfds.features dans la structure lisible par l'homme du tf.train.Example , vous pouvez appeler features.get_serialized_info() .
  • Pour obtenir la spécification exacte FixedLenFeature ,... transmise à tf.io.parse_single_example , vous pouvez utiliser spec = features.tf_example_spec

Obtenez des statistiques sur les fractionnements

TFDS nécessite de connaître le nombre exact d'exemples dans chaque fragment. Ceci est requis pour des fonctionnalités telles que len(ds) ou l' API subplit : split='train[75%:]' .

  • Si vous disposez de ces informations, vous pouvez créer explicitement une liste de tfds.core.SplitInfo et passer à la section suivante :

    split_infos = [
        tfds.core.SplitInfo(
            name='train',
            shard_lengths=[1024, ...],  # Num of examples in shard0, shard1,...
            num_bytes=0,  # Total size of your dataset (if unknown, set to 0)
        ),
        tfds.core.SplitInfo(name='test', ...),
    ]
    
  • Si vous ne connaissez pas ces informations, vous pouvez les calculer à l'aide du script compute_split_info.py (ou dans votre propre script avec tfds.folder_dataset.compute_split_info ). Il lancera un pipeline de faisceaux qui lira tous les fragments du répertoire donné et calculera les informations.

Ajouter des fichiers de métadonnées

Pour ajouter automatiquement les fichiers de métadonnées appropriés à votre ensemble de données, utilisez tfds.folder_dataset.write_metadata :

tfds.folder_dataset.write_metadata(
    data_dir='/path/to/my/dataset/1.0.0/',
    features=features,
    # Pass the `out_dir` argument of compute_split_info (see section above)
    # You can also explicitly pass a list of `tfds.core.SplitInfo`.
    split_infos='/path/to/my/dataset/1.0.0/',
    # Pass a custom file name template or use None for the default TFDS
    # file name template.
    filename_template='{SPLIT}-{SHARD_X_OF_Y}.{FILEFORMAT}',

    # Optionally, additional DatasetInfo metadata can be provided
    # See:
    # https://www.tensorflow.org/datasets/api_docs/python/tfds/core/DatasetInfo
    description="""Multi-line description."""
    homepage='http://my-project.org',
    supervised_keys=('image', 'label'),
    citation="""BibTex citation.""",
)

Une fois la fonction appelée une fois sur votre répertoire de jeux de données, les fichiers de métadonnées ( dataset_info.json ,...) ont été ajoutés et vos jeux de données sont prêts à être chargés avec TFDS (voir section suivante).

Charger l'ensemble de données avec TFDS

Directement depuis le dossier

Une fois les métadonnées générées, les ensembles de données peuvent être chargés à l'aide tfds.builder_from_directory qui renvoie un tfds.core.DatasetBuilder avec l'API TFDS standard (comme tfds.builder ) :

builder = tfds.builder_from_directory('~/path/to/my_dataset/3.0.0/')

# Metadata are available as usual
builder.info.splits['train'].num_examples

# Construct the tf.data.Dataset pipeline
ds = builder.as_dataset(split='train[75%:]')
for ex in ds:
  ...

Directement à partir de plusieurs dossiers

Il est également possible de charger des données à partir de plusieurs dossiers. Cela peut se produire, par exemple, dans l'apprentissage par renforcement lorsque plusieurs agents génèrent chacun un ensemble de données distinct et que vous souhaitez tous les charger ensemble. D'autres cas d'utilisation sont lorsqu'un nouvel ensemble de données est produit régulièrement, par exemple un nouvel ensemble de données par jour, et que vous souhaitez charger des données à partir d'une plage de dates.

Pour charger des données à partir de plusieurs dossiers, utilisez tfds.builder_from_directories , qui renvoie un tfds.core.DatasetBuilder avec l'API TFDS standard (comme tfds.builder ) :

builder = tfds.builder_from_directories(builder_dirs=[
    '~/path/my_dataset/agent1/1.0.0/',
    '~/path/my_dataset/agent2/1.0.0/',
    '~/path/my_dataset/agent3/1.0.0/',
])

# Metadata are available as usual
builder.info.splits['train'].num_examples

# Construct the tf.data.Dataset pipeline
ds = builder.as_dataset(split='train[75%:]')
for ex in ds:
  ...

Structure des dossiers (facultatif)

Pour une meilleure compatibilité avec TFDS, vous pouvez organiser vos données comme <data_dir>/<dataset_name>[/<dataset_config>]/<dataset_version> . Par exemple:

data_dir/
    dataset0/
        1.0.0/
        1.0.1/
    dataset1/
        config0/
            2.0.0/
        config1/
            2.0.0/

Cela rendra vos ensembles de données compatibles avec l'API tfds.load / tfds.builder , simplement en fournissant data_dir/ :

ds0 = tfds.load('dataset0', data_dir='data_dir/')
ds1 = tfds.load('dataset1/config0', data_dir='data_dir/')