프로필사진

IT Anthology/encyclopedia

[밑러닝] 손글씨 숫자 인식으로 해보는 간단한 인공신경망 예측(feat. MNIST 데이터셋)

다각 2020. 3. 17. 11:16

* 이 글은 <밑바닥부터 시작하는 딥러닝2 (저자: 사이토 고키)> 책을 읽으며 정리한 글입니다.
* 나중에라도 제가 참고하기 위해 정리해 두었으며, 모든 내용을 적은 것이 아닌,
필요하다고 생각되는 부분만 추려서 정리한 것임을 미리 밝힙니다.


목차

  1. 학습과 예측
  2. MNIST 데이터셋
  3. 인공신경망 구축
  4. 배치 처리

1. 학습과 예측

기계학습의 문제 해결은 두 단계를 거친다. 첫번째는 학습이고, 두번째는 예측이다. 학습의 과정은 적당한 가중치를 찾는 과정이고, 예측의 과정은 그렇게 찾아낸 적당한 가중치를 대입해서 새로운 데이터의 정답이 뭘지 찾아내는 과정이다.

학습의 과정은 간략하게 다음과 같다.

  1. 데이터셋 파악
  2. 훈련용 데이터와 테스트용 데이터로 나누기
  3. 인공신경망 구축
  4. 적당한 가중치 찾기

위의 과정을 거쳐 찾아낸 적당한 가중치를 대입한다.
예측의 과정은 다음과 같다.

  1. 학습된 적당한 가중치 대입
  2. 테스트용 데이터 입력
  3. 출력된 값과 원래의 정답 비교

여기서는 예측의 과정만 보여주겠다.

 

2. MNIST 데이터셋

MNIST 데이터셋은 손글씨 숫지 이미지를 이르는 말로, 기계학습을 공부하는 사용자라면 한번씩은 거쳐가는 유명한 데이터셋이다.
0부터 9까지의 숫자 이미지로 구성되며, 28pixel$\times$28pixel크기의 회색조 이미지이다. 각 하나의 픽셀은 0에서 255까지의 값을 가지고 있다. (255에 가까울 수록 짙은 농도를 표현한다).

MNIST 데이터셋은 지원 깃허브에서 dataset/mnist.py 파일을 실행시켜서 다운받을 수 있다. 이 mnist.py 파일은, 아래와 같은 코드 한 줄을 파이썬에서 실행시켜서 받을 수도 있다.

!curl -O https://raw.githubusercontent.com/WegraLee/deep-learning-from-scratch/master/dataset/mnist.py

mnist.py 파일이, 지금 있는 위치 디렉토리에 다운이 잘 받아졌을 것이다. 이제 다음 코드를 실행해보자.

from mnist import load_mnist
(x_train, t_train), (x_test, t_test) = load_mnist(flatten=True, normalize=False)

mnist 파일로부터 load_mnist라는 함수를 실행해서 각 변수에 값을 저장해준다. (같은 디렉토리에 mnist.pkl 파일이 생긴 것도 발견할 수 있을 것이다). 가능하면 이 코드는 한번만 실행하고 다음부터는 쓰지 않는 것으로 하자. 데이터를 받아오는 함수이기 때문에 인터넷에 연결되어 있어야하고, 시간이 조금 걸리기 때문이다.

데이터가 어떤 모양인지 확인하기 위해 데이터를 하나만 뽑아와보자.

import matplotlib.pyplot as plt
img = x_train[0].reshape(28,28) # 현재 flatten으로 데이터의 차원을 1차원으로 만들었기 때문에 다시 (28,28) 모양으로 만들어준다.
plt.imshow(img, cmap='Greys');

위의 코드를 실행하면 다음과 같은 그림이 뜬다.

한편, 다음 코드를 통해 이 데이터의 정답이 무엇인지 (이 그림을 보고 인공신경망이 이것을 뭐라고 분류해야 하는지) 확인할 수 있다.

print(t_train[0])

'5'가 위의 그림의 정답인 것을 확인할 수 있다.

 

3. 인공신경망 구축

여기서는 예측의 과정만 보여줄 것임으로, 이미 학습된 가중치를 불러오겠다.
여기에서 'view raw' 버튼을 클릭하면 다운받을 수 있다. 이렇게 다운받은 피클 파일을 같은 파일경로 내에 넣어준다.

인공신경망을 구축하기에 앞서, 이것이 크게 어떤 입력값과 출력값을 가지는지 속으로 그려볼 필요가 있다.
이번에 사용되는 데이터는 총 784개의 픽셀을 가지고 있다. 따라서 입력 노드는 784개가 되어야 할 것이다. 또한, 출력으로는 0에서 9 사이의 숫자를 구분해야 하기 때문에 10개의 노드를 가지게 된다.
여기서 은닉층은 총 두개로, 첫번째 은닉층은 50개의 노드를, 두번째 은닉층은 100개의 노드로 임의로 정했다.

import numpy as np
import pickle
from mnist import load_mnist

def init_network():
    with open("sample_weight.pkl", 'rb') as f:
        network = pickle.load(f)
    return network

def sigmoid(x): return 1 / (1 + np.exp(-x))
def softmax(x): 
    c = np.max(x)
    exp_x = np.exp(x-c)
    sum_exp_x = np.sum(exp_x)
    return exp_x/sum_exp_x

def predict(network, x):
    W1, W2, W3 = network['W1'], network['W2'], network['W3']
    b1, b2, b3 = network['b1'], network['b2'], network['b3']

    a1 = np.dot(x, W1) + b1
    z1 = sigmoid(a1)
    a2 = np.dot(z1, W2) + b2
    z2 = sigmoid(a2)
    a3 = np.dot(z2, W3) + b3
    y = softmax(a3)
    return y

저장해두었던 가중치를 이용하여 인공신경망을 만들어냈다. 이제 이 신경망을 이용하여 예측을 시작해보자.

# 테스트 데이터 로드
(x_train, t_train), (x_test, t_test)  = load_mnist(normalize=True, flatten=True, one_hot_label=False)

# 신경망 생성
network = init_network()

# 정확도 측정
accuracy_cnt = 0

for i in range(len(x_test)): # 지금은 훈련이 아니라 예측이기 때문에 테스트 셋만 가져온다.
    y = predict(network, x_test[i])
    p = np.argmax(y)         # 확률이 가장 높은 원소의 인덱스를 얻는다.
    if p == t_test[i]:       # 예측한 원소의 인덱스가 정답과 같은지 확인한다.
        accuracy_cnt += 1    # 정답이 같으면 정확도를 1씩 올려준다.
print("Accuracy: {}".format(float(accuracy_cnt)/len(x_test)))    

 

4. 배치 처리

위에서 구축한 신경망은 이미지를 하나씩 받아 처리하는 과정이다. 하지만 컴퓨터에서는 (특히 수치 계산 라이브러리에서는) 데이터 하나씩 처리하는 것보다 오히려 큰 배열의 처리가 더 빠를 수 있다. 커다란 신경망에서는 데이터 전송이 병목으로 작용하는 경우가 자주 있기 때문이다. 따라서 데이터 하나씩이 아니라, 수십 장, 수백 장씩 묶어서 계산하는 것이다. 이렇게 하나로 묶은 입력 데이터 다발을 배치(batch) 라고 한다.
배치를 할 때는, 입력 데이터 100개에 출력 데이터 100개가 나와야한다는 것은 자명한 사실이므로, 두 값의 배치 사이즈를 맞춰주면 된다.
배치 처리를 하는 코드는 다음과 같다.

network = init_network()

batch_size = 100    # 배치 사이즈 지정
accuracy_cnt = 0

for i in range(0, len(x_test), batch_size):    # 배치 사이즈만큼 건너 뛰며 i 변수 지정
    x_batch = x_test[i:i+batch_size]           # 배치 사이즈만큼의 구간 배열 지정
    y_batch = predict(network, x_batch)        # 배치 사이즈만큼의 데이터 모두 예측
    p = np.argmax(y_batch, axis=1)             # 각 출력값에서 제일 큰 값을 가지는 확률의 인덱스 배열 저장
    accuracy_cnt += np.sum(p == t_test[i:i+batch_size]) # np.sum(배열1==배열2)를 할 경우 True의 개수를 센다.

print("Accuracy: {}".format(float(accuracy_cnt)/len(x_test)))

결과는 배치처리 하기 전의 값과 같게 0.9352가 나온다.

 


 

정리
이상, 간단한 인공신경망의 추론 과정을 살펴보았다. 다음 포스트에서는 신경망의 '학습'에 대해 본격적으로 학습해보도록 하자.