Es ist ein Stoß. Vielleicht gibt es einen besseren Weg.
Wenn "min_after_dequeue" kleiner als die Dateigröße ist, ist "tf.shuffle_batch" voreingenommen. Dies liegt daran, dass shuffle_batch
nur Bilder in der Warteschlange mischt.
Angenommen, Sie sortieren 70.000 Mnist-Bilder (7.000 für jedes Etikett) in aufsteigender Reihenfolge der Etiketten und zeichnen sie zusammen mit den Etiketten in tfrecord auf. Wenn zu diesem Zeitpunkt "min_after_dequeue" von "tf.train.shuffle_batch" auf 10000 eingestellt ist und 50.000 Etiketten entfernt werden, erfolgt die Verteilung der Etiketten
Es sieht aus wie. Die horizontale Achse ist die Reihenfolge der Entfernung, und die vertikale Achse ist das erhaltene Etikett. Zu Beginn (bis 4000 Blätter herausgenommen werden = 1400, bis der erste "2" -Datensatz in die Warteschlange gestellt wird) werden nur "0" - oder "1" -Datensätze in die Warteschlange gestellt, sodass nur 0 oder 1 angezeigt werden. Hmm. Da das Etikett "9" erst nach dem 63001. Blatt enthalten ist, ist es beim Herausnehmen der 50.000 Etiketten nie erschienen.
Wenn Sie tfrecord eine Datensatznummer hinzufügen und diese Datensatznummer auf der vertikalen Achse nehmen,
Es sieht aus wie. In der ersten Hälfte des Stapels werden nur Datensätze mit niedrigen Nummern zurückgegeben, und Datensätze mit hohen Nummern werden hauptsächlich in der zweiten Hälfte des Stapels zurückgegeben. In der zweiten Hälfte der Partie gibt es jedoch noch einige junge Rekorde mit gemischten und glücklichen (?) Dequeue-Fluchten.
Weitere Informationen, z. B. bei Verwendung mehrerer tfrecords, finden Sie unter "[Tensorflow] Bias-Untersuchung beim Shuffle_batch mit TFRecord-Datei. 184840) ”.
Ich denke, es gibt einige Problemumgehungen.
min_after_dequeue
auf die Anzahl der Datensätze in der Datei oder mehr und legen Sie den gesamten Inhalt der Datei im Speicher ab.min_after_dequeue
auf die Anzahl der Datensätze in der Datei oder mehr.Ich denke, es gibt nur den letzten Weg, um gleichzeitig eine asynchrone Verarbeitung und eine ausreichend gute Randomisierung zu erreichen (aber wenn es noch etwas gibt, lassen Sie es mich bitte wissen). Daher werde ich die Maßnahmen, die ich dafür ergriffen habe, am Beispiel des MNIST-Datensatzes schreiben.
Im Folgenden werden zwei Implementierungsmethoden parallel geschrieben.
Die Implementierung von 1 funktioniert durch Kopieren von "Common Operation" und "im Fall von Image Tfrecord". Die Implementierung von 2 funktioniert durch Kopieren von "Common Operation" und "im Fall von Pfad tfrecord".
Um die übliche Methode (Speichern des Bildes in tfrecord) und die Methode, die Sie diesmal ausführen möchten (Speichern im Pfad tfrecord anstelle des Bildes), auszuprobieren, erstellen Sie mit jeder Methode ein tfrecord von MNIST-Daten.
import os
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
MNIST_DIR = './MNIST-data'
TFRECORD_DIR = './tfrecords'
IMAGE_DIR = './images'
def make_mnist_data():
mnist_data = input_data.read_data_sets(MNIST_DIR, validation_size=0)
#Sammeln Sie alle Trainingsdaten und Testdaten
labels = np.r_[mnist_data[0].labels, mnist_data[2].labels]
images = np.r_[mnist_data[0].images, mnist_data[2].images]
#Das Bild wird an die Form des Bildes angepasst.
images = (images * 255).astype(np.uint8).reshape((-1, 28, 28))
#Sortieren Sie die Bilder in der Reihenfolge von 0.
order = np.argsort(labels)
labels = labels[order] # np.repeat(np.arange(0, 10), 7000)Gleich wie
images = images[order] #Handschriftliche Bilder in aufsteigender Reihenfolge sortiert
indices = np.arange(len(labels), dtype=int) # 0~Index von 69999
return indices, labels, images
def int64_feature(value):
return tf.train.Feature(int64_list=tf.train.Int64List(value=[value]))
def bytes_feature(value):
return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))
Normalerweise wird tfrecord so erstellt (Index ist nicht erforderlich, aber vorerst).
def image_to_tfexample(index, label, image):
image_string = image.tostring()
return tf.train.Example(features=tf.train.Features(feature={
'index': int64_feature(index),
'label': int64_feature(label),
'image_string': bytes_feature(image_string)
}))
indices, labels, images = make_mnist_data()
tfrecord_path = os.path.join(TFRECORD_DIR, 'mnist_image.tfrecord')
with tf.python_io.TFRecordWriter(tfrecord_path) as writer:
for index, label, image in zip(indices, labels, images):
example = image_to_tfexample(index, label, image)
writer.write(example.SerializeToString())
Speichern Sie das PNG-Bild getrennt von der Erstellung von tfrecord in IMAGE_DIR.
def path_to_tfexample(index, label, path):
path_string = path.encode('utf-8')
return tf.train.Example(features=tf.train.Features(feature={
'index': int64_feature(index),
'label': int64_feature(label),
'path_string': bytes_feature(path_string)
}))
indices, labels, images = make_mnist_data()
paths = [os.path.join(IMAGE_DIR, f'{i}.png') for i in indices]
tfrecord_path = os.path.join(TFRECORD_DIR, 'mnist_path.tfrecord')
with tf.python_io.TFRecordWriter(tfrecord_path) as writer:
for index, label, path in zip(indices, labels, paths):
example = path_to_tfexample(index, label, path)
writer.write(example.SerializeToString())
#Speichern Sie das MNIST-Bild getrennt vom tfrecord
for path, image in zip(paths, images):
Image.fromarray(image).save(path)
Im folgenden Beispiel ist "min_after_dequeue" auf 10.000 festgelegt (die gemeinsame Eingabebildgröße beträgt 224 x 224 x 3 oder mehr, sodass je nach Speicher Zehntausende die Grenze sein können).
Bei dieser Größe von "min_after_dequeue" wird die Verteilung wie in der Abbildung am Anfang gezeigt vorgespannt (vielmehr wurden die Daten aus dieser "input_pipeline" erhalten und gezeichnet).
BATCH_SIZE = 20
def read_tfrecord(filename_queue):
reader = tf.TFRecordReader()
key, record_string = reader.read(filename_queue)
example = tf.parse_single_example(record_string, features={
'index': tf.FixedLenFeature([], tf.int64),
'label': tf.FixedLenFeature([], tf.int64),
'image_string': tf.FixedLenFeature([], tf.string)
})
index = tf.cast(example['index'], tf.int32)
label = tf.cast(example['label'], tf.int32)
image = tf.decode_raw(example['image_string'], tf.uint8)
image = tf.reshape(image, [28, 28, 1])
image.set_shape([28, 28, 1])
return index, label, image
def input_pipeline(filenames):
filename_queue = tf.train.string_input_producer(filenames)
index, label, image = read_tfrecord(filename_queue)
index_batch, label_batch, image_batch = tf.train.shuffle_batch(
[index, label, image],
batch_size=BATCH_SIZE,
min_after_dequeue=10000,
capacity=10000 + 3 * BATCH_SIZE,
num_threads=1,
)
return index_batch, label_batch, image_batch
tfrecord_path = os.path.join(TFRECORD_DIR, 'mnist_image.tfrecord')
index_batch, label_batch, image_batch = input_pipeline([tfrecord_path, ])
Im folgenden Beispiel ist die erste "min_after_dequeue" auf 70.000 festgelegt. Der Pfad ist nur eine Zeichenfolge und ich denke, er wird problemlos in den Speicher passen. Bei dieser Größe von "min_after_dequeue" ist die Verteilung nicht verzerrt, wie in der Abbildung am Anfang gezeigt.
Andererseits beträgt die Kapazität des Stapels, der Bilder enthält, ungefähr 10.000. Dies ist nur für die tfrecord-Version des Bildes vorgesehen und muss nicht gemischt werden, sodass es tatsächlich viel weniger sein kann (Standardkapazität ist 32).
Außerdem wird häufig eine Umformung eingefügt, sodass die Ausgabeform "[BATCH_SIZE,]" oder "[BATCH_SIZE, 28, 28, 1]" lautet.
Der Grund für einen zweistufigen Stapel ist einfach: Wenn Sie genau das Gleiche wie tfrecord für ein Bild tun, wird die asynchrone Verarbeitung beim Lesen des Pfads gestoppt. Die Verarbeitung dauert vom Laden des Bildes bis zur Vorverarbeitung einige Zeit. Wenn dies also hinter den Kulissen nicht funktioniert, gibt es fast keinen Geschmack.
BATCH_SIZE = 20
def read_tfrecord(filename_queue):
reader = tf.TFRecordReader()
key, record_string = reader.read(filename_queue)
example = tf.parse_single_example(record_string, features={
'index': tf.FixedLenFeature([], tf.int64),
'label': tf.FixedLenFeature([], tf.int64),
'path_string': tf.FixedLenFeature([], tf.string)
})
index = tf.cast(example['index'], tf.int32)
label = tf.cast(example['label'], tf.int32)
path = example['path_string']
return index, label, path
def image_from_path(path):
png_bytes = tf.read_file(path)
image = tf.image.decode_png(png_bytes, channels=1)
image.set_shape([28, 28, 1])
return image
def input_pipeline(filenames):
filename_queue = tf.train.string_input_producer(filenames)
index, label, path = read_tfrecord(filename_queue)
index_batch, label_batch, path_batch = tf.train.shuffle_batch(
[index, label, path],
batch_size=1,
min_after_dequeue=70000,
capacity=70000 + 3 * 1,
num_threads=1
)
index_batch_flatten = tf.reshape(index_batch, [-1])
label_batch_flatten = tf.reshape(label_batch, [-1])
path_batch_flatten = tf.reshape(path_batch, [-1])
image_batch_flatten = tf.map_fn(image_from_path, path_batch_flatten, dtype=tf.uint8)
index_batch, label_batch, image_batch = tf.train.batch(
[index_batch_flatten, label_batch_flatten, image_batch_flatten],
batch_size=BATCH_SIZE,
capacity=10000 + 3 * BATCH_SIZE,
num_threads=1,
)
index_batch = tf.reshape(index_batch, [-1])
label_batch = tf.reshape(label_batch, [-1])
image_batch = tf.reshape(image_batch, [-1, 28, 28, 1])
return index_batch, label_batch, image_batch
tfrecord_path = os.path.join(TFRECORD_DIR, 'mnist_path.tfrecord')
index_batch, label_batch, image_batch = input_pipeline([tfrecord_path, ])
Überprüfen Sie die Ausgabe von "index_batch", "label_batch" und "image_batch", die von jeder Methode erstellt wurden.
init_op = tf.local_variables_initializer()
results = {'index': [], 'label': []}
with tf.Session() as sess:
sess.run(init_op)
coord = tf.train.Coordinator()
threads = tf.train.start_queue_runners(coord=coord)
for i in range(2500): #Da die Chargengröße 20, 50.000 Blatt beträgt
result = sess.run([index_batch, label_batch])
results['index'].append(result[0])
results['label'].append(result[1])
coord.request_stop()
coord.join(threads)
fig = plt.figure(figsize=(10, 5))
for i, key in enumerate(('index', 'label')):
ax = fig.add_subplot(1, 2, i + 1)
y = np.array(results[key]).flatten()
x = np.arange(len(y))
ax.plot(x, y, '.')
fig.show()
Ich habe vergessen, es zu schreiben, aber die horizontale Achse ist die Reihenfolge, in der der Stapel entnommen wurde, und die vertikale Achse ist die Chargenprotokollnummer (links) oder das richtige Antwortetikett (rechts).
Es ist gut eingemischt.
Indem wir nur den Pfad in tfrecord speichern, konnten wir einen vollständig gemischten Stapel von Bildern asynchron erstellen und gleichzeitig Speicher sparen. Bei Daten von mehreren zehn Millionen Einheiten können meines Erachtens dieselben Maßnahmen durch Teilen der Datei ergriffen werden (sie sollte gemischt werden, anstatt das Bild direkt in tfrecord zu platzieren).
Recommended Posts