Halaman ini diterjemahkan oleh Cloud Translation API.
Switch to English

Klasifikasi gambar

Lihat di TensorFlow.org Jalankan di Google Colab Lihat sumber di GitHub Unduh buku catatan

Tutorial ini menunjukkan cara mengklasifikasikan gambar bunga. Itu menciptakan penggolong gambar menggunakan model keras.Sequential , dan memuat data menggunakan preprocessing.image_dataset_from_directory . Anda akan mendapatkan pengalaman praktis dengan konsep-konsep berikut:

  • Memuat dataset dari disk secara efisien.
  • Mengidentifikasi overfitting dan menerapkan teknik untuk memitigasinya, termasuk augmentasi data dan Dropout.

Tutorial ini mengikuti alur kerja pembelajaran mesin dasar:

  1. Periksa dan pahami data
  2. Bangun pipa input
  3. Bangun model
  4. Latih modelnya
  5. Uji modelnya
  6. Perbaiki model dan ulangi prosesnya
pip install -q tf-nightly

Impor TensorFlow dan perpustakaan lainnya

 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
 

Unduh dan jelajahi dataset

Tutorial ini menggunakan dataset sekitar 3.700 foto bunga. Dataset berisi 5 sub-direktori, satu per kelas:

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

Setelah mengunduh, Anda sekarang harus memiliki salinan dataset yang tersedia. Ada 3.670 total gambar:

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

Berikut ini beberapa mawar:

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

png

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

png

Dan beberapa tulip:

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

png

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

png

Load menggunakan hard.preprocessing

Mari memuat gambar-gambar ini dari disk menggunakan utilitas image_dataset_from_directory bermanfaat. Ini akan membawa Anda dari direktori gambar pada disk ke tf.data.Dataset hanya dalam beberapa baris kode. Jika suka, Anda juga dapat menulis kode pemuatan data sendiri dari awal dengan mengunjungi tutorial memuat gambar .

Buat dataset

Tentukan beberapa parameter untuk pemuat:

 batch_size = 32
img_height = 180
img_width = 180
 

Merupakan praktik yang baik untuk menggunakan pemisahan validasi saat mengembangkan model Anda. Kami akan menggunakan 80% dari gambar untuk pelatihan, dan 20% untuk validasi.

 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.

Anda dapat menemukan nama kelas di atribut class_names pada kumpulan data ini. Ini sesuai dengan nama direktori dalam urutan abjad.

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

Visualisasikan data

Berikut adalah 9 gambar pertama dari dataset pelatihan.

 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

Anda akan melatih model menggunakan dataset ini dengan mengirimkannya ke model.fit sebentar lagi. Jika suka, Anda juga dapat secara manual mengulangi set data dan mengambil kumpulan gambar:

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

image_batch adalah tensor bentuk (32, 180, 180, 3) . Ini adalah kumpulan 32 gambar bentuk 180x180x3 (dimensi terakhir mengacu pada saluran warna RGB). label_batch adalah tensor bentuk (32,) , ini adalah label yang sesuai dengan 32 gambar.

Anda dapat menghubungi .numpy() pada image_batch dan labels_batch tensor untuk mengkonversi mereka ke numpy.ndarray .

Konfigurasikan dataset untuk kinerja

Mari kita pastikan untuk menggunakan prefetching buffered sehingga kita dapat menghasilkan data dari disk tanpa I / O menjadi memblokir. Ini adalah dua metode penting yang harus Anda gunakan saat memuat data.

Dataset.cache() menyimpan gambar dalam memori setelah mereka dimuat dari disk selama zaman pertama. Ini akan memastikan dataset tidak menjadi hambatan saat melatih model Anda. Jika dataset Anda terlalu besar untuk dimasukkan ke dalam memori, Anda juga dapat menggunakan metode ini untuk membuat cache di-performan.

Dataset.prefetch() tumpang tindih preprocessing data dan eksekusi model saat pelatihan.

Pembaca yang tertarik dapat mempelajari lebih lanjut tentang kedua metode ini, serta cara menyimpan data ke disk dalam panduan kinerja data .

 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)
 

Membakukan data

Nilai-nilai saluran RGB berada dalam kisaran [0, 255] . Ini tidak ideal untuk jaringan saraf; secara umum Anda harus berusaha membuat nilai input Anda kecil. Di sini, kita akan menstandarkan nilai ke dalam [0, 1] dengan menggunakan layer Penskalaan Ulang.

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

Ada dua cara untuk menggunakan lapisan ini. Anda dapat menerapkannya pada dataset dengan memanggil peta:

 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

Atau, Anda dapat memasukkan lapisan di dalam definisi model Anda, yang dapat menyederhanakan penyebaran. Kami akan menggunakan pendekatan kedua di sini.

Buat model

Model ini terdiri dari tiga blok konvolusi dengan lapisan max pool di masing-masing. Ada lapisan yang sepenuhnya terhubung dengan 128 unit di atasnya yang diaktifkan oleh fungsi aktivasi relu . Model ini belum disetel untuk akurasi tinggi, tujuan tutorial ini adalah untuk menunjukkan pendekatan standar.

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

Kompilasi model

Untuk tutorial ini, memilih optimizers.Adam optimizer dan losses.SparseCategoricalCrossentropy fungsi kerugian. Untuk melihat akurasi pelatihan dan validasi untuk setiap zaman pelatihan, sampaikan argumen metrics .

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

Ringkasan model

Lihat semua lapisan jaringan menggunakan metode summary model:

 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
_________________________________________________________________

Latih modelnya

 epochs=10
history = model.fit(
  train_ds,
  validation_data=val_ds,
  epochs=epochs
)
 
Epoch 1/10
92/92 [==============================] - 4s 47ms/step - loss: 1.9328 - accuracy: 0.2896 - val_loss: 1.1022 - val_accuracy: 0.5245
Epoch 2/10
92/92 [==============================] - 1s 10ms/step - loss: 1.0441 - accuracy: 0.5761 - val_loss: 1.0057 - val_accuracy: 0.5913
Epoch 3/10
92/92 [==============================] - 1s 10ms/step - loss: 0.8640 - accuracy: 0.6763 - val_loss: 0.8951 - val_accuracy: 0.6499
Epoch 4/10
92/92 [==============================] - 1s 10ms/step - loss: 0.7106 - accuracy: 0.7472 - val_loss: 0.8992 - val_accuracy: 0.6621
Epoch 5/10
92/92 [==============================] - 1s 10ms/step - loss: 0.4817 - accuracy: 0.8285 - val_loss: 0.8997 - val_accuracy: 0.6662
Epoch 6/10
92/92 [==============================] - 1s 10ms/step - loss: 0.3131 - accuracy: 0.8903 - val_loss: 1.0014 - val_accuracy: 0.6567
Epoch 7/10
92/92 [==============================] - 1s 10ms/step - loss: 0.1782 - accuracy: 0.9436 - val_loss: 1.2140 - val_accuracy: 0.6431
Epoch 8/10
92/92 [==============================] - 1s 10ms/step - loss: 0.1024 - accuracy: 0.9703 - val_loss: 1.5144 - val_accuracy: 0.6240
Epoch 9/10
92/92 [==============================] - 1s 10ms/step - loss: 0.0736 - accuracy: 0.9815 - val_loss: 1.7651 - val_accuracy: 0.5926
Epoch 10/10
92/92 [==============================] - 1s 10ms/step - loss: 0.0761 - accuracy: 0.9775 - val_loss: 2.0429 - val_accuracy: 0.5967

Visualisasikan hasil pelatihan

Buat plot kehilangan dan akurasi pada set pelatihan dan validasi.

 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

Seperti yang dapat Anda lihat dari plot, akurasi pelatihan dan akurasi validasi dimatikan oleh margin besar dan model hanya mencapai akurasi sekitar 60% pada set validasi.

Mari kita lihat apa yang salah dan cobalah untuk meningkatkan kinerja keseluruhan model.

Overfitting

Dalam plot di atas, akurasi pelatihan meningkat secara linear dari waktu ke waktu, sedangkan akurasi validasi terhenti sekitar 60% dalam proses pelatihan. Juga, perbedaan akurasi antara pelatihan dan akurasi validasi terlihat — tanda overfitting .

Ketika ada sejumlah kecil contoh pelatihan, model kadang-kadang belajar dari kebisingan atau detail yang tidak diinginkan dari contoh pelatihan — sampai-sampai itu berdampak negatif terhadap kinerja model pada contoh baru. Fenomena ini dikenal sebagai overfitting. Ini berarti bahwa model akan mengalami kesulitan generalisasi pada dataset baru.

Ada banyak cara untuk melawan overfitting dalam proses pelatihan. Dalam tutorial ini, Anda akan menggunakan augmentasi data dan menambahkan Dropout ke model Anda.

Augmentasi data

Overfitting umumnya terjadi ketika ada sejumlah kecil contoh pelatihan. Augmentasi data mengambil pendekatan menghasilkan data pelatihan tambahan dari contoh yang ada dengan menambah lalu menggunakan transformasi acak yang menghasilkan gambar yang tampak dipercaya. Ini membantu memaparkan model pada lebih banyak aspek data dan menggeneralisasi dengan lebih baik.

Kami akan mengimplementasikan augmentasi data menggunakan Lapisan Keras Preprocessing eksperimental. Ini dapat dimasukkan ke dalam model Anda seperti layer lain, dan berjalan di 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),
  ]
)
 

Mari kita bayangkan bagaimana beberapa contoh yang diperbesar dengan menerapkan augmentasi data ke gambar yang sama beberapa kali:

 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

Kami akan menggunakan augmentasi data untuk melatih model suatu saat.

Keluar

Teknik lain untuk mengurangi overfitting adalah dengan memperkenalkan Dropout ke jaringan, suatu bentuk regularisasi .

Ketika Anda menerapkan Dropout ke sebuah layer, layer tersebut akan keluar secara acak (dengan mengatur aktivasi ke nol) sejumlah unit output dari layer selama proses pelatihan. Dropout mengambil angka pecahan sebagai nilai inputnya, dalam bentuk seperti 0,1, 0,2, 0,4, dll. Ini berarti mengeluarkan 10%, 20% atau 40% unit output secara acak dari lapisan yang diterapkan.

Mari kita buat jaringan saraf baru menggunakan layers.Dropout , lalu latih menggunakan gambar yang ditambah.

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

Kompilasi dan latih modelnya

 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 20ms/step - loss: 1.4842 - accuracy: 0.3279 - val_loss: 1.0863 - val_accuracy: 0.5640
Epoch 2/15
92/92 [==============================] - 1s 12ms/step - loss: 1.1215 - accuracy: 0.5284 - val_loss: 1.0374 - val_accuracy: 0.6022
Epoch 3/15
92/92 [==============================] - 1s 12ms/step - loss: 0.9680 - accuracy: 0.6117 - val_loss: 0.9200 - val_accuracy: 0.6485
Epoch 4/15
92/92 [==============================] - 1s 12ms/step - loss: 0.8538 - accuracy: 0.6753 - val_loss: 0.9206 - val_accuracy: 0.6417
Epoch 5/15
92/92 [==============================] - 1s 12ms/step - loss: 0.7744 - accuracy: 0.6977 - val_loss: 0.8169 - val_accuracy: 0.6839
Epoch 6/15
92/92 [==============================] - 1s 13ms/step - loss: 0.7758 - accuracy: 0.7093 - val_loss: 0.7743 - val_accuracy: 0.6880
Epoch 7/15
92/92 [==============================] - 1s 13ms/step - loss: 0.6805 - accuracy: 0.7481 - val_loss: 0.8598 - val_accuracy: 0.6540
Epoch 8/15
92/92 [==============================] - 1s 13ms/step - loss: 0.7132 - accuracy: 0.7278 - val_loss: 0.7177 - val_accuracy: 0.7207
Epoch 9/15
92/92 [==============================] - 1s 13ms/step - loss: 0.6634 - accuracy: 0.7580 - val_loss: 0.7152 - val_accuracy: 0.7166
Epoch 10/15
92/92 [==============================] - 1s 13ms/step - loss: 0.6562 - accuracy: 0.7538 - val_loss: 0.7251 - val_accuracy: 0.7248
Epoch 11/15
92/92 [==============================] - 1s 13ms/step - loss: 0.5798 - accuracy: 0.7840 - val_loss: 0.7016 - val_accuracy: 0.7357
Epoch 12/15
92/92 [==============================] - 1s 13ms/step - loss: 0.5635 - accuracy: 0.7913 - val_loss: 0.7755 - val_accuracy: 0.7248
Epoch 13/15
92/92 [==============================] - 1s 13ms/step - loss: 0.5361 - accuracy: 0.7982 - val_loss: 0.7575 - val_accuracy: 0.7153
Epoch 14/15
92/92 [==============================] - 1s 13ms/step - loss: 0.5420 - accuracy: 0.8022 - val_loss: 0.7375 - val_accuracy: 0.7302
Epoch 15/15
92/92 [==============================] - 1s 12ms/step - loss: 0.5132 - accuracy: 0.8120 - val_loss: 0.7561 - val_accuracy: 0.7289

Visualisasikan hasil pelatihan

Setelah menerapkan augmentasi data dan Dropout, terjadi overfitting lebih sedikit dari sebelumnya, dan akurasi pelatihan dan validasi lebih selaras.

 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

Prediksikan data baru

Akhirnya, mari kita gunakan model kami untuk mengklasifikasikan gambar yang tidak termasuk dalam set pelatihan atau validasi.

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