Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
tensorflow
GitHub Repository: tensorflow/docs-l10n
Path: blob/master/site/es-419/datasets/determinism.ipynb
25115 views
Kernel: Python 3
#@title Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License.

TFDS y determinismo

En este documento se explica:

  • Las garantías de TFDS en el determinismo

  • El orden en el que TFDS lee los ejemplos

  • Una variedad de precauciones y errores

Preparación

Conjuntos de datos

Se necesita algo de contexto para entender cómo TFDS lee los datos.

Durante la generación, TFDS escribe los datos originales en archivos .tfrecord estandarizados. Para los conjuntos de datos grandes, se crean varios archivos .tfrecord, cada uno de los cuales contiene varios ejemplos. A cada archivo .tfrecord lo llamamos partición.

Esta guía usa imagenet que tiene 1024 particiones:

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)

Encontrar los id de los ejemplos del conjunto de datos

Vaya a la siguiente sección si solo desea saber sobre el determinismo.

Cada ejemplo de los conjuntos de datos se identifica de forma única mediante un id (por ejemplo 'imagenet2012-train.tfrecord-01023-of-01024__32'). Se puede recuperar el id al pasar read_config.add_tfds_id = True, que agregará una clave 'tfds_id' en el dict de tf.data.Dataset.

En este tutorial, definimos una pequeña util que imprimirá los id de ejemplo del conjunto de datos (convertidos en números enteros para que sean más legibles para los humanos):

#@title 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)

Determinismo cuando se lee

En esta sección se explica la garantía determinista de tfds.load.

Con shuffle_files=False (predeterminado)

De forma predeterminada, TFDS produce ejemplos de forma determinista (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]

Para mejorar el rendimiento, TFDS lee varias particiones al mismo tiempo con tf.data.Dataset.interleave. En este ejemplo, vemos que TFDS cambia a la partición 2 después de leer 16 ejemplos (..., 14, 15, 1251, 1252, ...). A continuación, tiene más información sobre la intercalación.

De manera similar, la API de subdivisión también es determinista:

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 está entrenando para más de una época, no se recomienda la configuración anterior ya que todas las épocas leerán las particiones en el mismo orden (por lo que la aleatoriedad se limita al tamaño del búfer ds = ds.shuffle(buffer)).

Con shuffle_files=True

Con shuffle_files=True, las particiones se aleatorizan para cada época, por lo que la lectura ya no es determinista.

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]

Nota: Al configurar shuffle_files=True también se desactiva deterministic en tf.data.Options para mejorar el rendimiento. Por lo tanto, incluso los conjuntos de datos pequeños que solo tienen una partición (como mnist) se vuelven no deterministas.

Vea la receta a continuación para obtener una aleatorización de archivos determinista.

Advertencia de determinismo: intercalar args

Al cambiar read_config.interleave_cycle_length, read_config.interleave_block_length cambiará el orden de los ejemplos.

TFDS depende de tf.data.Dataset.interleave para cargar solo unas pocas particiones a la vez, lo que mejora el rendimiento y reduce el uso de memoria.

Solo se puede garantizar que el orden del ejemplo sea el mismo para un valor fijo de args intercalados. Consulte el documento de intercalación para entender qué cycle_length y block_length corresponden también.

  • cycle_length=16, block_length=16 (predeterminado, igual que antes):

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]

En el segundo ejemplo, vemos que el conjunto de datos lee 2 ejemplos (block_length=2) en una partición y luego cambia la siguiente partición. Cada 2 * 3 ejemplos (cycle_length=3), vuelve a la primera partición shard0-ex0, shard0-ex1, shard1-ex0, shard1-ex1, shard2-ex0, shard2-ex1, shard0-ex2, shard0-ex3, shard1-ex2, shard1-ex3, shard2-ex2,...).

Subdivisión y orden del ejemplo

Cada ejemplo tiene un id 0, 1, ..., num_examples-1. La API de subdivisión selecciona un segmento de ejemplos (por ejemplo, train[:x] selecciona 0, 1, ..., x-1).

Sin embargo, dentro de la subdivisión, los ejemplos no se leen en orden ascendiente del id (debido a las particiones y la intercalación).

Más específicamente, ds.take(x) y split='train[:x]' no son equivalentes.

Esto se puede ver fácilmente en el ejemplo de intercalación anterior, donde los ejemplos provienen de diferentes particiones.

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]

Después de los 16 ejemplos (block_length), .take(25) cambia a la siguiente partición mientras que train[:25] continúa leyendo ejemplos desde la primera partición.

Recetas

Obtenga una aleatorización de archivos determinista

Hay 2 formas de realizar una aleatorización determinista:

  1. Configurar shuffle_seed. Nota: Se requiere cambiar el valor de iniciación en cada época; de lo contrario, se leerán las particiones en el mismo orden entre épocas.

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. Con experimental_interleave_sort_fn: brinda control total sobre qué particiones se leen y en qué orden, en lugar de depender del orden ds.shuffle.

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]

Obtener una canalización determinista que se puede interrumpir

Éste es más complicado. No existe una solución fácil y satisfactoria.

  1. En teoría, sin ds.shuffle y con una aleatorización determinista, debería ser posible contar los ejemplos que se han leído y deducir qué ejemplos se han leído en cada partición (como una función de cycle_length, block_length y el orden de las particiones). Luego, se podría instertar skip, take para cada partición mediante experimental_interleave_sort_fn.

  2. Es probable que no sea posible con ds.shuffle sin volver a reproducir toda la canalización del entrenamiento. Sería necesario guardar el estado del búfer ds.shuffle para deducir qué ejemplos se han leído. Los ejemplos podrían ser discontinuos (por ejemplo, que se lea shard5_ex2, shard5_ex4 pero no shard5_ex3).

  3. Con ds.shuffle, una forma sería guardar todos los shards_ids/example_ids leídos (que se deducen de tfds_id) y luego deducir las instrucciones del archivo a partir de eso.

El caso más simple para 1. es hacer que .skip(x).take(y) coincida con train[x:x+y]. Se debe hacer lo siguiente:

  • Establecer cycle_length=1 (para que se lean las particiones secuencialmente)

  • Establecer shuffle_files=False

  • No usar ds.shuffle

Solo debe usarse en conjuntos de datos grandes donde el entrenamiento es de solo 1 época. Los ejemplos se leerán en el orden aleatorio predeterminado.

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]

Encuentrar qué particiones/ejemplos se leen para una subdivisión determinada

Con tfds.core.DatasetInfo, se tiene acceso directo a las instrucciones de lectura.

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)]