Carica e preelabora le immagini

Visualizza su TensorFlow.org Esegui in Google Colab Visualizza l'origine su GitHub Scarica quaderno

Questo tutorial mostra come caricare e preelaborare un set di dati di immagine in tre modi:

Impostare

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

Scarica il dataset dei fiori

Questo tutorial utilizza un set di dati di diverse migliaia di foto di fiori. Il dataset flowers contiene cinque sottodirectory, una per 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)

Dopo il download (218 MB), ora dovresti avere una copia delle foto dei fiori disponibile. Ci sono 3.670 immagini in totale:

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

Ogni directory contiene immagini di quel tipo di fiore. Ecco alcune rose:

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

Carica i dati utilizzando un'utilità Keras

Carichiamo queste immagini dal disco usando l'utile utility tf.keras.utils.image_dataset_from_directory .

Crea un set di dati

Definire alcuni parametri per il caricatore:

batch_size = 32
img_height = 180
img_width = 180

È buona norma utilizzare una divisione di convalida durante lo sviluppo del modello. Utilizzerai l'80% delle immagini per la formazione e il 20% per la convalida.

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.

Puoi trovare i nomi delle classi nell'attributo class_names su questi set di dati.

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

Visualizza i dati

Ecco le prime nove immagini del set di dati di addestramento.

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

Puoi addestrare un modello utilizzando questi set di dati passandoli a model.fit (mostrato più avanti in questo tutorial). Se lo desideri, puoi anche scorrere manualmente il set di dati e recuperare batch di immagini:

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

image_batch è un tensore della forma (32, 180, 180, 3) . Si tratta di un batch di 32 immagini di forma 180x180x3 (l'ultima dimensione si riferisce ai canali colore RGB). Il label_batch è un tensore della forma (32,) , queste sono etichette corrispondenti alle 32 immagini.

Puoi chiamare .numpy() su uno di questi tensori per convertirli in numpy.ndarray .

Standardizzare i dati

I valori del canale RGB sono compresi nell'intervallo [0, 255] . Questo non è l'ideale per una rete neurale; in generale dovresti cercare di ridurre i tuoi valori di input.

Qui, standardizzerai i valori in modo che rientrino nell'intervallo [0, 1] usando tf.keras.layers.Rescaling :

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

Esistono due modi per utilizzare questo livello. Puoi applicarlo al set di dati chiamando 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

In alternativa, puoi includere il livello all'interno della definizione del modello per semplificare la distribuzione. Utilizzerai il secondo approccio qui.

Configura il set di dati per le prestazioni

Assicuriamoci di utilizzare il prelettura bufferizzata in modo da poter produrre dati dal disco senza che l'I/O si blocchi. Questi sono due metodi importanti da utilizzare durante il caricamento dei dati:

  • Dataset.cache mantiene le immagini in memoria dopo che sono state caricate dal disco durante la prima epoca. Ciò garantirà che il set di dati non diventi un collo di bottiglia durante l'addestramento del modello. Se il tuo set di dati è troppo grande per essere contenuto nella memoria, puoi anche utilizzare questo metodo per creare una cache su disco performante.
  • Dataset.prefetch si sovrappone alla preelaborazione dei dati e all'esecuzione del modello durante l'addestramento.

I lettori interessati possono saperne di più su entrambi i metodi e su come memorizzare nella cache i dati su disco nella sezione Prefetching della Guida all'API tf.data .

AUTOTUNE = tf.data.AUTOTUNE

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

Addestra un modello

Per completezza, mostrerai come addestrare un modello semplice utilizzando i set di dati che hai appena preparato.

Il modello sequenziale è costituito da tre blocchi di convoluzione ( tf.keras.layers.Conv2D ) con uno strato di pooling massimo ( tf.keras.layers.MaxPooling2D ) in ciascuno di essi. C'è uno strato completamente connesso ( tf.keras.layers.Dense ) con 128 unità sopra che viene attivato da una funzione di attivazione ReLU ( 'relu' ). Questo modello non è stato ottimizzato in alcun modo: l'obiettivo è mostrarti i meccanismi utilizzando i set di dati che hai appena creato. Per ulteriori informazioni sulla classificazione delle immagini, visita il tutorial sulla classificazione delle immagini .

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

Scegli l'ottimizzatore tf.keras.optimizers.Adam e la funzione di perdita tf.keras.losses.SparseCategoricalCrossentropy . Per visualizzare l'accuratezza dell'addestramento e della convalida per ogni epoca di addestramento, passa l'argomento delle metrics a 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.3091 - accuracy: 0.4281 - val_loss: 1.0982 - val_accuracy: 0.5599
Epoch 2/3
92/92 [==============================] - 1s 12ms/step - loss: 1.0196 - accuracy: 0.5879 - val_loss: 0.9572 - val_accuracy: 0.6213
Epoch 3/3
92/92 [==============================] - 1s 12ms/step - loss: 0.8455 - accuracy: 0.6775 - val_loss: 0.8839 - val_accuracy: 0.6512
<keras.callbacks.History at 0x7ff10c168850>

Potresti notare che l'accuratezza della convalida è bassa rispetto all'accuratezza dell'allenamento, indicando che il tuo modello è overfitting. Puoi saperne di più sull'overfitting e su come ridurlo in questo tutorial .

Utilizzo di tf.data per un controllo più preciso

L'utilità di preelaborazione Keras sopra, tf.keras.utils.image_dataset_from_directory , è un modo conveniente per creare un tf.data.Dataset da una directory di immagini.

Per un controllo della grana più fine, puoi scrivere la tua pipeline di input usando tf.data . Questa sezione mostra come fare proprio questo, iniziando con i percorsi dei file dal file TGZ che hai scaricato in precedenza.

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/roses/14267691818_301aceda07.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/daisy/2641151167_3bf1349606_m.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/sunflowers/6495554833_86eb8faa8e_n.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/tulips/4578030672_e6aefd45af.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/dandelion/144686365_d7e96941ee_n.jpg'

La struttura ad albero dei file può essere utilizzata per compilare un elenco di class_names .

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

Suddividi il set di dati in set di addestramento e convalida:

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

È possibile stampare la lunghezza di ciascun set di dati come segue:

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

Scrivi una breve funzione che converta un percorso di file in una coppia (img, label) :

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

Usa Dataset.map per creare un set di dati di coppie di image, label :

# 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:  1

Configura il set di dati per le prestazioni

Per addestrare un modello con questo set di dati, vorrai i dati:

  • Per essere ben mescolato.
  • Da dosare.
  • I lotti saranno disponibili il prima possibile.

Queste funzionalità possono essere aggiunte utilizzando l'API tf.data . Per maggiori dettagli, visita la guida alle prestazioni della pipeline di input .

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)

Visualizza i dati

Puoi visualizzare questo set di dati in modo simile a quello che hai creato in precedenza:

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")
2022-01-26 06:29:45.209901: 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

Continua ad addestrare il modello

Ora hai creato manualmente un tf.data.Dataset simile a quello creato da tf.keras.utils.image_dataset_from_directory sopra. Puoi continuare ad addestrare il modello con esso. Come prima, ti allenerai solo per poche epoche per mantenere il tempo di corsa breve.

model.fit(
  train_ds,
  validation_data=val_ds,
  epochs=3
)
Epoch 1/3
92/92 [==============================] - 3s 21ms/step - loss: 0.7305 - accuracy: 0.7245 - val_loss: 0.7311 - val_accuracy: 0.7139
Epoch 2/3
92/92 [==============================] - 1s 13ms/step - loss: 0.5279 - accuracy: 0.8069 - val_loss: 0.7021 - val_accuracy: 0.7316
Epoch 3/3
92/92 [==============================] - 1s 13ms/step - loss: 0.3739 - accuracy: 0.8644 - val_loss: 0.8266 - val_accuracy: 0.6948
<keras.callbacks.History at 0x7ff0ee071f10>

Utilizzo dei set di dati TensorFlow

Finora, questo tutorial si è concentrato sul caricamento dei dati su disco. Puoi anche trovare un set di dati da utilizzare esplorando l'ampio catalogo di set di dati facili da scaricare su TensorFlow Datasets .

Poiché in precedenza hai caricato il set di dati Flowers su disco, ora importiamolo con TensorFlow Datasets.

Scarica il set di dati Flowers utilizzando i set di dati 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,
)

Il set di dati dei fiori ha cinque classi:

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

Recupera un'immagine dal set di dati:

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

image, label = next(iter(train_ds))
_ = plt.imshow(image)
_ = plt.title(get_label_name(label))
2022-01-26 06:29:54.281352: 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

Come prima, ricorda di raggruppare, mescolare e configurare i set di addestramento, convalida e test per le prestazioni:

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

È possibile trovare un esempio completo di utilizzo del set di dati Flowers e dei set di dati TensorFlow visitando il tutorial sull'aumento dei dati .

Prossimi passi

Questo tutorial ha mostrato due modi per caricare le immagini fuori disco. Innanzitutto, hai imparato come caricare e preelaborare un set di dati di immagini utilizzando i livelli e le utilità di preelaborazione di Keras. Successivamente, hai imparato come scrivere una pipeline di input da zero usando tf.data . Infine, hai imparato come scaricare un set di dati da TensorFlow Datasets.

Per i tuoi prossimi passi: