Selon la documentation de TensorFlow , les méthodes prefetch
et map
de la tf.contrib.data.Dataset
classe ont toutes deux un paramètre appelé buffer_size
.
Pour la prefetch
méthode, le paramètre est appelé buffer_size
et selon la documentation:
buffer_size: Un tf.Tensor scalaire tf.int64, représentant le nombre maximum d'éléments qui seront mis en mémoire tampon lors de la prélecture.
Pour la map
méthode, le paramètre est appelé output_buffer_size
et selon la documentation:
output_buffer_size: (Facultatif.) Un tf.Tensor scalaire tf.int64, représentant le nombre maximum d'éléments traités qui seront mis en mémoire tampon.
De même pour la shuffle
méthode, la même quantité apparaît et selon la documentation:
buffer_size: Un tf.Tensor scalaire tf.int64, représentant le nombre d'éléments de cet ensemble de données à partir desquels le nouvel ensemble de données sera échantillonné.
Quelle est la relation entre ces paramètres?
Supposons que je crée un Dataset
objet comme suit:
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)
Quel rôle jouent les buffer
paramètres dans l'extrait ci-dessus?
Réponses:
TL; DR Malgré leurs noms similaires, ces arguments ont des significations assez différentes. Le
buffer_size
inDataset.shuffle()
peut affecter le caractère aléatoire de votre ensemble de données, et donc l'ordre dans lequel les éléments sont produits. Lebuffer_size
enDataset.prefetch()
affecte uniquement le temps nécessaire pour produire l'élément suivant.L'
buffer_size
argument intf.data.Dataset.prefetch()
et l'output_buffer_size
argument intf.contrib.data.Dataset.map()
fournissent un moyen d'ajuster les performances de votre pipeline d'entrée: les deux arguments indiquent à TensorFlow de créer un tampon d'au plusbuffer_size
éléments, et un thread d'arrière-plan pour remplir ce tampon en arrière-plan. (Notez que nous avons supprimé l'output_buffer_size
argument deDataset.map()
quand il a été déplacé detf.contrib.data
àtf.data
. Le nouveau code doit utiliserDataset.prefetch()
aftermap()
pour obtenir le même comportement.)L'ajout d'un tampon de prélecture peut améliorer les performances en chevauchant le prétraitement des données avec le calcul en aval. En règle générale, il est plus utile d'ajouter un petit tampon de prélecture (avec peut-être un seul élément) à la toute fin du pipeline, mais les pipelines plus complexes peuvent bénéficier d'une prélecture supplémentaire, en particulier lorsque le temps de production d'un seul élément peut varier.
En revanche, l'
buffer_size
argument totf.data.Dataset.shuffle()
affecte le caractère aléatoire de la transformation. Nous avons conçu laDataset.shuffle()
transformation (comme latf.train.shuffle_batch()
fonction qu'elle remplace) pour gérer les ensembles de données trop volumineux pour tenir en mémoire. Au lieu de mélanger l'ensemble de données, il maintient un tampon d'buffer_size
éléments et sélectionne au hasard l'élément suivant de ce tampon (en le remplaçant par l'élément d'entrée suivant, s'il en existe un). La modification de la valeur debuffer_size
affecte l'uniformité du brassage: sibuffer_size
est supérieur au nombre d'éléments de l'ensemble de données, vous obtenez un brassage uniforme; Si c'est1
alors vous n'obtenez aucun mélange. Pour les très grands ensembles de données, une approche typique «assez bonne» consiste à fragmenter de manière aléatoire les données en plusieurs fichiers une fois avant l'entraînement, puis à mélanger les noms de fichiers de manière uniforme, puis à utiliser un tampon de mélange plus petit. Cependant, le choix approprié dépendra de la nature exacte de votre travail de formation.la source
tf.data.Dataset.shuffle()
. J'aimerais connaître le processus de remaniement exact. Disons que les premiersbatch_size
échantillons sont choisis au hasard parmi les premiersbuffer_size
éléments, et ainsi de suite.buffer_size
une taille égale à la taille du fichier (et de mélanger les fichiers bien sûr).dataset.shuffle(buffer_size=1)
lecture aléatoire se produit toujours. Des pensées?Importance de
buffer_size
dansshuffle()
Je voulais faire suite à la réponse précédente de @mrry pour souligner l' importance de
buffer_size
intf.data.Dataset.shuffle()
.Avoir un faible
buffer_size
ne vous donnera pas seulement un mélange inférieur dans certains cas: cela peut gâcher tout votre entraînement.Un exemple pratique: classificateur de chat
Supposons par exemple que vous entraînez un classificateur de chat sur des images et que vos données sont organisées de la manière suivante (avec des
10000
images dans chaque catégorie):Un moyen standard de saisir des données avec
tf.data
peut être d'avoir une liste de noms de fichiers et une liste d'étiquettes correspondantes, et d'utilisertf.data.Dataset.from_tensor_slices()
pour créer l'ensemble de données:Le gros problème avec le code ci-dessus est que l'ensemble de données ne sera pas mélangé de la bonne manière. Pendant environ la première moitié d'une époque, nous ne verrons que des images de chats, et pour la seconde moitié uniquement des images non félines. Cela nuira beaucoup à l'entraînement.
Au début de la formation, l'ensemble de données prendra les premiers
1000
noms de fichiers et les mettra dans sa mémoire tampon, puis en choisira un au hasard parmi eux. Puisque toutes les premières1000
images sont des images de chat, nous ne choisirons que des images de chat au début.Le correctif ici est de s'assurer que
buffer_size
c'est plus grand que20000
, ou de mélanger à l'avancefilenames
etlabels
(avec les mêmes indices évidemment).Étant donné que le stockage de tous les noms de fichiers et étiquettes en mémoire n'est pas un problème, nous pouvons en fait l'utiliser
buffer_size = len(filenames)
pour nous assurer que tout sera mélangé ensemble. Assurez-vous d'appelertf.data.Dataset.shuffle()
avant d'appliquer les transformations lourdes (comme la lecture des images, leur traitement, le traitement par lots ...).La chose à retenir est de toujours vérifier ce que fera le brassage. Un bon moyen d'attraper ces erreurs pourrait être de tracer la distribution des lots au fil du temps (assurez-vous que les lots contiennent à peu près la même distribution que l'ensemble d'entraînement, moitié chat et moitié non chat dans notre exemple).
la source
filename_01001
) et l'ajoute. Le deuxième échantillon est pris au hasard parmi ces 1000 noms de fichiers (1001 premiers noms de fichiers moins le premier échantillon).tf.summary.histogram
pour tracer la distribution des étiquettes au fil du temps.Code
Production
[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]
la source
En fait, la réponse de @ olivier-moindrot n'est pas correcte.
Vous pouvez le vérifier en créant des noms de fichiers et des étiquettes au fur et à mesure qu'il / elle mentionne et imprime les valeurs de lecture aléatoire.
Vous verrez que chaque procédure de mélange générera un échantillon de manière aléatoire avec la taille égale à la taille de la mémoire tampon à partir de l'ensemble de données.
la source
J'ai trouvé que @ olivier-moindrot est en effet correct, j'ai essayé le code fourni par @Houtarou Oreki, en utilisant les modifications pointées par @max. Le code que j'ai utilisé était le suivant:
La sortie de code était en effet un nombre allant de 1 à (buffer_size + (i * batch_size)), où i est le nombre de fois que vous avez exécuté next_element . Je pense que la façon dont cela fonctionne est la suivante. Tout d'abord, les échantillons buffer_size sont sélectionnés dans l'ordre dans fake_data . Ensuite, un par un, les échantillons batch_size sont prélevés dans le tampon. Chaque fois qu'un échantillon de lot est sélectionné dans le tampon, il est remplacé par un nouveau, pris dans l'ordre de fake_data . J'ai testé cette dernière chose en utilisant le code suivant:
La valeur maximale produite par le code était de 109. Vous devez donc garantir un échantillon équilibré au sein de votre batch_size pour garantir un échantillonnage uniforme pendant la formation.
J'ai également testé ce que @mrry a dit à propos des performances, j'ai trouvé que batch_size prélèverait cette quantité d'échantillons en mémoire. J'ai testé cela en utilisant le code suivant:
La modification de la quantité dataset.prefetch (10) n'a entraîné aucune modification de la mémoire (RAM) utilisée. Ceci est important lorsque vos données ne rentrent pas dans la RAM. Je pense que le meilleur moyen est de mélanger vos données / noms de fichier avant de les alimenter dans tf.dataset, puis de contrôler la taille de la mémoire tampon en utilisant buffer_size .
la source