Ajuda a proteger a Grande Barreira de Corais com TensorFlow em Kaggle Junte Desafio

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:

Configurar

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

from tensorflow.keras import layers

Baixe um conjunto de dados

Este tutorial usa o tf_flowers conjunto de dados. Por conveniência, baixar o conjunto de dados usando TensorFlow conjuntos de dados . Se você gostaria de aprender sobre outras formas de importar dados, consulte a carregar imagens 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,
)

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

Use camadas de pré-processamento Keras

Redimensionamento e redimensionamento

Você pode usar o Keras pré-processamento de camadas para redimensionar suas imagens para uma forma consistente (com tf.keras.layers.Resizing ), e valores de pixel redimensionar (com tf.keras.layers.Rescaling ).

IMG_SIZE = 180

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

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

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

png

Verifique se os pixels estão em [0, 1] intervalo:

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ê pode usar o Keras pré-processamento de camadas para o aumento de dados, bem como, tais como tf.keras.layers.RandomFlip e tf.keras.layers.RandomRotation .

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

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

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

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

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([
  # 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.
])

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ê exportar o seu modelo usando model.save , as camadas pré-processamento será salvo 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 esta 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-se a formação de seu modelo na GPU com os dados de pré-processamento, usando Dataset.prefetch , mostrado abaixo.
  • Neste caso, as camadas pré-processamento não serão exportados 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 na classificação Imagem tutorial. 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 Keras que você criou anteriormente. 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. (Saiba desempenho mais conjunto de dados no desempenho melhor com a API tf.data guia.)

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)

Treine uma modelo

Para completar, agora você treinará um modelo usando os conjuntos de dados que acabou de preparar.

O sequencial modelo consiste em três blocos de convolução ( tf.keras.layers.Conv2D ) com uma camada de pooling max ( tf.keras.layers.MaxPooling2D ) em cada um deles. Há uma camada completamente ligada ( tf.keras.layers.Dense ) com 128 unidades em cima do que é activada por uma função de activação Relu ( 'relu' ). 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)
])

Escolha o tf.keras.optimizers.Adam otimizador e tf.keras.losses.SparseCategoricalCrossentropy função de perda. Para treinamento vista e precisão de validação para cada época de treinamento, passar a metrics argumento para Model.compile .

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

Treine por algumas é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 dados personalizados

Você também pode criar camadas de aumento de dados personalizadas.

Esta seção do tutorial mostra duas maneiras de fazer isso:

  • Primeiro, você vai criar um tf.keras.layers.Lambda camada. Esta é uma boa maneira de escrever código conciso.
  • Em seguida, você vai escrever uma nova camada via subclasses , que lhe dá 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, implementar uma camada de costume por subclasse :

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

Os utilitários de pré-processamento Keras acima são convenientes. Mas, para um controle mais fino, você pode escrever seus próprios oleodutos aumento de dados ou camadas usando tf.data e tf.image . (Você também pode querer verificar para fora TensorFlow Complementos Image: Operações e TensorFlow I / O: Espaço de cor conversões .)

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

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

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

Virar uma imagem

Inverter uma imagem verticalmente ou horizontalmente, com tf.image.flip_left_right :

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

png

Escala de cinza uma imagem

Você pode tons de cinza uma imagem com tf.image.rgb_to_grayscale :

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

png

Saturar uma imagem

Saturar uma imagem com tf.image.adjust_saturation , proporcionando um fator de saturação:

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

png

Alterar o brilho da imagem

Alterar o brilho da imagem com tf.image.adjust_brightness , proporcionando um fator de brilho:

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

png

Cortar uma imagem no centro

Cortar a imagem do centro até a parte da imagem que você deseja usar tf.image.central_crop :

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

png

Girar uma imagem

Girar uma imagem 90 graus com tf.image.rot90 :

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 corrente tf.image API fornece oito tais operações 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ísticos de alto desempenho. Eles necessitam de uma seed valor ser introduzidos cada passo. Dada a mesma seed , eles retornam os mesmos resultados independente de quantas vezes eles são chamados.

Nas seções a seguir, você irá:

  1. Reveja 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

Aleatoriamente alterar o brilho da image usando tf.image.stateless_random_brightness , fornecendo um factor de brilho e seed . O factor de brilho é escolhido aleatoriamente no intervalo [-max_delta, max_delta) e está associada com o 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

Alterar aleatoriamente o contraste da imagem

Aleatoriamente alterar o contraste de image utilizando tf.image.stateless_random_contrast proporcionando um intervalo de contraste e seed . A escala de contraste é escolhido aleatoriamente no intervalo [lower, upper] e está associada com o 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

Cortar uma imagem aleatoriamente

Aleatoriamente cortar image usando tf.image.stateless_random_crop fornecendo alvo size e seed . A parte que fica cortada fora de image está a uma aleatoriamente escolhidos offset e está associada com o 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 um conjunto de dados

Vamos primeiro fazer o download do 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,
)

Em seguida, defina 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 augment função que pode aplicar as transformações aleatórias para as 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

Criar um tf.data.experimental.Counter objeto (vamos chamá-lo counter ) e Dataset.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 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

Mapear a augment função 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 uma tf.random.Generator objecto com uma primeira seed valor. Chamando o make_seeds função no mesmo objeto gerador sempre retorna uma nova, única seed valor.
  • Definir uma função de mensagens publicitárias que: 1) chama os make_seeds funcionar; e 2) passa a recém-gerado seed valor para o augment função para transformações aleatórios.
# 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 a função de invólucro f ao conjunto de dados de treinamento, eo resize_and_rescale função-para os conjuntos de validação e teste:

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 aumento de dados usando camadas de pré-processamento e Keras tf.image .

  • Para saber como incluir pré-processamento de camadas dentro do seu modelo, consulte a classificação de Imagem tutorial.
  • Você pode também estar interessado em aprender como camadas de pré-processamento pode ajudá-lo texto classificar, como mostra o texto básico classificação tutorial.
  • Você pode aprender mais sobre tf.data neste guia , e você pode aprender a configurar seus dutos de entrada para o desempenho aqui .