Gemäß der TensorFlow- Dokumentation haben beide prefetch
und map
Methoden der tf.contrib.data.Dataset
Klasse einen Parameter namens buffer_size
.
Für die prefetch
Methode ist der Parameter als buffer_size
und gemäß Dokumentation bekannt:
buffer_size: Ein tf.int64-Skalar tf.Tensor, der die maximale Anzahl von Elementen darstellt, die beim Vorabrufen gepuffert werden.
Für die map
Methode ist der Parameter als output_buffer_size
und gemäß Dokumentation bekannt:
output_buffer_size: (Optional.) Ein tf.int64-Skalar tf.Tensor, der die maximale Anzahl verarbeiteter Elemente darstellt, die gepuffert werden.
Ähnlich erscheint für die shuffle
Methode die gleiche Menge und laut Dokumentation:
buffer_size: Ein tf.int64-Skalar tf.Tensor, der die Anzahl der Elemente aus diesem Datensatz darstellt, aus denen der neue Datensatz abgetastet wird.
Welche Beziehung besteht zwischen diesen Parametern?
Angenommen, ich erstelle ein Dataset
Objekt wie folgt:
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)
Welche Rolle spielen die buffer
Parameter im obigen Snippet?
Antworten:
TL; DR Trotz ihrer ähnlichen Namen haben diese Argumente sehr unterschiedliche Bedeutungen. Das
buffer_size
InDataset.shuffle()
kann die Zufälligkeit Ihres Datensatzes und damit die Reihenfolge beeinflussen, in der Elemente erstellt werden. Dasbuffer_size
InDataset.prefetch()
wirkt sich nur auf die Zeit aus, die zum Erstellen des nächsten Elements benötigt wird.Das
buffer_size
Argument intf.data.Dataset.prefetch()
und dasoutput_buffer_size
Argument intf.contrib.data.Dataset.map()
bieten eine Möglichkeit, die Leistung Ihrer Eingabepipeline zu optimieren: Beide Argumente weisen TensorFlow an, einen Puffer mit höchstensbuffer_size
Elementen zu erstellen , und einen Hintergrundthread, um diesen Puffer im Hintergrund zu füllen. (Beachten Sie, dass wir dasoutput_buffer_size
Argument entfernt haben,Dataset.map()
als es vontf.contrib.data
nach verschoben wurdetf.data
. Neuer Code sollteDataset.prefetch()
after verwendenmap()
, um das gleiche Verhalten zu erzielen.)Das Hinzufügen eines Prefetch-Puffers kann die Leistung verbessern, indem die Vorverarbeitung von Daten mit der nachgeschalteten Berechnung überlappt wird. In der Regel ist es am nützlichsten, am Ende der Pipeline einen kleinen Prefetch-Puffer (mit möglicherweise nur einem einzelnen Element) hinzuzufügen. Komplexere Pipelines können jedoch von einem zusätzlichen Prefetch profitieren, insbesondere wenn die Zeit zum Erstellen eines einzelnen Elements variieren kann.
Im Gegensatz
buffer_size
dazutf.data.Dataset.shuffle()
beeinflusst das Argument, die Zufälligkeit der Transformation zu beeinflussen. Wir haben dieDataset.shuffle()
Transformation (wie dietf.train.shuffle_batch()
Funktion, die sie ersetzt) so konzipiert, dass sie Datensätze verarbeitet, die zu groß sind, um in den Speicher zu passen. Anstatt das gesamte Dataset zu mischen, wird ein Puffer mitbuffer_size
Elementen verwaltet und das nächste Element zufällig aus diesem Puffer ausgewählt (wobei es durch das nächste Eingabeelement ersetzt wird, sofern eines verfügbar ist). Das Ändern des Werts vonbuffer_size
beeinflusst, wie gleichmäßig das Mischen ist: wennbuffer_size
es größer als die Anzahl der Elemente im Datensatz ist, erhalten Sie ein einheitliches Mischen. wenn es so ist1
dann bekommst du überhaupt kein schlurfen. Bei sehr großen Datenmengen besteht ein typischer "gut genug" -Ansatz darin, die Daten vor dem Training einmal zufällig in mehrere Dateien zu zerlegen, dann die Dateinamen einheitlich zu mischen und dann einen kleineren Mischpuffer zu verwenden. Die richtige Wahl hängt jedoch von der genauen Art Ihres Ausbildungsberufs ab.quelle
tf.data.Dataset.shuffle()
. Ich würde gerne den genauen Mischvorgang kennen. Angenommen, die erstenbatch_size
Stichproben werden zufällig aus den erstenbuffer_size
Elementen ausgewählt und so weiter.buffer_size
, die Dateigröße gleichzusetzen (und die Dateien natürlich zu mischen).dataset.shuffle(buffer_size=1)
Mischen tritt immer noch auf. Irgendwelche Gedanken?Bedeutung von
buffer_size
inshuffle()
Ich wollte die vorherige Antwort von @mrry weiterverfolgen, um die Wichtigkeit von
buffer_size
in hervorzuhebentf.data.Dataset.shuffle()
.Ein niedriges Niveau führt in einigen Fällen
buffer_size
nicht nur zu einem minderwertigen Mischen, sondern kann auch Ihr gesamtes Training durcheinander bringen.Ein praktisches Beispiel: Katzenklassifikator
Angenommen, Sie trainieren einen Katzenklassifikator für Bilder und Ihre Daten sind folgendermaßen organisiert (mit
10000
Bildern in jeder Kategorie):Eine Standardmethode zur Dateneingabe mit
tf.data
kann darin bestehen, eine Liste mit Dateinamen und eine Liste der entsprechenden Beschriftungentf.data.Dataset.from_tensor_slices()
zu erstellen und den Datensatz zu erstellen:Das große Problem mit dem obigen Code ist, dass der Datensatz tatsächlich nicht richtig gemischt wird. In der ersten Hälfte einer Epoche werden nur Katzenbilder und in der zweiten Hälfte nur Nicht-Katzenbilder angezeigt. Dies wird das Training sehr verletzen.
Zu Beginn des Trainings nimmt der Datensatz die ersten
1000
Dateinamen und legt sie in seinen Puffer. Wählen Sie dann zufällig einen aus. Da alle ersten1000
Bilder Bilder von Katzen sind, werden wir nur zu Beginn Katzenbilder auswählen.Das Update hier ist, um sicherzustellen, dass
buffer_size
größer als ist20000
, oder im Voraus zu mischenfilenames
undlabels
(offensichtlich mit denselben Indizes).Da das Speichern aller Dateinamen und Beschriftungen im Speicher kein Problem darstellt, können wir tatsächlich
buffer_size = len(filenames)
sicherstellen, dass alles zusammengemischt wird. Stellen Sie sicher, dass Sie anrufen,tf.data.Dataset.shuffle()
bevor Sie die umfangreichen Transformationen anwenden (z. B. Lesen der Bilder, Verarbeiten, Stapeln ...).Zum Mitnehmen muss immer überprüft werden, was das Mischen bewirkt. Eine gute Möglichkeit, diese Fehler zu erkennen, besteht darin, die Verteilung der Chargen über die Zeit zu zeichnen (stellen Sie sicher, dass die Chargen ungefähr die gleiche Verteilung wie das Trainingsset enthalten, in unserem Beispiel halb Katze und halb Nichtkatze).
quelle
filename_01001
) und fügt sie hinzu. Die zweite Stichprobe wird zufällig aus diesen 1000 Dateinamen entnommen (1001 erste Dateinamen abzüglich der ersten Stichprobe).tf.summary.histogram
die Verteilung der Etiketten über die Zeit zeichnen.Code
Ausgabe
[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]
quelle
Eigentlich ist die Antwort von @ olivier-moindrot nicht richtig.
Sie können dies überprüfen, indem Sie Dateinamen und Beschriftungen erstellen, während er die Shuffle-Werte erwähnt und druckt.
Sie werden sehen, dass jede Shuffle-Prozedur zufällig eine Stichprobe generiert, deren Größe der Puffergröße aus dem Datensatz entspricht.
quelle
Ich fand, dass @ olivier-moindrot tatsächlich korrekt ist. Ich habe den von @Houtarou Oreki bereitgestellten Code unter Verwendung der von @max angegebenen Änderungen ausprobiert. Der Code, den ich verwendet habe, war folgender:
Die Code-Ausgabe war in der Tat eine Zahl zwischen 1 und (buffer_size + (i * batch_size)), wobei i die Häufigkeit ist, mit der Sie next_element ausgeführt haben . Ich denke, die Art und Weise, wie es funktioniert, ist die folgende. Zuerst werden buffer_size- Samples in der Reihenfolge aus den fake_data ausgewählt . Dann werden nacheinander die Batch_Size- Proben aus dem Puffer entnommen . Jedes Mal, wenn eine Chargenprobe aus dem Puffer entnommen wird, wird sie durch eine neue ersetzt, die in der Reihenfolge von fake_data entnommen wird . Ich habe dieses letzte Ding mit dem folgenden Code getestet:
Der vom Code erzeugte Maximalwert betrug 109. Sie müssen also eine ausgewogene Stichprobe innerhalb Ihrer Batch-Größe sicherstellen, um eine einheitliche Stichprobe während des Trainings zu gewährleisten.
Ich habe auch getestet, was @mrry über die Leistung gesagt hat. Ich habe festgestellt, dass batch_size diese Anzahl von Samples vorab in den Speicher abruft . Ich habe dies mit dem folgenden Code getestet:
Das Ändern der Menge von dataset.prefetch (10) führte zu keiner Änderung des verwendeten Speichers (RAM). Dies ist wichtig, wenn Ihre Daten nicht in den Arbeitsspeicher passen. Ich denke, der beste Weg ist, Ihre Daten / Dateinamen zu mischen, bevor Sie sie an tf.dataset weiterleiten, und dann die Puffergröße mit buffer_size zu steuern .
quelle