이미지 로드 및 전처리하기

TensorFlow.org에서 보기 Google Colab에서 실행하기 GitHub에서 소스 보기 노트북 다운론드하기

이 튜토리얼에서는 세 가지 방법으로 이미지 데이터세트를 로드하고 전처리하는 방법을 보여줍니다.

설정

import numpy as np
import os
import PIL
import PIL.Image
import tensorflow as tf
import tensorflow_datasets as tfds
2022-12-14 21:02:22.685311: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer.so.7'; dlerror: libnvinfer.so.7: cannot open shared object file: No such file or directory
2022-12-14 21:02:22.685432: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer_plugin.so.7'; dlerror: libnvinfer_plugin.so.7: cannot open shared object file: No such file or directory
2022-12-14 21:02:22.685442: W tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:38] TF-TRT Warning: Cannot dlopen some TensorRT libraries. If you would like to use Nvidia GPU with TensorRT, please make sure the missing libraries mentioned above are installed properly.
print(tf.__version__)
2.11.0

꽃 데이터세트 다운로드하기

이 튜토리얼은 수천 장의 꽃 사진 데이터세트를 사용합니다. 꽃 데이터세트에는 클래스당 하나씩 5개의 하위 디렉터리가 있습니다.

flowers_photos/
  daisy/
  dandelion/
  roses/
  sunflowers/
  tulips/

참고: 모든 이미지에는 CC-BY 라이선스가 있으며 크리에이터는 LICENSE.txt 파일에 나열됩니다.

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)
Downloading data from https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz
228813984/228813984 [==============================] - 1s 0us/step

다운로드한 후 (218MB), 이제 꽃 사진의 사본을 사용할 수 있습니다. 총 3670개의 이미지가 있습니다.

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

각 디렉토리에는 해당 유형의 꽃 이미지가 포함되어 있습니다. 다음은 장미입니다.

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

Keras 유틸리티를 사용하여 데이터 로드

유용한 tf.keras.utils.image_dataset_from_directory 유틸리티를 사용하여 디스크에서 이러한 이미지를 로드해 보겠습니다.

데이터세트 만들기

로더를 위해 일부 매개변수를 정의합니다.

batch_size = 32
img_height = 180
img_width = 180

모델을 개발할 때 검증 분할을 사용하는 것이 좋습니다. 이미지의 80%를 훈련에 사용하고 20%를 유효성 검사에 사용합니다.

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.

이러한 데이터세트의 class_names 속성에서 클래스 이름을 찾을 수 있습니다.

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

데이터 시각화하기

다음은 훈련 데이터세트의 처음 9개 이미지입니다.

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

이러한 데이터세트를 사용하는 모델을 model.fit(이 튜토리얼의 뒷부분에 표시)에 전달하여 모델을 훈련할 수 있습니다. 원하는 경우, 데이터세트를 수동으로 반복하고 이미지 배치를 검색할 수도 있습니다.

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

image_batch(32, 180, 180, 3) 형상의 텐서이며, 180x180x3 형상의 32개 이미지 묶음으로 되어 있습니다(마지막 차원은 색상 채널 RGB를 나타냄). label_batch는 형상 (32,)의 텐서이며 32개 이미지에 해당하는 레이블입니다.

이들 텐서 중 하나에서 .numpy()를 호출하여 numpy.ndarray로 변환할 수 있습니다.

데이터 표준화하기

RGB 채널 값은 [0, 255] 범위에 있습니다. 이것은 신경망에 이상적이지 않습니다. 일반적으로 입력 값을 작게 만들어야 합니다.

여기에서 tf.keras.layers.Rescaling을 사용하여 값을 [0, 1] 범위로 표준화합니다.

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

이 레이어를 사용하는 방법에는 두 가지가 있습니다. 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))
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.9/site-packages/tensorflow/python/autograph/pyct/static_analysis/liveness.py:83: Analyzer.lamba_check (from tensorflow.python.autograph.pyct.static_analysis.liveness) is deprecated and will be removed after 2023-09-23.
Instructions for updating:
Lambda fuctions will be no more assumed to be used in the statement where they are used, or at least in the same block. https://github.com/tensorflow/tensorflow/issues/56089
0.0 0.96902645

또는 모델 정의 내에 레이어를 포함하여 배포를 단순화할 수 있습니다. 여기서는 두 번째 접근법을 사용할 것입니다.

참고: 픽셀 값을 [-1,1]로 조정하려면 대신 tf.keras.layers.Rescaling(1./127.5, offset=-1)을 작성할 수 있습니다.

참고: 이전에 tf.keras.utils.image_dataset_from_directoryimage_size 인수를 사용하여 이미지 크기를 조정했습니다. 해당 모델에도 크기 조정 논리를 포함하려면 tf.keras.layers.Resizing 레이어를 사용할 수 있습니다.

성능을 위한 데이터세트 구성하기

버퍼링된 프리페치를 사용하여 I/O를 차단하지 않고 디스크에서 데이터를 생성할 수 있도록 하겠습니다. 데이터를 로드할 때 다음 두 가지 중요한 메서드를 사용해야 합니다.

  • Dataset.cache()는 첫 epoch 동안 디스크에서 이미지를 로드한 후 이미지를 메모리에 유지합니다. 이렇게 하면 모델을 훈련하는 동안 데이터세트가 병목 상태가 되지 않습니다. 데이터세트가 너무 커서 메모리에 맞지 않는 경우, 이 메서드를 사용하여 성능이 높은 온디스크 캐시를 생성할 수도 있습니다.
  • Dataset.prefetch는 훈련 중에 데이터 전처리 및 모델 실행과 겹칩니다.

관심 있는 독자는 tf.data API를 통한 성능 향상 가이드의 프리페치 섹션에서 두 가지 메서드와 데이터를 디스크에 캐시하는 방법에 대해 자세히 알아볼 수 있습니다.

AUTOTUNE = tf.data.AUTOTUNE

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

모델 훈련하기

완성을 위해 방금 준비한 데이터세트를 사용하여 간단한 모델을 훈련하는 방법을 보여줍니다.

순차 모델은 각각에 최대 풀링 레이어(tf.keras.layers.MaxPooling2D)가 있는 세 개의 컨볼루션 블록(tf.keras.layers.Conv2D)으로 구성됩니다. ReLU 활성화 함수('relu')에 의해 활성화되는 128개 유닛이 있는 완전 연결된 레이어(tf.keras.layers.Dense)가 있습니다. 이 모델은 어떤 식으로든 조정되지 않았습니다. 목표는 방금 만든 데이터세트를 사용하여 역학을 보여주는 것입니다. 이미지 분류에 대해 자세히 알아보려면 이미지 분류 튜토리얼을 방문하세요.

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

tf.keras.optimizers.Adam 옵티마이저 및 tf.keras.losses.SparseCategoricalCrossentropy 손실 함수를 선택합니다. 각 훈련 epoch에 대한 훈련 및 검증 정확도를 보려면 metrics 인수를 Model.compile에 전달하세요.

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

참고: 몇 개의 epoch에 대해서만 훈련하므로 이 튜토리얼은 빠르게 진행됩니다.

model.fit(
  train_ds,
  validation_data=val_ds,
  epochs=3
)
Epoch 1/3
92/92 [==============================] - 6s 22ms/step - loss: 1.2896 - accuracy: 0.4540 - val_loss: 1.0886 - val_accuracy: 0.5777
Epoch 2/3
92/92 [==============================] - 2s 18ms/step - loss: 1.0065 - accuracy: 0.6008 - val_loss: 1.0161 - val_accuracy: 0.6008
Epoch 3/3
92/92 [==============================] - 2s 18ms/step - loss: 0.8589 - accuracy: 0.6642 - val_loss: 0.9884 - val_accuracy: 0.6240
<keras.callbacks.History at 0x7f2ed079d5b0>

참고: Model.fit을 사용하는 대신 사용자 지정 훈련 루프를 작성할 수도 있습니다. 자세히 알아보려면 처음부터 훈련 루프 작성하기 튜토리얼을 방문하세요.

검증 정확도가 훈련 정확도에 비해 낮으므로 모델이 과대적합되었음을 알 수 있습니다. 이 튜토리얼에서 과대적합 및 이를 줄이는 방법에 대해 자세히 알아볼 수 있습니다.

미세 제어를 위해 tf.data 사용하기

위의 Keras 전처리 유틸리티 tf.keras.utils.image_dataset_from_directory는 이미지 디렉터리에서 tf.data.Dataset을 생성하는 편리한 방법입니다.

더 세밀한 제어를 위해 tf.data를 사용하여 고유한 입력 파이프라인을 작성할 수 있습니다. 이 섹션에서는 이전에 다운로드한 TGZ 파일의 파일 경로부터 시작하여 이를 수행하는 방법을 보여줍니다.

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/sunflowers/18237215308_a158d49f28_n.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/dandelion/9200211647_be34ce978b.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/dandelion/3518608454_c3fd3c311c_m.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/roses/5086249859_d066b37b8a_m.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/tulips/2418823693_72eec80f42_n.jpg'

파일의 트리 구조를 사용하여 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']

데이터세트를 학습 및 검증 세트로 분할합니다.

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

다음과 같이 각 데이터세트의 길이를 인쇄할 수 있습니다.

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

파일 경로를 (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

Dataset.map을 사용하여 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:  3

성능을 위한 데이터세트 구성하기

이 데이터세트로 모델을 훈련하려면 데이터에 대해 다음이 필요합니다.

  • 잘 섞는다.
  • 배치 처리한다.
  • 가능한 빨리 배치를 사용할 수 있어야 한다.

이러한 기능은 tf.data API를 사용하여 추가할 수 있습니다. 자세한 내용은 입력 파이프 라인 성능 가이드를 참조하세요.

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)

데이터 시각화하기

이 데이터세트를 이전에 작성한 데이터 세트와 유사하게 시각화할 수 있습니다.

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-12-14 21:02:42.480684: W tensorflow/core/kernels/data/cache_dataset_ops.cc:856] 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

모델 계속 훈련하기

이제 위의 tf.keras.utils.image_dataset_from_directory에 의해 생성된 것과 유사한 tf.data.Dataset을 수동으로 구축했습니다. 이를 사용하여 모델을 계속 학습할 수 있습니다. 이전과 마찬가지로 실행 시간을 짧게 유지하기 위해 몇 epoch 동안만 훈련합니다.

model.fit(
  train_ds,
  validation_data=val_ds,
  epochs=3
)
Epoch 1/3
92/92 [==============================] - 2s 21ms/step - loss: 0.7816 - accuracy: 0.7023 - val_loss: 0.7103 - val_accuracy: 0.7153
Epoch 2/3
92/92 [==============================] - 2s 18ms/step - loss: 0.6251 - accuracy: 0.7629 - val_loss: 0.7505 - val_accuracy: 0.7139
Epoch 3/3
92/92 [==============================] - 2s 18ms/step - loss: 0.4344 - accuracy: 0.8423 - val_loss: 0.8368 - val_accuracy: 0.7112
<keras.callbacks.History at 0x7f2f70ce89a0>

TensorFlow 데이터세트 사용하기

지금까지 이 튜토리얼은 디스크에서 데이터를 로드하는 데 중점을 두었습니다. TensorFlow Datasets에서 다운로드하기 쉬운 대규모 데이터세트 카탈로그를 탐색하여 사용할 데이터세트를 찾을 수도 있습니다.

이전에 디스크에서 꽃 데이터세트를 로드했으므로 이제 TensorFlow 데이터세트로 가져오겠습니다.

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

꽃 데이터세트에는 5가지 클래스가 있습니다.

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

데이터세트에서 이미지를 검색합니다.

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

image, label = next(iter(train_ds))
_ = plt.imshow(image)
_ = plt.title(get_label_name(label))
2022-12-14 21:02:50.993018: W tensorflow/core/kernels/data/cache_dataset_ops.cc:856] 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

이전과 마찬가지로 성능을 위해 훈련, 검증 및 테스트 세트를 일괄 처리, 셔플 및 구성해야 합니다.

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

데이터 강화 가이드를 방문하여 꽃 데이터세트 및 TensorFlow 데이터세트를 처리하는 전체 예제를 찾을 수 있습니다.

다음 단계

이 튜토리얼에서는 디스크에서 이미지를 로드하는 두 가지 방법을 보여주었습니다. 먼저 Keras 사전 처리 레이어 및 유틸리티를 사용하여 이미지 데이터세트를 로드하고 사전 처리하는 방법을 배웠습니다. 다음으로 tf.data를 사용하여 처음부터 입력 파이프라인을 작성하는 방법을 배웠습니다. 마지막으로 TensorFlow Datasets에서 데이터세트를 다운로드하는 방법을 배웠습니다.

다음 단계: