Ayuda a proteger la Gran Barrera de Coral con TensorFlow en Kaggle Únete Challenge

Aumento de datos

Ver en TensorFlow.org Ejecutar en Google Colab Ver fuente en GitHub Descargar libreta

Descripción general

Este tutorial demuestra el aumento de datos: una técnica para aumentar la diversidad de su conjunto de entrenamiento mediante la aplicación de transformaciones aleatorias (pero realistas), como la rotación de imágenes.

Aprenderá cómo aplicar el aumento de datos de dos maneras:

Configuración

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

from tensorflow.keras import layers

Descargar un conjunto de datos

En este tutorial se utiliza el tf_flowers conjunto de datos. Para mayor comodidad, descargar el conjunto de datos utilizando TensorFlow conjuntos de datos . Si desea conocer otras formas de importación de datos, revisar la carga de imágenes tutorial.

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

El conjunto de datos de flores tiene cinco clases.

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

Recuperemos una imagen del conjunto de datos y usémosla para demostrar el aumento de datos.

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

image, label = next(iter(train_ds))
_ = plt.imshow(image)
_ = plt.title(get_label_name(label))
2022-01-12 02:33:50.153629: W tensorflow/core/kernels/data/cache_dataset_ops.cc:768] The calling iterator did not fully read the dataset being cached. In order to avoid unexpected truncation of the dataset, the partially cached contents of the dataset  will be discarded. This can happen if you have an input pipeline similar to `dataset.cache().take(k).repeat()`. You should use `dataset.take(k).cache().repeat()` instead.

png

Usar capas de preprocesamiento de Keras

Redimensionamiento y reescalado

Se puede utilizar el pre-procesamiento Keras capas para cambiar el tamaño de las imágenes a una forma consistente (con tf.keras.layers.Resizing ), y para cambiar la escala de valores de píxeles (con tf.keras.layers.Rescaling ).

IMG_SIZE = 180

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

Puedes visualizar el resultado de aplicar estas capas a una imagen.

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

png

Verificar que los píxeles están en el [0, 1] rango:

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

Aumento de datos

Se puede utilizar el Keras procesamiento previo para el aumento de capas de datos, así, como tf.keras.layers.RandomFlip y tf.keras.layers.RandomRotation .

Vamos a crear algunas capas de preprocesamiento y aplicarlas repetidamente a la misma imagen.

data_augmentation = tf.keras.Sequential([
  layers.RandomFlip("horizontal_and_vertical"),
  layers.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")
WARNING:matplotlib.image:Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
WARNING:matplotlib.image:Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
WARNING:matplotlib.image:Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
WARNING:matplotlib.image:Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
WARNING:matplotlib.image:Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
WARNING:matplotlib.image:Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
WARNING:matplotlib.image:Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
WARNING:matplotlib.image:Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
WARNING:matplotlib.image:Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).

png

Hay una variedad de capas de pre-procesamiento se pueden utilizar para el aumento de los datos incluidos tf.keras.layers.RandomContrast , tf.keras.layers.RandomCrop , tf.keras.layers.RandomZoom , y otros.

Dos opciones para usar las capas de preprocesamiento de Keras

Hay dos formas de utilizar estas capas de preprocesamiento, con importantes compensaciones.

Opción 1: haga que las capas de preprocesamiento formen parte de su modelo

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

Hay dos puntos importantes a tener en cuenta en este caso:

  • El aumento de datos se ejecutará en el dispositivo, de forma sincronizada con el resto de sus capas, y se beneficiará de la aceleración de la GPU.

  • Al exportar el modelo mediante model.save , las capas de pre-procesamiento se guardarán junto con el resto de su modelo. Si luego implementa este modelo, automáticamente estandarizará las imágenes (según la configuración de sus capas). Esto puede ahorrarle el esfuerzo de tener que volver a implementar esa lógica del lado del servidor.

Opción 2: aplique las capas de preprocesamiento a su conjunto de datos

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

Con este enfoque, se utiliza Dataset.map para crear un conjunto de datos que produce lotes de imágenes aumentadas. En este caso:

  • El aumento de datos ocurrirá de forma asíncrona en la CPU y no bloqueará. Se puede superponerse a la formación de su modelo en la GPU con los datos de pre-procesamiento, utilizando Dataset.prefetch , se muestra a continuación.
  • En este caso las capas de pre-procesamiento no se exportarán con el modelo cuando llame Model.save . Deberá adjuntarlos a su modelo antes de guardarlo o volver a implementarlos en el lado del servidor. Después del entrenamiento, puede adjuntar las capas de preprocesamiento antes de exportar.

Se puede encontrar un ejemplo de la primera opción en la Clasificación de la imagen tutorial. Vamos a demostrar la segunda opción aquí.

Aplicar las capas de preprocesamiento a los conjuntos de datos

Configure los conjuntos de datos de entrenamiento, validación y prueba con las capas de preprocesamiento de Keras que creó anteriormente. También configurará los conjuntos de datos para el rendimiento, utilizando lecturas paralelas y búsqueda previa almacenada en búfer para generar lotes desde el disco sin que la E/S se convierta en un bloqueo. (Aprender un rendimiento más conjunto de datos en el rendimiento mejor con la API tf.data guía).

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

entrenar a un modelo

Para completar, ahora entrenará un modelo utilizando los conjuntos de datos que acaba de preparar.

El secuencial modelo consiste en tres bloques de convolución ( tf.keras.layers.Conv2D ) con una capa de puesta en común max ( tf.keras.layers.MaxPooling2D ) en cada uno de ellos. Hay una capa completamente conectado ( tf.keras.layers.Dense ) con 128 unidades en la parte superior de la misma que se activa mediante una función de activación relu ( 'relu' ). Este modelo no ha sido ajustado para precisión (el objetivo es mostrarle la mecánica).

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

Elija el tf.keras.optimizers.Adam optimizador y tf.keras.losses.SparseCategoricalCrossentropy función de pérdida. A la vista de la formación y la precisión de validación para cada época de entrenamiento, pasar la metrics argumento para Model.compile .

model.compile(optimizer='adam',
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
              metrics=['accuracy'])

Entrena por algunas épocas:

epochs=5
history = model.fit(
  train_ds,
  validation_data=val_ds,
  epochs=epochs
)
Epoch 1/5
92/92 [==============================] - 12s 93ms/step - loss: 1.2860 - accuracy: 0.4499 - val_loss: 1.2643 - val_accuracy: 0.5177
Epoch 2/5
92/92 [==============================] - 3s 28ms/step - loss: 1.0524 - accuracy: 0.5889 - val_loss: 1.0438 - val_accuracy: 0.6185
Epoch 3/5
92/92 [==============================] - 3s 28ms/step - loss: 0.9790 - accuracy: 0.6093 - val_loss: 1.0033 - val_accuracy: 0.6076
Epoch 4/5
92/92 [==============================] - 3s 29ms/step - loss: 0.9039 - accuracy: 0.6403 - val_loss: 1.0072 - val_accuracy: 0.5913
Epoch 5/5
92/92 [==============================] - 3s 31ms/step - loss: 0.8871 - accuracy: 0.6591 - val_loss: 0.8866 - val_accuracy: 0.6567
loss, acc = model.evaluate(test_ds)
print("Accuracy", acc)
12/12 [==============================] - 1s 16ms/step - loss: 0.8565 - accuracy: 0.6676
Accuracy 0.667574942111969

Aumento de datos personalizado

También puede crear capas de aumento de datos personalizadas.

Esta sección del tutorial muestra dos formas de hacerlo:

  • En primer lugar, se creará un tf.keras.layers.Lambda capa. Esta es una buena manera de escribir código conciso.
  • A continuación, se escribirá una nueva capa a través de subclases , que le da más control.

Ambas capas invertirán aleatoriamente los colores de una imagen, según alguna probabilidad.

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")
2022-01-12 02:34:18.836924: W tensorflow/compiler/xla/service/gpu/llvm_gpu_backend/gpu_backend_lib.cc:399] target triple not found in the module
2022-01-12 02:34:18.836999: W tensorflow/compiler/xla/service/gpu/llvm_gpu_backend/gpu_backend_lib.cc:399] target triple not found in the module
2022-01-12 02:34:18.837045: W tensorflow/compiler/xla/service/gpu/llvm_gpu_backend/gpu_backend_lib.cc:399] target triple not found in the module
2022-01-12 02:34:18.837100: W tensorflow/compiler/xla/service/gpu/llvm_gpu_backend/gpu_backend_lib.cc:399] target triple not found in the module
2022-01-12 02:34:18.837152: W tensorflow/compiler/xla/service/gpu/llvm_gpu_backend/gpu_backend_lib.cc:399] target triple not found in the module
2022-01-12 02:34:18.837236: W tensorflow/compiler/xla/service/gpu/llvm_gpu_backend/gpu_backend_lib.cc:399] target triple not found in the module
2022-01-12 02:34:18.837312: W tensorflow/compiler/xla/service/gpu/llvm_gpu_backend/gpu_backend_lib.cc:399] target triple not found in the module
2022-01-12 02:34:18.840572: W tensorflow/compiler/xla/service/gpu/llvm_gpu_backend/gpu_backend_lib.cc:399] target triple not found in the module

png

A continuación, aplicar una capa personalizada subclases :

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

Ambas capas se pueden usar como se describe en las opciones 1 y 2 anteriores.

Usando tf.imagen

Las utilidades de preprocesamiento de Keras anteriores son convenientes. Sin embargo, para un control más preciso, puede escribir sus propias tuberías de aumento de datos o capas usando tf.data y tf.image . (También es posible que desee comprobar hacia fuera TensorFlow Complementos Image: Operaciones y TensorFlow de E / S: Espacio de color conversiones ).

Dado que el conjunto de datos de flores se configuró previamente con el aumento de datos, volvamos a importarlo para comenzar de nuevo:

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

Recuperar una imagen para trabajar con:

image, label = next(iter(train_ds))
_ = plt.imshow(image)
_ = plt.title(get_label_name(label))
2022-01-12 02:34:21.842330: W tensorflow/core/kernels/data/cache_dataset_ops.cc:768] The calling iterator did not fully read the dataset being cached. In order to avoid unexpected truncation of the dataset, the partially cached contents of the dataset  will be discarded. This can happen if you have an input pipeline similar to `dataset.cache().take(k).repeat()`. You should use `dataset.take(k).cache().repeat()` instead.

png

Usemos la siguiente función para visualizar y comparar las imágenes originales y aumentadas una al lado de la otra:

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)

Aumento de datos

Voltear una imagen

Voltear una imagen ya sea vertical u horizontalmente con tf.image.flip_left_right :

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

png

escala de grises una imagen

Puede escala de grises de una imagen con tf.image.rgb_to_grayscale :

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

png

Saturar una imagen

Saturar una imagen con tf.image.adjust_saturation proporcionando un factor de saturación:

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

png

Cambiar el brillo de la imagen

Cambiar el brillo de la imagen con tf.image.adjust_brightness proporcionando un factor de brillo:

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

png

Recortar al centro una imagen

Recortar la imagen desde el centro hasta la parte de imagen que desee utilizando tf.image.central_crop :

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

png

Girar una imagen

Girar una imagen 90 grados con tf.image.rot90 :

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

png

Transformaciones aleatorias

Aplicar transformaciones aleatorias a las imágenes puede ayudar aún más a generalizar y expandir el conjunto de datos. La corriente tf.image API proporciona ocho de tales operaciones de imagen aleatoria (PO):

Estas operaciones de imágenes aleatorias son puramente funcionales: la salida solo depende de la entrada. Esto los hace fáciles de usar en canalizaciones de entrada deterministas de alto rendimiento. Requieren una seed valor sea de entrada de cada paso. Dada la misma seed , que devuelven los mismos resultados independientemente de cuántas veces se les llama.

En las siguientes secciones, usted:

  1. Repase ejemplos del uso de operaciones aleatorias de imágenes para transformar una imagen.
  2. Demostrar cómo aplicar transformaciones aleatorias a un conjunto de datos de entrenamiento.

Cambiar aleatoriamente el brillo de la imagen

Al azar cambiar el brillo de image utilizando tf.image.stateless_random_brightness proporcionando un factor de brillo y seed . El factor de luminancia se elige aleatoriamente en el intervalo [-max_delta, max_delta) y se asocia con el dado 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

Cambiar aleatoriamente el contraste de la imagen

Aleatoriamente cambiar el contraste de image usando tf.image.stateless_random_contrast proporcionando una gama de contraste y seed . El rango de contraste es elegido aleatoriamente en el intervalo [lower, upper] y se asocia con el dado 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

Recortar aleatoriamente una imagen

Aleatoriamente recortar image usando tf.image.stateless_random_crop proporcionando objetivo size y seed . La porción que consigue recortada de image está en un elegido compensar al azar y se asocia con el dado 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

Aplicar aumento a un conjunto de datos

Primero descarguemos el conjunto de datos de imágenes nuevamente en caso de que se hayan modificado en las secciones anteriores.

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

A continuación, defina una función de utilidad para cambiar el tamaño y la escala de las imágenes. Esta función se utilizará para unificar el tamaño y la escala de las imágenes en el conjunto de datos:

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

También vamos a definir el augment función que puede aplicar las transformaciones aleatorias a las imágenes. Esta función se usará en el conjunto de datos en el siguiente paso.

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

Opción 1: usar tf.data.experimental.Counter

Crear un tf.data.experimental.Counter objeto (llamémoslo counter ) y Dataset.zip el conjunto de datos con (counter, counter) . Esto asegurará que cada imagen en el conjunto de datos se asocia con un valor único (de forma (2,) ) basado en counter que más tarde puede conseguir pasado en el augment función que la seed valor para las transformaciones aleatorias.

# Create a `Counter` object and `Dataset.zip` it together with the training set.
counter = tf.data.experimental.Counter()
train_ds = tf.data.Dataset.zip((train_datasets, (counter, counter)))

Mapa del augment función para la formación de datos:

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

Opción 2: Usar tf.random.Generator

  • Crear un tf.random.Generator objeto con una inicial seed valor. Llamando a la make_seeds función en el mismo objeto generador siempre devuelve un nuevo, único seed valor.
  • Definir una función de contenedor que: 1) las llamadas make_seeds función; y 2) pasa el recién generado seed valor en el augment función para transformaciones aleatorias.
# Create a generator.
rng = tf.random.Generator.from_seed(123, alg='philox')
# Create 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

Mapear la función de contenedor f a la formación de datos, y la resize_and_rescale función de los conjuntos de validación y pruebas:

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

Estos conjuntos de datos ahora se pueden usar para entrenar un modelo como se mostró anteriormente.

Próximos pasos

Este tutorial demostrado aumento de datos utilizando capas Keras de preprocesamiento y tf.image .

  • Para aprender cómo incluir el procesamiento previo capas dentro de su modelo, consulte la Clasificación de la imagen tutorial.
  • Usted también puede estar interesado en aprender cómo las capas de pre-procesamiento que pueden ayudar a clasificar el texto, como se muestra en el texto básico de clasificación tutorial.
  • Usted puede aprender más sobre tf.data en esta guía , y se puede aprender cómo configurar sus tuberías de entrada para un rendimiento aquí .