[PYTHON] Mischen Sie Hunderttausende von Bildern gleichmäßig mit Tensorflow.

wichtiger Punkt

Es ist ein Stoß. Vielleicht gibt es einen besseren Weg.

Wenn min \ _after \ _dequeue klein ist, ist tf.train.shuffle \ _batch voreingenommen.

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

fig1.png

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,

fig2.png

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) ”.

Problemumgehung

Ich denke, es gibt einige Problemumgehungen.

  1. Ein Stapel, der eine große Anzahl von Bildpfaden enthält
  2. Ein Stapel, der einige Bilder liest und enthält

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.

  1. Beispiel, wenn das Bild in tfrecord gepackt ist (= normale Verwendung von tfrecord)
  2. Beispiel, wenn der Pfad in tfrecord gepackt ist (= Verwendung von tfrecord, das Sie diesmal ausführen möchten. Alle Bilder als Rohdaten im Pfad speichern)

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".

Tfrecord erstellen

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.

Gemeinsame Bedienung

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]))

Für die Aufzeichnung des Bildes

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())

Für die Aufzeichnung des Pfades

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)

Lesen Sie tfrecord

Für die Aufzeichnung des Bildes

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, ])

Für die Aufzeichnung des Pfades

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

Ü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()

Ergebnis (für tfrecord des Bildes)

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).

fig3.png

Ergebnis (für tfrecord of path)

Es ist gut eingemischt.

fig4.png

Fazit

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

Mischen Sie Hunderttausende von Bildern gleichmäßig mit Tensorflow.
Multi-Class Multi-Label-Klassifizierung von Bildern mit Pytorch
Die Geschichte der numerischen Berechnung von Differentialgleichungen mit TensorFlow 2.0
Transkription von Bildern mit der Vision API von GCP
Verstehen Sie die Bilder verschiedener Matrixoperationen, die in Keras (Tensorflow) verwendet werden, anhand von Beispielen
Ich habe Hunderte Millionen SQLite mit Python ausprobiert
Kategorisieren Sie Gesichtsbilder von Anime-Charakteren mit Chainer
Wavelet-Konvertierung von Bildern mit PyWavelets und OpenCV
Zundokokiyoshi mit TensorFlow
Brechen Sie Blöcke mit Tensorflow
Zeigen Sie eingebettete Bilder von MP3 und Flac mit Mutagen an
Höchstwahrscheinlich Schätzung des Mittelwerts und der Varianz mit TensorFlow
Versuchen Sie, die Bildkonvertierung mit OpenCV mit Python zu projizieren
Erstellen Sie einen Stapel von Bildern und blasen Sie sie mit ImageDataGenerator auf
Ich habe versucht, das Bild mit Python + OpenCV "morphologisch zu konvertieren"
Ich habe ein Convolutional Neural Network (CNN) mit einem TensorFlow-Tutorial zur Cloud9-Klassifizierung handgeschriebener Bilder ausprobiert.