본문 바로가기
Visual Intelligence/Image Classification

[Image Classification] EfficientNet (cats-and-dogs)

by goatlab 2022. 9. 14.
728x90
반응형
SMALL

Dog&Cat 데이터

 

# 데이터 불러오기 : Cats vs Dogs dataset
!curl -O https://download.microsoft.com/download/3/E/1/3E1C3F21-ECDB-4869-8368-6DEBA77B919F/kagglecatsanddogs_5340.zip
import os
import shutil
import zipfile

ROOT_DIR = '/content'

DATA_ROOT_DIR = os.path.join(ROOT_DIR, 'catsanddogs')

with zipfile.ZipFile(os.path.join(ROOT_DIR, 'kagglecatsanddogs_5340.zip'), 'r') as target_file:
    target_file.extractall(DATA_ROOT_DIR)

 

EfficientNet

 

import tensorflow as tf
from tensorflow.keras import Input
from tensorflow.keras.applications import EfficientNetB0
from tensorflow.keras.applications.efficientnet import preprocess_input, decode_predictions
from tensorflow.keras.preprocessing.image import load_img, img_to_array, array_to_img, ImageDataGenerator
from tensorflow.keras.layers import Activation, Dropout, Flatten, Dense, Lambda, GlobalAveragePooling2D
from tensorflow.keras.models import Model
from tensorflow.keras import optimizers
from tensorflow.keras.utils import to_categorical
import glob
import sys
import numpy as np
from skimage.io import imread
import matplotlib.pyplot as plt
from IPython.display import Image

batch_size = 48
width = 150
height = 150
epochs = 40
NUM_TRAIN = 2000
NUM_TEST = 1000
dropout_rate = 0.2
input_shape = (height, width, 3)

# 실제 데이터가 존재하는 패스
original_dataset_dir = DATA_ROOT_DIR + '/PetImages'

cat_images = glob.glob(os.path.join(original_dataset_dir, "Cat", '*.jpg'))

dog_images = glob.glob(os.path.join(original_dataset_dir, "Dog", '*.jpg'))
print("total cat images: {}\n\rtotal dog images: {}".format(len(cat_images), len(dog_images)))

# 데이터셋을 저장할 폴더
base_dir = './data/dog_vs_cat_small'

os.makedirs(base_dir, exist_ok=True)

# 학습, 검증, 테스트 데이터가 들어갈 폴더 설정
train_dir = os.path.join(base_dir, 'train')
os.makedirs(train_dir, exist_ok=True)

validation_dir = os.path.join(base_dir, 'validation')
os.makedirs(validation_dir, exist_ok=True)

test_dir = os.path.join(base_dir, 'test')
os.makedirs(test_dir, exist_ok=True)

# 각 dog, cat 이미지에 대한 폴더를 제작
train_cats_dir = os.path.join(train_dir, 'cats')
os.makedirs(train_cats_dir, exist_ok=True)

train_dogs_dir = os.path.join(train_dir, 'dogs')
os.makedirs(train_dogs_dir, exist_ok=True)

validation_cats_dir = os.path.join(validation_dir, 'cats')
os.makedirs(validation_cats_dir, exist_ok=True)

validation_dogs_dir = os.path.join(validation_dir, 'dogs')
os.makedirs(validation_dogs_dir, exist_ok=True)

test_cats_dir = os.path.join(test_dir, 'cats')
os.makedirs(test_cats_dir, exist_ok=True)

test_dogs_dir = os.path.join(test_dir, 'dogs')
os.makedirs(test_dogs_dir, exist_ok=True)

# NUM_TRAIN//2개의 영상을 나누어 각 폴더에 복사
fnames = cat_images[:NUM_TRAIN//2]

for fname in fnames:
  dst = os.path.join(train_cats_dir, os.path.basename(fname))
  shutil.copyfile(fname, dst)

offset = NUM_TRAIN//2
fnames = cat_images[offset:offset + NUM_TEST // 2]

for fname in fnames:
  dst = os.path.join(validation_cats_dir, os.path.basename(fname))
  shutil.copyfile(fname, dst)

offset = offset + NUM_TEST // 2

fnames = cat_images[offset:offset + NUM_TEST // 2]

for fname in fnames:
  dst = os.path.join(test_cats_dir, os.path.basename(fname))
  shutil.copyfile(fname, dst)

fnames = dog_images[:NUM_TRAIN//2]

for fname in fnames:
  dst = os.path.join(train_dogs_dir, os.path.basename(fname))
  shutil.copyfile(fname, dst)

offset = NUM_TRAIN//2

fnames = dog_images[offset:offset + NUM_TEST // 2]

for fname in fnames:
  dst = os.path.join(validation_dogs_dir, os.path.basename(fname))
  shutil.copyfile(fname, dst)

offset = offset + NUM_TEST // 2

fnames = dog_images[offset:offset + NUM_TEST // 2]

for fname in fnames:
  dst = os.path.join(test_dogs_dir, os.path.basename(fname))
  shutil.copyfile(fname, dst)
total cat images: 12500
total dog images: 12500

 

데이터셋 분리

 

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,
                                   fill_mode='nearest')

test_datagen = ImageDataGenerator(rescale=1./255)

train_generator = train_datagen.flow_from_directory(train_dir,
                                                    target_size=(height, width),
                                                    batch_size=batch_size,
                                                    class_mode='categorical')

validation_generator = test_datagen.flow_from_directory(validation_dir,
                                                        target_size=(height, width),
                                                        batch_size=batch_size,
                                                        class_mode='categorical')
Found 2000 images belonging to 2 classes.
Found 1000 images belonging to 2 classes.

 

모델 생성

 

Tensorflow.keras의 ImageDataGenerator 라이브러리를 통해 위에서 나눈 각 데이터들의 generator를 만들어준다.

 

base_model = EfficientNetB0(weights='imagenet', include_top=False)

 

모델을 transfer learning (전이 학습) 방법으로 사용할 때, Keras 라이브러리에서는 top-layer를 제거할 수 있는 옵션을 제공한다. 이 옵션은 1280개의 feature를 1000개의 ImageNet 클래스 예측으로 바꾸는 마지막 Dense layer를 제외한다. Top layer를 원하는 다른 레이어로 바꿈으로써 EfficientNet을 전이 학습 워크플로우의 feature extractor로써 사용할 수 있다.

 

x = base_model.output # 4d
print(np.shape(x))

x = GlobalAveragePooling2D()(x) # 2d
print(np.shape(x))

x = Dense(64, activation='relu')(x)

predictions = Dense(2, activation='softmax')(x)


model = Model(inputs=base_model.input, outputs=predictions)

for layer in base_model.layers:
  layer.trainable = False

model.summary()
model.compile(loss='categorical_crossentropy',
              optimizer=optimizers.RMSprop(lr=2e-5),
              metrics=['acc'])

history = model.fit_generator(train_generator,
                              steps_per_epoch=NUM_TRAIN // batch_size,
                              epochs=epochs,
                              validation_data=validation_generator,
                              validation_steps=NUM_TEST // batch_size,
                              verbose=1,
                              use_multiprocessing=True,
                              workers=4)

 

모델 평가

 

acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']

epochs_x = range(len(acc))

plt.plot(epochs_x, acc, 'bo', label='Training acc')
plt.plot(epochs_x, val_acc, 'b', label='Validation acc')

plt.title('Training and validation accuracy')
plt.legend()

plt.figure()

plt.plot(epochs_x, loss, 'bo', label='Training loss')
plt.plot(epochs_x, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()

plt.show()
cat_img = cat_images[-1]
dog_img= dog_images[-1]

 

앞서 학습한 모델을 새로운 test set에 적용하여 정확도를 평가할 수 있다.

 

from tensorflow.keras.preprocessing import image

def predict_image(img_path):
  img = image.load_img(img_path, target_size=(height, width))
  x = image.img_to_array(img)
  x = x.reshape((1,) + x.shape)
  x /= 255.
  
  result = model.predict([x])[0][0]
  
  if result > 0.5:
    animal = "cat"
    
  else:
    animal = "dog"
    
  result = 1 - result
  
  return animal,result

print(predict_image(cat_img))
print(predict_image(dog_img))
728x90
반응형
LIST