Questa pagina è stata tradotta dall'API Cloud Translation.
Switch to English

Classificazione delle immagini

Visualizza su TensorFlow.org Esegui in Google Colab Visualizza sorgente su GitHub Scarica notebook

Questo tutorial mostra come classificare le immagini dei fiori. Crea un classificatore di immagini utilizzando un modello keras.Sequential e carica i dati utilizzando preprocessing.image_dataset_from_directory . Acquisirai esperienza pratica con i seguenti concetti:

  • Caricamento efficiente di un set di dati dal disco.
  • Identificazione dell'overfitting e applicazione di tecniche per mitigarlo, inclusi l'aumento dei dati e Dropout.

Questo tutorial segue un flusso di lavoro di apprendimento automatico di base:

  1. Esamina e comprendi i dati
  2. Costruisci una pipeline di input
  3. Costruisci il modello
  4. Addestra il modello
  5. Prova il modello
  6. Migliora il modello e ripeti il ​​processo
pip install -q tf-nightly
WARNING: You are using pip version 20.2.2; however, version 20.2.3 is available.
You should consider upgrading via the '/tmpfs/src/tf_docs_env/bin/python -m pip install --upgrade pip' command.

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 5 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)
Downloading data from https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz
228818944/228813984 [==============================] - 1s 0us/step

Dopo il download, dovresti ora avere una copia del set di dati disponibile. Ci sono 3.670 immagini totali:

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

png

PIL.Image.open(str(roses[1]))

png

E alcuni tulipani:

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

png

PIL.Image.open(str(tulips[1]))

png

Carica usando keras.preprocessing

Carichiamo queste immagini dal disco usando l'utile utility 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 il tuo codice di caricamento dati da zero visitando il tutorial di caricamento delle immagini .

Crea un set di dati

Definisci 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. Useremo l'80% delle immagini per la formazione e il 20% per la convalida.

train_ds = tf.keras.preprocessing.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.preprocessing.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.

È possibile 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 9 immagini dal 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

model.fit un modello utilizzando questi set di dati passandoli a model.fit in un attimo. Se lo desideri, puoi anche iterare manualmente sul 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) . Questo è un lotto di 32 immagini di forma 180x180x3 (l'ultima dimensione si riferisce ai canali di colore RGB). label_batch è un tensore della forma (32,) , queste sono etichette corrispondenti alle 32 immagini.

Puoi chiamare .numpy() sui tensori image_batch e labels_batch per convertirli in un numpy.ndarray .

Configura il set di dati per le prestazioni

Assicuriamoci di utilizzare il precaricamento bufferizzato in modo da poter ottenere dati dal disco senza che l'I / O diventi un blocco. 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 in memoria, puoi anche utilizzare questo metodo per creare una cache su disco efficiente.

Dataset.prefetch() sovrappone alla pre-elaborazione dei dati e all'esecuzione del modello durante l'addestramento.

I lettori interessati possono ottenere ulteriori informazioni su entrambi i metodi e su come memorizzare nella cache i dati su disco nella guida alle prestazioni dei dati .

AUTOTUNE = tf.data.experimental.AUTOTUNE

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

Standardizza i dati

I valori del canale RGB sono nella gamma [0, 255] . Questo non è l'ideale per una rete neurale; in generale dovresti cercare di ridurre i valori di input. Qui standardizzeremo i valori in modo che siano in [0, 1] utilizzando un livello di ridimensionamento.

normalization_layer = layers.experimental.preprocessing.Rescaling(1./255)

Esistono due modi per utilizzare questo livello. Puoi applicarlo al set di dati chiamando 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 pixels values are now in `[0,1]`.
print(np.min(first_image), np.max(first_image)) 
0.0 1.0

Oppure puoi includere il livello all'interno della definizione del modello, che può semplificare la distribuzione. Useremo il secondo approccio qui.

Crea il modello

Il modello è costituito da tre blocchi di convoluzione con uno strato di pool max in ciascuno di essi. C'è uno strato completamente connesso con 128 unità sopra di esso che viene attivato da una funzione di attivazione relu . Questo modello non è stato ottimizzato per un'elevata precisione, l'obiettivo di questo tutorial è mostrare un approccio standard.

num_classes = 5

model = Sequential([
  layers.experimental.preprocessing.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 la funzione optimizers.Adam optimizer e losses.SparseCategoricalCrossentropy loss. Per visualizzare l'addestramento e l'accuratezza della convalida per ciascuna epoca di addestramento, passare l'argomento delle metrics .

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 di 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 (MaxPooling2 (None, 45, 45, 32)        0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 45, 45, 64)        18496     
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 22, 22, 64)        0         
_________________________________________________________________
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
_________________________________________________________________

Addestra il modello

epochs=10
history = model.fit(
  train_ds,
  validation_data=val_ds,
  epochs=epochs
)
Epoch 1/10
92/92 [==============================] - 28s 295ms/step - loss: 1.5722 - accuracy: 0.3664 - val_loss: 1.0810 - val_accuracy: 0.5477
Epoch 2/10
92/92 [==============================] - 27s 289ms/step - loss: 1.0409 - accuracy: 0.5835 - val_loss: 1.0014 - val_accuracy: 0.5954
Epoch 3/10
92/92 [==============================] - 26s 287ms/step - loss: 0.8835 - accuracy: 0.6517 - val_loss: 0.9068 - val_accuracy: 0.6281
Epoch 4/10
92/92 [==============================] - 27s 298ms/step - loss: 0.7079 - accuracy: 0.7252 - val_loss: 0.9219 - val_accuracy: 0.6471
Epoch 5/10
92/92 [==============================] - 27s 290ms/step - loss: 0.5099 - accuracy: 0.8101 - val_loss: 0.9138 - val_accuracy: 0.6526
Epoch 6/10
92/92 [==============================] - 27s 292ms/step - loss: 0.3452 - accuracy: 0.8870 - val_loss: 0.9905 - val_accuracy: 0.6703
Epoch 7/10
92/92 [==============================] - 27s 295ms/step - loss: 0.1839 - accuracy: 0.9490 - val_loss: 1.2020 - val_accuracy: 0.6322
Epoch 8/10
92/92 [==============================] - 27s 289ms/step - loss: 0.1096 - accuracy: 0.9673 - val_loss: 1.4612 - val_accuracy: 0.6308
Epoch 9/10
92/92 [==============================] - 27s 289ms/step - loss: 0.0611 - accuracy: 0.9846 - val_loss: 1.6569 - val_accuracy: 0.6403
Epoch 10/10
92/92 [==============================] - 26s 286ms/step - loss: 0.0679 - accuracy: 0.9799 - val_loss: 1.8237 - val_accuracy: 0.6199

Visualizza i risultati dell'allenamento

Crea grafici di perdita e precisione 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()

png

Come puoi vedere dai grafici, l'accuratezza dell'addestramento e l'accuratezza della convalida sono largamente discostanti e il modello ha raggiunto solo circa il 60% di precisione sul set di convalida.

Diamo un'occhiata a cosa è andato storto e proviamo ad aumentare le prestazioni complessive del modello.

Overfitting

Nei grafici precedenti, la precisione dell'addestramento aumenta in modo lineare nel tempo, mentre l'accuratezza della convalida si ferma intorno al 60% nel processo di addestramento. Inoltre, la differenza di accuratezza tra l'addestramento e l'accuratezza della convalida è evidente, 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 sui 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 ci sono un piccolo numero di esempi di formazione. L'aumento dei dati utilizza l'approccio della generazione di dati di addestramento aggiuntivi dagli esempi esistenti aumentando quindi utilizzando trasformazioni casuali che producono immagini dall'aspetto credibile. Ciò consente di esporre il modello a più aspetti dei dati e di generalizzarlo meglio.

Implementeremo l'aumento dei dati utilizzando Keras Preprocessing Layer sperimentali. Questi possono essere inclusi nel tuo modello come altri livelli ed essere eseguiti sulla GPU.

data_augmentation = keras.Sequential(
  [
    layers.experimental.preprocessing.RandomFlip("horizontal", 
                                                 input_shape=(img_height, 
                                                              img_width,
                                                              3)),
    layers.experimental.preprocessing.RandomRotation(0.1),
    layers.experimental.preprocessing.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")

png

Useremo l'aumento dei dati per addestrare un modello in un momento.

Buttare fuori

Un'altra tecnica per ridurre l'overfitting consiste nell'introdurre Dropout nella rete, una forma di regolarizzazione .

Quando si applica Dropout a un livello, viene eliminato in modo casuale (impostando l'attivazione su zero) un numero di unità di output dal livello durante il processo di addestramento. Dropout prende un numero frazionario come valore di input, nella forma come 0.1, 0.2, 0.4, ecc. Ciò significa eliminare il 10%, 20% o 40% delle unità di output in modo casuale dal livello applicato.

Creiamo una nuova rete neurale utilizzando i layers.Dropout , layers.Dropout , quindi addestriamola utilizzando immagini aumentate.

model = Sequential([
  data_augmentation,
  layers.experimental.preprocessing.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)
])

Compilare e addestrare 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 (MaxPooling2 (None, 90, 90, 16)        0         
_________________________________________________________________
conv2d_4 (Conv2D)            (None, 90, 90, 32)        4640      
_________________________________________________________________
max_pooling2d_4 (MaxPooling2 (None, 45, 45, 32)        0         
_________________________________________________________________
conv2d_5 (Conv2D)            (None, 45, 45, 64)        18496     
_________________________________________________________________
max_pooling2d_5 (MaxPooling2 (None, 22, 22, 64)        0         
_________________________________________________________________
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 [==============================] - 30s 324ms/step - loss: 1.5318 - accuracy: 0.2934 - val_loss: 1.1438 - val_accuracy: 0.5245
Epoch 2/15
92/92 [==============================] - 30s 322ms/step - loss: 1.0707 - accuracy: 0.5490 - val_loss: 1.0091 - val_accuracy: 0.6104
Epoch 3/15
92/92 [==============================] - 30s 331ms/step - loss: 0.9404 - accuracy: 0.6322 - val_loss: 0.9887 - val_accuracy: 0.6281
Epoch 4/15
92/92 [==============================] - 30s 327ms/step - loss: 0.8895 - accuracy: 0.6538 - val_loss: 0.8148 - val_accuracy: 0.6717
Epoch 5/15
92/92 [==============================] - 31s 337ms/step - loss: 0.8084 - accuracy: 0.6749 - val_loss: 0.9402 - val_accuracy: 0.6649
Epoch 6/15
92/92 [==============================] - 30s 327ms/step - loss: 0.7779 - accuracy: 0.7044 - val_loss: 1.0416 - val_accuracy: 0.6253
Epoch 7/15
92/92 [==============================] - 30s 324ms/step - loss: 0.7517 - accuracy: 0.7207 - val_loss: 0.8159 - val_accuracy: 0.6948
Epoch 8/15
92/92 [==============================] - 30s 326ms/step - loss: 0.7096 - accuracy: 0.7305 - val_loss: 0.8161 - val_accuracy: 0.7016
Epoch 9/15
92/92 [==============================] - 30s 329ms/step - loss: 0.7199 - accuracy: 0.7166 - val_loss: 0.7851 - val_accuracy: 0.7030
Epoch 10/15
92/92 [==============================] - 30s 327ms/step - loss: 0.6464 - accuracy: 0.7562 - val_loss: 0.7606 - val_accuracy: 0.7180
Epoch 11/15
92/92 [==============================] - 29s 319ms/step - loss: 0.6099 - accuracy: 0.7694 - val_loss: 0.8871 - val_accuracy: 0.6866
Epoch 12/15
92/92 [==============================] - 30s 325ms/step - loss: 0.6185 - accuracy: 0.7629 - val_loss: 0.7661 - val_accuracy: 0.7016
Epoch 13/15
92/92 [==============================] - 30s 324ms/step - loss: 0.5980 - accuracy: 0.7751 - val_loss: 0.7051 - val_accuracy: 0.7262
Epoch 14/15
92/92 [==============================] - 30s 322ms/step - loss: 0.5628 - accuracy: 0.7928 - val_loss: 0.8075 - val_accuracy: 0.6921
Epoch 15/15
92/92 [==============================] - 30s 322ms/step - loss: 0.5702 - accuracy: 0.7811 - val_loss: 0.8650 - val_accuracy: 0.6935

Visualizza i risultati dell'allenamento

Dopo aver applicato l'aumento dei dati e Dropout, c'è meno overfitting rispetto a prima e la precisione di addestramento e 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()

png

Prevedi nuovi dati

Infine, utilizziamo il nostro modello per classificare un'immagine che non era 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 = keras.preprocessing.image.load_img(
    sunflower_path, target_size=(img_height, img_width)
)
img_array = keras.preprocessing.image.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
This image most likely belongs to sunflowers with a 99.91 percent confidence.