Python/딥러닝

CNN(Convolutional Neural Network)으로 디카프리오와 저스틴비버 분류하기

강떡볶 2022. 3. 26. 18:31
원래는... 저스틴비버와 김종민을 분류하는 모델을 만드려고 했었으나...

 

저스틴비버와 김종민

  • 김종민은 '정치인 김종민'도 있을 뿐더러, 1박 2일이나 코요태 단체사진이 많이 섞여 있어 가비지가 너무 많음
  • 그래서 저스틴 비버와 레오나르도 디카프리오를 분류하기로 결정

 

 

1. 레오나르도 디카프리오, 저스틴 비버 사진 크롤링 하기

from selenium import webdriver
from bs4 import BeautifulSoup as soups
import time

def search_selenium(search_name, search_path, search_limit) :
    search_url = "https://www.google.com/search?q=" + str(search_name) + "&hl=ko&tbm=isch"
    
    browser = webdriver.Chrome('chromedriver.exe')
    browser.get(search_url)
    
    image_count = len(browser.find_elements_by_tag_name("img"))
    
    print("로드된 이미지 개수 : ", image_count)

    browser.implicitly_wait(2)
    
    try:
        for i in range( search_limit ) :
            image = browser.find_elements_by_tag_name("img")[i]
            image.screenshot("C:/Users/hy101/Desktop/mabrain/CNN 이미지 분류/레오나르도 디카프리오/" + str(i) + ".png")
            time.sleep(0.5)
            
        try:
            more = browser.find_elements_by_tag_name("input.mye4qd")[0]
            more.click()

            for i in range(search_limit) :
                image = browser.find_elements_by_tag_name("img")[i]
                image.screenshot("C:/Users/hy101/Desktop/mabrain/CNN 이미지 분류/레오나르도 디카프리오/" + str(i+449) + ".png")
                time.sleep(0.5)
            
        except:
            more = browser.find_elements_by_tag_name("input.mye4qd")[0]
            more.click()

            for i in range(search_limit) :
                image = browser.find_elements_by_tag_name("img")[i]
                image.screenshot("C:/Users/hy101/Desktop/mabrain/CNN 이미지 분류/레오나르도 디카프리오/" + str(i+898) + ".png")
                time.sleep(0.5)

    except:
        more = browser.find_elements_by_tag_name("input.mye4qd")[0]
        more.click()

        for i in range(search_limit) :
            image = browser.find_elements_by_tag_name("img")[i]
            image.screenshot("C:/Users/hy101/Desktop/mabrain/CNN 이미지 분류/레오나르도 디카프리오/" + str(i+449) + ".png")
            time.sleep(0.5)
        
        
    browser.close()

if __name__ == "__main__" :

    search_name = input("검색하고 싶은 키워드 : ")
    search_limit = int(input("원하는 이미지 수집 개수 : "))
    search_path = "Your Path"
    # search_maybe(search_name, search_limit, search_path)
    search_selenium(search_name, search_path, search_limit)

  • 저스틴 비버와 레오나르도 디카프리오 이미지가 폴더에 저장된 것을 확인할 수 있음
  • 이미지 이름을 '사람 이름(숫자)'의 형식으로 바꿈
  • 가비지 데이터 삭제(직접 노가다로 함...^^)
    • 레오나르도 디카프리오의 경우 리즈 시절 사진 삭제
    • 저스틴 비버의 경우 너무 어린 시절 사진 삭제
    • 그 외 인물이 두 명 이상 나오거나 사람이 없는 사진 삭제

 


2. openCV 라이브러리로 얼굴을 인식하여 얼굴 부분만 자르기

import cv2
from tqdm import tqdm

for i in tqdm(range(1, 244)):
    # Read the input image
    img = cv2.imread('leo('+str(i)+').png')

    # Convert into grayscale
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_alt2.xml')

    # Detect faces
    faces = face_cascade.detectMultiScale(gray, 1.1, 4)



    for (x, y, w, h) in faces:
        cv2.rectangle(img, (x, y), (x+w, y+h), 
                      (0, 0, 255), 2)

        faces = img[y:y + h, x:x + w]
        cv2.imshow("crop("+str(i)+")",faces)
        cv2.imwrite("crop("+str(i)+").jpg", faces)

    cv2.imshow('img', img)
    cv2.waitKey()
    
cv2.destroyAllWindows()

  • 얼굴만 크롭된 사진이 폴더에 저장됨
  • 얼굴 인식이 잘못된 가비지 사진 삭제
  • 레오나르도 디카프리오와 저스틴 비버 사진 최종 159장(너무 적음 ㅜㅜ)

 

 

3. 이미지 전처리

** Google Colab으로 진행

 

1) 데이터 개수 확인

import os

train_dir = '/content/drive/MyDrive/Colab Notebooks/mabrain 이미지 분류 data/train'
test_dir = '/content/drive/MyDrive/Colab Notebooks/mabrain 이미지 분류 data/test'
val_dir = '/content/drive/MyDrive/Colab Notebooks/mabrain 이미지 분류 data/val'

train_justin_dir = '/content/drive/MyDrive/Colab Notebooks/mabrain 이미지 분류 data/train/justin_train'
train_leo_dir = '/content/drive/MyDrive/Colab Notebooks/mabrain 이미지 분류 data/train/leo_train'

test_justin_dir = '/content/drive/MyDrive/Colab Notebooks/mabrain 이미지 분류 data/test/justin_test'
test_leo_dir = '/content/drive/MyDrive/Colab Notebooks/mabrain 이미지 분류 data/test/leo_test'

val_justin_dir = '/content/drive/MyDrive/Colab Notebooks/mabrain 이미지 분류 data/val/justin_val'
val_leo_dir = '/content/drive/MyDrive/Colab Notebooks/mabrain 이미지 분류 data/val/leo_val'
# listdir : 해당 폴더에 있는 파일을 가져온다

os.listdir(train_dir)

print('train justin 데이터 수 : {}'.format(len(os.listdir(train_justin_dir))))
print('train leo 데이터 수 : {}'.format(len(os.listdir(train_leo_dir))))

print('test justin 데이터 수 : {}'.format(len(os.listdir(test_justin_dir))))
print('test leo 데이터 수 : {}'.format(len(os.listdir(test_leo_dir))))

print('val justin 데이터 수 : {}'.format(len(os.listdir(val_justin_dir))))
print('val leo 데이터 수 : {}'.format(len(os.listdir(val_leo_dir))))

 

 

2) 이미지 전처리

- 이미지를 같은 크기로 만들어 주어야 함 0 ~ 255 범위의 픽셀 값을 0 ~ 1 사이의 범위로 변환(ImageDataGenerator() 함수를 사용)
- (수가 크면 학습을 할 때 범위가 너무 커서 학습이 잘 진행되지 않을 수도 있음)
- 라벨링
from tensorflow.keras.preprocessing.image import ImageDataGenerator

# 픽셀값 0 ~ 1 사이로 변환

train_gen = ImageDataGenerator(rescale = 1/255)
test_gen = ImageDataGenerator(rescale = 1/255)
val_gen = ImageDataGenerator(rescale = 1/255)
# flow_from_directory() : 폴더에서 이미지 가져오기
# 폴더명, 이미지 크기, 한 번에 변환 할 이미지 수, 라벨링 모드
# 이진 분류 : binary, 다중 분류: categorical

train_generator = train_gen.flow_from_directory(train_dir,
                              target_size = (150, 150),
                              batch_size=10, # 한 번에 가져올 이미지 수
                              class_mode='binary')

test_generator = test_gen.flow_from_directory(test_dir,
                              target_size = (150, 150),
                              batch_size=10,
                              class_mode='binary')

val_generator = val_gen.flow_from_directory(val_dir,
                              target_size = (150, 150),
                              batch_size=10,
                              class_mode='binary')

 

 

3) 라벨링 결과 확인

print(train_generator.class_indices)
print(test_generator.class_indices)
print(val_generator.class_indices)

 

 

 

3. CNN(Convolutional Neural Network) 구현

 

1) 모델 정의

# 초기 시드 설정

import numpy as np
import tensorflow as tf

seed = 0
np.random.seed(seed)
tf.random.set_seed(seed)
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten

# Con2D : 특징 찾기
# MaxPooling2D : 불필요한 부분 삭제
# Flatten : 데이터를 1차원으로 펴줌


c_model = Sequential()

# 입력층 (CNN)
# 특징을 도드라지게 해준다
c_model.add(Conv2D(filters = 16,   # 사진에서 찾을 특성 개수
                   kernel_size = (3,3), # 한 번에 확인할 픽셀의 수
                   input_shape = (150, 150, 3), # 입력 데이터의 크기
                   padding = 'same', # 가장 자리를 0으로 채움
                   # same : 입력 데이터의 크기와 동일하게 맞춰줌
                   activation = 'relu'))

# 불필요한 부분 삭제
c_model.add(MaxPooling2D(pool_size=(2,2)))

# ----------

c_model.add(Conv2D(filters = 16,   # 사진에서 찾을 특성 개수
                   kernel_size = (3,3), # 한 번에 확인할 픽셀의 수
                   input_shape = (150, 150, 3), # 입력 데이터의 크기
                   padding = 'same', # 가장 자리를 0으로 채움
                   # same : 입력 데이터의 크기와 동일하게 맞춰줌
                   activation = 'relu'))
c_model.add(MaxPooling2D(pool_size=(2,2)))

# 1차원으로 데이터 축소
c_model.add(Flatten())

# 은닉층
c_model.add(Dense(units=1, activation = 'sigmoid'))

# 출력층
c_model.add(Dense(units=1, activation='sigmoid'))

c_model.summary()

 

 

2) 학습시키기

c_model.compile(loss='binary_crossentropy',
                optimizer='adam',
                metrics=['accuracy'])
c_history = c_model.fit_generator(generator=train_generator,
            steps_per_epoch=25,
            epochs=200,
            validation_data = val_generator,
            validation_steps = 25)

 

 

3) 훈련 데이터 정확도 확인

import matplotlib.pyplot as plt

acc = c_history.history['accuracy']
# val_acc = c_history.history['val_accuracy']

epoch = range(1, len(acc) + 1) # 정확도의 개수

plt.plot(epoch, acc, c='red', label='Train acc') # 훈련 데이터 정확도
# plt.plot(epoch, val_acc, c='blue', label='Val acc') # 검증 데이터 정확도
plt.legend() # 범례
plt.plot()

 

 

 

음... 이상해도 너무 이상하다 ㅎㅎㅎㅎ

애초에 데이터 양이 너무 적기도 하였고, 아무래도 수기로 가비지 처리를 하다 보니까 문제가 많았다 ㅠㅠ

그리고 CNN 자체를 처음 해본 거라 시행착오도 많았다....

 

그래도 직접 데이터를 수집해서 모델을 구현하는 과정까지 진행해본 것에 의의를 두자!