Muat dan praproses gambar

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

Tutorial ini menunjukkan cara memuat dan melakukan praproses dataset gambar dalam tiga cara:

Mempersiapkan

import numpy as np
import os
import PIL
import PIL.Image
import tensorflow as tf
import tensorflow_datasets as tfds
print(tf.__version__)
2.8.0-rc1

Unduh kumpulan data bunga

Tutorial ini menggunakan kumpulan data beberapa ribu foto bunga. Dataset bunga berisi lima sub-direktori, satu per kelas:

flowers_photos/
  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(origin=dataset_url,
                                   fname='flower_photos',
                                   untar=True)
data_dir = pathlib.Path(data_dir)

Setelah mengunduh (218MB), Anda sekarang harus memiliki salinan foto bunga yang tersedia. Ada 3.670 total gambar:

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

Setiap direktori berisi gambar dari jenis bunga tersebut. Berikut beberapa bunga mawar:

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

png

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

png

Muat data menggunakan utilitas Keras

Mari muat gambar-gambar ini dari disk menggunakan utilitas tf.keras.utils.image_dataset_from_directory yang bermanfaat.

Buat kumpulan data

Tentukan beberapa parameter untuk loader:

batch_size = 32
img_height = 180
img_width = 180

Ini adalah praktik yang baik untuk menggunakan pemisahan validasi saat mengembangkan model Anda. Anda akan menggunakan 80% gambar untuk pelatihan dan 20% untuk validasi.

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.

Anda dapat menemukan nama kelas di atribut class_names pada kumpulan data ini.

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

Visualisasikan datanya

Berikut adalah sembilan 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 dapat melatih model menggunakan kumpulan data ini dengan meneruskannya ke model.fit (ditampilkan nanti dalam tutorial ini). Jika Anda suka, Anda juga dapat secara manual mengulangi 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 dari bentuk (32, 180, 180, 3) . Ini adalah kumpulan 32 gambar dengan bentuk 180x180x3 (dimensi terakhir mengacu pada saluran warna RGB). label_batch adalah tensor dari bentuk (32,) , ini adalah label yang sesuai dengan 32 gambar.

Anda dapat memanggil .numpy() pada salah satu dari tensor ini untuk mengonversinya menjadi numpy.ndarray .

Standarisasi data

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, Anda akan menstandardisasi nilai agar berada dalam kisaran [0, 1] dengan menggunakan tf.keras.layers.Rescaling :

normalization_layer = tf.keras.layers.Rescaling(1./255)

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

Atau, Anda dapat menyertakan lapisan di dalam definisi model Anda untuk menyederhanakan penerapan. Anda akan menggunakan pendekatan kedua di sini.

Konfigurasikan kumpulan data untuk kinerja

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

  • Dataset.cache menyimpan gambar dalam memori setelah mereka dimuat dari disk selama epoch pertama. Ini akan memastikan kumpulan 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 di disk yang berkinerja baik.
  • Dataset.prefetch tumpang tindih dengan prapemrosesan data dan eksekusi model saat pelatihan.

Pembaca yang tertarik dapat mempelajari lebih lanjut tentang kedua metode tersebut, serta cara menyimpan data ke disk di bagian Prefetching pada Performa yang lebih baik dengan panduan API tf.data .

AUTOTUNE = tf.data.AUTOTUNE

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

Latih seorang model

Untuk kelengkapan, Anda akan menunjukkan cara melatih model sederhana menggunakan kumpulan data yang baru saja Anda siapkan.

Model Sequential terdiri dari tiga blok konvolusi ( tf.keras.layers.Conv2D ) dengan lapisan pooling maks ( tf.keras.layers.MaxPooling2D ) di masing-masing blok. Ada lapisan yang sepenuhnya terhubung ( tf.keras.layers.Dense ) dengan 128 unit di atasnya yang diaktifkan oleh fungsi aktivasi ReLU ( 'relu' ). Model ini belum disetel dengan cara apa pun—tujuannya adalah untuk menunjukkan kepada Anda mekanisme menggunakan kumpulan data yang baru saja Anda buat. Untuk mempelajari lebih lanjut tentang klasifikasi gambar, kunjungi Tutorial klasifikasi gambar .

num_classes = 5

model = tf.keras.Sequential([
  tf.keras.layers.Rescaling(1./255),
  tf.keras.layers.Conv2D(32, 3, activation='relu'),
  tf.keras.layers.MaxPooling2D(),
  tf.keras.layers.Conv2D(32, 3, activation='relu'),
  tf.keras.layers.MaxPooling2D(),
  tf.keras.layers.Conv2D(32, 3, activation='relu'),
  tf.keras.layers.MaxPooling2D(),
  tf.keras.layers.Flatten(),
  tf.keras.layers.Dense(128, activation='relu'),
  tf.keras.layers.Dense(num_classes)
])

Pilih fungsi kehilangan tf.keras.optimizers.Adam dan tf.keras.losses.SparseCategoricalCrossentropy . Untuk melihat akurasi pelatihan dan validasi untuk setiap periode pelatihan, teruskan argumen metrics ke Model.compile .

model.compile(
  optimizer='adam',
  loss=tf.losses.SparseCategoricalCrossentropy(from_logits=True),
  metrics=['accuracy'])
model.fit(
  train_ds,
  validation_data=val_ds,
  epochs=3
)
Epoch 1/3
92/92 [==============================] - 4s 21ms/step - loss: 1.3091 - accuracy: 0.4281 - val_loss: 1.0982 - val_accuracy: 0.5599
Epoch 2/3
92/92 [==============================] - 1s 12ms/step - loss: 1.0196 - accuracy: 0.5879 - val_loss: 0.9572 - val_accuracy: 0.6213
Epoch 3/3
92/92 [==============================] - 1s 12ms/step - loss: 0.8455 - accuracy: 0.6775 - val_loss: 0.8839 - val_accuracy: 0.6512
<keras.callbacks.History at 0x7ff10c168850>

Anda mungkin melihat akurasi validasi rendah dibandingkan dengan akurasi pelatihan, yang menunjukkan model Anda terlalu pas. Anda dapat mempelajari lebih lanjut tentang overfitting dan cara menguranginya dalam tutorial ini.

Menggunakan tf.data untuk kontrol yang lebih baik

Utilitas prapemrosesan Keras di tf.keras.utils.image_dataset_from_directory —adalah cara mudah untuk membuat tf.data.Dataset dari direktori gambar.

Untuk kontrol butir yang lebih halus, Anda dapat menulis saluran input Anda sendiri menggunakan tf.data . Bagian ini menunjukkan bagaimana melakukan hal itu, dimulai dengan jalur file dari file TGZ yang Anda unduh sebelumnya.

list_ds = tf.data.Dataset.list_files(str(data_dir/'*/*'), shuffle=False)
list_ds = list_ds.shuffle(image_count, reshuffle_each_iteration=False)
for f in list_ds.take(5):
  print(f.numpy())
b'/home/kbuilder/.keras/datasets/flower_photos/roses/14267691818_301aceda07.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/daisy/2641151167_3bf1349606_m.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/sunflowers/6495554833_86eb8faa8e_n.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/tulips/4578030672_e6aefd45af.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/dandelion/144686365_d7e96941ee_n.jpg'

Struktur pohon file dapat digunakan untuk mengkompilasi daftar class_names .

class_names = np.array(sorted([item.name for item in data_dir.glob('*') if item.name != "LICENSE.txt"]))
print(class_names)
['daisy' 'dandelion' 'roses' 'sunflowers' 'tulips']

Pisahkan set data menjadi set pelatihan dan validasi:

val_size = int(image_count * 0.2)
train_ds = list_ds.skip(val_size)
val_ds = list_ds.take(val_size)

Anda dapat mencetak panjang setiap kumpulan data sebagai berikut:

print(tf.data.experimental.cardinality(train_ds).numpy())
print(tf.data.experimental.cardinality(val_ds).numpy())
2936
734

Tulis fungsi singkat yang mengubah jalur file menjadi pasangan (img, label) :

def get_label(file_path):
  # Convert the path to a list of path components
  parts = tf.strings.split(file_path, os.path.sep)
  # The second to last is the class-directory
  one_hot = parts[-2] == class_names
  # Integer encode the label
  return tf.argmax(one_hot)
def decode_img(img):
  # Convert the compressed string to a 3D uint8 tensor
  img = tf.io.decode_jpeg(img, channels=3)
  # Resize the image to the desired size
  return tf.image.resize(img, [img_height, img_width])
def process_path(file_path):
  label = get_label(file_path)
  # Load the raw data from the file as a string
  img = tf.io.read_file(file_path)
  img = decode_img(img)
  return img, label

Gunakan Dataset.map untuk membuat kumpulan data image, label :

# Set `num_parallel_calls` so multiple images are loaded/processed in parallel.
train_ds = train_ds.map(process_path, num_parallel_calls=AUTOTUNE)
val_ds = val_ds.map(process_path, num_parallel_calls=AUTOTUNE)
for image, label in train_ds.take(1):
  print("Image shape: ", image.numpy().shape)
  print("Label: ", label.numpy())
Image shape:  (180, 180, 3)
Label:  1

Konfigurasikan kumpulan data untuk kinerja

Untuk melatih model dengan kumpulan data ini, Anda memerlukan data:

  • Untuk dikocok dengan baik.
  • Untuk di-batch.
  • Batch akan tersedia sesegera mungkin.

Fitur-fitur ini dapat ditambahkan menggunakan tf.data API. Untuk detail selengkapnya, kunjungi panduan Kinerja Saluran Pipa Input .

def configure_for_performance(ds):
  ds = ds.cache()
  ds = ds.shuffle(buffer_size=1000)
  ds = ds.batch(batch_size)
  ds = ds.prefetch(buffer_size=AUTOTUNE)
  return ds

train_ds = configure_for_performance(train_ds)
val_ds = configure_for_performance(val_ds)

Visualisasikan datanya

Anda dapat memvisualisasikan kumpulan data ini mirip dengan yang Anda buat sebelumnya:

image_batch, label_batch = next(iter(train_ds))

plt.figure(figsize=(10, 10))
for i in range(9):
  ax = plt.subplot(3, 3, i + 1)
  plt.imshow(image_batch[i].numpy().astype("uint8"))
  label = label_batch[i]
  plt.title(class_names[label])
  plt.axis("off")
2022-01-26 06:29:45.209901: W tensorflow/core/kernels/data/cache_dataset_ops.cc:768] The calling iterator did not fully read the dataset being cached. In order to avoid unexpected truncation of the dataset, the partially cached contents of the dataset  will be discarded. This can happen if you have an input pipeline similar to `dataset.cache().take(k).repeat()`. You should use `dataset.take(k).cache().repeat()` instead.

png

Lanjutkan melatih model

Anda sekarang telah membuat tf.data.Dataset serupa secara manual dengan yang dibuat oleh tf.keras.utils.image_dataset_from_directory di atas. Anda dapat melanjutkan melatih model dengannya. Seperti sebelumnya, Anda akan berlatih hanya beberapa epoch untuk mempersingkat waktu lari.

model.fit(
  train_ds,
  validation_data=val_ds,
  epochs=3
)
Epoch 1/3
92/92 [==============================] - 3s 21ms/step - loss: 0.7305 - accuracy: 0.7245 - val_loss: 0.7311 - val_accuracy: 0.7139
Epoch 2/3
92/92 [==============================] - 1s 13ms/step - loss: 0.5279 - accuracy: 0.8069 - val_loss: 0.7021 - val_accuracy: 0.7316
Epoch 3/3
92/92 [==============================] - 1s 13ms/step - loss: 0.3739 - accuracy: 0.8644 - val_loss: 0.8266 - val_accuracy: 0.6948
<keras.callbacks.History at 0x7ff0ee071f10>

Menggunakan Kumpulan Data TensorFlow

Sejauh ini, tutorial ini berfokus pada memuat data dari disk. Anda juga dapat menemukan kumpulan data untuk digunakan dengan menjelajahi katalog besar kumpulan data yang mudah diunduh di TensorFlow Datasets .

Karena sebelumnya Anda telah memuat set data Flowers dari disk, sekarang mari impor dengan TensorFlow Datasets.

Unduh kumpulan data Bunga menggunakan Kumpulan Data TensorFlow:

(train_ds, val_ds, test_ds), metadata = tfds.load(
    'tf_flowers',
    split=['train[:80%]', 'train[80%:90%]', 'train[90%:]'],
    with_info=True,
    as_supervised=True,
)

Dataset bunga memiliki lima kelas:

num_classes = metadata.features['label'].num_classes
print(num_classes)
5

Ambil gambar dari kumpulan data:

get_label_name = metadata.features['label'].int2str

image, label = next(iter(train_ds))
_ = plt.imshow(image)
_ = plt.title(get_label_name(label))
2022-01-26 06:29:54.281352: W tensorflow/core/kernels/data/cache_dataset_ops.cc:768] The calling iterator did not fully read the dataset being cached. In order to avoid unexpected truncation of the dataset, the partially cached contents of the dataset  will be discarded. This can happen if you have an input pipeline similar to `dataset.cache().take(k).repeat()`. You should use `dataset.take(k).cache().repeat()` instead.

png

Seperti sebelumnya, ingatlah untuk mengelompokkan, mengacak, dan mengonfigurasi set pelatihan, validasi, dan pengujian untuk kinerja:

train_ds = configure_for_performance(train_ds)
val_ds = configure_for_performance(val_ds)
test_ds = configure_for_performance(test_ds)

Anda dapat menemukan contoh lengkap bekerja dengan kumpulan data Flowers dan TensorFlow Dataset dengan mengunjungi tutorial augmentasi Data .

Langkah selanjutnya

Tutorial ini menunjukkan dua cara memuat gambar dari disk. Pertama, Anda mempelajari cara memuat dan melakukan praproses kumpulan data gambar menggunakan lapisan dan utilitas prapemrosesan Keras. Selanjutnya, Anda mempelajari cara menulis saluran input dari awal menggunakan tf.data . Terakhir, Anda telah mempelajari cara mendownload set data dari TensorFlow Datasets.

Untuk langkah Anda selanjutnya: