본문 바로가기

Deep Learning/Vision

Image Classification < Basic To Transfer > - (1)

CNN Image Classification


출처: https://www.kdnuggets.com/2018/09/object-detection-image-classification-yolo.html

 

 Image에 대해 딥러닝이 해결할 수 있는 범주의 문제들은 여러가지가 있다. 위 그림에서 제시한 바와 같이 Classification, Object Detection의 문제들이 대표적이다. 두가지는 서로 유사하지만 논리상으로 엄연히 다른 문제이다. 사물인식은 이미지에 주어진 개체들을 인식해 어떤 개체인지 판명하는 것으로 요즘 화두가 되고있는 자율주행에 있어서 필수적인 요소이다. 단순히 이미지를 분류하는 것에서 벗어나 개체에 대한 이해의 범주로 들어가게되며 이는 'YOLO(You Look Only Once)' 계열의 수많은 논문들이 이를 대표하고있다. 반면 Classification은 여러가지 카테고리중 이미지의 범주가 어느 곳에 속하는 지를 분류하는 문제이며 CNN계열을 통해 다양한 데이터셋에 대해 실험을 거치며 어느정도 정형화된 방법론은 구축해왔다. 여기서 다루는 문제는 엄연히 Classification에 대한 문제이며 Self Model Building을 포함해 개선된 방법까지 다루고자한다. 사용하는 데이터 셋은 과거 많은 논문들에서 사용되었고 대중적으로도 친숙한 데이터 셋인 'Cats and Dogs' 이미지 데이터 셋을 사용한다. 

 


1. Load Datasets and Setting

 데이터는 언급했듯이 'Cats and Dogs' 데이터셋을 사용하며 이 중 고양이와 강아지 각각 1000장씩을 Train 셋으로 사용하며 500장씩을 Validation 셋, Test셋으로 설정하였다. Shtil Library를 사용하면 손쉽게 새로운 디렉토리 폴더들을 생성하고 그에 맞게 이미지들을 이동, 복사할 수 있다. 여기서는 Train, Validation, Test 각각 서로 다른 디렉토리를 구축했으며 이후 Generator를 사용하게된다.

import os, shutil
import matplotlib as mpl
import matplotlib.pyplot as plt
import random
import numpy as np
# Load file lists
root, dir_list, cat_images = next(os.walk(train_cats_dir))
root, dir_list, dog_images = next(os.walk(train_dogs_dir))
# Cat Images
fig, ax = plt.subplots(3,3, figsize=(20,10))

for idx, img in enumerate(random.sample(cat_images, 9)):
    img_read = plt.imread(os.path.join(train_cats_dir,img))
    ax[int(idx/3), idx%3].imshow(img_read)
    ax[int(idx/3), idx%3].axis('off')
    ax[int(idx/3), idx%3].set_title('Cat - '+img.split('.'[1]))

plt.show()

# Dogs Images
fig, ax = plt.subplots(3,3, figsize=(20,10))

for idx, img in enumerate(random.sample(dog_images, 9)):
    img_read = plt.imread(os.path.join(train_dogs_dir,img))
    ax[int(idx/3), idx%3].imshow(img_read)
    ax[int(idx/3), idx%3].axis('off')
    ax[int(idx/3), idx%3].set_title('Dog - '+img.split('.')[1])

plt.show()

Sample Images

2. Image Augumentation

 현재 학습에 사용되는 데이터 셋은 약 2000장의 이미지를 학습데이터로 사용하게된다. 딥러닝은 일반적으로 많은 데이터가 확보되어있을 때 높은 성능을 발휘하는 것으로 알려져있고 현재 사용되는 데이터는 해당 기준에 한참 못미치는 규모인 것이 분명하다. 이럴때 작은 데이터셋으로도 높은 성능을 발휘하게 도움을 줄 수 있는것이 바로 Image Augumentation을 통한 데이터 증강 방법이다. 해당 기법은 기존에 갖고있는 이미지의 direction, brightness , crop 등을 조정하여 변형된 이미지들을 추가생성해 학습시에 사용한다. 이를 쉽게 가능하도록 하는 ImageDataGenerator는 많은 인자들을 갖고있으며 학습에 큰 도움을 줄 수 있다.

import tensorflow as tf
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.preprocessing import image
# Image Augumentation
image_generator = ImageDataGenerator(
      rotation_range=40,
      width_shift_range=0.2,
      height_shift_range=0.2,
      shear_range=0.2,
      zoom_range=0.2,
      horizontal_flip=True,
      fill_mode='nearest')

- ImageDataGenerator -

  • rotation_range: 회전 범위 (degrees)
  • width_shift, height_shift: 수평 또는 수직으로 랜덤하게 평행 이동시키는 범위 (원본 가로, 세로 길이에 대한 비율 값)
  • rescale: 원본은 0-255의 RGB 계수로 구성되는데, 이를 1/255로 스케일링하여 0-1 범위로 변환. 다른 전처리 과정에 앞서 가장 먼저 적용.
  • shear_range: shearing transformation
  • zoom_range: 확대/축소 범위.
  • horizontal_flip: True로 설정할 경우, 50% 확률로 이미지를 수평으로 flip함. 뒤집어도 자연스러울 때 사용.
  • fill_mode 이미지를 회전, 이동하거나 축소할 때 생기는 공간을 채우는 방식.
# ImageDataGenerator Debugging
fig, ax = plt.subplots(2,3, figsize=(20,10))
all_images = []

_, _, dog_images = next(os.walk(train_cats_dir))
random_img = random.sample(cat_images, 1)[0]
print(random_img)
random_img = plt.imread(os.path.join(train_cats_dir,random_img))
all_images.append(random_img)

# 위에서 적용한 image augumentation을 적용
random_img = random_img.reshape((1,) + random_img.shape)
sample_augmented_images = image_generator.flow(random_img)

for _ in range(5):
    augmented_imgs = sample_augmented_images.next()
    for img in augmented_imgs:
        all_images.append(img.astype('uint8'))

for idx, img in enumerate(all_images):
    ax[int(idx/3), idx%3].imshow(img)
    ax[int(idx/3), idx%3].axis('off')
    if idx == 0:
        ax[int(idx/3), idx%3].set_title('Original Image')
    else:
        ax[int(idx/3), idx%3].set_title('Augmented Image {}'.format(idx))

plt.show()

Comparision Between Before & After

train_datagen = ImageDataGenerator(
    rescale=1./255,
    rotation_range=40,
    width_shift_range=0.2,
    height_shift_range=0.2,
    shear_range=0.2,
    zoom_range=0.2,
    horizontal_flip=True,)

# Test 셋은 증식 되어서는 안됨. Rescale만 적용.
test_datagen = ImageDataGenerator(rescale=1./255)

train_generator = train_datagen.flow_from_directory(
        train_dir,
        target_size=(150, 150),
        batch_size=32,
        class_mode='binary')

validation_generator = test_datagen.flow_from_directory(
        validation_dir,
        target_size=(150, 150),
        batch_size=32,
        class_mode='binary')

 본격적인 학습을 위해 Train과 Test, Validation셋에 적용하는 Image Augumenation 방식을 다르게 적용한다. 주의점은 학습에 있어서 데이터의 변형 및 증강은 유효하게 작용한다. 하지만 Test 또는 Validation 셋은 모델의 성능을 평가하기 위한 셋으로써 증강의 방법이 적용되어서는 안된다. Rescale을 통한 정규화만 적용시킨다. 다음은 Modeling 단계다.

3. Basic Modeling

 학습은 Colab환경에서 진행하였고 학습시간의 고속화를 위해 GPU를 사용하였다. GPU를 사용하기 위한 설정을 해주고 모델을 설계한다. 기본적인 CNN은 Convolution Layer에 적절한 kernal size, filter size, stride 등의 값을 설정해주고 pooling층을 이용해 특징적인 부분을 부각시키는 방식으로 진행된다. 사용한 모델 역시 이와 유사한 방향으로 층들을 설정했다. 4개의 Convolution과 Pooling Layer들을 통과하고 Flatten으로 1차원으로 벡터를 펼쳐준다. 최종적으로 이를 Fully Connected Layer에 접목시켜 완전 연결 분류기를 생성했다.  

출처: https://cezannec.github.io/Convolutional_Neural_Networks/

# GPU 사용시 tensorflow로 load해줘야함
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Dropout, Dense,Flatten
import warnings
warnings.filterwarnings('ignore')
# GPU 사용
device_name = tf.test.gpu_device_name()
if device_name != '/device:GPU:0':
  raise SystemError('GPU device not found')
print('Found GPU at: {}'.format(device_name))
with tf.device('/device:GPU:0'):
  model = Sequential()
  model.add(Conv2D(32, (3, 3), activation='relu', input_shape=(150, 150, 3)))
  model.add(MaxPooling2D((2, 2)))
  model.add(Conv2D(64, (3, 3), activation='relu'))
  model.add(MaxPooling2D((2, 2)))
  model.add(Conv2D(128, (3, 3), activation='relu'))
  model.add(MaxPooling2D((2, 2)))
  model.add(Conv2D(128, (3, 3), activation='relu'))
  model.add(MaxPooling2D((2, 2)))
  model.add(Flatten())
  model.add(Dropout(0.5))
  model.add(Dense(512, activation='relu'))
  model.add(Dense(1, activation='sigmoid'))

  model.compile(loss='binary_crossentropy',optimizer='adam',metrics=['acc'])
  
  history = model.fit(train_generator,steps_per_epoch=62, 
      epochs=50, validation_data=validation_generator, validation_steps=30)

...

 Epochs가 진행됨에 따라 train accuracy와 validation accuracy가 증가하는 것을 볼 수 있다. 마지막 epoch에서는 약 78%에 준하는 성능을 보여준다. 학습된 모델을 이후 사용을 위해 저장했다.

model.save('cats_and_dogs_small_2.h5')

 지금까지 이어진 프로세스는 CNN을 이용한 Image Classification에 있어서 기본적이고 정형화된 루틴을 통한 학습 방법이다. 이어지는 글에서는 이미지 문제에서 매우 높은 빈도로 사용되는 Tranfer Learning을 활용한 학습을 진행할 것이다. 전이학습은 기존에 대량의 학습데이터에서 학습한 가중치들을 포함하고있으며 이를 다른 field의 소규모 이미지셋에 적용시켰을때 사전에 학습된 좋은 가중치값들로 인해 유의미한 성능개선을 이끌어내게된다. 

'Deep Learning > Vision' 카테고리의 다른 글

AutoEncoder  (0) 2021.06.21
Image Classification < Basic To Transfer > - (3)  (0) 2021.04.05
Image Classification < Basic To Transfer > - (2)  (0) 2021.04.01