Merken Sie den Termin vor! Google I / O kehrt vom 18. bis 20. Mai zurück Registrieren Sie sich jetzt
Diese Seite wurde von der Cloud Translation API übersetzt.
Switch to English

Bildklassifizierung

Ansicht auf TensorFlow.org In Google Colab ausführen Quelle auf GitHub anzeigen Notizbuch herunterladen

Dieses Tutorial zeigt, wie Sie Bilder von Blumen klassifizieren. Es erzeugt ein Bild Klassifikator ein mit keras.Sequential Modell und lädt Daten mit preprocessing.image_dataset_from_directory . Sie sammeln praktische Erfahrungen mit folgenden Konzepten:

  • Effizientes Laden eines Datensatzes von der Festplatte.
  • Identifizieren von Überanpassungen und Anwenden von Techniken, um diese zu verringern, einschließlich Datenerweiterung und Ausfall.

Dieses Tutorial folgt einem grundlegenden Workflow für maschinelles Lernen:

  1. Daten untersuchen und verstehen
  2. Erstellen Sie eine Eingabepipeline
  3. Erstellen Sie das Modell
  4. Trainiere das Modell
  5. Testen Sie das Modell
  6. Verbessern Sie das Modell und wiederholen Sie den Vorgang

Importieren Sie TensorFlow und andere Bibliotheken

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

Laden Sie den Datensatz herunter und erkunden Sie ihn

In diesem Tutorial wird ein Datensatz mit ca. 3.700 Fotos von Blumen verwendet. Der Datensatz enthält 5 Unterverzeichnisse, eines pro Klasse:

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 [==============================] - 2s 0us/step

Nach dem Herunterladen sollte nun eine Kopie des Datensatzes verfügbar sein. Es gibt insgesamt 3.670 Bilder:

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

Hier sind einige Rosen:

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

png

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

png

Und ein paar Tulpen:

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

png

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

png

Laden Sie mit keras.preprocessing

Laden Sie diese Bilder mit dem hilfreichen Dienstprogramm image_dataset_from_directory von der Festplatte. Dadurch gelangen Sie in nur wenigen Codezeilen von einem Verzeichnis mit Bildern auf der Festplatte zu einemtf.data.Dataset . Wenn Sie möchten, können Sie auch Ihren eigenen Code zum Laden von Daten von Grund auf neu schreiben, indem Sie das Tutorial zum Laden von Bildern besuchen.

Erstellen Sie einen Datensatz

Definieren Sie einige Parameter für den Loader:

batch_size = 32
img_height = 180
img_width = 180

Es wird empfohlen, bei der Entwicklung Ihres Modells einen Validierungssplit zu verwenden. Verwenden wir 80% der Bilder für das Training und 20% für die Validierung.

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.

Sie finden die Klassennamen im Attribut class_names für diese Datasets. Diese entsprechen den Verzeichnisnamen in alphabetischer Reihenfolge.

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

Visualisieren Sie die Daten

Hier sind die ersten 9 Bilder aus dem Trainingsdatensatz.

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

Sie trainieren ein Modell mit diesen Datensätzen, indem Sie sie gleich an model.fit . Wenn Sie möchten, können Sie den Datensatz auch manuell durchlaufen und Stapel von Bildern abrufen:

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

Der image_batch ist ein Tensor der Form (32, 180, 180, 3) . Dies ist ein Stapel von 32 Bildern mit der Form 180x180x3 (die letzte Dimension bezieht sich auf Farbkanäle RGB). Der label_batch ist ein Tensor der Form (32,) , dies sind entsprechende Beschriftungen zu den 32 Bildern.

Sie können .numpy() für die Tensoren image_batch und labels_batch , um sie in numpy.ndarray zu konvertieren.

Konfigurieren Sie das Dataset für die Leistung

Stellen Sie sicher, dass Sie gepuffertes Prefetching verwenden, damit Sie Daten von der Festplatte liefern können, ohne dass E / A blockiert werden. Dies sind zwei wichtige Methoden, die Sie beim Laden von Daten verwenden sollten.

Dataset.cache() speichert die Bilder im Speicher, nachdem sie in der ersten Epoche von der Festplatte geladen wurden. Dadurch wird sichergestellt, dass der Datensatz beim Training Ihres Modells nicht zu einem Engpass wird. Wenn Ihr Dataset zu groß ist, um in den Speicher zu passen, können Sie mit dieser Methode auch einen performanten Cache auf der Festplatte erstellen.

Dataset.prefetch() überlappt die Datenvorverarbeitung und Modellausführung während des Trainings.

Interessierte Leser erfahren mehr über beide Methoden sowie über das Zwischenspeichern von Daten auf der Festplatte im Handbuch zur Datenleistung .

AUTOTUNE = tf.data.AUTOTUNE

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

Standardisieren Sie die Daten

Die RGB-Kanalwerte liegen im Bereich [0, 255] . Dies ist nicht ideal für ein neuronales Netzwerk. Im Allgemeinen sollten Sie versuchen, Ihre Eingabewerte klein zu machen. Hier standardisieren Sie Werte im Bereich [0, 1] mithilfe einer Ebene für die Neuskalierung.

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

Es gibt zwei Möglichkeiten, diese Ebene zu verwenden. Sie können es auf den Datensatz anwenden, indem Sie map aufrufen:

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 0.9407408

Sie können die Ebene auch in Ihre Modelldefinition aufnehmen, um die Bereitstellung zu vereinfachen. Verwenden wir hier den zweiten Ansatz.

Erstellen Sie das Modell

Das Modell besteht aus drei Faltungsblöcken mit jeweils einer maximalen Poolschicht. Es gibt eine vollständig verbundene Schicht mit 128 Einheiten darüber, die durch eine relu Aktivierungsfunktion aktiviert wird. Dieses Modell wurde nicht auf hohe Genauigkeit abgestimmt. Ziel dieses Tutorials ist es, einen Standardansatz aufzuzeigen.

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

Kompilieren Sie das Modell

Wählen Sie für dieses Tutorial die optimizers.Adam losses.SparseCategoricalCrossentropy Optimierer und Verluste. losses.SparseCategoricalCrossentropy Verlustfunktion. Um Ansicht Probe- und Validierungsgenauigkeit für jede Trainings Epoche, übergeben Sie die metrics Argument.

model.compile(optimizer='adam',
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
              metrics=['accuracy'])

Modellzusammenfassung

Zeigen Sie alle Ebenen des Netzwerks mithilfe der summary des Modells an:

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
_________________________________________________________________

Trainiere das Modell

epochs=10
history = model.fit(
  train_ds,
  validation_data=val_ds,
  epochs=epochs
)
Epoch 1/10
92/92 [==============================] - 5s 27ms/step - loss: 1.7103 - accuracy: 0.2977 - val_loss: 1.2054 - val_accuracy: 0.4986
Epoch 2/10
92/92 [==============================] - 1s 11ms/step - loss: 1.0959 - accuracy: 0.5544 - val_loss: 1.0688 - val_accuracy: 0.5763
Epoch 3/10
92/92 [==============================] - 1s 11ms/step - loss: 0.9331 - accuracy: 0.6329 - val_loss: 0.9369 - val_accuracy: 0.6267
Epoch 4/10
92/92 [==============================] - 1s 11ms/step - loss: 0.8064 - accuracy: 0.6951 - val_loss: 0.9546 - val_accuracy: 0.6335
Epoch 5/10
92/92 [==============================] - 1s 11ms/step - loss: 0.5643 - accuracy: 0.7933 - val_loss: 0.9191 - val_accuracy: 0.6444
Epoch 6/10
92/92 [==============================] - 1s 11ms/step - loss: 0.3910 - accuracy: 0.8717 - val_loss: 1.2451 - val_accuracy: 0.5954
Epoch 7/10
92/92 [==============================] - 1s 11ms/step - loss: 0.2849 - accuracy: 0.9094 - val_loss: 1.2454 - val_accuracy: 0.6335
Epoch 8/10
92/92 [==============================] - 1s 11ms/step - loss: 0.1562 - accuracy: 0.9567 - val_loss: 1.2909 - val_accuracy: 0.6322
Epoch 9/10
92/92 [==============================] - 1s 11ms/step - loss: 0.1154 - accuracy: 0.9622 - val_loss: 1.4424 - val_accuracy: 0.6322
Epoch 10/10
92/92 [==============================] - 1s 11ms/step - loss: 0.0649 - accuracy: 0.9866 - val_loss: 1.6675 - val_accuracy: 0.6308

Visualisieren Sie die Trainingsergebnisse

Erstellen Sie Diagramme mit Verlust und Genauigkeit in den Trainings- und Validierungssätzen.

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

Wie Sie den Darstellungen entnehmen können, sind die Trainingsgenauigkeit und die Validierungsgenauigkeit um ein Vielfaches geringer, und das Modell hat im Validierungssatz nur eine Genauigkeit von etwa 60% erreicht.

Schauen wir uns an, was schief gelaufen ist, und versuchen wir, die Gesamtleistung des Modells zu steigern.

Überanpassung

In den obigen Darstellungen steigt die Trainingsgenauigkeit im Laufe der Zeit linear an, während die Validierungsgenauigkeit im Trainingsprozess um 60% liegt. Auch der Unterschied in der Genauigkeit zwischen Trainings- und Validierungsgenauigkeit ist spürbar - ein Zeichen für eine Überanpassung .

Wenn es eine kleine Anzahl von Trainingsbeispielen gibt, lernt das Modell manchmal aus Geräuschen oder unerwünschten Details aus Trainingsbeispielen - in einem Ausmaß, das sich negativ auf die Leistung des Modells bei neuen Beispielen auswirkt. Dieses Phänomen ist als Überanpassung bekannt. Dies bedeutet, dass es für das Modell schwierig sein wird, einen neuen Datensatz zu verallgemeinern.

Es gibt mehrere Möglichkeiten, die Überanpassung im Trainingsprozess zu bekämpfen. In diesem Tutorial verwenden Sie die Datenerweiterung und fügen Ihrem Modell Dropout hinzu .

Datenerweiterung

Überanpassung tritt im Allgemeinen auf, wenn es eine kleine Anzahl von Trainingsbeispielen gibt. Bei der Datenerweiterung werden zusätzliche Trainingsdaten aus Ihren vorhandenen Beispielen generiert, indem sie mithilfe zufälliger Transformationen erweitert werden, die glaubwürdig aussehende Bilder liefern. Dies hilft, das Modell mehr Aspekten der Daten auszusetzen und besser zu verallgemeinern.

Sie implementieren die tf.keras.layers.experimental.preprocessing mithilfe der Ebenen aus tf.keras.layers.experimental.preprocessing . Diese können wie andere Ebenen in Ihr Modell aufgenommen und auf der GPU ausgeführt werden.

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

Lassen Sie uns visualisieren, wie einige erweiterte Beispiele aussehen, indem Sie die Datenerweiterung mehrmals auf dasselbe Bild anwenden:

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

Sie werden die Datenerweiterung verwenden, um ein Modell in einem Moment zu trainieren.

Ausfallen

Eine andere Technik zur Reduzierung der Überanpassung besteht darin, Dropout in das Netzwerk einzuführen, eine Form der Regularisierung .

Wenn Sie Dropout auf eine Ebene anwenden, wird während des Trainingsvorgangs zufällig eine Anzahl von Ausgabeeinheiten aus der Ebene entfernt (indem die Aktivierung auf Null gesetzt wird). Dropout verwendet eine Bruchzahl als Eingabewert in Form von 0,1, 0,2, 0,4 usw. Dies bedeutet, dass 10%, 20% oder 40% der Ausgabeeinheiten zufällig aus der angewendeten Ebene entfernt werden.

Erstellen wir ein neues neuronales Netzwerk mit layers.Dropout und trainieren Sie es dann mit erweiterten Bildern.

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

Kompilieren und trainieren Sie das Modell

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 [==============================] - 2s 13ms/step - loss: 1.5399 - accuracy: 0.3400 - val_loss: 1.2366 - val_accuracy: 0.4986
Epoch 2/15
92/92 [==============================] - 1s 11ms/step - loss: 1.0382 - accuracy: 0.5900 - val_loss: 0.9774 - val_accuracy: 0.6090
Epoch 3/15
92/92 [==============================] - 1s 11ms/step - loss: 0.9348 - accuracy: 0.6472 - val_loss: 0.9294 - val_accuracy: 0.6362
Epoch 4/15
92/92 [==============================] - 1s 11ms/step - loss: 0.8891 - accuracy: 0.6486 - val_loss: 0.9018 - val_accuracy: 0.6621
Epoch 5/15
92/92 [==============================] - 1s 11ms/step - loss: 0.8445 - accuracy: 0.6727 - val_loss: 0.9248 - val_accuracy: 0.6485
Epoch 6/15
92/92 [==============================] - 1s 11ms/step - loss: 0.8139 - accuracy: 0.6912 - val_loss: 0.8845 - val_accuracy: 0.6485
Epoch 7/15
92/92 [==============================] - 1s 11ms/step - loss: 0.7541 - accuracy: 0.7093 - val_loss: 0.7796 - val_accuracy: 0.6921
Epoch 8/15
92/92 [==============================] - 1s 11ms/step - loss: 0.7094 - accuracy: 0.7369 - val_loss: 0.7921 - val_accuracy: 0.6757
Epoch 9/15
92/92 [==============================] - 1s 11ms/step - loss: 0.6755 - accuracy: 0.7444 - val_loss: 0.8087 - val_accuracy: 0.6880
Epoch 10/15
92/92 [==============================] - 1s 11ms/step - loss: 0.6329 - accuracy: 0.7627 - val_loss: 0.7770 - val_accuracy: 0.7016
Epoch 11/15
92/92 [==============================] - 1s 11ms/step - loss: 0.6456 - accuracy: 0.7634 - val_loss: 0.7409 - val_accuracy: 0.7180
Epoch 12/15
92/92 [==============================] - 1s 11ms/step - loss: 0.5807 - accuracy: 0.7889 - val_loss: 0.7696 - val_accuracy: 0.7098
Epoch 13/15
92/92 [==============================] - 1s 11ms/step - loss: 0.5716 - accuracy: 0.7772 - val_loss: 0.7634 - val_accuracy: 0.6989
Epoch 14/15
92/92 [==============================] - 1s 11ms/step - loss: 0.5665 - accuracy: 0.7859 - val_loss: 0.7610 - val_accuracy: 0.7112
Epoch 15/15
92/92 [==============================] - 1s 11ms/step - loss: 0.5303 - accuracy: 0.7959 - val_loss: 0.7306 - val_accuracy: 0.7371

Visualisieren Sie die Trainingsergebnisse

Nach dem Anwenden von Datenerweiterung und Dropout gibt es weniger Überanpassungen als zuvor, und die Genauigkeit von Training und Validierung ist enger aufeinander abgestimmt.

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

Vorhersage neuer Daten

Lassen Sie uns abschließend unser Modell verwenden, um ein Bild zu klassifizieren, das nicht in den Trainings- oder Validierungssätzen enthalten war.

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 97.38 percent confidence.