본문 바로가기
딥러닝관련/기초 이론

MNIST 인식(밑바닥부터 시작하는 딥러닝)

by 머리올리자 2021. 5. 4.

MNIST 데이터셋

MNIST : 손글씨 숫자 이미지 집합

(엠엔아이에스티 or 엠니스트 라고 많이 이야기 하는 듯)

 

- 0~9까지의 숫자 이미지

- 학습 이미지 : 60,000장

- 테스트 이미지 : 10,000장

 

github.com/WegraLee/deep-learning-from-scratch

 

WegraLee/deep-learning-from-scratch

『밑바닥부터 시작하는 딥러닝』(한빛미디어, 2017). Contribute to WegraLee/deep-learning-from-scratch development by creating an account on GitHub.

github.com

위 깃헙에서 코드를 다운받아 활용한다.

 

MNIST 데이터 살펴보기

from dataset.mnist import load_mnist

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

print(x_train.shape)
print(t_train.shape)
print(x_test.shape)
print(t_test.shape)

결과

(60000, 784)
(60000,)
(10000, 784)
(10000,)

 

28 x 28 크기의 1채널 이미지, 0에서 255값을 취한다.

 

load_mnist 함수에 정의된 인자들이 아래와 같이 flatten, normalize, ont_hot_label이 있다.

 

def load_mnist(normalize=True, flatten=True, one_hot_label=False):
    """MNIST 데이터셋 읽기
    
    Parameters
    ----------
    normalize : 이미지의 픽셀 값을 0.0~1.0 사이의 값으로 정규화할지 정한다.
    one_hot_label : 
        one_hot_label이 True면、레이블을 원-핫(one-hot) 배열로 돌려준다.
        one-hot 배열은 예를 들어 [0,0,1,0,0,0,0,0,0,0]처럼 한 원소만 1인 배열이다.
    flatten : 입력 이미지를 1차원 배열로 만들지를 정한다. 
    
    Returns
    -------
    (훈련 이미지, 훈련 레이블), (시험 이미지, 시험 레이블)
    """

 

MNIST 이미지 화면으로 불러오기

from dataset.mnist import load_mnist
import numpy as np
from PIL import Image

def img_show(img):
    pil_img = Image.fromarray(np.uint8(img))
    pil_img.show()



(x_train, t_train), (x_test, t_test) = load_mnist(flatten=True, normalize=False, one_hot_label=False)

img = x_train[0]
label = t_train[0]
print(label)

print(img.shape)
img = img.reshape(28, 28)
print(img.shape)

img_show(img)

 

flatten=True로 읽어 들인 이미지는 1차원 numpy 배열로 저장되기 때문에 원래 형상인 28 x 28 크기로 다시 변형해야 한다.

 

신경망의 추론

 

신경망의 입력층 뉴런 784개, 출력층 뉴런을 10개 구성

 

입력층 뉴런이 784개인 이유 : 28 x 28 = 784이기 때문

 

출력이 10개인 이유 : 0~9까지의 숫자 구분

 

은닉층 : 두 개 (첫 번째 50개의 뉴런, 두 번째 100개의 뉴런)

 

 

코드

from dataset.mnist import load_mnist
import numpy as np
import pickle
from PIL import Image

def sigmoid(x):
    return 1 / (1 + np.exp(-x))

def softmax(a):
    c = np.max(a)
    exp_a = np.exp(a-c)
    sum_exp_a = np.sum(exp_a)
    y = exp_a / sum_exp_a

    return y

def get_data():
    (x_train, t_train), (x_test, t_test) = load_mnist(flatten=True, normalize=True, one_hot_label=False)
    return x_test, t_test

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

    return network



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, t = get_data()
network = init_network()


accuracy_cnt = 0

for i in range(len(x)):
    y = predict(network, x[i])
    p = np.argmax(y) # 확률이 가장 높은 원소의 인덱스
    if p == t[i]:
        accuracy_cnt += 1


print("Accuracy : " + str(float(accuracy_cnt)/ len(x)))

 

1) MNIST 데이터를 얻는다.

2) sample_weight로 부터 미리 학습된 가중치 매개변수를 읽는다.

3) 반복문을 돌며 x에 저장된 이미지를 꺼내 predict 함수로 분류 -> predict output [0.1, 0.3, 0.2, ... 0.04]와 같은 배열

4) np.argmax() 함수로 배열에서 값이 가장 큰(확률이 가장 높은) 원소의 인덱스를 구한다. - 예측의 결과

5) 신경망이 예측한 답변과 정답 레이블을 비교하여 맞힌 숫자를 세고, 이를 전체 이미지 숫자로 나눠 정확도 계산

 

normalization True이면 픽셀의 값을 0.0 ~ 1.0 범위로 변환.

 

데이터를 특정 범위로 변환하는 처리를 정규화(normalization)

 

신경망의 입력 데이터에 특정 변환을 가하는 것을 전처리(pre-processing)

 

결국 위는 데이터에 대한 전처리 작업으로 정규화를 수행한 것과 같음.

 


정규화 종료는 매우 다양

 

단순히 255로 나누기도 하지만

 

데이터 전체의 분포를 고려해 처리하는 경우도 많음

 

ex 1) 데이터 전체 평균과 표준편차를 이용하여 데이터들이 0을 중심으로 분포하도록 이동시키거나 데이터의 확산 범위를 제한하는 정규화를 수행

 

ex 2) 전체 데이터를 균일하게 분포시키는 데이터 백색화(whitening) 

 

등등.. 


 

배치 처리

 

x, _ = get_data()
network = init_network()
W1, W2, W3 = network['W1'], network['W2'], network['W3']

print(x.shape)
print(x[0].shape)
print(W1.shape)
print(W2.shape)
print(W3.shape)

결과

(10000, 784)
(784,)
(784, 50)
(50, 100)
(100, 10)

 

다차원 배열의 대응하는 차원의 원소 수가 일치함

 

784개로 구성된 1차원 배열 ( 28 x 28)

마지막 원소 10개인 1차원 배열로 출력

 

위는 이미지 한 장만 입력했을 때의 흐름

 

아래는 이미지를 한꺼번에 여러 장을 넣었을 때

 

100자의 이미지를 묶어 predict 함수에 넘긴다면 아래와 같이 표현

 

 

입력 데이터 사이즈 : 100 x 784

 

출력 데이터 사이즈 : 100 x 10

 

100장 분량 입력 데이터의 결과가 한 번에 출력됨

 

이처럼 하나로 묶은 입력 데이터를 배치(batch) 라 표현

 

 

배치 처리는 컴퓨터로 계산할 때 큰 이점이 있다.

 

 - 이미지 1장당 처리 시간을대폭 줄여준다.

 

왜?

 

1. 수치 계산 라이브러리의 대부분이 큰 배열을 효율적으로 처리할 수 있도록 최적화되어 있기 때문에

 

2. 커다란 신경망에서는 데이터 전송이 병목이 되는 경우가 자주 있다. -> 배치 처리를 함으로써 버스에 주는 부하를 줄임

(느린 I/O를 통해 데이터를 읽는 횟수에 비해, 빠른 CPU나 GPU로 순수 계산을 수행하는 비율이 높아진다)

 

즉, 배치 처리를 수행함으로써 큰 배열이 이뤄진 계산을 하게 되는데, 컴퓨터에서는 큰 배열을 한꺼번에 계산하는 것이 분할된 작은 배열을 여러 번 계산하는 것보다 빠르다.

 

from dataset.mnist import load_mnist
import numpy as np
import pickle
from PIL import Image

def sigmoid(x):
    return 1 / (1 + np.exp(-x))

def softmax(a):
    c = np.max(a)
    exp_a = np.exp(a-c)
    sum_exp_a = np.sum(exp_a)
    y = exp_a / sum_exp_a

    return y

def get_data():
    (x_train, t_train), (x_test, t_test) = load_mnist(flatten=True, normalize=True, one_hot_label=False)
    return x_test, t_test

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

    return network

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, t = get_data()
network = init_network()


accuracy_cnt = 0
x, _ = get_data()
batch_size = 100
network = init_network()


for i in range(0, len(x), batch_size): # 0부터 x의 크기까지 batch_size 단위로 건너뛰면서
    x_batch = x[i:i+batch_size]
    y_batch = predict(network, x_batch)

    p = np.argmax(y_batch, axis=1) # 확률이 가장 높은 원소의 인덱스, axis 1, 즉, 새로운 배치 배열 축을 중심으로 max값 계산
    accuracy_cnt += np.sum(p == t[i:i+batch_size])


print("Accuracy : " + str(float(accuracy_cnt)/ len(x)))

 

내용 참고

book.naver.com/bookdb/book_detail.nhn?bid=11492334

 

밑바닥부터 시작하는 딥러닝

직접 구현하고 움직여보며 익히는 가장 쉬운 딥러닝 입문서!『밑바닥부터 시작하는 딥러닝』은 라이브러리나 프레임워크에 의존하지 않고, 딥러닝의 핵심을 ‘밑바닥부터’ 직접 만들어보며

book.naver.com