Path: blob/master/site/pt-br/datasets/determinism.ipynb
25115 views
Copyright 2020 The TensorFlow Authors.
TFDS e determinismo
Este documento explica:
As garantias do TFDS sobre o determinismo
Em que ordem o TFDS lê os exemplos
Diversas ressalvas e problemas
Configuração
Datasets
É necessário algum contexto para entender como o TFDS lê os dados.
Durante a geração, o TFDS grava os dados originais em arquivos .tfrecord
padronizados. Para grandes datasets, múltiplos arquivos .tfrecord
são criados, cada um contendo múltiplos exemplos. Chamamos cada arquivo .tfrecord
de fragmento (shard).
Este guia usa imagenet que possui 1024 fragmentos:
Encontrando os IDs dos exemplos de datasets
Você pode pular para a seção seguinte se quiser apenas saber sobre determinismo.
Cada exemplo de dataset é identificado exclusivamente por um id
(por exemplo 'imagenet2012-train.tfrecord-01023-of-01024__32'
). Você pode recuperar esse id
passando read_config.add_tfds_id = True
que adicionará uma chave 'tfds_id'
no dict do tf.data.Dataset
.
Neste tutorial, definimos um pequeno utilitário que imprimirá os ids de exemplo do dataset (convertidos em inteiro para serem mais legíveis por humanos):
Determinismo ao ler
Esta seção explica a garantia determinística de tfds.load
.
Com shuffle_files=False
(padrão)
Por padrão, o TFDS entrega exemplos de forma determinística (shuffle_files=False
)
Para melhor desempenho, o TFDS lê vários fragmentos ao mesmo tempo usando tf.data.Dataset.interleave. Vemos neste exemplo que o TFDS muda para o fragmento 2 após ler 16 exemplos (..., 14, 15, 1251, 1252, ...
). Mais sobre o interleave abaixo.
Da mesma forma, a API subsplit também é determinística:
Se você estiver treinando para mais de uma época, a configuração acima não é recomendada, pois todas as épocas lerão os fragmentos na mesma ordem (portanto, a aleatoriedade será limitada ao tamanho do buffer ds = ds.shuffle(buffer)
).
Com shuffle_files=True
Com shuffle_files=True
, os fragmentos são embaralhados para cada época, portanto a leitura não é mais determinística.
Observação: Definir shuffle_files=True
também desativa deterministic
em tf.data.Options
para aumentar o desempenho. Portanto, mesmo pequenos datasets que possuem apenas um único fragmento (como mnist) tornam-se não determinísticos.
Veja a receita abaixo para obter embaralhamento determinístico de arquivos.
Ressalva ao determinismo: argumentos de intercalação
Alterar read_config.interleave_cycle_length
, read_config.interleave_block_length
irá mudar a ordem dos exemplos.
O TFDS depende de tf.data.Dataset.interleave para carregar apenas alguns fragmentos de cada vez, melhorando o desempenho e reduzindo o uso de memória.
A ordem dos exemplos só é garantida como sendo a mesma para um valor fixo de argumentos de intercalação. Veja o documento sobre intercalação (interleave) para também entender a que correspondem cycle_length
e block_length
.
cycle_length=16
,block_length=16
(padrão, igual ao acima):
cycle_length=3
,block_length=2
:
No segundo exemplo, vemos que o dataset lê 2 (block_length=2
) exemplos num fragmento e depois muda para o próximo fragmento. A cada 2 * 3 (cycle_length=3
) exemplos, ele volta para o primeiro shard0-ex0, shard0-ex1, shard1-ex0, shard1-ex1, shard2-ex0, shard2-ex1, shard0-ex2, shard0-ex3, shard1-ex2, shard1-ex3, shard2-ex2,...
).
Subsplit e ordem dos exemplos
Cada exemplo possui um id 0, 1, ..., num_examples-1
. A API subsplit seleciona uma fatia de exemplos (por exemplo, train[:x]
select 0, 1, ..., x-1
).
No entanto, dentro do subsplit, os exemplos não são lidos em ordem crescente de id (devido a fragmentos e intercalação).
Mais especificamente, ds.take(x)
e split='train[:x]'
não são equivalentes!
Isto pode ser visto facilmente no exemplo de intercalação acima, onde os exemplos vêm de fragmentos diferentes.
Depois dos 16 exemplos (block_length), .take(25)
muda para o fragmento seguinte enquanto train[:25]
continua lendo exemplos do primeiro fragmento.
Receitas
Obtenha embaralhamento determinístico de arquivos
Há 2 maneiras de se obter embaralhamento determinístico:
Configurando o
shuffle_seed
. Observação: Isto requer a alteração da semente em cada época, caso contrário, os fragmentos serão lidos na mesma ordem entre épocas.
Usando
experimental_interleave_sort_fn
: Isto garante controle total sobre quais fragmentos são lidos e em qual ordem, em vez de depender da ordem emds.shuffle
.
Obtenha a pipeline preemptiva determinística
Este é mais complicado. Não existe uma solução fácil e satisfatória.
Sem
ds.shuffle
e com embaralhamento determinístico, em teoria deveria ser possível contar os exemplos que foram lidos e deduzir quais exemplos foram lidos em cada fragmento (como função decycle_length
,block_length
e ordem do fragmento). Então oskip
,take
para cada fragmento poderia ser injetado através deexperimental_interleave_sort_fn
.Com
ds.shuffle
, é provavelmente impossível sem repetir o pipeline de treinamento completo. Seria necessário salvar o estado do bufferds.shuffle
para deduzir quais exemplos foram lidos. Os exemplos podem ser não contínuos (por exemploshard5_ex2
,shard5_ex4
foram lidos, mas nãoshard5_ex3
).Com
ds.shuffle
, uma solução seria salvar todos os shards_ids/example_ids lidos (deduzidos detfds_id
) e, em seguida, deduzir as instruções do arquivo a partir daí.
O caso mais simples para 1.
é ter .skip(x).take(y)
match train[x:x+y]
match. Isto requer:
Definir o
cycle_length=1
(para que os fragmentos sejam lidos sequencialmente)Definir
shuffle_files=False
Não usar
ds.shuffle
Ele só deve ser usado em grandes datasets onde o treinamento dura apenas 1 época. Os exemplos seriam lidos na ordem aleatória padrão.
Descubra quais fragmentos/exemplos são lidos para uma determinada subdivisão
Com o tfds.core.DatasetInfo
, você tem acesso direto às instruções de leitura.