¡El Día de la Comunidad de ML es el 9 de noviembre! Únase a nosotros para recibir actualizaciones de TensorFlow, JAX, y más Más información

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:

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

Descarga 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))
2021-10-22 01:22:18.891161: 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

Utilice capas de preprocesamiento de Keras

Cambiar el tamaño y la escala

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

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

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 utilizar las capas de preprocesamiento de Keras

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

Opción 1: hacer 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 sincrónica 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 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, se utiliza 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á. 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. 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 de Keras 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 la E / S se bloquee. (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)

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

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 [==============================] - 30s 190ms/step - loss: 1.3237 - accuracy: 0.4183 - val_loss: 1.0881 - val_accuracy: 0.5668
Epoch 2/5
92/92 [==============================] - 3s 26ms/step - loss: 1.0428 - accuracy: 0.5841 - val_loss: 1.0630 - val_accuracy: 0.5886
Epoch 3/5
92/92 [==============================] - 3s 26ms/step - loss: 0.9704 - accuracy: 0.6202 - val_loss: 1.0317 - val_accuracy: 0.6076
Epoch 4/5
92/92 [==============================] - 3s 26ms/step - loss: 0.9190 - accuracy: 0.6356 - val_loss: 0.8869 - val_accuracy: 0.6621
Epoch 5/5
92/92 [==============================] - 3s 26ms/step - loss: 0.8766 - accuracy: 0.6594 - val_loss: 0.8312 - val_accuracy: 0.6730
loss, acc = model.evaluate(test_ds)
print("Accuracy", acc)
12/12 [==============================] - 3s 32ms/step - loss: 0.8001 - accuracy: 0.7030
Accuracy 0.7029972672462463

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 forma 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 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, 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 utilizar como se describe en las opciones 1 y 2 anteriores.

Usando tf.image

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:

image, label = next(iter(train_ds))
_ = plt.imshow(image)
_ = plt.title(get_label_name(label))
2021-10-22 01:23:14.609482: 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 de 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 una imagen en el centro

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

Rotar una imagen

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

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 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, podrá:

  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

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

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

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

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 trainining set.
counter = tf.data.experimental.Counter()
train_ds = tf.data.Dataset.zip((train_datasets, (counter, counter)))
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/data/experimental/ops/counter.py:66: scan (from tensorflow.python.data.experimental.ops.scan_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use `tf.data.Dataset.scan(...) instead
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/data/experimental/ops/counter.py:66: scan (from tensorflow.python.data.experimental.ops.scan_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use `tf.data.Dataset.scan(...) instead

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