Visualizza su TensorFlow.org | Esegui in Google Colab | Visualizza l'origine su GitHub | Scarica quaderno |
Questo tutorial mostra come classificare le immagini dei fiori. Crea un classificatore di immagini utilizzando un modello tf.keras.Sequential
e carica i dati utilizzando tf.keras.utils.image_dataset_from_directory
. Acquisirai esperienza pratica con i seguenti concetti:
- Caricamento efficiente di un set di dati da disco.
- Identificazione dell'overfitting e applicazione di tecniche per mitigarlo, inclusi data augmentation e dropout.
Questo tutorial segue un flusso di lavoro di apprendimento automatico di base:
- Esaminare e comprendere i dati
- Costruisci una pipeline di input
- Costruisci il modello
- Allena il modello
- Prova il modello
- Migliora il modello e ripeti il processo
Importa TensorFlow e altre librerie
import matplotlib.pyplot as plt
import numpy as np
import os
import PIL
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.keras.models import Sequential
Scarica ed esplora il set di dati
Questo tutorial utilizza un set di dati di circa 3.700 foto di fiori. Il set di dati contiene cinque sottodirectory, una per classe:
flower_photo/
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('flower_photos', origin=dataset_url, untar=True)
data_dir = pathlib.Path(data_dir)
Dopo il download, dovresti ora avere una copia del set di dati disponibile. Ci sono 3.670 immagini in totale:
image_count = len(list(data_dir.glob('*/*.jpg')))
print(image_count)
3670
Ecco alcune rose:
roses = list(data_dir.glob('roses/*'))
PIL.Image.open(str(roses[0]))
PIL.Image.open(str(roses[1]))
E qualche tulipano:
tulips = list(data_dir.glob('tulips/*'))
PIL.Image.open(str(tulips[0]))
PIL.Image.open(str(tulips[1]))
Carica i dati utilizzando un'utilità Keras
Carichiamo queste immagini dal disco usando l'utile utility tf.keras.utils.image_dataset_from_directory
. Questo ti porterà da una directory di immagini su disco a un tf.data.Dataset
in solo un paio di righe di codice. Se lo desideri, puoi anche scrivere da zero il tuo codice di caricamento dei dati visitando il tutorial Carica e preelabora le immagini .
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. Usiamo 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. Questi corrispondono ai nomi delle directory in ordine alfabetico.
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")
Addestrerai un modello utilizzando questi set di dati passandoli a Model.fit
in un attimo. 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()
sui tensori image_batch
ed labels_batch
per convertirli in un numpy.ndarray
.
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().shuffle(1000).prefetch(buffer_size=AUTOTUNE)
val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)
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 = 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 1.0
In alternativa, puoi includere il livello all'interno della definizione del modello, il che può semplificare la distribuzione. Usiamo qui il secondo approccio.
Crea il modello
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 per un'elevata precisione: l'obiettivo di questo tutorial è mostrare un approccio standard.
num_classes = len(class_names)
model = Sequential([
layers.Rescaling(1./255, input_shape=(img_height, img_width, 3)),
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)
])
Compila il modello
Per questo tutorial, 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.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
Riepilogo del modello
Visualizza tutti i livelli della rete utilizzando il metodo Model.summary
del modello:
model.summary()
Model: "sequential" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= rescaling_1 (Rescaling) (None, 180, 180, 3) 0 conv2d (Conv2D) (None, 180, 180, 16) 448 max_pooling2d (MaxPooling2D (None, 90, 90, 16) 0 ) conv2d_1 (Conv2D) (None, 90, 90, 32) 4640 max_pooling2d_1 (MaxPooling (None, 45, 45, 32) 0 2D) conv2d_2 (Conv2D) (None, 45, 45, 64) 18496 max_pooling2d_2 (MaxPooling (None, 22, 22, 64) 0 2D) flatten (Flatten) (None, 30976) 0 dense (Dense) (None, 128) 3965056 dense_1 (Dense) (None, 5) 645 ================================================================= Total params: 3,989,285 Trainable params: 3,989,285 Non-trainable params: 0 _________________________________________________________________
Allena il modello
epochs=10
history = model.fit(
train_ds,
validation_data=val_ds,
epochs=epochs
)
Epoch 1/10 92/92 [==============================] - 3s 16ms/step - loss: 1.2769 - accuracy: 0.4489 - val_loss: 1.0457 - val_accuracy: 0.5804 Epoch 2/10 92/92 [==============================] - 1s 11ms/step - loss: 0.9386 - accuracy: 0.6328 - val_loss: 0.9665 - val_accuracy: 0.6158 Epoch 3/10 92/92 [==============================] - 1s 11ms/step - loss: 0.7390 - accuracy: 0.7200 - val_loss: 0.8768 - val_accuracy: 0.6540 Epoch 4/10 92/92 [==============================] - 1s 11ms/step - loss: 0.5649 - accuracy: 0.7963 - val_loss: 0.9258 - val_accuracy: 0.6540 Epoch 5/10 92/92 [==============================] - 1s 11ms/step - loss: 0.3662 - accuracy: 0.8733 - val_loss: 1.1734 - val_accuracy: 0.6267 Epoch 6/10 92/92 [==============================] - 1s 11ms/step - loss: 0.2169 - accuracy: 0.9343 - val_loss: 1.3728 - val_accuracy: 0.6499 Epoch 7/10 92/92 [==============================] - 1s 11ms/step - loss: 0.1191 - accuracy: 0.9629 - val_loss: 1.3791 - val_accuracy: 0.6471 Epoch 8/10 92/92 [==============================] - 1s 11ms/step - loss: 0.0497 - accuracy: 0.9871 - val_loss: 1.8002 - val_accuracy: 0.6390 Epoch 9/10 92/92 [==============================] - 1s 11ms/step - loss: 0.0372 - accuracy: 0.9922 - val_loss: 1.8545 - val_accuracy: 0.6390 Epoch 10/10 92/92 [==============================] - 1s 11ms/step - loss: 0.0715 - accuracy: 0.9813 - val_loss: 2.0656 - val_accuracy: 0.6049
Visualizza i risultati dell'allenamento
Creare grafici di perdita e accuratezza sui set di addestramento e convalida:
acc = history.history['accuracy']
val_acc = history.history['val_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs_range = range(epochs)
plt.figure(figsize=(8, 8))
plt.subplot(1, 2, 1)
plt.plot(epochs_range, acc, label='Training Accuracy')
plt.plot(epochs_range, val_acc, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')
plt.subplot(1, 2, 2)
plt.plot(epochs_range, loss, label='Training Loss')
plt.plot(epochs_range, val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()
I grafici mostrano che l'accuratezza dell'addestramento e l'accuratezza della convalida sono di gran lunga inferiori e il modello ha raggiunto solo un'accuratezza del 60% circa sul set di convalida.
Esaminiamo cosa è andato storto e cerchiamo di aumentare le prestazioni complessive del modello.
Sovraccarico
Nei grafici sopra, l'accuratezza dell'addestramento aumenta linearmente nel tempo, mentre l'accuratezza della convalida si ferma a circa il 60% nel processo di addestramento. Inoltre, è evidente la differenza di accuratezza tra addestramento e accuratezza di convalida, un segno di overfitting .
Quando è presente un numero limitato di esempi di addestramento, il modello a volte apprende da rumori o dettagli indesiderati dagli esempi di addestramento, in una misura tale da influire negativamente sulle prestazioni del modello su nuovi esempi. Questo fenomeno è noto come overfitting. Significa che il modello avrà difficoltà a generalizzare su un nuovo set di dati.
Esistono diversi modi per combattere l'overfitting nel processo di formazione. In questo tutorial utilizzerai l'aumento dei dati e aggiungerai Dropout al tuo modello.
Aumento dei dati
L'overfitting si verifica generalmente quando c'è un piccolo numero di esempi di formazione. L'aumento dei dati adotta l'approccio di generare dati di addestramento aggiuntivi dai tuoi esempi esistenti aumentandoli utilizzando trasformazioni casuali che producono immagini dall'aspetto credibile. Ciò consente di esporre il modello a più aspetti dei dati e di generalizzare meglio.
Implementerai l'aumento dei dati utilizzando i seguenti livelli di preelaborazione Keras: tf.keras.layers.RandomFlip
, tf.keras.layers.RandomRotation
e tf.keras.layers.RandomZoom
. Questi possono essere inclusi all'interno del tuo modello come altri livelli ed essere eseguiti sulla GPU.
data_augmentation = keras.Sequential(
[
layers.RandomFlip("horizontal",
input_shape=(img_height,
img_width,
3)),
layers.RandomRotation(0.1),
layers.RandomZoom(0.1),
]
)
Visualizziamo l'aspetto di alcuni esempi aumentati applicando più volte l'aumento dei dati alla stessa immagine:
plt.figure(figsize=(10, 10))
for images, _ in train_ds.take(1):
for i in range(9):
augmented_images = data_augmentation(images)
ax = plt.subplot(3, 3, i + 1)
plt.imshow(augmented_images[0].numpy().astype("uint8"))
plt.axis("off")
Utilizzerai l'aumento dei dati per addestrare un modello in un momento.
Ritirarsi
Un'altra tecnica per ridurre l' overfitting consiste nell'introdurre la regolarizzazione dell'abbandono nella rete.
Quando si applica il dropout a un livello, durante il processo di addestramento vengono eliminate casualmente (impostando l'attivazione su zero) un numero di unità di output dal livello. Dropout accetta un numero frazionario come valore di input, nella forma come 0.1, 0.2, 0.4, ecc. Ciò significa eliminare casualmente il 10%, 20% o 40% delle unità di output dal livello applicato.
Creiamo una nuova rete neurale con tf.keras.layers.Dropout
prima di addestrarla utilizzando le immagini aumentate:
model = Sequential([
data_augmentation,
layers.Rescaling(1./255),
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.Dropout(0.2),
layers.Flatten(),
layers.Dense(128, activation='relu'),
layers.Dense(num_classes)
])
Compila e addestra il modello
model.compile(optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
model.summary()
Model: "sequential_2" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= sequential_1 (Sequential) (None, 180, 180, 3) 0 rescaling_2 (Rescaling) (None, 180, 180, 3) 0 conv2d_3 (Conv2D) (None, 180, 180, 16) 448 max_pooling2d_3 (MaxPooling (None, 90, 90, 16) 0 2D) conv2d_4 (Conv2D) (None, 90, 90, 32) 4640 max_pooling2d_4 (MaxPooling (None, 45, 45, 32) 0 2D) conv2d_5 (Conv2D) (None, 45, 45, 64) 18496 max_pooling2d_5 (MaxPooling (None, 22, 22, 64) 0 2D) dropout (Dropout) (None, 22, 22, 64) 0 flatten_1 (Flatten) (None, 30976) 0 dense_2 (Dense) (None, 128) 3965056 dense_3 (Dense) (None, 5) 645 ================================================================= Total params: 3,989,285 Trainable params: 3,989,285 Non-trainable params: 0 _________________________________________________________________
epochs = 15
history = model.fit(
train_ds,
validation_data=val_ds,
epochs=epochs
)
Epoch 1/15 92/92 [==============================] - 2s 14ms/step - loss: 1.3840 - accuracy: 0.3999 - val_loss: 1.0967 - val_accuracy: 0.5518 Epoch 2/15 92/92 [==============================] - 1s 12ms/step - loss: 1.1152 - accuracy: 0.5395 - val_loss: 1.1123 - val_accuracy: 0.5545 Epoch 3/15 92/92 [==============================] - 1s 12ms/step - loss: 1.0049 - accuracy: 0.6052 - val_loss: 0.9544 - val_accuracy: 0.6253 Epoch 4/15 92/92 [==============================] - 1s 12ms/step - loss: 0.9452 - accuracy: 0.6257 - val_loss: 0.9681 - val_accuracy: 0.6213 Epoch 5/15 92/92 [==============================] - 1s 12ms/step - loss: 0.8804 - accuracy: 0.6591 - val_loss: 0.8450 - val_accuracy: 0.6798 Epoch 6/15 92/92 [==============================] - 1s 12ms/step - loss: 0.8001 - accuracy: 0.6945 - val_loss: 0.8715 - val_accuracy: 0.6594 Epoch 7/15 92/92 [==============================] - 1s 12ms/step - loss: 0.7736 - accuracy: 0.6965 - val_loss: 0.8059 - val_accuracy: 0.6935 Epoch 8/15 92/92 [==============================] - 1s 12ms/step - loss: 0.7477 - accuracy: 0.7078 - val_loss: 0.8292 - val_accuracy: 0.6812 Epoch 9/15 92/92 [==============================] - 1s 12ms/step - loss: 0.7053 - accuracy: 0.7251 - val_loss: 0.7743 - val_accuracy: 0.6989 Epoch 10/15 92/92 [==============================] - 1s 12ms/step - loss: 0.6884 - accuracy: 0.7340 - val_loss: 0.7867 - val_accuracy: 0.6907 Epoch 11/15 92/92 [==============================] - 1s 12ms/step - loss: 0.6536 - accuracy: 0.7469 - val_loss: 0.7732 - val_accuracy: 0.6785 Epoch 12/15 92/92 [==============================] - 1s 12ms/step - loss: 0.6456 - accuracy: 0.7500 - val_loss: 0.7801 - val_accuracy: 0.6907 Epoch 13/15 92/92 [==============================] - 1s 12ms/step - loss: 0.5941 - accuracy: 0.7735 - val_loss: 0.7185 - val_accuracy: 0.7330 Epoch 14/15 92/92 [==============================] - 1s 12ms/step - loss: 0.5824 - accuracy: 0.7735 - val_loss: 0.7282 - val_accuracy: 0.7357 Epoch 15/15 92/92 [==============================] - 1s 12ms/step - loss: 0.5771 - accuracy: 0.7851 - val_loss: 0.7308 - val_accuracy: 0.7343
Visualizza i risultati dell'allenamento
Dopo aver applicato l'aumento dei dati e tf.keras.layers.Dropout
, c'è meno overfitting rispetto a prima e l'accuratezza della formazione e della convalida sono più allineate:
acc = history.history['accuracy']
val_acc = history.history['val_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs_range = range(epochs)
plt.figure(figsize=(8, 8))
plt.subplot(1, 2, 1)
plt.plot(epochs_range, acc, label='Training Accuracy')
plt.plot(epochs_range, val_acc, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')
plt.subplot(1, 2, 2)
plt.plot(epochs_range, loss, label='Training Loss')
plt.plot(epochs_range, val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()
Prevedere nuovi dati
Infine, utilizziamo il nostro modello per classificare un'immagine che non è stata inclusa nei set di addestramento o convalida.
sunflower_url = "https://storage.googleapis.com/download.tensorflow.org/example_images/592px-Red_sunflower.jpg"
sunflower_path = tf.keras.utils.get_file('Red_sunflower', origin=sunflower_url)
img = tf.keras.utils.load_img(
sunflower_path, target_size=(img_height, img_width)
)
img_array = tf.keras.utils.img_to_array(img)
img_array = tf.expand_dims(img_array, 0) # Create a batch
predictions = model.predict(img_array)
score = tf.nn.softmax(predictions[0])
print(
"This image most likely belongs to {} with a {:.2f} percent confidence."
.format(class_names[np.argmax(score)], 100 * np.max(score))
)
Downloading data from https://storage.googleapis.com/download.tensorflow.org/example_images/592px-Red_sunflower.jpg 122880/117948 [===============================] - 0s 0us/step 131072/117948 [=================================] - 0s 0us/step This image most likely belongs to sunflowers with a 89.13 percent confidence.