오늘은 AI의 대표적인 데이터셋인 MNIST를 이용하여
손글씨 숫자 이미지를 인식하는 실습을 해보고자 한다.
스마트폰 앱에서 글씨를 캡쳐하면 실제 데이터로 변형되어 출력되는 것을 본적이 있을 것이다.
오늘은 이런 것이 어떻게 가능한 것인지 알아보고, 실습하는데 목적을 둔다.
*추후 모델과 기초 개념에 대해서도 다룰 예정이다.
0. 목표
우리는 위 숫자가 '8'이란 것을 안다. 어떻게 알게 되었는가?
어렸을 때 우린 1부터 9까지 숫자들의 모양을 학습하였고,
위 이미지와 우리가 알고있는 모양인 숫자 8과 일치시켜 답을 내놓을 수 있었을 것이다.
컴퓨터로는 어떻게 위 이미지를 인식하여 데이터를 처리할 수 있을까?
우리는 해당 포스팅을 통해 '손글씨 숫자 이미지를 인식하여 예측'해보는 모델을 만들어보고자 한다.
- 1부터 9까지의 데이터를 학습하여 손글씨 이미지를 예측할 수 있는 '모델'을 만들고
- 임의의 손글씨 이미지를 만들어 이 그림이 어떠한 숫자를 의미하는지 예측해 볼 것이다.
*참고로 이미지 데이터를 처리할때 높은 정확도를 도출하기 위해 CNN 방식을 많이 사용하지만 본 포스팅에선 CNN을 중점적으로 다루지 않는다. 추후 다룰 예정이다.
1. MNIST 데이터 셋
이번 포스팅에서도 다시 얘기하지만 Deep Learning의 핵심은 '좋은 데이터'이다.
이번은 AI에서 대표적인 손글씨 데이터 셋인 MNIST 데이터 셋에 대해 이야기해보겠다.
MNIST는 다음과 같은 특징을 갖는다.
- 대표 데이터 셋인 만큼 import하여 손쉽게 데이터를 불러올 수 있다.
- 60,000개의 학습 데이터, 10,000개의 테스트 데이터로 나뉘어 있다.
- 각 이미지는 28x28 크기로 784개의 픽셀을 가진다.
(1) 데이터를 불러오는 방법은 다음과 같다.
from keras.datasets import mnist
(2) 데이터 갯수는 다음과 같다.
(X_train, Y_class_train), (X_test, Y_class_test) = mnist.load_data()
print("학습셋 이미지 수 : %d 개" % (X_train.shape[0]))
print("테스트셋 이미지 수 : %d 개" % (X_test.shape[0]))
- 첫번째 라인에서 X_train에는 학습할 60,000개의 각 784개 픽셀 정보가 배열 형태로 저장되게 되고
- Y_class_train에는 60,000개 이미지 정답인 클래스 정보가 담기게 된다.
- X_test와 Y_class_test는 테스트 데이터로 10,000개이고 형태는 위 학습데이터와 같다.
(3) 다음은 데이터 형태이다(X_train 데이터의 첫번째 원소).
# 이미지 형태 출력을 위한 pyplot 모듈 import
import matplotlib.pyplot as plt
# 위 60000개 데이터 배열에서 0번째 원소를 흑백으로 출력
plt.imshow(X_train[0], cmap='Greys')
plt.show()
################################
# 0번째 원소의 모든 데이터를 출력
for x in X_train[0]:
for i in x:
sys.stdout.write('%d ' % i)
sys.stdout.write('\n')
- 위 그림을 보면 가로 28개 픽셀, 세로 28개 필셀의 집합으로 하나의 그림이 만들어 진 것을 볼 수 있다.
- 흰색은 0, 검정색은 255 이렇게 각 픽셀의 색상을 데이터화하여 X_train의 원소에 담겨져 있는 것이다.
2. Training
# 딥러닝에 필요한 케라스 함수 호출
from keras.utils import np_utils
from keras.models import Sequential
from keras.layers import Dense
# 필요 라이브러리 호출
import numpy
import tensorflow as tf
# 데이터 셋 호출
from keras.datasets import mnist
# 실행 시마다 같은 결과값 도출을 위한 시드 설정
numpy.random.seed(0)
tf.random.set_seed(0)
# 데이터를 불러와서 각 변수에 저장
(X_train, Y_train), (X_test, Y_test) = mnist.load_data()
# 학습에 적합한 형태로 데이터 가공
X_train = X_train.reshape(X_train.shape[0], 784).astype('float32') / 255
X_test = X_test.reshape(X_test.shape[0], 784).astype('float32') / 255
# 클래스를 학습에 이용하기 위해 데이터 가공
Y_train = np_utils.to_categorical(Y_train, 10)
Y_test = np_utils.to_categorical(Y_test, 10)
# 딥러닝 모델 구조 설정(2개층, 512개의 뉴런 연결, 10개 클래스 출력 뉴런, 784개 픽셀 input 값, relu와 softmax 활성화 함수 이용)
model = Sequential()
model.add(Dense(512, input_dim=784, activation='relu'))
model.add(Dense(10, activation='softmax'))
# 딥러닝 구조 설정(loss 옵션을 다중 클래스에 적합한 categorical_crossentropy, 옵티마이저는 adam 설정)
model.compile(loss='categorical_crossentropy', optimizer='adam',
metrics=['accuracy'])
# 모델 실행(X_test, Y_test로 검증, 200개씩 30번 학습)
model.fit(X_train, Y_train, validation_data=(X_test, Y_test), epochs=30, batch_size=200, verbose=2)
# 학습 정확도, 검증 정확도 출력
print('\nAccuracy: {:.4f}'.format(model.evaluate(X_train, Y_train)[1]))
print('\nVal_Accuracy: {:.4f}'.format(model.evaluate(X_test, Y_test)[1]))
# 모델 저장
model.save('Predict_Model.h5')
다음 부분에 설명이 필요할 것이다.
- X_train.reshape(X_train.shape[0], 784).astype('float32') / 255
- 학습을 위해 각 데이터는 0-255에서 0-1사이의 숫자로 변환해야한다. 그래서 위 연산을 수행한다.
- 위 함수를 아래 3줄로 풀어 출력하면 다음과 같다. 천천히 이해를 해보기 바란다.
- np_utils.to_categorical(Y_train, 10)
- 해당 클래스 정보도 0-10 값을 0, 1, 2, 3~~ 이렇게 저장할 수 있지만 앞서 말했듯이 0, 1값으로 저장해야 원할한 데이터 처리가 가능하다.
- 5를 표현하기 위해 클래스 갯수를 가지는 리스트를 만들어 5번째 원소에만 1을 주는 방식으로 one-hot vector화하는 함수다.
이후 학습을 진행한다.
CNN을 사용하지 않았지만 높은 정확도를 보였다. 검증 정확도가 98%이다.
해당 모델은 다음과 같이 잘 저장되었다.
3. Testing
테스트를 위해 이미지를 하나 만들어보았다.
픽셀을 28x28로 만들고(어차피 resize해서 필요없긴하나 편의를 위해), 붓으로 숫자 8을 그려보았다.
이제 jupyter notebook에 업로드하고 다음과 같이 코딩한다.
# 딥러닝에 필요한 케라스 함수 호출
from keras.models import load_model
from keras.utils import np_utils
# 필요 라이브러리 호출(PIL은 이미지파일 처리위함)
from PIL import Image
import numpy as np
# test.png는 그림판에서 붓으로 숫자 8을 그린 이미지 파일
# test.png 파일 열어서 L(256단계 흑백이미지)로 변환
img = Image.open("test.png").convert("L")
# 이미지를 784개 흑백 픽셀로 사이즈 변환
img = np.resize(img, (1, 784))
# 데이터를 모델에 적용할 수 있도록 가공
test_data = ((np.array(img) / 255) - 1) * -1
# 모델 불러오기
model = load_model('Predict_Model.h5')
# 클래스 예측 함수에 가공된 테스트 데이터 넣어 결과 도출
res = model.predict_classes(test_data)
# 2021/10/02 수정 - 오류시 아래 명령어로 대체 가능합니다.
# res =(model.predict(test_data) > 0.5).astype("int32")
print(res)
출력 결과는 다음과 같다.
정확히 예측에 성공하였다.
학습 데이터가 좋아 쉽게 성공하였는데 이후에 CNN을 이용하여 실습해 보겠다.
4. 블랙박스 부수기
현재 우리는 블랙박스를 부수지 못하고있다.
인식, 예측엔 성공했지만 '어떻게' 이것이 가능한지는 원리적으로 알지 못한다.
아직 model.add(Dense(~~)) 부분도 상세히 설명하지 않았다.
해당 포스팅은 딥러닝이란 분야에 흥미를 불러일으키기 위한 목적을 가진다.
이후 글부터 '어떻게'에 초점을 맞추어 활성화 함수부터 모델에 대한 전반적인 개념에 대한 포스팅을 진행할 예정이다.
그리고 다시 해당 포스팅으로 돌아오면, 안보이던 코드(블랙 박스)가 하나하나 보이기 시작할 것이다.
같이 공부해요 ㅎㅎ
'Programming > AI' 카테고리의 다른 글
[Deep Learning] 폐암 환자 수술 후 생존 여부 예측해보기 (0) | 2020.08.28 |
---|---|
[Deep Learning] 아나콘다, 텐서플로, 주피터노트북 설치 및 환경세팅 (0) | 2020.08.27 |
[AI] 인공지능과 머신러닝, 그리고 딥러닝 - 순한맛 (0) | 2020.06.13 |