De acordo com a documentação do TensorFlow , os métodos prefetch
e map
da tf.contrib.data.Dataset
classe, ambos têm um parâmetro chamado buffer_size
.
Para o prefetch
método, o parâmetro é conhecido como buffer_size
e de acordo com a documentação:
buffer_size: Um tf.int64 escalar tf.Tensor, representando o número máximo de elementos que serão armazenados em buffer durante a pré-busca.
Para o map
método, o parâmetro é conhecido como output_buffer_size
e de acordo com a documentação:
output_buffer_size: (Opcional.) Um tf.int64 escalar tf.Tensor, representando o número máximo de elementos processados que serão armazenados em buffer.
Da mesma forma para o shuffle
método, aparece a mesma quantidade e de acordo com a documentação:
buffer_size: Um tf.int64 escalar tf.Tensor, representando o número de elementos deste conjunto de dados do qual o novo conjunto de dados terá uma amostra.
Qual é a relação entre esses parâmetros?
Suponha que eu crie um Dataset
objeto da seguinte maneira:
tr_data = TFRecordDataset(trainfilenames)
tr_data = tr_data.map(providefortraining, output_buffer_size=10 * trainbatchsize, num_parallel_calls\
=5)
tr_data = tr_data.shuffle(buffer_size= 100 * trainbatchsize)
tr_data = tr_data.prefetch(buffer_size = 10 * trainbatchsize)
tr_data = tr_data.batch(trainbatchsize)
Que papel está sendo desempenhado pelos buffer
parâmetros no snippet acima?
Respostas:
TL; DR Apesar de seus nomes semelhantes, esses argumentos têm significados bastante diferentes. O
buffer_size
inDataset.shuffle()
pode afetar a aleatoriedade de seu conjunto de dados e, portanto, a ordem em que os elementos são produzidos. Obuffer_size
emDataset.prefetch()
somente afeta o tempo que leva para produzir o próximo elemento.O
buffer_size
argumento intf.data.Dataset.prefetch()
e ooutput_buffer_size
argumento intf.contrib.data.Dataset.map()
fornecem uma maneira de ajustar o desempenho do pipeline de entrada: ambos os argumentos dizem ao TensorFlow para criar um buffer de no máximobuffer_size
elementos e um thread de segundo plano para preencher esse buffer no segundo plano. (Observe que removemos ooutput_buffer_size
argumento deDataset.map()
quando ele mudou detf.contrib.data
paratf.data
. O novo código deve ser usadoDataset.prefetch()
depoismap()
para obter o mesmo comportamento.)Adicionar um buffer de pré-busca pode melhorar o desempenho ao sobrepor o pré-processamento de dados à computação downstream. Normalmente, é mais útil adicionar um pequeno buffer de pré-busca (talvez com apenas um único elemento) no final do pipeline, mas pipelines mais complexos podem se beneficiar de pré-busca adicional, especialmente quando o tempo para produzir um único elemento pode variar.
Em contraste, o
buffer_size
argumento paratf.data.Dataset.shuffle()
afeta a aleatoriedade da transformação. Projetamos aDataset.shuffle()
transformação (como atf.train.shuffle_batch()
função que ela substitui) para lidar com conjuntos de dados que são muito grandes para caber na memória. Em vez de embaralhar todo o conjunto de dados, ele mantém um buffer debuffer_size
elementos e seleciona aleatoriamente o próximo elemento desse buffer (substituindo-o pelo próximo elemento de entrada, se houver um disponível). Alterar o valor debuffer_size
afeta o quão uniforme é o embaralhamento: sebuffer_size
for maior que o número de elementos no conjunto de dados, você obtém um embaralhamento uniforme; se for1
então você não terá nenhum embaralhamento. Para conjuntos de dados muito grandes, uma abordagem "boa o suficiente" típica é fragmentar aleatoriamente os dados em vários arquivos uma vez antes do treinamento, depois embaralhar os nomes de arquivo uniformemente e usar um buffer de embaralhamento menor. No entanto, a escolha apropriada dependerá da natureza exata do seu trabalho de treinamento.fonte
tf.data.Dataset.shuffle()
. Gostaria de saber o processo exato de embaralhamento. Digamos que as primeirasbatch_size
amostras sejam escolhidas aleatoriamente a partir dos primeirosbuffer_size
elementos e assim por diante.buffer_size
para igualar o tamanho do arquivo (e embaralhar os arquivos, é claro).dataset.shuffle(buffer_size=1)
embaralhamento ainda ocorre. Alguma ideia?Importância de
buffer_size
emshuffle()
Eu queria continuar a resposta anterior de @mrry para enfatizar a importância de
buffer_size
intf.data.Dataset.shuffle()
.Ter uma baixa
buffer_size
não vai apenas causar embaralhamento inferior em alguns casos: pode atrapalhar todo o seu treinamento.Um exemplo prático: classificador gato
Suponha, por exemplo, que você esteja treinando um classificador de gato em imagens e seus dados sejam organizados da seguinte maneira (com
10000
imagens em cada categoria):Uma maneira padrão de inserir dados
tf.data
pode ser ter uma lista de nomes de arquivos e uma lista de rótulos correspondentes e usartf.data.Dataset.from_tensor_slices()
para criar o conjunto de dados:O grande problema com o código acima é que o conjunto de dados não será embaralhado da maneira certa. Por volta da primeira metade de uma época, veremos apenas imagens de gatos e, na segunda metade, apenas imagens que não sejam de gatos. Isso vai prejudicar muito o treinamento.
No início do treinamento, o conjunto de dados
1000
pegará os primeiros nomes de arquivo e os colocará em seu buffer, em seguida, escolherá um aleatoriamente entre eles. Como todas as primeiras1000
imagens são imagens de gato, escolheremos apenas imagens de gato no início.A correção aqui é ter certeza de que
buffer_size
é maior que20000
, ou embaralhar antecipadamentefilenames
elabels
(com os mesmos índices, obviamente).Uma vez que armazenar todos os nomes de arquivos e rótulos na memória não é um problema, podemos usar
buffer_size = len(filenames)
para garantir que tudo seja misturado. Certifique-se de chamartf.data.Dataset.shuffle()
antes de aplicar as transformações pesadas (como ler as imagens, processá-las, lote ...).A lição é sempre verificar novamente o que o embaralhamento fará. Uma boa maneira de detectar esses erros pode ser traçar a distribuição dos lotes ao longo do tempo (certifique-se de que os lotes contêm aproximadamente a mesma distribuição do conjunto de treinamento, metade gato e metade não gato em nosso exemplo).
fonte
filename_01001
) e a adiciona. A segunda amostra é retirada aleatoriamente desses 1000 nomes de arquivos (1001 primeiros nomes de arquivos menos a primeira amostra).tf.summary.histogram
para traçar a distribuição dos rótulos ao longo do tempo.Código
Resultado
[298] [326] [2] [351] [92] [398] [72] [134] [404] [378] [238] [131] [369] [324] [35] [182] [441 ] [370] [372] [144] [77] [11] [199] [65] [346] [418] [493] [343] [444] [470] [222] [83] [61] [ 81] [366] [49] [295] [399] [177] [507] [288] [524] [401] [386] [89] [371] [181] [489] [172] [159] [195] [232] [160] [352] [495] [241] [435] [127] [268] [429] [382] [479] [519] [116] [395] [165] [233 ] [37] [486] [553] [111] [525] [170] [571] [215] [530] [47] [291] [558] [21] [245] [514] [103] [ 45] [545] [219] [468] [338] [392] [54] [139] [339] [448] [471] [589] [321] [223] [311] [234] [314]
fonte
Na verdade, a resposta de @olivier-moindrot não está correta.
Você pode verificá-lo criando nomes de arquivos e etiquetas conforme ele menciona e imprime os valores aleatórios.
Você verá que cada procedimento de embaralhamento gerará amostra aleatoriamente com o tamanho igual ao tamanho do buffer do conjunto de dados.
fonte
Achei que @olivier-moindrot está mesmo correto, tentei o código fornecido por @Houtarou Oreki, usando as modificações apontadas por @max. O código que usei foi o seguinte:
A saída do código era de fato um número variando de 1 a (buffer_size + (i * batch_size)), onde i é o número de vezes que você executou next_element . Eu acho que a forma como está funcionando é a seguinte. Primeiro, as amostras buffer_size são selecionadas em ordem a partir de fake_data . Então, uma a uma, as amostras batch_size são retiradas do buffer. Cada vez que uma amostra de lote é retirada do buffer, ela é substituída por uma nova, obtida em ordem de fake_data . Testei essa última coisa usando o seguinte código:
O valor máximo produzido pelo código foi 109. Portanto, você precisa garantir uma amostra balanceada dentro de batch_size para garantir uma amostragem uniforme durante o treinamento.
Eu também testei o que @mrry disse sobre desempenho, descobri que batch_size irá pré- buscar essa quantidade de amostras na memória. Testei isso usando o seguinte código:
Alterar a quantidade de dataset.prefetch (10) resultou em nenhuma alteração na memória (RAM) usada. Isso é importante quando seus dados não cabem na RAM. Acho que a melhor maneira é embaralhar seus dados / file_names antes de alimentá-los para tf.dataset e, em seguida, controlar o tamanho do buffer usando buffer_size .
fonte