Memuat Gambar

Lihat di TensorFlow.org Jalankan di Google Colab Lihat source di GitHub Unduh notebook

Tutorial ini menunjukkan sebuah contoh sederhana bagaimana cara memuat dataset gambar menggunakan tf.data.

Dataset yang digunakan dalam contoh ini terdistribusi sebagai kumpulan direktori dari gambar, dengan satu kelas gambar setiap direktori.

Pengaturan

from __future__ import absolute_import, division, print_function, unicode_literals
try:
  # %tensorflow_version hanya tersedia di Colab.
  !pip install -q tf-nightly
except Exception:
  pass
import tensorflow as tf
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.

AUTOTUNE = tf.data.experimental.AUTOTUNE
import IPython.display as display
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
import os
tf.__version__
'2.4.0-dev20200922'

Memperoleh gambar

Sebelum Anda memulai proses training, Anda membutuhkan sebuah set gambar untuk melatih neural network tentang kelas-kelas baru yang ingin Anda kenali. Anda dapat menggunakan arsip dari foto bunga berlisensi dari Google.

Catatan: semua gambar terlisensi CC-BY, para pembuat terdaftar di file LICENSE.txt.

import pathlib
data_dir = tf.keras.utils.get_file(origin='https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz',
                                         fname='flower_photos', 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 [==============================] - 3s 0us/step

Setelah mengunduh (218MB), Anda sekarang memiliki salinan gambar-gambar bunga yang tersedia.

Direktori tersebut terdiri atas 5 subdirektori, satu setiap kelas:

image_count = len(list(data_dir.glob('*/*.jpg')))
image_count
3670
CLASS_NAMES = np.array([item.name for item in data_dir.glob('*') if item.name != "LICENSE.txt"])
CLASS_NAMES
array(['sunflowers', 'daisy', 'roses', 'tulips', 'dandelion'],
      dtype='<U10')

Setiap direktori terdiri atas gambar-gambar dari tipe bunga. Ini adalah gambar beberapa mawar:

roses = list(data_dir.glob('roses/*'))

for image_path in roses[:3]:
    display.display(Image.open(str(image_path)))

png

png

png

Memuat menggunakan keras.preprocessing

Cara sederhana untuk memuat gambar adalah dengan menggunakan tf.keras.preprocessing.

# 1./255 dilakukan untuk mengubah dari uint8 menjadi float32 dalam rentang [0,1].
image_generator = tf.keras.preprocessing.image.ImageDataGenerator(rescale=1./255)

Mendefinisikan beberapa parameter untuk loader:

BATCH_SIZE = 32
IMG_HEIGHT = 224
IMG_WIDTH = 224
STEPS_PER_EPOCH = np.ceil(image_count/BATCH_SIZE)
train_data_gen = image_generator.flow_from_directory(directory=str(data_dir),
                                                     batch_size=BATCH_SIZE,
                                                     shuffle=True,
                                                     target_size=(IMG_HEIGHT, IMG_WIDTH),
                                                     classes = list(CLASS_NAMES))
Found 3670 images belonging to 5 classes.

Pengamatan sebuah batch:

def show_batch(image_batch, label_batch):
  plt.figure(figsize=(10,10))
  for n in range(25):
      ax = plt.subplot(5,5,n+1)
      plt.imshow(image_batch[n])
      plt.title(CLASS_NAMES[label_batch[n]==1][0].title())
      plt.axis('off')
image_batch, label_batch = next(train_data_gen)
show_batch(image_batch, label_batch)

png

Memuat menggunakan tf.data

The above keras.preprocessing method is convienient, but has two downsides: Metode keras.preprocessing di atas sangat mudah digunakan, tetapi memiliki beberapa kekurangan:

  1. Metode ini lambat. Lihat bagian performa di bawah.
  2. Kurang kontrol yang lebih detail.
  3. Metode tersebut tidak terintegrasi secara sempurna dengan keseluruhan TensorFlow.

Untuk memuat file sebagai sebuah tf.data.Dataset pertama-tama buat sebuah dataset dari path file:

list_ds = tf.data.Dataset.list_files(str(data_dir/'*/*'))
for f in list_ds.take(5):
  print(f.numpy())
b'/home/kbuilder/.keras/datasets/flower_photos/dandelion/10294487385_92a0676c7d_m.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/dandelion/163702807_e508544acd_n.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/dandelion/854593001_c57939125f_n.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/roses/4553266758_09d4dbdac9_n.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/dandelion/10437652486_aa86c14985.jpg'

Tulis sebuah fungsi pendek tensorflow yang mengubah sebuah path file menjadi pasangan (image_data, label):

def get_label(file_path):
  # ubah path menjadi list komponen path
  parts = tf.strings.split(file_path, os.path.sep)
  # dua dari akhir adalah direktori kelas
  return parts[-2] == CLASS_NAMES
def decode_img(img):
  # ubah string terkompresi menjadi tensor uint8 3D
  img = tf.image.decode_jpeg(img, channels=3)
  # Gunakan `convert_image_dtype` untuk mengkonversi menjadi float dengan range [0,1].
  img = tf.image.convert_image_dtype(img, tf.float32)
  # Atur ukuran image menjadi ukuran yang diinginkan
  return tf.image.resize(img, [IMG_WIDTH, IMG_HEIGHT])
def process_path(file_path):
  label = get_label(file_path)
  # Muat raw data dari file sebagai sebuah string
  img = tf.io.read_file(file_path)
  img = decode_img(img)
  return img, label

Gunakan Dataset.map untuk membuat sebuah dataset dari pasangan image, label

# Set `num_parallel_calls` sehingga beberapa gambar dimuat/diproses secara paralel
labeled_ds = list_ds.map(process_path, num_parallel_calls=AUTOTUNE)
for image, label in labeled_ds.take(1):
  print("Image shape: ", image.numpy().shape)
  print("Label: ", label.numpy())
Image shape:  (224, 224, 3)
Label:  [False False False  True False]

Metode dasar untuk proses training

To train a model with this dataset you will want the data: Untuk melatih sebuah model dengan dataset ini, Anda ingin data:

  • Ter-shuffle dengan baik.
  • Dalam batch.
  • Batch-batch tersedia secepatnya.

Fitur-fitur ini dapat dengan mudah ditambahkan menggunakan api tf.data

def prepare_for_training(ds, cache=True, shuffle_buffer_size=1000):
  # Dataset ini berukuran kecil, muat dataset sekali, dan simpan dalam memori.
  # Gunakan `.cache(filename)` untuk melakukan proses *cache preprocessing* dataset yang tidak 
  # muat di memori.
  if cache:
    if isinstance(cache, str):
      ds = ds.cache(cache)
    else:
      ds = ds.cache()

  ds = ds.shuffle(buffer_size=shuffle_buffer_size)

  # ulangi selamanya
  ds = ds.repeat()

  ds = ds.batch(BATCH_SIZE)

  # `prefetch` memungkinan dataset mengambil batch pada background ketika model
  # ditraining
    
  ds = ds.prefetch(buffer_size=AUTOTUNE)

  return ds
train_ds = prepare_for_training(labeled_ds)

image_batch, label_batch = next(iter(train_ds))
show_batch(image_batch.numpy(), label_batch.numpy())

png

Performa

Catatan: Bagian ini hanya menunjukkan beberapa trik mudah yang mungkin dapat membantu performa. Untuk petunjuk yang lebih dalam lihat Inputasi Performa Pipeline.

Untuk pengamatan, pertama-tama buat sebuah fungsi untuk mengecek performa dari dataset kita:

import time
default_timeit_steps = 1000

def timeit(ds, steps=default_timeit_steps):
  start = time.time()
  it = iter(ds)
  for i in range(steps):
    batch = next(it)
    if i%10 == 0:
      print('.',end='')
  print()
  end = time.time()

  duration = end-start
  print("{} batches: {} s".format(steps, duration))
  print("{:0.5f} Images/s".format(BATCH_SIZE*steps/duration))

Mari kita bandingkan kecepatan dari dua generator data:

# `keras.preprocessing`
timeit(train_data_gen)
....................................................................................................
1000 batches: 81.1426727771759 s
394.36709 Images/s

# `tf.data`
timeit(train_ds)
....................................................................................................
1000 batches: 5.450112819671631 s
5871.43809 Images/s

Sebagian besar perolehan performa datang dari penggunaan .cache.

uncached_ds = prepare_for_training(labeled_ds, cache=False)
timeit(uncached_ds)
....................................................................................................
1000 batches: 20.65564799308777 s
1549.21308 Images/s

Apabila dataset tidak muat dalam memori gunakan sebuah file cache untuk mendapatkan beberapa manfaat:

filecache_ds = prepare_for_training(labeled_ds, cache="./flowers.tfcache")
timeit(filecache_ds)
....................................................................................................
1000 batches: 14.96683931350708 s
2138.05997 Images/s