본문 바로가기

데이터 다루기/Vision (Tensorflow)

[Vision] CNN을 통한 이미지 분류

728x90
반응형

1. 사용할 패키지 불러오기.

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

import를 활용하여 사용할 패키지를 불어왔습니다.

2. 데이터 불러오기.

이번 실습에서 사용할 데이터는 꽃 사진입니다.

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)

해당 데이터셋은 총 3,670개의 꽃 이미지가 있습니다.

꽃으로는 'daisy', 'dandelion', 'roses', 'sunflowers', 'tulips'가 있습니다.

3. 데이터 전처리

데이터 분석 프로세스에 있어서 모델링 전에 반드시 진행해야 하는 절차가 있습니다.

바로 데이터 전처리입니다.

batch_size = 32
img_height = 180
img_width = 180

데이터 전처리에 앞서, Batch 사이즈와 이미지의 가로 세로 길이를 지정해주었습니다.

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

그리고 데이터셋을 훈련 데이터셋과 검증 데이터셋으로 분할하였습니다. (8:2)

4. 데이터 시각화

실제로 한 번 꽃 이미지 사진들을 시각화 해보겠습니다.

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

실제로 꽃 사진들이 아름답게 잘 찍혀있네요!!

저희는 이러한 꽃 이미지만을 보고 어떤 꽃 종류인지를 맞추는 것이 목표입니다.

5. 데이터 셋 사전 처리

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)

저희는 메모리의 효율적인 사용을 위해서 다음과 같은 두 가지 작업을 수행하였습니다.

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

Dataset.prefetch()는 훈련 중에 데이터 전처리 및 모델 실행과 겹칩니다.

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

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

또한 신경망 모델은 Input을 0~1 사이로 scaling 해주어야 합니다.

map 함수를 사용해서, 모든 pixel에 대하여 0~1 사이로 Rescaling 해주었습니다.

실제로 pixel은 최대로 255값을 가지기 때문에 255로 나누어준 것 입니다.

6. 모델 생성

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

Convolutional layer는 keras.layers의 Conv2D를 통해 쌓을 수 있습니다.

첫 번째 인자는, channel의 수를 의미하고, 두 번째는 필터의 사이즈를 의미합니다.

padding = 'same'은 이미지의 크기를 보존하도록 zero padding을 하겠다는 의미입니다.

Maxpooling2D는 말 그대로 Maxpooling을 수행하는 레이어입니다.

3개의 Convolutional layer와 Maxpooling layer를 지난 후, Flatten layer를 통해 feature를 풀어줍니다.

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

옵티마이저는 Adam을 사용하고, Loss는 SparseCategoricalCrossentropy를 사용합니다.

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
_________________________________________________________________

Summary를 통해 CNN 네트워크를 볼 수 있습니다.

7. 모델 훈련

epochs=10
history = model.fit(
  train_ds,
  validation_data=val_ds,
  epochs=epochs
)

Epoch 1/10
92/92 [==============================] - 5s 33ms/step - loss: 1.6419 - accuracy: 0.3327 - val_loss: 1.1735 - val_accuracy: 0.4946
Epoch 2/10
92/92 [==============================] - 1s 13ms/step - loss: 1.0534 - accuracy: 0.5796 - val_loss: 0.9537 - val_accuracy: 0.6158
Epoch 3/10
92/92 [==============================] - 1s 13ms/step - loss: 0.8391 - accuracy: 0.6721 - val_loss: 1.1106 - val_accuracy: 0.5954
Epoch 4/10
92/92 [==============================] - 1s 13ms/step - loss: 0.6115 - accuracy: 0.7686 - val_loss: 0.9626 - val_accuracy: 0.6485
Epoch 5/10
92/92 [==============================] - 1s 13ms/step - loss: 0.3815 - accuracy: 0.8720 - val_loss: 1.2001 - val_accuracy: 0.6281
Epoch 6/10
92/92 [==============================] - 1s 13ms/step - loss: 0.2186 - accuracy: 0.9286 - val_loss: 1.4116 - val_accuracy: 0.6322
Epoch 7/10
92/92 [==============================] - 1s 13ms/step - loss: 0.1143 - accuracy: 0.9669 - val_loss: 1.4109 - val_accuracy: 0.5559
Epoch 8/10
92/92 [==============================] - 1s 13ms/step - loss: 0.1500 - accuracy: 0.9593 - val_loss: 1.6583 - val_accuracy: 0.6253
Epoch 9/10
92/92 [==============================] - 1s 13ms/step - loss: 0.0409 - accuracy: 0.9901 - val_loss: 1.7982 - val_accuracy: 0.6294
Epoch 10/10
92/92 [==============================] - 1s 13ms/step - loss: 0.0254 - accuracy: 0.9953 - val_loss: 2.1679 - val_accuracy: 0.5967

fit 함수를 통해 모델을 Train 데이터셋으로 학습시킬 수 있습니다.

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

훈련 결과를 위와 같이 시각화 할 수 있습니다.

반응형