Ayuda a proteger la Gran Barrera de Coral con TensorFlow en Kaggle Únete Challenge

Cargar y preprocesar imágenes

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

Este tutorial muestra cómo cargar y preprocesar un conjunto de datos de imagen de tres formas:

Configuración

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

Descargar el conjunto de datos de flores

Este tutorial utiliza un conjunto de datos de varios miles de fotos de flores. El conjunto de datos de flores contiene cinco subdirectorios, uno por clase:

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)

Después de descargar (218 MB), ahora debería tener una copia de las fotos de flores disponibles. Hay 3670 imágenes en total:

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

Cada directorio contiene imágenes de ese tipo de flor. Aquí hay algunas 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

Cargar datos usando una utilidad de Keras

Carga de dejar que estas imágenes desde el disco utilizando el útil tf.keras.utils.image_dataset_from_directory utilidad.

Crea un conjunto de datos

Defina algunos parámetros para el cargador:

batch_size = 32
img_height = 180
img_width = 180

Es una buena práctica utilizar una división de validación al desarrollar su modelo. Utilizará el 80% de las imágenes para entrenamiento y el 20% para validación.

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.

Puede encontrar los nombres de las clases en el class_names atributo en estos conjuntos de datos.

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

Visualiza los datos

Aquí están las primeras nueve imágenes del conjunto de datos de entrenamiento.

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

Usted puede entrenar a un modelo usando estos conjuntos de datos pasándolos a model.fit (mostradas más adelante en este tutorial). Si lo desea, también puede iterar manualmente sobre el conjunto de datos y recuperar lotes de imágenes:

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

El image_batch es un tensor de la forma (32, 180, 180, 3) . Este es un lote de 32 imágenes de forma 180x180x3 (la última dimensión se refiere a los canales de color RGB). El label_batch es un tensor de la forma (32,) , estos son etiquetas que corresponden a las 32 imágenes.

Puede llamar .numpy() en cualquiera de estos tensores para convertirlos a un numpy.ndarray .

Estandarizar los datos

Los valores de los canales RGB están en la [0, 255] gama. Esto no es ideal para una red neuronal; en general, debe tratar de hacer que sus valores de entrada sean pequeños.

A continuación, se le estandarizar los valores de estar en el [0, 1] rango utilizando tf.keras.layers.Rescaling :

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

Hay dos formas de utilizar esta capa. Se puede aplicar al conjunto de datos llamando 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

O puede incluir la capa dentro de la definición de su modelo para simplificar la implementación. Utilizará el segundo enfoque aquí.

Configurar el conjunto de datos para el rendimiento

Asegurémonos de utilizar la captación previa en búfer para que pueda obtener datos del disco sin que la E / S se bloquee. Estos son dos métodos importantes que debe utilizar al cargar datos:

  • Dataset.cache mantiene las imágenes en la memoria después de que se cargan de disco durante la primera época. Esto asegurará que el conjunto de datos no se convierta en un cuello de botella mientras entrena su modelo. Si su conjunto de datos es demasiado grande para caber en la memoria, también puede usar este método para crear una caché en disco de alto rendimiento.
  • Dataset.prefetch se superpone datos de pre-procesamiento y la ejecución del modelo durante el entrenamiento.

Los lectores interesados pueden obtener más información sobre ambos métodos, así como la forma de datos de la caché en el disco en la sección de captura previa de la Mejor rendimiento con la API tf.data guía.

AUTOTUNE = tf.data.AUTOTUNE

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

Entrena un modelo

Para completar, mostrará cómo entrenar un modelo simple 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 de ninguna manera; el objetivo es mostrarle la mecánica utilizando los conjuntos de datos que acaba de crear. Para obtener más información sobre la clasificación de imágenes, visite la Clasificación de la imagen 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)
])

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.losses.SparseCategoricalCrossentropy(from_logits=True),
  metrics=['accuracy'])
model.fit(
  train_ds,
  validation_data=val_ds,
  epochs=3
)
Epoch 1/3
92/92 [==============================] - 4s 21ms/step - loss: 1.2935 - accuracy: 0.4588 - val_loss: 1.1072 - val_accuracy: 0.5463
Epoch 2/3
92/92 [==============================] - 1s 11ms/step - loss: 1.0370 - accuracy: 0.5834 - val_loss: 0.9830 - val_accuracy: 0.6226
Epoch 3/3
92/92 [==============================] - 1s 11ms/step - loss: 0.8740 - accuracy: 0.6689 - val_loss: 0.9003 - val_accuracy: 0.6676
<keras.callbacks.History at 0x7f44180b31d0>

Puede notar que la precisión de la validación es baja en comparación con la precisión del entrenamiento, lo que indica que su modelo está sobreajustado. Usted puede aprender más sobre el sobreajuste y cómo reducirlo en este tutorial .

Uso de tf.data para un control más preciso

El procesamiento previo Utilidades- anterior Keras tf.keras.utils.image_dataset_from_directory -es una forma conveniente de crear un tf.data.Dataset de un directorio de imágenes.

Para el control de grano más fino, puede escribir su propia línea de entrada utilizando tf.data . Esta sección muestra cómo hacer precisamente eso, comenzando con las rutas de archivo del archivo TGZ que descargó 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/tulips/16055807744_000bc07afc_m.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/daisy/14399435971_ea5868c792.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/tulips/13509973805_bda5fa8982.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/dandelion/6994925894_030e157fe0.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/sunflowers/4890268276_563f40a193.jpg'

La estructura de árbol de los archivos se puede utilizar para compilar una 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 el conjunto de datos en conjuntos de entrenamiento y validación:

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

Puede imprimir la longitud de cada conjunto de datos de la siguiente manera:

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

Escribir una función corto que convierte una ruta de archivo a un (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

Uso Dataset.map para crear un conjunto de datos 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:  2

Configurar el conjunto de datos para el rendimiento

Para entrenar un modelo con este conjunto de datos, necesitará los datos:

  • Estar bien barajado.
  • Para ser procesado.
  • Los lotes estarán disponibles lo antes posible.

Estas características se pueden agregar mediante la tf.data API. Para más detalles, visite la canalización Rendimiento guía.

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)

Visualiza los datos

Puede visualizar este conjunto de datos de manera similar al que creó 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-26 01:32:12.160250: 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

Continuar entrenando el modelo

Ahora ha construido manualmente un similares tf.data.Dataset a la creada por tf.keras.utils.image_dataset_from_directory anteriormente. Puede continuar entrenando el modelo con él. Como antes, entrenarás durante unas pocas épocas para reducir el tiempo de ejecución.

model.fit(
  train_ds,
  validation_data=val_ds,
  epochs=3
)
Epoch 1/3
92/92 [==============================] - 2s 20ms/step - loss: 0.7521 - accuracy: 0.7183 - val_loss: 0.6710 - val_accuracy: 0.7452
Epoch 2/3
92/92 [==============================] - 1s 12ms/step - loss: 0.5771 - accuracy: 0.7861 - val_loss: 0.7169 - val_accuracy: 0.7221
Epoch 3/3
92/92 [==============================] - 1s 12ms/step - loss: 0.3940 - accuracy: 0.8542 - val_loss: 0.7691 - val_accuracy: 0.7180
<keras.callbacks.History at 0x7f437822c710>

Usar conjuntos de datos de TensorFlow

Hasta ahora, este tutorial se ha centrado en cargar datos fuera del disco. También puede encontrar un conjunto de datos para su uso por la exploración de la gran catálogo de conjuntos de datos de fácil descarga en TensorFlow conjuntos de datos .

Como anteriormente cargó el conjunto de datos de Flowers fuera del disco, ahora importémoslo con los conjuntos de datos de TensorFlow.

Flores descargar el conjunto de datos utilizando conjuntos de datos TensorFlow:

(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

Recupere una imagen del conjunto 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-26 01:32:22.877036: 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, recuerde agrupar, mezclar y configurar los conjuntos de entrenamiento, validación y prueba para el rendimiento:

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

Se puede encontrar un ejemplo completo de trabajo con el conjunto de datos Flores y TensorFlow Conjuntos de datos visitando el aumento de Datos tutorial.

Próximos pasos

Este tutorial mostró dos formas de cargar imágenes desde el disco. Primero, aprendió cómo cargar y preprocesar un conjunto de datos de imágenes utilizando las capas de preprocesamiento y las utilidades de Keras. A continuación, ha aprendido a escribir una tubería de entrada desde el principio con tf.data . Finalmente, aprendió cómo descargar un conjunto de datos de TensorFlow Datasets.

Para sus próximos pasos: