Broccoli's House

#2-(5) 신경망 : MNIST 손글씨 숫자 인식 본문

공부/딥러닝

#2-(5) 신경망 : MNIST 손글씨 숫자 인식

김콜리 2018. 1. 30. 23:19

※ 이 글의 내용은 O'REILLY의 <밑바닥부터 시작하는 딥러닝> 책을 기반으로 한다.




MNIST 손글씨 숫자 인식




  • MNIST 데이터 세트
 - MNIST(Modified National Institute of Standards and Technology database) : MNIST는 기계학습 분야에서 매우 유명한 데이트 세트로, 손으로 쓴 숫자들로 이루어진 대형 데이터베이스이다. 일반적으로 기계 학습 분야의 훈련과 테스트에 널리 사용된다. 60,000개의 훈련 이미지와 10,000개의 테스트 이미지를 포함하고 있다. 




 - 데이터 세트 불러오기

import numpy as np
import pickle

import sys,os
sys.path.append(os.pardir)
from dataset.mnist import load_mnist
# MNIST 데이터 세트 라이브러리를 사용할수 있도록 한다.

(x_train, t_train), (x_test, t_test) = load_mnist(flatten=True,normalize=False)
# MNIST 데이터 세트에서 훈련 이미지 6만장, 시험 이미지 1만장을 가져온다.
# flatten : 입력 이미지를 평탄화한다. 즉, 28x28 크기의 2차원 배열 이미지를 784의 1차원 배열로 만든다.
# normalize : 입력 이미지를 정규화한다. 즉, 0부터 255사이의 이미지 픽셀 값을 0부터 1까지로 만든다.



 - 이미지 출력

from PIL import Image
def img_show(img) :
pil_img = Image.fromarray(np.uint8(img))
pil_img.show()
# 이미지를 보여주는 함수

img = x_train[0] # 훈련 데이터의 첫 이미지
label = t_train[0] # 훈련 데이터 첫 이미지의 분류
print(label)
print(img.shape) # 이미지의 크기
img = img.reshape(28, 28)
# 불러오는 도중에 평탄화를 하였으므로, 다시 28x28로 바꿔주어야한다.
print(img.shape)

img_show(img)





  • 신경망의 추론 처리
 - 책에서 제공하는 load_mnist 함수는 훈련 데이터에 대한 학습을 알아서 해주기 때문에, 따로 학습에 대한 프로그래밍을 할 필요가 없다. 따라서 학습이 완료된 알고리즘의 성능을 테스트하고 정확도를 측정하는 것만 필요하다.


 - 구성하는 신경망의 입력층 뉴런(노드)의 수는 28x28 크기의 이미지이기 때문에 784개이다. 출력층은 0부터 9까지 구분하는 문제이기 때문에 10개의 뉴런으로 구성된다.



 - 데이터 받아오기

def get_data() :
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, flatten=True, one_hot_label=False)
return x_test, t_test
# MNIST 데이터 세트에서 훈련 이미지 6만장, 시험 이미지 1만장을 가져오는 함수
# flatten : 입력 이미지를 평탄화한다. 즉, 28x28 크기의 2차원 배열 이미지를 784의 1차원 배열로 만든다.
# normalize : 입력 이미지를 정규화한다. 즉, 0부터 255사이의 이미지 픽셀 값을 0부터 1까지로 만든다.
# one_hot_label : 레이블을 원-핫-엔코딩한다. 즉, 판단된 라벨의 확률 중 가장 높은 값만을 1로 하고 나머지는 0으로 한다.



 - 매개변수(가중치, 편향) 받아오기

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

return network
# 'sample_weight.pkl' 파일에는 학습된 가중치 매개변수(가중치, 편향)가 dictionary 타입으로 저장되어있다.
# pickle은 파이썬에서 지원하는 모듈로서, 텍스트가 아닌 자료형들을 저장할 수 있도록 한다.
# 저장되어 있는 pickle 파일을 불러오면 실행 당시의 객체를 즉시 복원할 수 있어, 데이터를 빠르게 준비할 수 있다.



 - 3층 신경망 구성

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
# 3층 신경망의 가중치(weight)와 편향(bias)를 받아온다.
# 각 층의 가중치와 편향으로 뉴런(노드)의 출력을 계산하는 함수



 - 정확도 측정

x, t = get_data()
network = init_network()
# MNIST 데이터 세트를 얻고, 가중치와 편향을 가진 dictionary 타입의 네트워크를 생성한다.

accuracy_cnt = 0
for i in range(len(x)) :
y = predict(network, x[i])
# 훈련 데이터의 크기만큼 반복 구문을 수행하면서, 이미지 데이터를 1장씩 꺼내어 3층 신경망을 통과시켜 출력 값을 계산한다.

p = np.argmax(y)
# 출력 y는 0부터 9까지의 숫자에 해당하는 확률을 가진 배열이다.
# np.argmax(y)는 배열에서 가장 큰 원소 값의 인덱스를 반환한다. 즉, 가장 높은 확률의 순서를 가져온다.

if p == t[i] :
accuracy_cnt += 1
# 프로그램이 분류한 라벨과 실제 라벨이 같은지 검사한다.

print("정확도 : " + str(float(accuracy_cnt)/len(x)))

>> 정확도 : 0.7967




  • 배치 처리

 - 배치(batch) : 입력 데이터를 하나로 묶은 것을 배치라 한다. 기존에는 이미지 1장의 데이터를 불러와 가중치 계산을 하여서 결과 값을 내었다. 그러나 컴퓨터에서는 1장씩 불러와 여러 번 계산하는 것보다, 여러 장의 데이터를 합친 복잡한 배열을 한 번 계산하는 것이 더 빠르다. 수치 계산 라이브러리들이 대게 큰 배열을 처리하는 데에 최적화되어 있기도 하고, 데이터를 읽는 것이 컴퓨터에 부하를 많이 줄 수 있어서 데이터를 읽는 횟수가 적어지면 CPU가 순수 계산을 수행하는 비율이 높아지는 이유도 있다. 따라서 입력 데이터를 여러 개 묶어 한꺼번에 전달하는 것을 배치 처리라 한다.


batch_size = 100 # 배치 크기, 100장의 이미지를 한꺼번에 처리한다는 의미
accuracy_batch = 0

for i in range(0, len(x), batch_size) :
# 0부터 x의 크기-1 까지 배치 크기 간격으로 반복문을 수행한다. (i = 0, 100, 200 ...)

x_batch = x[i:i+batch_size]
# x에는 이미지 데이터가 저장되어 있다. x의 리스트에서 이미지를 반복문의 순서대로 배치 크기만큼 가져온다.
# (0~99, 100~199, 200~299 ...)

y_batch = predict(network, x_batch)
# 배치 크기만큼 묶인 이미지를 3층 신경망에 한꺼번에 통과시켜 출력 값을 계산한다.
# y_batch는 10개의 라벨에 대한 각각의 확률을 가진 100개의 리스트로 구성된다. 즉, y_batch의 크기는 (100x10)이다.

p = np.argmax(y_batch, axis = 1)
# np.argmax는 해당 리스트에서 가장 높은 값의 인덱스, 순서의 번호를 반환한다.
# axis=1은 (100x10) 크기의 y_batch에서 1차원을 구성하는 원소에서 최댓값의 인덱스를 찾도록 한것이다.
# 0차원 : 각 라벨 별 이미지의 확률(100), 1차원 : 각 이미지 별 라벨의 확률(10)

accuracy_batch += np.sum(p == t[i:i+batch_size])
# 실제 라벨과 분류 라벨이 같은지 검사한다. 배열로 반환되기 때문에 모두 더하여 정확도를 계산한다.

print("정확도 : " + str(float(accuracy_batch)/len(x)))




  • 구현 정리

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

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

def get_data() :
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, flatten=True, one_hot_label=False)
return x_test, t_test
# MNIST 데이터 세트에서 훈련 이미지 6만장, 시험 이미지 1만장을 가져오는 함수
# flatten : 입력 이미지를 평탄화한다. 즉, 28x28 크기의 2차원 배열 이미지를 784의 1차원 배열로 만든다.
# normalize : 입력 이미지를 정규화한다. 즉, 0부터 255사이의 이미지 픽셀 값을 0부터 1까지로 만든다.
# one_hot_label : 레이블을 원-핫-엔코딩한다. 즉, 판단된 라벨의 확률 중 가장 높은 값만을 1로 하고 나머지는 0으로 한다.

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

return network
# 'sample_weight.pkl' 파일에는 학습된 가중치 매개변수(가중치, 편향)가 dictionary 타입으로 저장되어있다.
# pickle은 파이썬에서 지원하는 모듈로서, 텍스트가 아닌 자료형들을 저장할 수 있도록 한다.
# 저장되어 있는 pickle 파일을 불러오면 실행 당시의 객체를 즉시 복원할 수 있어, 데이터를 빠르게 준비할 수 있다.

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
# 3층 신경망의 가중치(weight)와 편향(bias)를 받아온다.
# 각 층의 가중치와 편향으로 뉴런(노드)의 출력을 계산하는 함수

x, t = get_data()
network = init_network()
# MNIST 데이터 세트를 얻고, 가중치와 편향을 가진 dictionary 타입의 네트워크를 생성한다.

accuracy_cnt = 0
for i in range(len(x)) :
y = predict(network, x[i])
# 훈련 데이터의 크기만큼 반복 구문을 수행하면서, 이미지 데이터를 1장씩 꺼내어 3층 신경망을 통과시켜 출력 값을 계산한다.

p = np.argmax(y)
# 출력 y는 0부터 9까지의 숫자에 해당하는 확률을 가진 배열이다.
# np.argmax(y)는 배열에서 가장 큰 원소 값이 있는 인덱스를 반환한다. 즉, 가장 높은 확률이 있는 곳의 순서를 가져온다.

if p == t[i] :
accuracy_cnt += 1
# 프로그램이 분류한 라벨과 실제 라벨이 같은지 검사한다.

print("정확도 : " + str(float(accuracy_cnt)/len(x)))

#-----------------------------------------------------------------------------------------------------------------------#

batch_size = 100 # 배치 크기, 100장의 이미지를 한꺼번에 처리한다는 의미
accuracy_batch = 0

for i in range(0, len(x), batch_size) :
# 0부터 x의 크기-1 까지 배치 크기 간격으로 반복문을 수행한다. (i = 0, 100, 200 ...)

x_batch = x[i:i+batch_size]
# x에는 이미지 데이터가 저장되어 있다. x의 리스트에서 이미지를 반복문의 순서대로 배치 크기만큼 가져온다.
# (0~99, 100~199, 200~299 ...)

y_batch = predict(network, x_batch)
# 배치 크기만큼 묶인 이미지를 3층 신경망에 한꺼번에 통과시켜 출력 값을 계산한다.
# y_batch는 10개의 라벨에 대한 각각의 확률을 가진 100개의 리스트로 구성된다. 즉, y_batch의 크기는 (100x10)이다.

p = np.argmax(y_batch, axis = 1)
# np.argmax는 해당 리스트에서 가장 높은 값의 인덱스, 순서의 번호를 반환한다.
# axis=1은 (100x10) 크기의 y_batch에서 1차원을 구성하는 원소에서 최댓값의 인덱스를 찾도록 한것이다.
# 0차원 : 각 라벨 별 이미지의 확률(100), 1차원 : 각 이미지 별 라벨의 확률(10)

accuracy_batch += np.sum(p == t[i:i+batch_size])
# 실제 라벨과 분류 라벨이 같은지 검사한다. 배열로 반환되기 때문에 모두 더하여 정확도를 계산한다.

print("정확도 : " + str(float(accuracy_batch)/len(x)))




'공부 > 딥러닝' 카테고리의 다른 글

#3-(2) 학습 : 손실 함수  (0) 2018.02.25
#3-(1) 학습 : 신경망 학습  (0) 2018.02.07
#2-(4) 신경망 : 출력층 설계  (0) 2018.01.26
#2-(3) 신경망 : 3층 신경망 구현  (1) 2018.01.25
#2-(2) 신경망 : 활성화 함수  (0) 2018.01.24
Comments