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) :
def load_dataset(builder, **as_dataset_kwargs):
"""Load the dataset with the tfds_id."""
read_config = as_dataset_kwargs.pop('read_config', tfds.ReadConfig())
read_config.add_tfds_id = True # Set `True` to return the 'tfds_id' key
return builder.as_dataset(read_config=read_config, **as_dataset_kwargs)
def print_ex_ids(
builder,
*,
take: int,
skip: int = None,
**as_dataset_kwargs,
) -> None:
"""Print the example ids from the given dataset split."""
ds = load_dataset(builder, **as_dataset_kwargs)
if skip:
ds = ds.skip(skip)
ds = ds.take(take)
exs = [ex['tfds_id'].numpy().decode('utf-8') for ex in ds]
exs = [id_to_int(tfds_id, builder=builder) for tfds_id in exs]
print(exs)
def id_to_int(tfds_id: str, builder) -> str:
"""Format the tfds_id in a more human-readable."""
match = re.match(r'\w+-(\w+).\w+-(\d+)-of-\d+__(\d+)', tfds_id)
split_name, shard_id, ex_id = match.groups()
split_info = builder.info.splits[split_name]
return sum(split_info.shard_lengths[:int(shard_id)]) + int(ex_id)
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 :
- 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]
- 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 surds.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.
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 decycle_length
,block_length
et de l' ordre de tesson). Puis leskip
,take
pour chaque tesson pourrait être injecté parexperimental_interleave_sort_fn
.Avec
ds.shuffle
il est probablement impossible sans rejouant le pipeline de formation complète. Il faudrait sauver leds.shuffle
état tampon pour en déduire que des exemples ont été lus. Les exemples pourraient être non continu (par exempleshard5_ex2
,shard5_ex4
lu mais passhard5_ex3
).Avec
ds.shuffle
, d' une façon serait de sauver tous les shards_ids / example_ids lecture (déduisenttfds_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)]