TFDS et déterminisme

Voir sur TensorFlow.org Exécuter dans Google Colab Voir sur GitHub Télécharger le cahier

Ce document explique :

  • Les garanties TFDS sur le déterminisme
  • Dans quel ordre TFDS lit-il les exemples
  • Diverses mises en garde et pièges

Installer

Ensembles de données

Un certain contexte est nécessaire pour comprendre comment TFDS lit les données.

Lors de la génération, TFDS écrire les données originales dans normalisées .tfrecord fichiers. Pour les grands ensembles de données, plusieurs .tfrecord fichiers sont créés, chacun contenant de multiples exemples. Nous appelons chaque .tfrecord fichier un tesson.

Ce guide utilise imagenet qui contient 1024 fragments :

import re
import tensorflow_datasets as tfds

imagenet = tfds.builder('imagenet2012')

num_shards = imagenet.info.splits['train'].num_shards
num_examples = imagenet.info.splits['train'].num_examples
print(f'imagenet has {num_shards} shards ({num_examples} examples)')
imagenet has 1024 shards (1281167 examples)

Trouver les identifiants des exemples d'ensembles de données

Vous pouvez passer à la section suivante si vous souhaitez uniquement en savoir plus sur le déterminisme.

Chaque exemple de jeu de données est identifié de manière unique par un id (par exemple 'imagenet2012-train.tfrecord-01023-of-01024__32' ). Vous pouvez récupérer cette id en passant read_config.add_tfds_id = True qui ajoutera une 'tfds_id' clé du dict du tf.data.Dataset .

Dans ce tutoriel, nous définissons un petit util qui imprimera les exemples d'identifiants de l'ensemble de données (convertis en entier pour être plus lisibles par l'homme) :

Déterminisme à la lecture

Cette section explique garantie deterministim de tfds.load .

Avec shuffle_files=False (par défaut)

Par défaut TFDS donnent des exemples de manière déterministe ( shuffle_files=False )

# Same as: imagenet.as_dataset(split='train').take(20)
print_ex_ids(imagenet, split='train', take=20)
print_ex_ids(imagenet, split='train', take=20)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 1251, 1252, 1253, 1254]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 1251, 1252, 1253, 1254]

Pour les performances, TFDS lire plusieurs tessons en même temps à l' aide tf.data.Dataset.interleave . On voit dans cet exemple que TFDS passer à tesson 2 après avoir lu 16 exemples ( ..., 14, 15, 1251, 1252, ... ). Plus d'informations sur l'entrelacement ci-dessous.

De même, l'API subsplit est également déterministe :

print_ex_ids(imagenet, split='train[67%:84%]', take=20)
print_ex_ids(imagenet, split='train[67%:84%]', take=20)
[858382, 858383, 858384, 858385, 858386, 858387, 858388, 858389, 858390, 858391, 858392, 858393, 858394, 858395, 858396, 858397, 859533, 859534, 859535, 859536]
[858382, 858383, 858384, 858385, 858386, 858387, 858388, 858389, 858390, 858391, 858392, 858393, 858394, 858395, 858396, 858397, 859533, 859534, 859535, 859536]

Si vous êtes de formation pour plus d'une époque, la configuration ci - dessus n'est pas recommandé que toutes les époques lisent les tessons dans le même ordre (si aléatoire est limitée aux ds = ds.shuffle(buffer) taille de mémoire tampon).

Avec shuffle_files=True

Avec shuffle_files=True , tessons sont mélangées pour chaque époque, si la lecture n'est pas déterministe plus.

print_ex_ids(imagenet, split='train', shuffle_files=True, take=20)
print_ex_ids(imagenet, split='train', shuffle_files=True, take=20)
[568017, 329050, 329051, 329052, 329053, 329054, 329056, 329055, 568019, 568020, 568021, 568022, 568023, 568018, 568025, 568024, 568026, 568028, 568030, 568031]
[43790, 43791, 43792, 43793, 43796, 43794, 43797, 43798, 43795, 43799, 43800, 43801, 43802, 43803, 43804, 43805, 43806, 43807, 43809, 43810]

Voir la recette ci-dessous pour obtenir un brassage de fichiers déterministe.

Avertissement de déterminisme : entrelacer les arguments

Changement read_config.interleave_cycle_length , read_config.interleave_block_length va changer l'ordre des exemples.

TFDS repose sur tf.data.Dataset.interleave pour ne charger que quelques tessons à la fois, améliorer les performances et réduire la consommation de mémoire.

L'ordre d'exemple n'est garanti être le même que pour une valeur fixe d'arguments entrelacés. Voir entrelacer doc pour comprendre ce cycle_length et block_length correspondent aussi.

  • cycle_length=16 , block_length=16 (valeur par défaut, comme ci - dessus):
print_ex_ids(imagenet, split='train', take=20)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 1251, 1252, 1253, 1254]
  • cycle_length=3 , block_length=2 :
read_config = tfds.ReadConfig(
    interleave_cycle_length=3,
    interleave_block_length=2,
)
print_ex_ids(imagenet, split='train', read_config=read_config, take=20)
[0, 1, 1251, 1252, 2502, 2503, 2, 3, 1253, 1254, 2504, 2505, 4, 5, 1255, 1256, 2506, 2507, 6, 7]

Dans le second exemple, on voit que l'ensemble de données lu 2 ( block_length=2 ) exemples dans un tesson, puis passer à la prochaine tesson. Chaque 2 * 3 ( cycle_length=3 ) exemples, il remonte à la première tesson ( shard0-ex0, shard0-ex1, shard1-ex0, shard1-ex1, shard2-ex0, shard2-ex1, shard0-ex2, shard0-ex3, shard1-ex2, shard1-ex3, shard2-ex2,... ).

Sous-split et exemple de commande

Chaque exemple a un id 0, 1, ..., num_examples-1 . L' API subsplit sélectionner une tranche d'exemples (par exemple , le train[:x] sélectionner 0, 1, ..., x-1 ).

Cependant, dans le sous-split, les exemples ne sont pas lus dans l'ordre croissant des identifiants (en raison des fragments et de l'entrelacement).

Plus précisément, ds.take(x) et split='train[:x]' le split='train[:x]' ne sont pas équivalents!

Cela peut être vu facilement dans l'exemple d'entrelacement ci-dessus où les exemples proviennent de différents fragments.

print_ex_ids(imagenet, split='train', take=25)  # tfds.load(..., split='train').take(25)
print_ex_ids(imagenet, split='train[:25]', take=-1)  # tfds.load(..., split='train[:25]')
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 1251, 1252, 1253, 1254, 1255, 1256, 1257, 1258, 1259]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]

Après les 16 exemples de (block_length), .take(25) passe à la prochaine tesson alors que le train[:25] continuer à lire dans les exemples du premier fragment.

Recettes

Obtenez un brassage de fichiers déterministe

Il y a 2 façons d'avoir un brassage déterministe :

  1. Réglage de la shuffle_seed . Remarque : Cela nécessite de changer la graine à chaque époque, sinon les fragments seront lus dans le même ordre entre les époques.
read_config = tfds.ReadConfig(
    shuffle_seed=32,
)

# Deterministic order, different from the default shuffle_files=False above
print_ex_ids(imagenet, split='train', shuffle_files=True, read_config=read_config, take=22)
print_ex_ids(imagenet, split='train', shuffle_files=True, read_config=read_config, take=22)
[176411, 176412, 176413, 176414, 176415, 176416, 176417, 176418, 176419, 176420, 176421, 176422, 176423, 176424, 176425, 176426, 710647, 710648, 710649, 710650, 710651, 710652]
[176411, 176412, 176413, 176414, 176415, 176416, 176417, 176418, 176419, 176420, 176421, 176422, 176423, 176424, 176425, 176426, 710647, 710648, 710649, 710650, 710651, 710652]
  1. L' utilisation experimental_interleave_sort_fn : Cela donne un contrôle total sur lequel tessons sont lus et dans quel ordre, plutôt que de compter sur ds.shuffle ordre.
def _reverse_order(file_instructions):
  return list(reversed(file_instructions))

read_config = tfds.ReadConfig(
    experimental_interleave_sort_fn=_reverse_order,
)

# Last shard (01023-of-01024) is read first
print_ex_ids(imagenet, split='train', read_config=read_config, take=5)
[1279916, 1279917, 1279918, 1279919, 1279920]

Obtenez un pipeline préemptif déterministe

Celui-ci est plus compliqué. Il n'y a pas de solution simple et satisfaisante.

  1. Sans ds.shuffle et déterministe brassage, en théorie , il devrait être possible de compter les exemples qui ont été lus et en déduire que des exemples ont été lus à l' intérieur de chaque tesson (en fonction de cycle_length , block_length et de l' ordre de tesson). Puis le skip , take pour chaque tesson pourrait être injecté par experimental_interleave_sort_fn .

  2. Avec ds.shuffle il est probablement impossible sans rejouant le pipeline de formation complète. Il faudrait sauver le ds.shuffle état tampon pour en déduire que des exemples ont été lus. Les exemples pourraient être non continu (par exemple shard5_ex2 , shard5_ex4 lu mais pas shard5_ex3 ).

  3. Avec ds.shuffle , d' une façon serait de sauver tous les shards_ids / example_ids lecture (déduisent tfds_id ), déduisant ensuite les instructions de fichiers de cela.

Le cas le plus simple pour 1. est d'avoir .skip(x).take(y) rencontre train[x:x+y] rencontre. Cela demande:

  • Set cycle_length=1 (si les fragments sont lus séquentiellement)
  • Set shuffle_files=False
  • Ne pas utiliser ds.shuffle

Il ne doit être utilisé que sur un énorme ensemble de données où la formation n'est que d'une époque. Les exemples seraient lus dans l'ordre aléatoire par défaut.

read_config = tfds.ReadConfig(
    interleave_cycle_length=1,  # Read shards sequentially
)

print_ex_ids(imagenet, split='train', read_config=read_config, skip=40, take=22)
# If the job get pre-empted, using the subsplit API will skip at most `len(shard0)`
print_ex_ids(imagenet, split='train[40:]', read_config=read_config, take=22)
[40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61]
[40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61]

Trouver quels fragments/exemples sont lus pour un sous-split donné

Avec le tfds.core.DatasetInfo , vous avez un accès direct aux instructions de lecture.

imagenet.info.splits['train[44%:45%]'].file_instructions
[FileInstruction(filename='imagenet2012-train.tfrecord-00450-of-01024', skip=700, take=-1, num_examples=551),
 FileInstruction(filename='imagenet2012-train.tfrecord-00451-of-01024', skip=0, take=-1, num_examples=1251),
 FileInstruction(filename='imagenet2012-train.tfrecord-00452-of-01024', skip=0, take=-1, num_examples=1251),
 FileInstruction(filename='imagenet2012-train.tfrecord-00453-of-01024', skip=0, take=-1, num_examples=1251),
 FileInstruction(filename='imagenet2012-train.tfrecord-00454-of-01024', skip=0, take=-1, num_examples=1252),
 FileInstruction(filename='imagenet2012-train.tfrecord-00455-of-01024', skip=0, take=-1, num_examples=1251),
 FileInstruction(filename='imagenet2012-train.tfrecord-00456-of-01024', skip=0, take=-1, num_examples=1251),
 FileInstruction(filename='imagenet2012-train.tfrecord-00457-of-01024', skip=0, take=-1, num_examples=1251),
 FileInstruction(filename='imagenet2012-train.tfrecord-00458-of-01024', skip=0, take=-1, num_examples=1251),
 FileInstruction(filename='imagenet2012-train.tfrecord-00459-of-01024', skip=0, take=-1, num_examples=1251),
 FileInstruction(filename='imagenet2012-train.tfrecord-00460-of-01024', skip=0, take=1001, num_examples=1001)]