¡Reserva! Google I / O regresa del 18 al 20 de mayo Regístrese ahora
Se usó la API de Cloud Translation para traducir esta página.
Switch to English

Aumento de datos

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

Visió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á a aplicar el aumento de datos de dos formas. En primer lugar, utilizará las capas de preprocesamiento de Keras . A continuación, utilizará tf.image .

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
from tensorflow.keras.datasets import mnist

Descarga un conjunto de datos

Este tutorial usa el conjunto de datos tf_flowers . Para mayor comodidad, descargue el conjunto de datos con TensorFlow Datasets . Si desea conocer otras formas de importar datos, consulte el tutorial de carga de imágenes .

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

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

png

Utilice capas de preprocesamiento de Keras

Cambiar el tamaño y la escala

Puede utilizar capas de preprocesamiento para cambiar el tamaño de sus imágenes a una forma coherente y para cambiar la escala de los valores de píxeles.

IMG_SIZE = 180

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

Puede ver el resultado de aplicar estas capas a una imagen.

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

png

Puede verificar que los píxeles están en [0-1] .

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

Aumento de datos

También puede utilizar capas de preprocesamiento para el aumento de datos.

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

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

Hay una variedad de capas de preprocesamiento que puede utilizar para el aumento de datos, incluidas las layers.RandomContrast , layers.RandomCrop , layers.RandomZoom , layers.RandomContrast , layers.RandomCrop , layers.RandomZoom y otras.

Dos opciones para usar las capas de preprocesamiento

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([
  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 sincrónica con el resto de sus capas, y se beneficiará de la aceleración de la GPU.

  • Cuando exporta su modelo usando model.save , las capas de preprocesamiento se guardarán junto con el resto de su modelo. Si luego implementas este modelo, automáticamente estandarizará las imágenes (según la configuración de tus 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, usa Dataset.map para crear un conjunto de datos que produce lotes de imágenes aumentadas. En este caso:

  • El aumento de datos se realizará de forma asincrónica en la CPU y no se bloqueará. Puede superponer el entrenamiento de su modelo en la GPU con el preprocesamiento de datos mediante Dataset.prefetch , que se muestra a continuación.
  • En este caso, las capas de preprocesamiento no se exportarán con el modelo cuando llame a 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.

Puede encontrar un ejemplo de la primera opción en el tutorial de clasificación de imágenes . Demostremos 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 que creó anteriormente. También configurará los conjuntos de datos para el rendimiento, utilizando lecturas paralelas y captación previa en búfer para generar lotes del disco sin que las E / S se bloqueen. Puede obtener más información sobre el rendimiento del conjunto de datos en la guía Mejor rendimiento con la API 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)

Entrena un modelo

Para completar, ahora entrenará un modelo usando estos conjuntos de datos. Este modelo no se ha ajustado para la 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)
])
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

Aumento de datos personalizado

También puede crear capas de aumento de datos personalizadas. Este tutorial muestra dos formas de hacerlo. Primero, creará una capa layers.Lambda . Esta es una buena forma de escribir código conciso. A continuación, escribirá una nueva capa mediante subclases , lo que le dará más control. Ambas capas invertirán aleatoriamente los colores en 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")

png

A continuación, implemente una capa personalizada mediante 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 utilizar como se describe en las opciones 1 y 2 anteriores.

Usando tf.image

Las layers.preprocessing anteriores de layers.preprocessing utilidades de layers.preprocessing son convenientes. Para un control más tf.data , puede escribir sus propias canalizaciones o capas de aumento de datos utilizando tf.data y tf.image . También puede consultar la Imagen de los complementos de TensorFlow: Operaciones y E / S de TensorFlow: Conversiones del espacio de color

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

Recupere una imagen para trabajar.

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

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 la imagen

Voltea la imagen vertical u horizontalmente.

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

png

Escala de grises la imagen

Escala de grises una imagen.

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

png

Saturar la imagen

Sature una imagen proporcionando un factor de saturación.

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

png

Cambiar el brillo de la imagen

Cambie el brillo de la imagen proporcionando un factor de brillo.

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

png

Centro recorta la imagen

Recorta la imagen desde el centro hasta la parte de la imagen que desees.

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

png

Rotar la imagen

Gire una imagen 90 grados.

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

png

Transformaciones aleatorias

La aplicación de transformaciones aleatorias a las imágenes puede ayudar a generalizar y expandir el conjunto de datos. La API tf.image actual proporciona 8 operaciones de imágenes aleatorias (ops) de este tipo:

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 que se ingrese un valor de seed cada paso. Dada la misma seed , devuelven los mismos resultados independientemente de cuántas veces se llamen.

En las siguientes secciones, haremos lo siguiente:

  1. Repase ejemplos de uso de operaciones de imagen aleatorias para transformar una imagen y
  2. Demuestre cómo aplicar transformaciones aleatorias a un conjunto de datos de entrenamiento.

Cambiar aleatoriamente el brillo de la imagen

Cambie aleatoriamente el brillo de la image proporcionando un factor de brillo y una seed . El factor de brillo se elige aleatoriamente en el rango [-max_delta, max_delta) y se asocia con la seed dada.

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

Cambie aleatoriamente el contraste de la image proporcionando un rango de contraste y una seed . El rango de contraste se elige aleatoriamente en el intervalo [lower, upper] y está asociado con la seed dada.

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 una imagen al azar

Recorte la image al azar proporcionando el size y la seed objetivo. La parte que se recorta de la image es una oferta elegida al azar y está asociada con la seed dada.

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

Definamos 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

Definamos también la función de augment que puede aplicar las transformaciones aleatorias a las imágenes. Esta función se utilizará 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()

Cree un objeto tf.data.experimental.Counter() (llamémoslo counter ) y zip el conjunto de datos con (counter, counter) . Esto asegurará que cada imagen en el conjunto de datos se asocie con un valor único (de forma (2,) ) basado en un counter que luego se puede pasar a la función de augment como valor seed para transformaciones aleatorias.

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

Asigne la función de augment al conjunto de datos de entrenamiento.

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

Cree un objeto tf.random.Generator con un valor seed . Llamar a la función make_seeds en el mismo objeto generador siempre devuelve un valor seed nuevo y único. Defina una función contenedora que 1) llame a la función make_seeds y que 2) pase el valor seed recién generado a la función de augment para transformaciones aleatorias.

# 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

Asigne la función contenedora f al conjunto de datos de entrenamiento.

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 demostró el aumento de datos utilizando Keras Preprocessing Layers y tf.image . Para aprender cómo incluir capas de preprocesamiento dentro de su modelo, consulte el tutorial de clasificación de imágenes . También puede estar interesado en aprender cómo el preprocesamiento de capas puede ayudarlo a clasificar texto, como se muestra en el tutorial de clasificación básica de texto . Puede obtener más información sobre tf.data en esta guía y puede aprender a configurar sus canalizaciones de entrada para el rendimiento aquí .