이제 배운 것들을 응용하여 손글씨 숫자 분류를 해보도록 하겠다.
이번 실습에서 활용하는 데이터셋은 MNIST라는 손글씨 숫자 이미지 집합이다. MINIST는 머신러닝 분야에서 아주 유명한 데이터 셋이다.

다음과 같은 훈련이미지 60,000장과 시험 이미지 10,000장이 준비되어있다.
우리는 MNIST 데이터셋을 내려받아 이미지를 넘파이 배열로 변환하는 파이썬 스크립트를 사용하겠다.
import sys , os
# 부모 디렉토리의 파일을 가져올 수 있도록 커서의 현 위치를 수정( 개인 환경마다 다름 )
sys.path.append(os.path.join(os.path.dirname(__file__),'../..'))
# 옮긴 위치의 파일을 열어서 함수 불러오기
from dataset.mnist import load_mnist
# 데이터 셋 받아오기.
(x_train, t_train), (x_test, t_test) = load_mnist(flatten=True,normalize=False)
print(x_train.shape) #(60000, 784)
print(t_train.shape) #(60000,)
print(x_test.shape) #(10000, 784)
print(t_test.shape) #(10000,)
만일 이 글을 보고 실습을 해보고 싶다면
에서 데이터셋을 받아올 수 있다.
데이터를 받아온거에서 그치지않고 이미지 파일을 실행해보자.
import sys , os
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
# 부모 디렉터리의 파일을 가져올 수 있도록 설정
# 옮긴 위치의 파일을 열어서 함수 불러오기
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)
img = x_train[0]
label = t_train[0]
print(label) # 5
print(img.shape) # (784,)
img = img.reshape(28,28) # 원래 이미지 모양으로 변형
print(img.shape) #(28,28)
img_show(img)

다음과 같이 이미지가 잘 노출된다 !
이제 이 MNIST 데이터셋을 가지고 추론을 할 신경망을 구현해보자.
이 신경망의 입력층은 이미지 크기가 28 * 28 이라서 748로 설정을 하고, 출력층은 숫자 0에서 9를 구분하는 문제이므로 10개로 설정해주었다.
한편 은닉층은 총 두 개로 구성되며 첫 은닉층에는 50개의 뉴런을, 두 번째 은닉층에는 100개의 뉴런을 배치하겠다. (임의로 정한 값)
import sys , os
# 부모 디렉토리의 파일을 가져올 수 있도록 커서의 현 위치를 수정( 개인 환경마다 다름 )
sys.path.append(os.path.join(os.path.dirname(__file__),'../..'))
# 옮긴 위치의 파일을 열어서 함수 불러오기
from dataset.mnist import load_mnist
import numpy as np
import pickle
from activation_function import *
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(os.path.dirname(__file__) + "/sample_weigh.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)))
# Accuracy :0.9352
init_network()sampe_weight.pkl에 저장된 학습된 가충치 매개변수를 읽어온다.load_mnist의 인수인 normalize를 True로 설정하면 0~255범위인 픽셀 값들을 0.0 ~ 1.0의 범위로 정규화 한다.정답을 판정하는 기준은 np.argmax(y)를 통해 해당 배열에서 확률이 가장 높은 원소의 인덱스를 구하여 예측 결과를 구하고, 이 결과(p)와 정답 레이블(t[i])를 비교화여 같다면 정답으로 처리하는것이다.
위 함수의 실행결과 Accuracy : 0.9352 가 나온다. 올바르게 분류한 비율이 93.52% 이라는 의미이다.
이번에는 입력 데이터와 가중치 매개변수의 '형상'에 대해 주의 해 보도록 하겠다.
x , _ = get_data()
network = init_network()
w1, w2, w3 = network['W1'], network['W2'], network['W3']
print(x.shape) # (10000,784)
print(x[0].shape) # (784,)
print(w1.shape) # (784,50)
print(w2.shape) # (50,100)
print(w3.shape) # (100,10)
이 결과에서 다차원 배열의 대응하는 차원의 원소 수가 일치함을 확인할 수 있다.
(784(입력층의 수) -> 50(은닉 1층의 수) -> 100(은닉 2층의 수) -> 10(출력층의 수))
원소 784개로 구성된 1차원 배열 (원래는 28 X 28인 2차원 배열)이 입력되어 마지막에는 원소가 10개인 1차원 배열이 출력되는 흐름을 나타내고 있다.
이처럼 하나로 묶은 입력 데이터를 배치 batch라고 한다.
배치 처리는 컴퓨터로 계산할 때 큰 이점을 준다.
x, t = get_data()
network = init_network()
batch_size = 100
accuracy_cnt = 0
for i in range(0, len(x), batch_size) :
x_batch = x[i:i+batch_size]
y_batch = predict(network, x_batch)
p = np.argmax(y_batch, axis=1)
accuracy_cnt += np.sum(p == t[i:i+batch_size])
print("Accuracy: "+ str(float(accuracy_cnt)/len(x)))
이번 장에서는 신경망의 기본 구조와 작동 원리를 깊이 있게 탐구했다. 특히 신경망이 데이터로부터 가중치 매개변수를 자동으로 학습하는 능력의 중요성을 강조하며, 이를 가능하게 하는 다양한 요소들을 살펴보았다.
다음 챕터인 4에서는 신경망 학습이란 주제를 공부해보겠다 !