Merken Sie den Termin vor! Google I / O kehrt vom 18. bis 20. Mai zurück Registrieren Sie sich jetzt
Diese Seite wurde von der Cloud Translation API übersetzt.
Switch to English

Datenerweiterung

Ansicht auf TensorFlow.org In Google Colab ausführen Quelle auf GitHub anzeigen Notizbuch herunterladen

Überblick

Dieses Tutorial demonstriert die Datenerweiterung: Eine Technik, mit der Sie die Vielfalt Ihres Trainingssatzes durch zufällige (aber realistische) Transformationen wie Bildrotation erhöhen können. Sie lernen, wie Sie die Datenerweiterung auf zwei Arten anwenden. Zunächst verwenden Sie Keras-Vorverarbeitungsebenen . Als nächstes verwenden Sie tf.image .

Einrichten

import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
import tensorflow_datasets as tfds

from tensorflow.keras import layers
from tensorflow.keras.datasets import mnist

Laden Sie einen Datensatz herunter

In diesem Lernprogramm wird das Dataset tf_flowers verwendet . Laden Sie den Datensatz zur Vereinfachung mit TensorFlow-Datensätzen herunter. Wenn Sie mehr über andere Möglichkeiten zum Importieren von Daten erfahren möchten, lesen Sie das Tutorial zum Laden von Bildern .

(train_ds, val_ds, test_ds), metadata = tfds.load(
    'tf_flowers',
    split=['train[:80%]', 'train[80%:90%]', 'train[90%:]'],
    with_info=True,
    as_supervised=True,
)
Downloading and preparing dataset 218.21 MiB (download: 218.21 MiB, generated: 221.83 MiB, total: 440.05 MiB) to /home/kbuilder/tensorflow_datasets/tf_flowers/3.0.1...
Dataset tf_flowers downloaded and prepared to /home/kbuilder/tensorflow_datasets/tf_flowers/3.0.1. Subsequent calls will reuse this data.

Der Blumendatensatz hat fünf Klassen.

num_classes = metadata.features['label'].num_classes
print(num_classes)
5

Lassen Sie uns ein Bild aus dem Datensatz abrufen und es zur Demonstration der Datenerweiterung verwenden.

get_label_name = metadata.features['label'].int2str

image, label = next(iter(train_ds))
_ = plt.imshow(image)
_ = plt.title(get_label_name(label))

png

Verwenden Sie Keras-Vorverarbeitungsebenen

Größenänderung und Neuskalierung

Sie können Vorverarbeitung Schichten verwenden , um die Größe Ihrer Bilder auf eine konsistente Form und rescale Pixelwerte.

IMG_SIZE = 180

resize_and_rescale = tf.keras.Sequential([
  layers.experimental.preprocessing.Resizing(IMG_SIZE, IMG_SIZE),
  layers.experimental.preprocessing.Rescaling(1./255)
])

Sie können das Ergebnis der Anwendung dieser Ebenen auf ein Bild sehen.

result = resize_and_rescale(image)
_ = plt.imshow(result)

png

Sie können überprüfen, ob sich die Pixel in [0-1] .

print("Min and max pixel values:", result.numpy().min(), result.numpy().max())
Min and max pixel values: 0.0 1.0

Datenerweiterung

Sie können Vorverarbeitungsebenen auch zur Datenerweiterung verwenden.

Lassen Sie uns einige Vorverarbeitungsebenen erstellen und diese wiederholt auf dasselbe Bild anwenden.

data_augmentation = tf.keras.Sequential([
  layers.experimental.preprocessing.RandomFlip("horizontal_and_vertical"),
  layers.experimental.preprocessing.RandomRotation(0.2),
])
# Add the image to a batch
image = tf.expand_dims(image, 0)
plt.figure(figsize=(10, 10))
for i in range(9):
  augmented_image = data_augmentation(image)
  ax = plt.subplot(3, 3, i + 1)
  plt.imshow(augmented_image[0])
  plt.axis("off")

png

Es gibt eine Vielzahl von Vorverarbeitungsebenen , die Sie zur layers.RandomContrast , einschließlich layers.RandomContrast , layers.RandomCrop , layers.RandomZoom und andere.

Zwei Optionen zur Verwendung der Vorverarbeitungsebenen

Es gibt zwei Möglichkeiten, wie Sie diese Vorverarbeitungsebenen mit wichtigen Kompromissen verwenden können.

Option 1: Machen Sie die Vorverarbeitungsebenen zu einem Teil Ihres Modells

model = tf.keras.Sequential([
  resize_and_rescale,
  data_augmentation,
  layers.Conv2D(16, 3, padding='same', activation='relu'),
  layers.MaxPooling2D(),
  # Rest of your model
])

In diesem Fall sind zwei wichtige Punkte zu beachten:

  • Die Datenerweiterung wird auf dem Gerät synchron mit den übrigen Ebenen ausgeführt und profitiert von der GPU-Beschleunigung.

  • Wenn Sie Ihr Modell mit model.save , werden die Vorverarbeitungsebenen zusammen mit dem Rest Ihres Modells gespeichert. Wenn Sie dieses Modell später bereitstellen, werden Bilder automatisch standardisiert (entsprechend der Konfiguration Ihrer Ebenen). Dies erspart Ihnen den Aufwand, diese logikserverseitig neu implementieren zu müssen.

Option 2: Wenden Sie die Vorverarbeitungsebenen auf Ihr Dataset an

aug_ds = train_ds.map(
  lambda x, y: (resize_and_rescale(x, training=True), y))

Bei diesem Ansatz verwenden Sie Dataset.map , um ein Dataset zu erstellen, das Dataset.map erweiterter Bilder liefert. In diesem Fall:

  • Die Datenerweiterung erfolgt asynchron auf der CPU und ist nicht blockierend. Sie können das Training Ihres Modells auf der GPU mit der Datenvorverarbeitung unter Verwendung von Dataset.prefetch (siehe unten) Dataset.prefetch .
  • In diesem Fall werden die Vorverarbeitungsebenen nicht mit dem Modell exportiert, wenn Sie model.save . Sie müssen sie an Ihr Modell anhängen, bevor Sie es speichern oder serverseitig neu implementieren. Nach dem Training können Sie die Vorverarbeitungsebenen vor dem Export anhängen.

Sie können ein Beispiel für die erste Option in dem finden Bildklassifizierungs- Tutorial. Lassen Sie uns hier die zweite Option demonstrieren.

Wenden Sie die Vorverarbeitungsebenen auf die Datensätze an

Konfigurieren Sie die Zug-, Validierungs- und Testdatensätze mit den oben erstellten Vorverarbeitungsebenen. Sie konfigurieren die Datasets auch für die Leistung, indem Sie parallele Lesevorgänge und gepuffertes Prefetching verwenden, um Stapel von der Festplatte zu erhalten, ohne dass E / A blockiert werden. Weitere Informationen zur Dataset-Leistung finden Sie im Abschnitt Bessere Leistung mit dem API- Handbuch tf.data .

batch_size = 32
AUTOTUNE = tf.data.AUTOTUNE

def prepare(ds, shuffle=False, augment=False):
  # Resize and rescale all datasets
  ds = ds.map(lambda x, y: (resize_and_rescale(x), y), 
              num_parallel_calls=AUTOTUNE)

  if shuffle:
    ds = ds.shuffle(1000)

  # Batch all datasets
  ds = ds.batch(batch_size)

  # Use data augmentation only on the training set
  if augment:
    ds = ds.map(lambda x, y: (data_augmentation(x, training=True), y), 
                num_parallel_calls=AUTOTUNE)

  # Use buffered prefecting on all datasets
  return ds.prefetch(buffer_size=AUTOTUNE)
train_ds = prepare(train_ds, shuffle=True, augment=True)
val_ds = prepare(val_ds)
test_ds = prepare(test_ds)

Trainiere ein Modell

Der Vollständigkeit halber trainieren Sie nun ein Modell mit diesen Datensätzen. Dieses Modell wurde nicht auf Genauigkeit abgestimmt (das Ziel ist es, Ihnen die Mechanik zu zeigen).

model = tf.keras.Sequential([
  layers.Conv2D(16, 3, padding='same', activation='relu'),
  layers.MaxPooling2D(),
  layers.Conv2D(32, 3, padding='same', activation='relu'),
  layers.MaxPooling2D(),
  layers.Conv2D(64, 3, padding='same', activation='relu'),
  layers.MaxPooling2D(),
  layers.Flatten(),
  layers.Dense(128, activation='relu'),
  layers.Dense(num_classes)
])
model.compile(optimizer='adam',
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
              metrics=['accuracy'])
epochs=5
history = model.fit(
  train_ds,
  validation_data=val_ds,
  epochs=epochs
)
Epoch 1/5
92/92 [==============================] - 17s 47ms/step - loss: 1.6206 - accuracy: 0.2998 - val_loss: 1.1271 - val_accuracy: 0.5613
Epoch 2/5
92/92 [==============================] - 3s 28ms/step - loss: 1.0935 - accuracy: 0.5384 - val_loss: 1.0343 - val_accuracy: 0.5858
Epoch 3/5
92/92 [==============================] - 3s 27ms/step - loss: 0.9785 - accuracy: 0.6022 - val_loss: 0.8810 - val_accuracy: 0.6540
Epoch 4/5
92/92 [==============================] - 3s 27ms/step - loss: 0.9167 - accuracy: 0.6399 - val_loss: 0.8247 - val_accuracy: 0.6594
Epoch 5/5
92/92 [==============================] - 3s 27ms/step - loss: 0.8545 - accuracy: 0.6615 - val_loss: 0.8591 - val_accuracy: 0.6458
loss, acc = model.evaluate(test_ds)
print("Accuracy", acc)
12/12 [==============================] - 0s 13ms/step - loss: 0.9010 - accuracy: 0.6131
Accuracy 0.6130790114402771

Benutzerdefinierte Datenerweiterung

Sie können auch benutzerdefinierte Datenerweiterungsebenen erstellen. Dieses Tutorial zeigt zwei Möglichkeiten, dies zu tun. Zuerst erstellen Sie eine Ebene. layers.Lambda Ebene. Dies ist eine gute Möglichkeit, präzisen Code zu schreiben. Als Nächstes schreiben Sie eine neue Ebene über Unterklassen , wodurch Sie mehr Kontrolle erhalten. Beide Ebenen invertieren die Farben in einem Bild nach einer gewissen Wahrscheinlichkeit nach dem Zufallsprinzip.

def random_invert_img(x, p=0.5):
  if  tf.random.uniform([]) < p:
    x = (255-x)
  else:
    x
  return x
def random_invert(factor=0.5):
  return layers.Lambda(lambda x: random_invert_img(x, factor))

random_invert = random_invert()
plt.figure(figsize=(10, 10))
for i in range(9):
  augmented_image = random_invert(image)
  ax = plt.subplot(3, 3, i + 1)
  plt.imshow(augmented_image[0].numpy().astype("uint8"))
  plt.axis("off")

png

Implementieren Sie als Nächstes eine benutzerdefinierte Ebene durch Unterklassen .

class RandomInvert(layers.Layer):
  def __init__(self, factor=0.5, **kwargs):
    super().__init__(**kwargs)
    self.factor = factor

  def call(self, x):
    return random_invert_img(x)
_ = plt.imshow(RandomInvert()(image)[0])

png

Diese beiden Schichten können wie in den Optionen 1 und 2 oben beschrieben verwendet werden.

Verwenden von tf.image

Die oben layers.preprocessing Dienstprogramme zur layers.preprocessing sind praktisch. Zur genaueren Steuerung können Sie mithilfe von tf.data und tf.image eigene Pipelines oder Layer zur tf.data tf.image . Möglicherweise möchten Sie auch TensorFlow Addons Image: Operations und TensorFlow I / O: Color Space Conversions ausprobieren

Da das Blumendatensatz zuvor mit Datenerweiterung konfiguriert wurde, importieren wir es erneut, um neu zu beginnen.

(train_ds, val_ds, test_ds), metadata = tfds.load(
    'tf_flowers',
    split=['train[:80%]', 'train[80%:90%]', 'train[90%:]'],
    with_info=True,
    as_supervised=True,
)

Rufen Sie ein Bild ab, mit dem Sie arbeiten möchten.

image, label = next(iter(train_ds))
_ = plt.imshow(image)
_ = plt.title(get_label_name(label))

png

Verwenden Sie die folgende Funktion, um die ursprünglichen und erweiterten Bilder nebeneinander zu visualisieren und zu vergleichen.

def visualize(original, augmented):
  fig = plt.figure()
  plt.subplot(1,2,1)
  plt.title('Original image')
  plt.imshow(original)

  plt.subplot(1,2,2)
  plt.title('Augmented image')
  plt.imshow(augmented)

Datenerweiterung

Bild umdrehen

Drehen Sie das Bild entweder vertikal oder horizontal.

flipped = tf.image.flip_left_right(image)
visualize(image, flipped)

png

Graustufen das Bild

Graustufen eines Bildes.

grayscaled = tf.image.rgb_to_grayscale(image)
visualize(image, tf.squeeze(grayscaled))
_ = plt.colorbar()

png

Sättigen Sie das Bild

Sättigen Sie ein Bild, indem Sie einen Sättigungsfaktor angeben.

saturated = tf.image.adjust_saturation(image, 3)
visualize(image, saturated)

png

Bildhelligkeit ändern

Ändern Sie die Bildhelligkeit, indem Sie einen Helligkeitsfaktor angeben.

bright = tf.image.adjust_brightness(image, 0.4)
visualize(image, bright)

png

Bild in der Mitte zuschneiden

Schneiden Sie das Bild von der Mitte bis zum gewünschten Bildteil zu.

cropped = tf.image.central_crop(image, central_fraction=0.5)
visualize(image,cropped)

png

Drehen Sie das Bild

Drehen Sie ein Bild um 90 Grad.

rotated = tf.image.rot90(image)
visualize(image, rotated)

png

Zufällige Transformationen

Das Anwenden zufälliger Transformationen auf die Bilder kann die Verallgemeinerung und Erweiterung des Datensatzes weiter unterstützen. Die aktuelle tf.image API bietet 8 solcher zufälligen Bildoperationen (ops):

Diese zufälligen Bildoperationen sind rein funktional: Die Ausgabe hängt nur von der Eingabe ab. Dies macht sie einfach in hochleistungsfähigen, deterministischen Eingabepipelines zu verwenden. Sie benötigen einen seed Wert eingegeben werden , jeder Schritt. Bei gleichem seed sie dieselben Ergebnisse zurück, unabhängig davon, wie oft sie aufgerufen werden.

In den folgenden Abschnitten werden wir:

  1. Sehen Sie sich Beispiele für die Verwendung zufälliger Bildoperationen zum Transformieren eines Bildes an
  2. Demonstrieren Sie, wie Sie zufällige Transformationen auf einen Trainingsdatensatz anwenden.

Ändern Sie die Bildhelligkeit nach dem Zufallsprinzip

Ändern Sie die Bildhelligkeit nach dem image indem Sie einen Helligkeitsfaktor und einen Startwert seed . Der Helligkeitsfaktor wird zufällig in dem Bereich gewählt [-max_delta, max_delta) und ist mit dem zugehörigen gegebenen seed .

for i in range(3):
  seed = (i, 0)  # tuple of size (2,)
  stateless_random_brightness = tf.image.stateless_random_brightness(
      image, max_delta=0.95, seed=seed)
  visualize(image, stateless_random_brightness)

png

png

png

Ändern Sie den Bildkontrast nach dem Zufallsprinzip

Ändern Sie den Kontrast des image nach dem Zufallsprinzip, indem Sie einen Kontrastbereich und einen Startwert seed . Der Kontrastbereich wird zufällig im Intervall [lower, upper] und ist dem gegebenen seed .

for i in range(3):
  seed = (i, 0)  # tuple of size (2,)
  stateless_random_contrast = tf.image.stateless_random_contrast(
      image, lower=0.1, upper=0.9, seed=seed)
  visualize(image, stateless_random_contrast)

png

png

png

Schneiden Sie ein Bild nach dem Zufallsprinzip zu

Ernte Randomly image durch genauere size und seed . Der Teil, der abgeschnitten wird aus image ist an einem zufällig gewählten offet und wird mit dem gegebenen zugehörigen seed .

for i in range(3):
  seed = (i, 0)  # tuple of size (2,)
  stateless_random_crop = tf.image.stateless_random_crop(
      image, size=[210, 300, 3], seed=seed)
  visualize(image, stateless_random_crop)

png

png

png

Wenden Sie eine Erweiterung auf einen Datensatz an

Laden Sie zunächst den Bilddatensatz erneut herunter, falls er in den vorherigen Abschnitten geändert wurde.

(train_datasets, val_ds, test_ds), metadata = tfds.load(
    'tf_flowers',
    split=['train[:80%]', 'train[80%:90%]', 'train[90%:]'],
    with_info=True,
    as_supervised=True,
)

Definieren wir eine Dienstprogrammfunktion zum Ändern der Größe und Neuskalieren der Bilder. Diese Funktion wird verwendet, um die Größe und den Maßstab der Bilder im Datensatz zu vereinheitlichen:

def resize_and_rescale(image, label):
  image = tf.cast(image, tf.float32)
  image = tf.image.resize(image, [IMG_SIZE, IMG_SIZE])
  image = (image / 255.0)
  return image, label

Definieren wir auch eine augment , mit der die zufälligen Transformationen auf die Bilder angewendet werden können. Diese Funktion wird im nächsten Schritt für den Datensatz verwendet.

def augment(image_label, seed):
  image, label = image_label
  image, label = resize_and_rescale(image, label)
  image = tf.image.resize_with_crop_or_pad(image, IMG_SIZE + 6, IMG_SIZE + 6)
  # Make a new seed
  new_seed = tf.random.experimental.stateless_split(seed, num=1)[0, :]
  # Random crop back to the original size
  image = tf.image.stateless_random_crop(
      image, size=[IMG_SIZE, IMG_SIZE, 3], seed=seed)
  # Random brightness
  image = tf.image.stateless_random_brightness(
      image, max_delta=0.5, seed=new_seed)
  image = tf.clip_by_value(image, 0, 1)
  return image, label

Option 1: Verwenden von tf.data.experimental.Counter()

Erstellen Sie ein tf.data.experimental.Counter() -Objekt (nennen wir es counter ) und zip das Dataset mit (counter, counter) . Dadurch wird sichergestellt , dass jedes Bild in dem Datensatz mit einem eindeutigen Wert zugeordnet wird (der Form (2,) ) , basierend auf counter , die in die vergangen später bekommen augment Funktion als seed Wert für Zufall Transformationen.

# Create counter and zip together with train dataset
counter = tf.data.experimental.Counter()
train_ds = tf.data.Dataset.zip((train_datasets, (counter, counter)))

Ordnen Sie die augment dem Trainingsdatensatz zu.

train_ds = (
    train_ds
    .shuffle(1000)
    .map(augment, num_parallel_calls=AUTOTUNE)
    .batch(batch_size)
    .prefetch(AUTOTUNE)
)
val_ds = (
    val_ds
    .map(resize_and_rescale, num_parallel_calls=AUTOTUNE)
    .batch(batch_size)
    .prefetch(AUTOTUNE)
)
test_ds = (
    test_ds
    .map(resize_and_rescale, num_parallel_calls=AUTOTUNE)
    .batch(batch_size)
    .prefetch(AUTOTUNE)
)

Option 2: Verwenden von tf.random.Generator

Erstellen Sie ein tf.random.Generator Objekt mit einem anfänglichen seed Wert. Aufruf make_seeds Funktion auf dem gleichen Generator Objekt gibt einen neuen, einzigartigen seed Wert immer. Definieren eine Wrapper - Funktion , dass 1) ruft make_seeds Funktion und dass 2) übergibt den neu erzeugten seed Wert in die augment Funktion für Zufall Transformationen.

# Create a generator
rng = tf.random.Generator.from_seed(123, alg='philox')
# A wrapper function for updating seeds
def f(x, y):
  seed = rng.make_seeds(2)[0]
  image, label = augment((x, y), seed)
  return image, label

Ordnen Sie die Wrapper-Funktion f dem Trainingsdatensatz zu.

train_ds = (
    train_datasets
    .shuffle(1000)
    .map(f, num_parallel_calls=AUTOTUNE)
    .batch(batch_size)
    .prefetch(AUTOTUNE)
)
val_ds = (
    val_ds
    .map(resize_and_rescale, num_parallel_calls=AUTOTUNE)
    .batch(batch_size)
    .prefetch(AUTOTUNE)
)
test_ds = (
    test_ds
    .map(resize_and_rescale, num_parallel_calls=AUTOTUNE)
    .batch(batch_size)
    .prefetch(AUTOTUNE)
)

Diese Datensätze können jetzt verwendet werden, um ein Modell wie zuvor gezeigt zu trainieren.

Nächste Schritte

In diesem Lernprogramm wurde die tf.image mithilfe von Keras Preprocessing Layers und tf.image . Um zu erfahren , wie man eine Vorverarbeitung Schichten in Ihrem Modell enthält, finden Sie in der Bildklassifizierung Tutorial. Möglicherweise möchten Sie auch erfahren, wie Sie mit der Vorverarbeitung von Ebenen Text klassifizieren können, wie im Lernprogramm zur grundlegenden Textklassifizierung gezeigt. Sie können mehr darüber erfahren , tf.data in dieser Anleitung , und Sie können lernen , wie Sie Ihre Eingangsleitungen für die Leistung konfigurieren hier .