코딩은 실력보다 시력이지

카테고리 없음

최종 프로젝트 - 이미지 학습(2)

Listeria 2021. 7. 1. 05:31

사진을 크롤링하면 수백장이 되지만 해상도가 낮거나 학습에 활용하기 힘들다고 판단되는 것들을 거르고나면 실제 데이터는 100여장 수준으로 굉장이 적게 남았다. 그리고 이를 통해 학습을 시켰을땐 역시 원하는 수준의 결과가 나오질 않았다. 그렇기에 이를 해결할려면 이미지의 특징을 충분히 추출할 수 있을 정도로 깊은 수준의 CNN 모델이 필요한데, 프로젝트 마무리가 1주일도 채 남지 않은 시점이었기에 이는 불가능하다고 판단하였고 새로운 방법을 모색하였다.

그리고 전이학습(Transfer Learning)을 시키기로 생각하였다. 전이학습이란 기존에 만들어진 모델에서 이미지 특징을 추출 레이어를 활용, 분류 레이어만 직접 만드는 방법이다. 이번에는 얼굴의 특징을 가장 잘 추출 할 수 있는 것이 얼굴인식 대회에서 우승한 모델이라고 생각되어 Face Resnet 모델을 활용하기로 하였다. 또한 이미지를 확인하기 위해서 Jupyter notebook을 활용하였다.

 

from pasta.augment import inline

from inception_resnet_v1_lcl import *

from functools import  partial

from keras.models import Model
from keras.layers import Activation
from keras.layers import BatchNormalization
from keras.layers import Concatenate
from keras.layers import Conv2D
from keras.layers import Dense
from keras.layers import GlobalAveragePooling2D
from keras.layers import Input
from keras.layers import Lambda
from keras.layers import MaxPooling2D
from keras.layers import add
from keras.preprocessing.image import ImageDataGenerator
from keras.callbacks import ModelCheckpoint
from keras.optimizers import Adam
from keras.applications.imagenet_utils import preprocess_input
from keras import backend as K
import numpy as np
import matplotlib.image as mpimg
from PIL import Image
import os
import matplotlib.pyplot as plt


base_model = InceptionResNetV1(weights_path='./transfer/facenet_keras_weights.h5',
                               input_shape=(96,96,3),
                               dropout_keep_prob=0.8)

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

base_model.summary()

for i, layer in enumerate(base_model.layers) :
    print(i,layer.name)

classes = 7
epochs = 500
targetx = 96
targety = 96

x = base_model.get_layer(index=442).output
x = GlobalAveragePooling2D()(x)
x = BatchNormalization()(x)
x = Dropout(0.5)(x)
x = Dense(1024,activation='relu', kernel_initializer='he_normal', bias_initializer= 'zeros')(x)
x = BatchNormalization()(x)
x = Dropout(0.5)(x)

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

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

my_model.summary()

for i, layer in enumerate(my_model.layers) :
    print(i,layer.name)

train_datagen = ImageDataGenerator(rescale=1./255,
                                   rotation_range=30,
                                   width_shift_range=0.2,
                                   height_shift_range=0.2,
                                   shear_range=0.2,
                                   zoom_range=0.2,
                                   horizontal_flip=True,
                                   fill_mode='nearest')

val_datagen = ImageDataGenerator(rescale=1./255)
train_dir = os.getcwd()+ "/images/newTrainData2"
val_dir = os.getcwd()+"/images/newValData2"

train_generator = train_datagen.flow_from_directory(train_dir,
                                                    batch_size=20,
                                                    target_size=(targetx,targety),
                                                    shuffle=True,
                                                    class_mode='categorical')
val_generator = val_datagen.flow_from_directory(val_dir,
                                                batch_size=10,
                                                target_size=(targetx,targety),
                                                shuffle=True,
                                                class_mode='categorical')

#setting model chk point
checkpoint_dir = './transfer'
os.makedirs(checkpoint_dir, exist_ok=True)
checkpoint = ModelCheckpoint(filepath=checkpoint_dir+'/'+'weightMk02.hdf5',
                             monitor='loss',
                             mode='min',
                             save_best_only=True)

#compiling
optimizer = Adam(learning_rate=0.0001)
loss = 'categorical_crossentropy'

my_model.compile(optimizer=optimizer,
                 loss = loss,
                 metrics=['accuracy'])

#training
history = my_model.fit(train_generator,
                     steps_per_epoch= len(train_generator),
                     epochs=epochs,
                     validation_data=val_generator,
                     validation_steps=len(val_generator),
                     callbacks=[checkpoint])

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

epochs = range(1,len(acc)+1)

plt.plot(epochs,acc,'ro',label='Train loss')
plt.plot(epochs,val_loss,'r',label='val loss')
plt.title('loss')
plt.legend()

plt.show()

전이학습을 하는 코드인데, 일반적으로 keras.optimizer에 adam 이 버전에 맞지 않아 빨간줄이 뜰것이다. 다운그래이드를 시켜야 하는데, 개인적으론 그러긴 너무 귀찮았으므로 문제가 되는 파일을 찾고 낮은 버전에 코드를 찾아서 메소드를 복사 붙여넣기 해서 하나로 합쳤다.

학습을 하다가 정확도가 개선됐을때만 체크포인트를 만들어서 모델을 저장하였다.

그리고 학습된 모델을 이용하여 실제로 인물을 정확하게 분석할 수 있는지 확인하였다.

import os
import cv2
from keras.models import load_model
from inception_resnet_v1_lcl import *

checkpoint_dir = os.getcwd() + "/transfer/"
# model = load_model(checkpoint_dir+'weight_1.hdf5')
model = load_model(checkpoint_dir + 'weightMk02.hdf5')
# model.summary()
import numpy as np
import matplotlib.image as mpimg
from PIL import Image
from keras.applications.imagenet_utils import preprocess_input
from keras import backend as K
import matplotlib.pyplot as plt

targetx = 96
targety = 96
test_dir = os.getcwd() + '/testimg2/'

image_path = []
face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_alt.xml')
for i in os.listdir(test_dir):
    test_image_name = test_dir + i
    image_path.append(test_image_name)

for image in image_path:
    print('로드 이미지 :', image)
    src = cv2.imread(image)
    src_gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
    try:
        faces = face_cascade.detectMultiScale(src_gray, 1.1, 1, minSize=(90, 90))
    except ExceptionError as e:
        print(e)
    if len(faces) > 0:
        img = Image.open(image)
        for x, y, w, h in faces:
            plt.imshow(img)
            plt.show()
            img = img.crop([x + int(w / 14), y + int(h / 14), x + w - int(w / 14), y + h - int(h / 14)])

        img = img.convert("RGB")
        img = img.resize((targetx, targety))
        data = np.asarray(img)
        X = np.array(data)
        X = X.astype('float') / 256
        X = X.reshape(-1, targetx, targety, 3)
        categories = ['토르', '혜진', '명수', '사나', '흥민', '아이유', '재석']
        pred = model.predict(X)
        result = [np.argmax(value) for value in pred]
        acc = max(pred[0])
        print('new image pred : ', categories[result[0]])
        print('acc : {}'.format(acc))

        plt.imshow(img)
        plt.show()
    else:
        print("Can't find face")

사진을 입력받으면 OpenCV를 통해 얼굴을 크롭하고 이를 모델을 이용해 유사도를 판별, 결과를 반환해준다. 전반적으로 꽤 높은 정확도로 결과를 반환해주었다.

from PIL import Image

import matplotlib.pyplot as plt

targetx = 96
targety = 96

def detectImg(image) :
    checkpoint_dir = os.getcwd() + "/FaceFind/transfer/"
    model = load_model(checkpoint_dir + 'weightMk02.hdf5')
    haar = os.getcwd() + '/FaceFind/haarcascade_frontalface_alt.xml'

    gpus = tf.config.experimental.list_physical_devices('GPU')
    if gpus:
        try:  # 메모리 증가를 허용(필요한 만큼만 GPU 메모리 할당)
            tf.config.experimental.set_memory_growth(gpus[0], True)
        except RuntimeError as e:
            print(e)  # 프로그램 시작시에 설정되어야만 합니다
    fileStore_dir = os.getcwd()
    image = fileStore_dir+"/fileStore/"+image
    face_cascade = cv2.CascadeClassifier(haar)
    src = cv2.imread(image)
    src_gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
    faces = face_cascade.detectMultiScale(src_gray, 1.1, 1, minSize=(90, 90))

    if len(faces) > 0:
        img = Image.open(image)
        for x, y, w, h in faces:
            plt.imshow(img)
            plt.show()
            img = img.crop([x + int(w / 14), y + int(h / 14), x + w - int(w / 14), y + h - int(h / 14)])

        img = img.convert("RGB")
        img = img.resize((targetx, targety))
        data = np.asarray(img)
        X = np.array(data)
        X = X.astype('float') / 256
        X = X.reshape(-1, targetx, targety, 3)
        categories = ['토르', '혜진', '명수', '사나', '흥민', '아이유', '재석']
        pred = model.predict(X)
        result = [np.argmax(value) for value in pred]
        acc = max(pred[0])
        percent = (100*format(acc))[2:4]+"%"

        return categories[result[0]],percent

    else:
        return  print("Can't find face")

우리 프로그램에서 활용할 때는 굳이 이미지를 확인할 필요가 없으므로 이미지 플롯을 삭제하였다. 좀 더 많은 시간과 보다 좋은 컴퓨터가 있었으면 더 많은 사람을 학습시켰겠지만 시간이 촉박하여 이쯤에서 CNN 프로젝트를 마무리 하였다.