차밍이

[Keras] CNN 이미지 분류 실습 : 손글씨 이미지 분류 본문

파이썬/Tensorflow & Keras

[Keras] CNN 이미지 분류 실습 : 손글씨 이미지 분류

2020. 4. 29. 08:54
반응형

안녕하세요. 오늘은 Keras 수업을 들으면서 진행했던 실습 내용을 적어볼까 합니다.

Tensorflow와 Keras를 사용한 딥러닝 공부를 진행 중에 CNN 파트가 시작되었습니다.

대부분 mnist를 사용한 이미지 분류는 해보셨을 것이라 생각합니다. 그래서 저희는 각자가 글씨를 작성해서 각 글씨를 분류하는 CNN ㅣ모델을 만들어보기로 하였습니다.

 

이미지 데이터

간단한 실습을 하고자 가, 다, 라, 카, 사 5개의 글자를 선택했고 18명의 글씨가 있습니다.

글자의 데이터입니다.

이러한 형태로 5개의 글자를 train 데이터로 2번 test 데이터로 1번 작성하여 총 52개의 이미지 데이터가 존재합니다.

데이터를 보시면 중간중간 기울어진 글씨와 90도 회전된 것도 존재하는 것이 보입니다.

이미지는 28x28 형태로 제작되었습니다.

 

파일 이름 형식

파일 이름 형식을 통일해 두었습니다. 전처리에 용이함을 위해서죠.

라벨_이니셜_글자수_개인고유번호.jpg 순서입니다.

라벨은 aa == 아, ga == 가 이러한 형식입니다.

이니셜과 글자 수는 무시하셔도 됩니다.

고유번호는 이니셜이 같은 경우를 대비해서 각 개인마다 자기의 번호를 지정해서 넣었습니다. 이것을 통해서 어떤 사람이 쓴 글씨인지 구분할 수 있습니다. 이를 통해서 어떤 사람이 쓴 글인지 맞춰보는 것도 진행해볼 예정입니다.

 

데이터 불러오기

mnist처럼 정리되어있어서 불러오기만 하면 되는 데이터와 다르게 파일로 각각 있으니 직접 불러오는 것이 생각보다 번거로웠습니다. ImageGenerator를 사용해서 불러오면 조금 더 쉽게 불러올 수 있지만 해당 내용은 다음 포스팅에서 진행하겠습니다.

직접 os 모듈을 통해서 직접 경로에 들어가 파일들을 불러오는 작업을 진행했습니다.

from keras.utils import np_utils
from keras.models import Sequential
from keras.layers import Dense
from keras.callbacks import ModelCheckpoint, EarlyStopping
import numpy as np
import tensorflow as tf # 1.15.0 버전 사용
import os
import pandas as pd
import numpy as np
from keras.preprocessing.image import array_to_img, img_to_array, load_img
path = r"Q:\이미지공유폴더0\\"
trainFileList = os.listdir(path+'train')
testFileList = os.listdir(path+'test')

파일 중 jpg 파일에 해당되는 파일 목록만 따로 리스트에 담았습니다. 숨김 파일이나 다른 파일들이 섞이는 것을 방지하기 위해서 진행하였습니다.

trainFileList = [x for x in trainFileList if 'jpg' in x]
testFileList = [x for x in testFileList if 'jpg' in x]

각 단어 별 label encoding을 위해서 미리 숫자를 매겨놓았습니다.

word = {'ga' : 0, 'da' : 1, 'sa' : 2, 'aa' : 3, 'ka' : 4}
# np.array에 각 데이터를 넣어주는 작업
for i, file in enumerate(trainFileList):
    label, nm, tr, _ = file.split('_') # 예시 aa_bc_01_02.jpg
    code, ext = _.split('.') # 예시 02.jpg

    # 이미지 불러오기
    new_img = load_img(path+'train\\'+file)
    # 이미지를 array 형식으로 변형
    arr_img = img_to_array(new_img)
    # (1, 28, 28, 3) 검정색 글씨뿐이지만 나름 RGB...zz
    img = arr_img.reshape((1,)+arr_img.shape)

    # 첫 번째 데이터의 경우 container를 생성
    if i == 0 :
        container = img
        labels = word[label]
    # 첫 번쨰가 아닌경우 array에 계속 쌓아나감
    else:
        container = np.vstack([container, img])
        labels = np.vstack([labels, word[label]])

xTrain = container
# 카테고리 데이터 one-hot encoding
yTrain = np_utils.to_categorical(labels,5)

# 위의 작업을 동일하게 test데이터에도 진행
for i, file in enumerate(testFileList):
    label, nm, tr, _ = file.split('_')
    code, ext = _.split('.')

    new_img = load_img(path+'test\\'+file)
    arr_img = img_to_array(new_img)
    img = arr_img.reshape((1,)+arr_img.shape)
    if i == 0 :
        container = img
        labels = word[label]
    else:
        container = np.vstack([container, img])
        labels = np.vstack([labels, word[label]])

xTest = container
yTest = np_utils.to_categorical(labels,5)   
print(container.shape, labels.shape)
print(xTrain.shape, yTrain.shape)
print(xTest.shape, yTest.shape)
(90, 28, 28, 3) (90, 1)
(180, 28, 28, 3) (180, 5)
(90, 28, 28, 3) (90, 5)

 

정규화 진행

xTrain = xTrain.astype('float32')/255
xTest = xTest.astype('float32')/255

 

Keras CNN 모델 생성

Keras를 사용해서 간단한 CNN 구조를 만들어 학습을 진행해보겠습니다.

from keras.layers import *
from sklearn.metrics import accuracy_score
# 모델 구성
model = Sequential()
model.add(Conv2D(64, kernel_size=(3,3), padding='same',
                input_shape=(28,28,3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Dropout(0.25))

model.add(Conv2D(128, kernel_size=(3,3),padding='same', activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Dropout(0.25))

model.add(Conv2D(256, kernel_size=(3,3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Dropout(0.25))

model.add(Flatten())
model.add(Dense(256, activation='relu'))
model.add(Dropout(0.25))
model.add(Dense(5, activation='softmax'))
model.compile(loss='categorical_crossentropy',
             optimizer='adam',
             metrics=['accuracy'])
history = model.fit(xTrain, yTrain, 
                    epochs=200, batch_size=30)
Epoch 1/200
180/180 [==============================] - 3s 16ms/step - loss: 1.6721 - accuracy: 0.2111
Epoch 2/200
180/180 [==============================] - 0s 521us/step - loss: 1.6322 - accuracy: 0.1778
Epoch 3/200
180/180 [==============================] - 0s 543us/step - loss: 1.6236 - accuracy: 0.1778
...
Epoch 199/200
180/180 [==============================] - 0s 543us/step - loss: 0.0140 - accuracy: 1.0000
Epoch 200/200
180/180 [==============================] - 0s 532us/step - loss: 0.0172 - accuracy: 0.9944

 

결과 확인 및 정확도 확인

result = model.predict_classes(xTest)

answer = []
for i in yTest:
    answer.append(np.argmax(i))

yTest_label = []
for y in yTest:
    yTest_label.append(np.argmax(y))

accuracy_score(yTest_label, result)
>> 0.8

간단하게 만들었기에 validation을 안 줬고 callback도 진행하지 않았습니다.

학습을 마쳤습니다. 80%의 정답률을 가지는 것을 확인하였습니다.

train 데이터에 대해서 거의 100%에 가까운 정답을 보이지만 test데이터에서는 80%의 정답률로 약간 아쉬움이 있습니다.

 

더 나은 성능을 위해서는?!

CNN 구조를 바꾸거나, 데이터를 증식하거나, 과적합 이전에 학습을 마치는 방법 등을 생각해볼 수 있겠습니다.

다음 포스팅에서는 위의 작업을 Keras의 ImageDataGenerator를 사용해서 데이터를 증식해서 학습을 하는 방향으로 진행해보겠습니다.

과연 이미지를 증식하면 얼마나 더 좋은 결과를 가질 수 있는지 알아보겠습니다.

다음 포스팅 : [Keras] CNN ImageDataGenerator : 손글씨 글자 분류

반응형

관련된 글 보기

Comments