본문 바로가기

Back/Deep Learning

[Python] [CNN]점자번역 프로그램(1)

3. 구현

3-1. 모델 분석

https://www.kaggle.com/kwisatzhaderach/braille-classifier-keras

 

Braille Classifier (Keras)

Explore and run machine learning code with Kaggle Notebooks | Using data from Braille Character Dataset

www.kaggle.com

우선 이전글에서 봤던 이미지 셋에서 한 외국인분이 예측 모델을 구현해 놓았습니다. 


1. import

import os
import numpy as np
import pandas as pd
from shutil import copyfile

os, shutil : 파일복사, dir 생성 삭제 등을 다루기 위한 lib

numpy : 딥러닝에 사용되는 모델들은 주로 array 방식으로 데이터 입력을 받고 출력하는데 이를 다루기 위한 lib

pandas : 외부 자료(대표적으로 excel, csv 등)를 읽거나 쓰기위해 자주 쓰는 lib , 안썼는데 습관적으로 넣은 것 같습니다. 


2. 이미지 준비

os.mkdir('./images/')
alpha = 'a'
for i in range(0, 26): 
    os.mkdir('./images/' + alpha)
    alpha = chr(ord(alpha) + 1)

rootdir = '/kaggle/input/braille-character-dataset/Braille Dataset/Braille Dataset/'
for file in os.listdir(rootdir):
    letter = file[0]
    copyfile(rootdir+file, './images/' + letter + '/' + file)    

이미지 저장을 위한 이미지 폴더 경로를 만듭니다. 

rootdir에 데이터 셋의 이미지를 해당하는 폴더로 나누어서 복사합니다.

이런식으로 폴더가 생성되어집니다.

 

for file in os.listdir(rootdir):
    letter = file[0]
    copyfile(rootdir+file, './images/' + letter + '/' + file)  

file 로 각각 이미지의 이름과 확장자명을 불러오고 letter로 파일의 첫번째 알파벳을 확인합니다.

데이터 셋의 모든 파일이 (알파벳)+(구분)+(확장자)로 이루어져서 가능합니다.


3. 이미지 불러오기

from keras.preprocessing.image import ImageDataGenerator
datagen = ImageDataGenerator(rotation_range=20,
                             shear_range=10,
                             validation_split=0.2)

train_generator = datagen.flow_from_directory('./images/',
                                              target_size=(28,28),
                                              subset='training')

val_generator = datagen.flow_from_directory('./images/',
                                            target_size=(28,28),
                                            subset='validation')

케라스 에서 제공하는 ImageDataenerator를 import합니다.(https://keras.io/ko/preprocessing/image/)

 

ImageDataenerator는 사실 이름에서 보면 알 수 있듯이 이미지를 발생시키는 일종의 도구 입니다.

경로안의 이미지를 입력받아 ImageDataenerator의 속성을 랜덤으로 적용하여 분류합니다.

또한 지도학습용 자료를 만들때 매우 편리하게도 폴더이름에 따라서 Categorical Variable을 자동으로 생성해 주기 떄문에 따로 만들어낼 필요 없습니다.

 

자세한 속성 설정은 이후 이미지부풀리는 부분에서 설명하도록 하겠습니다.

 

위의 코드에선 약간의 회전과, 교육용 자료와 시험용 자료를 분리할 수 있도록 하였으며(validation_split)

이미지를 불러올때 28*28의 크기로 불러오도록 하였습니다.

 

validation_split 사용시 subset으로 training 혹은 validation을 지정하여야 0.2 = 20%가 validation 으로 들어가게 됩니다.

원문에 있는 출력 결과물. 20%가 나뉘어 들어가 있는것을 볼 수 있습니다.


4. 모델 생성

from keras import backend as K
from keras import layers as L
from keras.models import Model,load_model
from keras.regularizers import l2
from keras.callbacks import ModelCheckpoint,ReduceLROnPlateau,EarlyStopping

K.clear_session()

model_ckpt = ModelCheckpoint('BrailleNet.h5',save_best_only=True)
reduce_lr = ReduceLROnPlateau(patience=8,verbose=0)
early_stop = EarlyStopping(patience=15,verbose=1)

entry = L.Input(shape=(28,28,3))
x = L.SeparableConv2D(64,(3,3),activation='relu')(entry)
x = L.MaxPooling2D((2,2))(x)
x = L.SeparableConv2D(128,(3,3),activation='relu')(x)
x = L.MaxPooling2D((2,2))(x)
x = L.SeparableConv2D(256,(2,2),activation='relu')(x)
x = L.GlobalMaxPooling2D()(x)
x = L.Dense(256)(x)
x = L.LeakyReLU()(x)
x = L.Dense(64,kernel_regularizer=l2(2e-4))(x)
x = L.LeakyReLU()(x)
x = L.Dense(26,activation='softmax')(x)

model = Model(entry,x)
model.compile(loss='categorical_crossentropy',optimizer='adam',metrics=['accuracy'])

history = model.fit_generator(train_generator,validation_data=val_generator,epochs=666,
                              callbacks=[model_ckpt,reduce_lr,early_stop],verbose=0)

본격적인 모델 생성 부분입니다. 가장 먼저 클리어를 한번해주고 3가지 콜백을 설정합니다.

(https://teddylee777.github.io/tensorflow/keras-%EC%BD%9C%EB%B0%B1%ED%95%A8%EC%88%98-vol-01)

※콜백함수에 대하여 이쪽을 참고했습니다.

 

ModelCheckpoint : 특정조건을 만족했을때 그때까지의 상황을 저장해 줍니다. 시간이 오래 걸리는 학습에서 중간 포인트를 저장하여 그떄부터 다시 학습을 시킬 수도 있고, 이번 예문 처럼 최종 결과(가장 좋은 부분)을 저장하게 할수 있습니다.

ReduceLROnPlateau : 모델학습시 학습률 개선을 위한 부분입니다. 최종 목표로 도달하기전 국지적인 구덩이에 빠질경우 학습률을 조절해서 빠져 나오게 합니다.

EarlyStopping : 학습시 유의미한 변화가 없는 경우 빠져나오게 합니다. 과대적합과 과소적합을 적절히 잡아줄 수 있습니다.

 

모델은 데이터를 28*28 size로 불러와 학습을 시작합니다. 즉 이부분의 크기와 위 Datagenerator의 이미지 크기를 맞춰줘야 합니다. 

 

또한 SeperableConv2D를 이용하였는데. 이는 기존의 Conv2D와는 살짝 다릅니다. 

youtu.be/T7o3xvJLuHk

간단하게 요약하면 3개의RGB 채널을 전부 convalution 하는 것이 아닌 따로 적용하여 합치는 방식으로 기존의 방법과 비교했을때 보다 작은 연산을 하게 되고 성능상의 이득을 볼수 있다는 내용입니다.

 

이후 모델 컴파일 및 fit 하게 되는데. fit_generator를 이용하여 Genarator로 생성된 Categorical Variable을 바로 사용할 수 있습니다.

 


5. 모델 불러오기, 정확도 확인.

model = load_model('BrailleNet.h5')
acc = model.evaluate_generator(val_generator)[1]
print('model accuracy: {}'.format(round(acc,4)))

위의 ModelCheckPoint로 저장한 모델을 불러오고 그 모델의 정확도를 측정합니다.

 

눈치채신분들도 있으시겠지만 이미 학습된 모델이 있다면 위의 모델 생성 부분 전체를 주석처리하여도 예측 및 정확도 확인을 할 수 있습니다.

 

여기까지가 kaggle에 올라온 코드입니다.

이전 포스트에서 언급했던 점인 점자가 부실하고 문제있다는 점을 감안하면 95%라는 정확도는 정말 높은 결과입니다.

점자만 제대로 고쳐도 2~3% 이상 올라갈 것 같습니다.

 

다음 포스트에선 점자 이미지 생성에 대해서 다뤄 보도록 하겠습니다.

 


이하 코드 전문

import os
import numpy as np
import pandas as pd
from shutil import copyfile
os.mkdir('./images/')
alpha = 'a'
for i in range(0, 26): 
    os.mkdir('./images/' + alpha)
    alpha = chr(ord(alpha) + 1)

rootdir = '/kaggle/input/braille-character-dataset/Braille Dataset/Braille Dataset/'
for file in os.listdir(rootdir):
    letter = file[0]
    copyfile(rootdir+file, './images/' + letter + '/' + file)    
from keras.preprocessing.image import ImageDataGenerator
datagen = ImageDataGenerator(rotation_range=20,
                             shear_range=10,
                             validation_split=0.2)

train_generator = datagen.flow_from_directory('./images/',
                                              target_size=(28,28),
                                              subset='training')

val_generator = datagen.flow_from_directory('./images/',
                                            target_size=(28,28),
                                            subset='validation')
#Using TensorFlow backend.
#Found 1248 images belonging to 26 classes.
#Found 312 images belonging to 26 classes.
from keras import backend as K
from keras import layers as L
from keras.models import Model,load_model
from keras.regularizers import l2
from keras.callbacks import ModelCheckpoint,ReduceLROnPlateau,EarlyStopping

K.clear_session()

model_ckpt = ModelCheckpoint('BrailleNet.h5',save_best_only=True)
reduce_lr = ReduceLROnPlateau(patience=8,verbose=0)
early_stop = EarlyStopping(patience=15,verbose=1)

entry = L.Input(shape=(28,28,3))
x = L.SeparableConv2D(64,(3,3),activation='relu')(entry)
x = L.MaxPooling2D((2,2))(x)
x = L.SeparableConv2D(128,(3,3),activation='relu')(x)
x = L.MaxPooling2D((2,2))(x)
x = L.SeparableConv2D(256,(2,2),activation='relu')(x)
x = L.GlobalMaxPooling2D()(x)
x = L.Dense(256)(x)
x = L.LeakyReLU()(x)
x = L.Dense(64,kernel_regularizer=l2(2e-4))(x)
x = L.LeakyReLU()(x)
x = L.Dense(26,activation='softmax')(x)

model = Model(entry,x)
model.compile(loss='categorical_crossentropy',optimizer='adam',metrics=['accuracy'])

history = model.fit_generator(train_generator,validation_data=val_generator,epochs=666,
                              callbacks=[model_ckpt,reduce_lr,early_stop],verbose=0)

#Epoch 00071: early stopping
model = load_model('BrailleNet.h5')
acc = model.evaluate_generator(val_generator)[1]
print('model accuracy: {}'.format(round(acc,4)))
model accuracy: 0.9519