O Dia da Comunidade de ML é dia 9 de novembro! Junte-nos para atualização de TensorFlow, JAX, e mais Saiba mais

Carregar e pré-processar imagens

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

Este tutorial mostra como carregar e pré-processar um conjunto de dados de imagem de três maneiras:

Configurar

import numpy as np
import os
import PIL
import PIL.Image
import tensorflow as tf
import tensorflow_datasets as tfds
print(tf.__version__)
2.6.0

Baixe o conjunto de dados de flores

Este tutorial usa um conjunto de dados de vários milhares de fotos de flores. O conjunto de dados de flores contém cinco subdiretórios, um por classe:

flowers_photos/
  daisy/
  dandelion/
  roses/
  sunflowers/
  tulips/
import pathlib
dataset_url = "https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz"
data_dir = tf.keras.utils.get_file(origin=dataset_url,
                                   fname='flower_photos',
                                   untar=True)
data_dir = pathlib.Path(data_dir)

Após o download (218 MB), você deve ter uma cópia das fotos das flores disponíveis. Existem 3.670 imagens no total:

image_count = len(list(data_dir.glob('*/*.jpg')))
print(image_count)
3670

Cada diretório contém imagens desse tipo de flor. Aqui estão algumas rosas:

roses = list(data_dir.glob('roses/*'))
PIL.Image.open(str(roses[0]))

png

roses = list(data_dir.glob('roses/*'))
PIL.Image.open(str(roses[1]))

png

Carregue dados usando um utilitário Keras

Vamos carregar essas imagens fora do disco usando o útil tf.keras.utils.image_dataset_from_directory utilidade.

Crie um conjunto de dados

Defina alguns parâmetros para o carregador:

batch_size = 32
img_height = 180
img_width = 180

É uma boa prática usar uma divisão de validação ao desenvolver seu modelo. Você usará 80% das imagens para treinamento e 20% para validação.

train_ds = tf.keras.utils.image_dataset_from_directory(
  data_dir,
  validation_split=0.2,
  subset="training",
  seed=123,
  image_size=(img_height, img_width),
  batch_size=batch_size)
Found 3670 files belonging to 5 classes.
Using 2936 files for training.
val_ds = tf.keras.utils.image_dataset_from_directory(
  data_dir,
  validation_split=0.2,
  subset="validation",
  seed=123,
  image_size=(img_height, img_width),
  batch_size=batch_size)
Found 3670 files belonging to 5 classes.
Using 734 files for validation.

Você pode encontrar os nomes de classe no class_names atributo sobre esses conjuntos de dados.

class_names = train_ds.class_names
print(class_names)
['daisy', 'dandelion', 'roses', 'sunflowers', 'tulips']

Visualize os dados

Aqui estão as primeiras nove imagens do conjunto de dados de treinamento.

import matplotlib.pyplot as plt

plt.figure(figsize=(10, 10))
for images, labels in train_ds.take(1):
  for i in range(9):
    ax = plt.subplot(3, 3, i + 1)
    plt.imshow(images[i].numpy().astype("uint8"))
    plt.title(class_names[labels[i]])
    plt.axis("off")

png

Você pode treinar um modelo usando esses conjuntos de dados, passando-os para model.fit (mostrado mais adiante neste tutorial). Se desejar, você também pode iterar manualmente no conjunto de dados e recuperar lotes de imagens:

for image_batch, labels_batch in train_ds:
  print(image_batch.shape)
  print(labels_batch.shape)
  break
(32, 180, 180, 3)
(32,)

O image_batch é um tensor da forma (32, 180, 180, 3) . Este é um lote de 32 imagens de forma 180x180x3 (a última dimensão refere-se a canais de cor RGB). O label_batch é um tensor da forma (32,) , estes são etiquetas correspondentes aos 32 imagens.

Você pode chamar .numpy() em qualquer um desses tensores para convertê-los para um numpy.ndarray .

Padronize os dados

Os valores de canais RGB são no [0, 255] gama. Isso não é ideal para uma rede neural; em geral, você deve tentar diminuir os valores de entrada.

Aqui, você vai padronizar os valores para estar no [0, 1] intervalo usando tf.keras.layers.Rescaling :

normalization_layer = tf.keras.layers.Rescaling(1./255)

Existem duas maneiras de usar essa camada. Você pode aplicá-lo para o conjunto de dados chamando Dataset.map :

normalized_ds = train_ds.map(lambda x, y: (normalization_layer(x), y))
image_batch, labels_batch = next(iter(normalized_ds))
first_image = image_batch[0]
# Notice the pixel values are now in `[0,1]`.
print(np.min(first_image), np.max(first_image))
0.0 0.96902645

Ou você pode incluir a camada dentro da definição do modelo para simplificar a implantação. Você usará a segunda abordagem aqui.

Configure o conjunto de dados para desempenho

Vamos nos certificar de usar a pré-busca em buffer para que você possa gerar dados do disco sem que o I / O se torne um bloqueio. Estes são dois métodos importantes que você deve usar ao carregar dados:

  • Dataset.cache mantém as imagens na memória depois de serem carregadas fora do disco durante a primeira época. Isso garantirá que o conjunto de dados não se torne um gargalo durante o treinamento do seu modelo. Se o seu conjunto de dados for muito grande para caber na memória, você também pode usar este método para criar um cache em disco de alto desempenho.
  • Dataset.prefetch sobrepõe dados de pré-processamento e execução do modelo durante o treinamento.

Os leitores interessados podem saber mais sobre ambos os métodos, bem como a forma de dados em cache no disco na seção Prefetching do desempenho melhor com a tf.data API guia.

AUTOTUNE = tf.data.AUTOTUNE

train_ds = train_ds.cache().prefetch(buffer_size=AUTOTUNE)
val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)

Treine uma modelo

Para completar, você mostrará como treinar um modelo simples 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 de forma alguma - o objetivo é mostrar a você a mecânica usando os conjuntos de dados que você acabou de criar. Para saber mais sobre classificação de imagens, visite o classificação de Imagem tutorial.

num_classes = 5

model = tf.keras.Sequential([
  tf.keras.layers.Rescaling(1./255),
  tf.keras.layers.Conv2D(32, 3, activation='relu'),
  tf.keras.layers.MaxPooling2D(),
  tf.keras.layers.Conv2D(32, 3, activation='relu'),
  tf.keras.layers.MaxPooling2D(),
  tf.keras.layers.Conv2D(32, 3, activation='relu'),
  tf.keras.layers.MaxPooling2D(),
  tf.keras.layers.Flatten(),
  tf.keras.layers.Dense(128, activation='relu'),
  tf.keras.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.losses.SparseCategoricalCrossentropy(from_logits=True),
  metrics=['accuracy'])
model.fit(
  train_ds,
  validation_data=val_ds,
  epochs=3
)
Epoch 1/3
92/92 [==============================] - 4s 22ms/step - loss: 1.3233 - accuracy: 0.4326 - val_loss: 1.0868 - val_accuracy: 0.5450
Epoch 2/3
92/92 [==============================] - 1s 11ms/step - loss: 0.9960 - accuracy: 0.6056 - val_loss: 0.9698 - val_accuracy: 0.6117
Epoch 3/3
92/92 [==============================] - 1s 11ms/step - loss: 0.8776 - accuracy: 0.6625 - val_loss: 0.9740 - val_accuracy: 0.6417
<keras.callbacks.History at 0x7fcb6076f4d0>

Você pode notar que a precisão da validação é baixa em comparação com a precisão do treinamento, indicando que seu modelo está superdimensionado. Você pode aprender mais sobre overfitting e como reduzi-lo neste tutorial .

Usando tf.data para um controle mais preciso

O acima Keras pré-processamento utility- tf.keras.utils.image_dataset_from_directory -is uma maneira conveniente de criar um tf.data.Dataset de um diretório de imagens.

Para o controle de grão mais fino, você pode escrever seu próprio gasoduto de entrada usando tf.data . Esta seção mostra como fazer exatamente isso, começando com os caminhos de arquivo do arquivo TGZ que você baixou anteriormente.

list_ds = tf.data.Dataset.list_files(str(data_dir/'*/*'), shuffle=False)
list_ds = list_ds.shuffle(image_count, reshuffle_each_iteration=False)
for f in list_ds.take(5):
  print(f.numpy())
b'/home/kbuilder/.keras/datasets/flower_photos/dandelion/8663932737_0a603ab718_n.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/dandelion/4634716478_1cbcbee7ca.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/daisy/21402054779_759366efb0_n.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/sunflowers/5970301989_fe3a68aac8_m.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/sunflowers/45045003_30bbd0a142_m.jpg'

A estrutura de árvore dos arquivos pode ser usado para compilar uma class_names lista.

class_names = np.array(sorted([item.name for item in data_dir.glob('*') if item.name != "LICENSE.txt"]))
print(class_names)
['daisy' 'dandelion' 'roses' 'sunflowers' 'tulips']

Divida o conjunto de dados em conjuntos de treinamento e validação:

val_size = int(image_count * 0.2)
train_ds = list_ds.skip(val_size)
val_ds = list_ds.take(val_size)

Você pode imprimir o comprimento de cada conjunto de dados da seguinte forma:

print(tf.data.experimental.cardinality(train_ds).numpy())
print(tf.data.experimental.cardinality(val_ds).numpy())
2936
734

Escrever pequena função que converte um caminho de arquivo para um (img, label) par:

def get_label(file_path):
  # Convert the path to a list of path components
  parts = tf.strings.split(file_path, os.path.sep)
  # The second to last is the class-directory
  one_hot = parts[-2] == class_names
  # Integer encode the label
  return tf.argmax(one_hot)
def decode_img(img):
  # Convert the compressed string to a 3D uint8 tensor
  img = tf.io.decode_jpeg(img, channels=3)
  # Resize the image to the desired size
  return tf.image.resize(img, [img_height, img_width])
def process_path(file_path):
  label = get_label(file_path)
  # Load the raw data from the file as a string
  img = tf.io.read_file(file_path)
  img = decode_img(img)
  return img, label

Use Dataset.map para criar um conjunto de dados de image, label pares:

# Set `num_parallel_calls` so multiple images are loaded/processed in parallel.
train_ds = train_ds.map(process_path, num_parallel_calls=AUTOTUNE)
val_ds = val_ds.map(process_path, num_parallel_calls=AUTOTUNE)
for image, label in train_ds.take(1):
  print("Image shape: ", image.numpy().shape)
  print("Label: ", label.numpy())
Image shape:  (180, 180, 3)
Label:  4

Configure o conjunto de dados para desempenho

Para treinar um modelo com este conjunto de dados, você vai querer os dados:

  • Para ser bem embaralhado.
  • Para ser agrupado.
  • Os lotes estarão disponíveis o mais rápido possível.

Esses recursos podem ser adicionados usando o tf.data API. Para mais detalhes, visite o Pipeline desempenho de entrada guia.

def configure_for_performance(ds):
  ds = ds.cache()
  ds = ds.shuffle(buffer_size=1000)
  ds = ds.batch(batch_size)
  ds = ds.prefetch(buffer_size=AUTOTUNE)
  return ds

train_ds = configure_for_performance(train_ds)
val_ds = configure_for_performance(val_ds)

Visualize os dados

Você pode visualizar este conjunto de dados de forma semelhante ao criado anteriormente:

image_batch, label_batch = next(iter(train_ds))

plt.figure(figsize=(10, 10))
for i in range(9):
  ax = plt.subplot(3, 3, i + 1)
  plt.imshow(image_batch[i].numpy().astype("uint8"))
  label = label_batch[i]
  plt.title(class_names[label])
  plt.axis("off")
2021-10-01 03:21:04.086862: 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

Continue treinando o modelo

Você já construído manualmente um semelhante tf.data.Dataset ao criado por tf.keras.utils.image_dataset_from_directory acima. Você pode continuar treinando o modelo com ele. Como antes, você treinará por apenas algumas épocas para reduzir o tempo de corrida.

model.fit(
  train_ds,
  validation_data=val_ds,
  epochs=3
)
Epoch 1/3
92/92 [==============================] - 2s 20ms/step - loss: 0.7723 - accuracy: 0.7081 - val_loss: 0.9020 - val_accuracy: 0.6580
Epoch 2/3
92/92 [==============================] - 1s 12ms/step - loss: 0.5976 - accuracy: 0.7810 - val_loss: 0.7204 - val_accuracy: 0.7234
Epoch 3/3
92/92 [==============================] - 1s 12ms/step - loss: 0.4157 - accuracy: 0.8437 - val_loss: 0.9229 - val_accuracy: 0.7153
<keras.callbacks.History at 0x7fcb603f0590>

Usando conjuntos de dados TensorFlow

Até agora, este tutorial se concentrou em carregar dados fora do disco. Você também pode encontrar um conjunto de dados para uso por explorar o grande catálogo de conjuntos de dados de fácil download em TensorFlow conjuntos de dados .

Como você carregou anteriormente o conjunto de dados Flowers fora do disco, agora vamos importá-lo com os conjuntos de dados TensorFlow.

Baixe o Flores conjunto de dados usando TensorFlow conjuntos de dados:

(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

Recupere uma imagem do conjunto de 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-01 03:21:12.800729: 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

Como antes, lembre-se de agrupar, embaralhar e configurar os conjuntos de treinamento, validação e teste para desempenho:

train_ds = configure_for_performance(train_ds)
val_ds = configure_for_performance(val_ds)
test_ds = configure_for_performance(test_ds)

Você pode encontrar um exemplo completo de trabalhar com o conjunto de dados Flores e TensorFlow conjuntos de dados, visitando o aumento de dados tutorial.

Próximos passos

Este tutorial mostrou duas maneiras de carregar imagens do disco. Primeiro, você aprendeu como carregar e pré-processar um conjunto de dados de imagem usando camadas e utilitários de pré-processamento Keras. Em seguida, você aprendeu a escrever um gasoduto de entrada a partir do zero usando tf.data . Por fim, você aprendeu como fazer o download de um conjunto de dados do TensorFlow Datasets.

Para suas próximas etapas: