Catat tanggalnya! Google I / O mengembalikan 18-20 Mei Daftar sekarang
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 bagaimana mengklasifikasikan gambar bunga. Ini membuat pengklasifikasi gambar menggunakan model keras.Sequential , dan memuat data menggunakan preprocessing.image_dataset_from_directory . Anda akan mendapatkan pengalaman praktis dengan konsep-konsep berikut:

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

Tutorial ini mengikuti alur kerja pembelajaran mesin dasar:

  1. Memeriksa dan memahami data
  2. Bangun pipeline masukan
  3. Bangun modelnya
  4. Latih modelnya
  5. Uji modelnya
  6. Perbaiki model dan ulangi prosesnya

Impor TensorFlow dan pustaka 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 kumpulan data

Tutorial ini menggunakan kumpulan data 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 [==============================] - 2s 0us/step

Setelah mengunduh, Anda sekarang memiliki salinan set data yang tersedia. Ada total 3.670 gambar:

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

Berikut 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

Muat dengan menggunakan keras.preprocessing

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

Buat set data

Tentukan beberapa parameter untuk loader:

batch_size = 32
img_height = 180
img_width = 180

Praktik yang baik untuk menggunakan pemisahan validasi saat mengembangkan model Anda. Mari gunakan 80% 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 dalam atribut class_names pada class_names 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 datanya

Berikut adalah 9 gambar pertama dari set data 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 kumpulan data ini dengan meneruskannya ke model.fit sebentar lagi. Jika mau, Anda juga dapat melakukan iterasi secara manual atas kumpulan 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 dengan 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 .

Konfigurasi set data untuk kinerja

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

Dataset.cache() menyimpan gambar dalam memori setelah dimuat dari disk selama periode pertama. Ini akan memastikan set data tidak menjadi hambatan saat melatih model Anda. Jika kumpulan data Anda terlalu besar untuk dimasukkan ke dalam memori, Anda juga dapat menggunakan metode ini untuk membuat cache pada disk yang berkinerja baik.

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

Pembaca yang tertarik dapat mempelajari lebih lanjut tentang kedua metode tersebut, serta cara menyimpan data ke dalam cache ke disk di panduan kinerja 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)

Standarisasi data

Nilai saluran RGB berada dalam kisaran [0, 255] . Ini tidak ideal untuk jaringan saraf; secara umum Anda harus berusaha membuat nilai masukan Anda kecil. Di sini, Anda akan menstandarisasi nilai berada dalam kisaran [0, 1] dengan menggunakan lapisan Rescaling.

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

Ada dua cara untuk menggunakan lapisan ini. Anda dapat menerapkannya ke dataset dengan memanggil 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 0.9407408

Atau, Anda dapat menyertakan lapisan di dalam definisi model Anda, yang dapat menyederhanakan penerapan. Mari gunakan pendekatan kedua di sini.

Buat modelnya

Model ini terdiri dari tiga blok konvolusi dengan masing-masing lapisan kolam maksimum. 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 modelnya

Untuk tutorial ini, pilih fungsi optimizers.Adam pengoptimal dan losses.SparseCategoricalCrossentropy loss. Untuk melihat pelatihan dan akurasi validasi untuk setiap periode pelatihan, teruskan 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 [==============================] - 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

Visualisasikan hasil pelatihan

Buat plot kerugian 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 Anda lihat dari plot, akurasi pelatihan dan akurasi validasi sangat jauh dan model hanya mencapai akurasi sekitar 60% pada set validasi.

Mari kita lihat apa yang salah dan coba tingkatkan performa model secara keseluruhan.

Overfitting

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

Jika ada sejumlah kecil contoh pelatihan, terkadang model belajar dari suara bising atau detail yang tidak diinginkan dari contoh pelatihan — sejauh hal itu berdampak negatif pada performa model pada contoh baru. Fenomena ini disebut overfitting. Artinya, model akan kesulitan menggeneralisasi kumpulan data 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 jika ada sejumlah kecil contoh pelatihan. Augmentasi data mengambil pendekatan untuk menghasilkan data pelatihan tambahan dari contoh yang ada dengan menambahkannya menggunakan transformasi acak yang menghasilkan gambar yang tampak meyakinkan. Ini membantu memaparkan model ke lebih banyak aspek data dan menggeneralisasi dengan lebih baik.

Anda akan mengimplementasikan augmentasi data menggunakan lapisan dari tf.keras.layers.experimental.preprocessing . Ini dapat dimasukkan ke dalam model Anda seperti lapisan 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 visualisasikan seperti apa tampilan beberapa contoh yang ditambah 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

Anda akan menggunakan augmentasi data untuk melatih model dalam sekejap.

Keluar

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

Saat Anda menerapkan Pelepasan ke sebuah lapisan, Pelepasan (dropout) secara acak (dengan menyetel aktivasi ke nol) sejumlah unit keluaran dari lapisan selama proses pelatihan. Putus sekolah mengambil bilangan pecahan sebagai nilai masukannya, dalam bentuk seperti 0,1, 0,2, 0,4, dll. Ini berarti melepaskan 10%, 20% atau 40% unit keluaran secara acak dari lapisan yang diterapkan.

Mari buat jaringan neural baru menggunakan layers.Dropout , lalu latih menggunakan gambar yang diperbesar.

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

Kumpulkan 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 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

Visualisasikan hasil pelatihan

Setelah menerapkan augmentasi data dan Pelepasan, terjadi overfitting yang lebih sedikit dibandingkan sebelumnya, dan akurasi pelatihan dan validasi semakin 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

Memprediksi data baru

Terakhir, mari gunakan model kita untuk mengklasifikasikan gambar yang tidak disertakan 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 97.38 percent confidence.