본문 바로가기
공부하는 중~~/인공지능

[Keras] mnist 데이터셋을 사용하여 CNN 분류모델 만들어보기

by 임대추 2020. 10. 12.

 

안녕하세요 대추입니다.

이번에는 CNN 공부를 시작할때 누구나 한번쯤은 거쳐가는 예제인

mnist 데이터셋을 이용한 분류모델 만들기 입니다.

 

keras 정식 사이트의 예제 코드에서 약간씩 바뀐 코드입니다.

숫자 분류 모델은 GPU가 필요없기는 한데 나중에는 GPU가 필요하니

지금부터 구글의 Colab 환경에서 진행하겠습니다.

 

1. mnist 데이터셋 다운로드 후 확인

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers

mnist = tf.keras.datasets.mnist

(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0
print(x_train.shape, y_train.shape, x_test.shape, y_test.shape)

데이터셋의 shape

데이터셋을 받은 뒤 확인해보면

train : 60000장, test : 10000장, 데이터가 28*28 크기로 다운로드 된것을 확인할 수 있습니다.

여기서 x_train, x_test에 255를 나누는 이유는 사진을 읽어오면 컬러 기준으로

(R, G, B)로 최대 (255, 255, 255) 형태로 가져오는데 여기에 255를 나눠 0~1의 값을 가지게 만들어 

데이터의 분산의 정도를 바꾸는, 즉 데이터 정규화 과정을 거치게 하기 위해서 입니다.

 

 

2. 데이터를 시각화 해보기

import matplotlib.pyplot as plt


plt.figure(figsize=(10, 10))
c = 0
for x in range(5):
    for y in range(3):
        plt.subplot(5,3,c+1)
        plt.imshow(x_train[c], cmap='gray')
        c+=1
    
plt.show()

print(y_train[:15])

데이터 시각화

3*5로 데이터와 라벨을 출력한 모습입니다. 

비교해보면 사진과 라벨이 맞는 것을 확인할 수 있습니다.

지금은 답이 써져있지만 이제 저 라벨부분을 예측하는 모델을 만들 것입니다.

 

 

3. 모델을 Sequential 모델로 직접 만들어보기

# 모델 레이어 설정
model = tf.keras.models.Sequential([
  tf.keras.layers.Flatten(input_shape=(28, 28)),
  tf.keras.layers.Dense(128, activation='relu'),
  tf.keras.layers.Dropout(0.2),
  tf.keras.layers.Dense(10, activation='softmax')
])

# 옵티마이저와 손실함수를 설정하고 정확도 매트릭스가 나오게 컴파일
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])
         
# 모델의 모습을 표로 출력
model.summary()
     
# 위의 레이어를 가진 모델로 5번 학습시킴
print(model.fit(x_train, y_train, epochs=5))

# 검증을 해봄
print(model.evaluate(x_test, y_test))

위의 모델 실행 사진

모델 : Sequential

레이어 : 설정했던 4개

5번 학습한 후 loss 값과 정확도가 출력 (0.07의 loss 값이 나왔고 97%의 정확도를 가진다고 해석하면 됩니다.)

 

 

4. 모델에 레이어를 더 추가하여 정확도를 높여보자

# Conv, Pooling 레이어를 추가한 후 Dense 레이어가 들어간 모델
model = tf.keras.models.Sequential([
    tf.keras.layers.Conv2D(input_shape=(28, 28, 1), kernel_size=3, filters=64),
    tf.keras.layers.MaxPool2D(pool_size=(2,2), strides=(1,1)),
    tf.keras.layers.Conv2D(input_shape=(28, 28, 1), kernel_size=3, filters=32),
    tf.keras.layers.MaxPool2D(pool_size=(2,2), strides=(1,1)),
    tf.keras.layers.Flatten(),

    tf.keras.layers.Dense(64, activation='relu'),
    tf.keras.layers.Dropout(0.2),
    tf.keras.layers.Dense(32, activation='relu'),
    tf.keras.layers.Dropout(0.2),
    tf.keras.layers.Dense(10, activation='softmax'),
])

# 옵티마이저와 손실함수를 설정하고 정확도 매트릭스가 나오게 컴파일
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])
         
# 모델의 모습을 표로 출력
model.summary()
     
# 위의 레이어를 가진 모델로 5번 학습시킴
print(model.fit(x_train, y_train, epochs=5))

# 검증을 해봄
print(model.evaluate(x_test, y_test))

위의 모델 실행 사진

똑같이 Sequential 모델이고 위의 모델에서 파라미터가 10만개에서 100만개로 늘어난 모습을 볼 수 있습니다.

예상할수 있듯 Conv 레이어가 추가될 수록, Dense 레이어가 많아질 수록 파라미터의 수는 기하급수적으로 증가합니다.

레이어가 많을 수록 성능이 좋아지지만 학습에 더 많은 시간이 필요하기 때문에

성능과 효율의 적정선을 찾는 것이 중요합니다.

첫 번째 심플한 모델은 1epoch당 3~4초가 걸렸는데 이 모델의 경우에는 6초가 걸렸네요

2배정도의 시간을 더 사용하고 정확도가 약 1%정도 증가했습니다. 

레이어의 수와 성능이 한계없이 쭉 비례관계인지는 잘 모르겠지만 어느정도 비례이고 파라미터의 수와 학습 시간도

비례인 것을 확인 했습니다.

 

 

5. 내가 실제 손으로 쓴 숫자를 모델에 적용시켜 분류해보자!

# 구글 드라이브와 연동
# 링크가 나오고 입력창이 생기는데 링크를 따라 들어가서 진행하면 어떤 코드를 줍니다.
# 코드를 복사하고 입력창에 넣으면 인증이되고 구글 드라이브와 연동이 됩니다.
from google.colab import auth
from google.colab import drive
drive.mount('/content/gdrive')


# 사진을 찍어 이미지를 구글 드라이브의 원하는 곳에 넣으세요
# 저는 구글 드라이브 맨 처음 폴더에 넣겠습니다.
%cd ~/../content/gdrive/My\ Drive
!ls

ls 명령어로 사진이 있는 폴더로 마운트된 것을 확인 가능, 우측에는 실제 쓴 숫자 사진의 모습

8로 손글씨를 쓴다음 구글 드라이브에 저장을 했고

구글 드라이브와 연동을 통해 사진에 접근 할 수 있는 환경이 만들어 졌습니다.

(자세한 Colaboratory 사용법은 추후 꿀팁 카테고리에 올리겠습니다.)

이제 만들었던 모델에 넣어서 잘 분류가 되나 확인해 볼까요?

import cv2
import numpy as np
import matplotlib.pyplot as plt

src = cv2.imread('./myNum.jpg', cv2.IMREAD_GRAYSCALE)
ret , binary = cv2.threshold(src,170,255,cv2.THRESH_BINARY_INV)
myNum = np.asarray(cv2.resize(binary, dsize=(28, 28), interpolation=cv2.INTER_AREA))/255

plt.title('The number I write')
plt.imshow(myNum, cmap='gray')
print(myNum.shape)

먼저 이미지를 불러와야겠죠? mnist의 데이터처럼 검은 바탕에 흰색으로 보이게 만들어야 합니다. 사진으로는 흰색과 

검은색으로 보이지만 컴퓨터는 세세한 컬러까지 숫자로 변환하기 때문에 분류에 방해가 되기 때문입니다.

그래서 함수를 사용해 이진화를 시켜줍니다. 저의 경우 배열을 출력해보자

흰색 바탕은 170~190의 값을 가지고 검은색은 7~80의 값을 가지는 것을 보고 170이상, 이하로

이진화가 되게 코드를 작성했습니다.

이번에는 사진의 크기를 설정해야 하는데 아까 만든 모델의 input 형태인 (28, 28)로 resize를 시켜줘야 합니다.

(잘모르겠다면 글 4번으로 올라가 확인해보세요.)

28*28의 형태를 가지도록 resize를 해주고 잘 되었는지 출력하면 잘 나오는 것을 볼 수 있습니다.

(28, 28)의 shape를 가지는 것은 위에 cv2.IMREAD_GRAYSCALE로 가져왔기 때문이고

cv2.IMREAD_COLOR로 가져오면 (28, 28, 3)의 shape를 가지게 됩니다.

(왜 1과 3, 각각 다른지는 CNN 기초 게시글에서 말씀드렸죠?)

이제 모델에 이 것을 넣어 분류를 해봅시다. 

categoris = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

print(np.argmax(model.predict(myNum.reshape(-1, 28, 28, 1))[0]))

8로 잘 분류가 되는 것을 확인 할 수 있습니다.

 

모델을 직접 만들어보고 자신의 데이터를 넣어 분류까지 해보니 재미있으셨나요? 지금이야 간단하니 재미있겠지만

레이어를 넣고 빼고 한번씩 학습을 해보고 최적의 모델을 찾는 것은 연구목적이 아니면 해보기가 힘듭니다.

그렇기 때문에 저희보다 뛰어난 사람들이 만들고 많은 돈을 써 검증받은 모델의 레이어들을 가져와 쓰면 성능도 챙기고

시간도 아낄수 있겠죠?

다음 시간에는 VGG16이라는 뛰어난 모델을 가지고 간단하게 전이학습을 해보는 예제를 하겠습니다.

 

 

 

저도 공부를 하며 포스팅을 하기때문에 틀리거나 다른 내용이 있다면 댓글로 남겨주세요 바로 피드백 하겠습니다 :)

반응형

댓글