Reserve a data! O Google I / O retorna de 18 a 20 de maio Registre-se agora
Esta página foi traduzida pela API Cloud Translation.
Switch to English

Aumento de dados

Ver no TensorFlow.org Executar no Google Colab Ver fonte no GitHub Baixar caderno

Visão geral

Este tutorial demonstra o aumento de dados: uma técnica para aumentar a diversidade de seu conjunto de treinamento aplicando transformações aleatórias (mas realistas), como rotação de imagem. Você aprenderá como aplicar o aumento de dados de duas maneiras. Primeiro, você usará as camadas de pré-processamento Keras . Em seguida, você usará tf.image .

Configurar

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

Baixe um conjunto de dados

Este tutorial usa o conjunto de dados tf_flowers . Por conveniência, faça o download do conjunto de dados usando os conjuntos de dados TensorFlow . Se você gostaria de aprender sobre outras maneiras de importar dados, consulte o tutorial de carregamento de imagens .

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

O conjunto de dados de flores tem cinco classes.

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

Vamos recuperar uma imagem do conjunto de dados e usá-la para demonstrar o aumento dos dados.

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

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

png

Use camadas de pré-processamento Keras

Redimensionamento e redimensionamento

Você pode usar camadas de pré-processamento para redimensionar suas imagens para uma forma consistente e para redimensionar os valores de pixel.

IMG_SIZE = 180

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

Você pode ver o resultado da aplicação dessas camadas a uma imagem.

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

png

Você pode verificar se os pixels estão em [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 dados

Você também pode usar camadas de pré-processamento para aumento de dados.

Vamos criar algumas camadas de pré-processamento e aplicá-las repetidamente à mesma imagem.

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

Há uma variedade de camadas de pré-processamento que você pode usar para aumento de dados, incluindo layers.RandomContrast , layers.RandomCrop , layers.RandomZoom e outros.

Duas opções para usar as camadas de pré-processamento

Existem duas maneiras de usar essas camadas de pré-processamento, com importantes compensações.

Opção 1: tornar as camadas de pré-processamento parte do seu modelo

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

Existem dois pontos importantes a serem considerados neste caso:

  • O aumento de dados será executado no dispositivo, de forma síncrona com o resto de suas camadas, e se beneficiará da aceleração da GPU.

  • Quando você exporta seu modelo usando model.save , as camadas de pré-processamento serão salvas junto com o resto do seu modelo. Se você implantar posteriormente este modelo, ele padronizará automaticamente as imagens (de acordo com a configuração de suas camadas). Isso pode evitar que você precise reimplementar essa lógica no lado do servidor.

Opção 2: aplique as camadas de pré-processamento ao seu conjunto de dados

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

Com essa abordagem, você usa Dataset.map para criar um conjunto de dados que produz lotes de imagens aumentadas. Nesse caso:

  • O aumento de dados acontecerá de forma assíncrona na CPU e não é bloqueador. Você pode sobrepor o treinamento do seu modelo na GPU com o pré-processamento de dados, usando Dataset.prefetch , mostrado abaixo.
  • Neste caso, as camadas de pré-processamento não serão exportadas com o modelo quando você chamar model.save . Você precisará anexá-los ao seu modelo antes de salvá-lo ou reimplementá-los no lado do servidor. Após o treinamento, você pode anexar as camadas de pré-processamento antes de exportar.

Você pode encontrar um exemplo da primeira opção no tutorial de classificação de imagens . Vamos demonstrar a segunda opção aqui.

Aplique as camadas de pré-processamento aos conjuntos de dados

Configure os conjuntos de dados de treinamento, validação e teste com as camadas de pré-processamento que você criou acima. Você também configurará os conjuntos de dados para desempenho, usando leituras paralelas e pré-busca em buffer para gerar lotes do disco sem que o I / O se torne um bloqueio. Você pode aprender mais sobre o desempenho do conjunto de dados no guia Melhor desempenho com a 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)

Treine uma modelo

Para completar, agora você treinará um modelo usando esses conjuntos de dados. Este modelo não foi ajustado para precisão (o objetivo é mostrar a você a 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 dados personalizados

Você também pode criar camadas de aumento de dados personalizadas. Este tutorial mostra duas maneiras de fazer isso. Primeiro, você criará uma camada layers.Lambda . Esta é uma boa maneira de escrever código conciso. Em seguida, você escreverá uma nova camada por meio de subclasses , o que lhe dará mais controle. Ambas as camadas inverterão aleatoriamente as cores em uma imagem, de acordo com alguma probabilidade.

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

Em seguida, implemente uma camada personalizada por meio de subclasses .

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 as camadas podem ser usadas conforme descrito nas opções 1 e 2 acima.

Usando tf.image

As layers.preprocessing acima. layers.preprocessing utilitários de layers.preprocessing são convenientes. Para um controle mais tf.data , você pode escrever seus próprios pipelines ou camadas de aumento de dados usando tf.data e tf.image . Você também pode conferir Imagem de complementos do TensorFlow: operações e E / S do TensorFlow: conversões de espaço de cores

Como o conjunto de dados de flores foi configurado anteriormente com aumento de dados, vamos reimportá-lo para começar do zero.

(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 uma imagem para trabalhar.

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

png

Vamos usar a seguinte função para visualizar e comparar as imagens originais e aumentadas lado a lado.

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 dados

Invertendo a imagem

Vire a imagem vertical ou horizontalmente.

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

png

Escale a imagem em tons de cinza

Escala de cinza uma imagem.

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

png

Saturar a imagem

Sature uma imagem fornecendo um fator de saturação.

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

png

Alterar o brilho da imagem

Altere o brilho da imagem fornecendo um fator de brilho.

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

png

Corte a imagem no centro

Corte a imagem do centro até a parte desejada.

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

png

Gire a imagem

Girar uma imagem em 90 graus.

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

png

Transformações aleatórias

Aplicar transformações aleatórias às imagens pode ajudar ainda mais a generalizar e expandir o conjunto de dados. A API tf.image atual fornece 8 dessas operações de imagem aleatória (ops):

Essas operações de imagem aleatória são puramente funcionais: a saída depende apenas da entrada. Isso os torna simples de usar em pipelines de entrada determinística de alto desempenho. Eles exigem que um valor de seed seja inserido em cada etapa. Dada a mesma seed , eles retornam os mesmos resultados, independentemente de quantas vezes são chamados.

Nas seções a seguir, iremos:

  1. Veja exemplos de como usar operações de imagem aleatória para transformar uma imagem e
  2. Demonstre como aplicar transformações aleatórias a um conjunto de dados de treinamento.

Alterar aleatoriamente o brilho da imagem

Altere aleatoriamente o brilho da image , fornecendo um fator de brilho e seed . O fator de brilho é escolhido aleatoriamente no intervalo [-max_delta, max_delta) e está associado à seed fornecida.

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

Alterar aleatoriamente o contraste da imagem

Altere aleatoriamente o contraste da image , fornecendo uma faixa de contraste e seed . A faixa de contraste é escolhida aleatoriamente no intervalo [lower, upper] e está associada à seed fornecida.

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

Cortar uma imagem aleatoriamente

Recorte a image aleatoriamente, fornecendo o size destino e a seed . A parte que é cortada da image está em um deslocamento escolhido aleatoriamente e está associada à seed fornecida.

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 um conjunto de dados

Vamos primeiro baixar o conjunto de dados de imagens novamente, caso eles sejam modificados nas seções 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,
)

Vamos definir uma função de utilidade para redimensionar e redimensionar as imagens. Esta função será usada para unificar o tamanho e a escala das imagens no conjunto de dados:

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

Vamos também definir a função de augment que pode aplicar as transformações aleatórias às imagens. Esta função será usada no conjunto de dados na próxima etapa.

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

Opção 1: usando tf.data.experimental.Counter()

Crie um objeto tf.data.experimental.Counter() (vamos chamá-lo de counter ) e zip o conjunto de dados com (counter, counter) . Isso irá garantir que cada imagem no conjunto de dados fica associado a um valor único (de forma (2,) ) com base no counter que mais tarde pode se passou para a augment função que a seed valor para transformações aleatórias.

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

Mapeie a função de augment para o conjunto de dados de treinamento.

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

Opção 2: usando tf.random.Generator

Criar um tf.random.Generator objeto com um intial seed valor. Chamar a função make_seeds no mesmo objeto gerador retorna um valor de seed novo e exclusivo sempre. Defina uma função de invólucro que 1) chame a função make_seeds e que 2) passe o valor de seed recém-gerado para a função de augment para transformações aleatórias.

# 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

Mapeie a função de wrapper f para o conjunto de dados de treinamento.

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

Esses conjuntos de dados agora podem ser usados ​​para treinar um modelo, conforme mostrado anteriormente.

Próximos passos

Este tutorial demonstrou o aumento de dados usando Keras Preprocessing Layers e tf.image . Para aprender como incluir camadas de pré-processamento dentro de seu modelo, consulte o tutorial de classificação de imagens . Você também pode estar interessado em aprender como as camadas de pré-processamento podem ajudar a classificar o texto, conforme mostrado no tutorial de classificação de texto básico . Você pode aprender mais sobre tf.data neste guia e como configurar seus pipelines de entrada para desempenho aqui .