[밑바닥딥러닝] 4. 신경망 구현 - 손글씨 인식

[밑바닥딥러닝] 4. 신경망 구현 - 손글씨 인식

본 게시글은 한빛미디어 『밑바닥부터 시작하는 딥러닝, 사이토 고키, 2020』의 내용을 참조하였음을 밝힙니다.

이번 장에서는 손글씨 이미지를 인식하는 신경망을 구현하여 신경망의 순전파(forward propagation)가

실제로 어떻게 이루어지는지 알아보도록 하자.

출처:https://ko.wikipedia.org/wiki/MNIST_%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4

MNIST 데이터셋은 훈련용 이미지 60000개, 테스트용 이미지 10000개로 이루어진 손글씨 이미지 집합이다.

이미지 하나당 (28, 28)의 크기이며 각 픽셀은 0 ~ 255까지의 값을 취한다.

import os import pickle from dataset.mnist import load_mnist import numpy as np from activationFunc import (sigmoid, softmax) class mnist_predictor: def __init__(self): with open(os.getcwd()+'\ch03'+"\sample_weight.pkl", 'rb') as f: network = pickle.load(f) self.network = network self.x, self.t = self.get_data()

우리가 구현할 mnist_predictor의 초기화함수이다.

사실 손글씨 이미지를 인식하기 위해서는 올바른 가중치들이 학습되어야 하지만 이번 단계에서는

순전파를 공부하는 장이기 때문에 미리 학습되어있는 가중치를 이용하도록 한다.

sample_weight.pkl 파일에 저장되어 있는 가중치를 읽어와서 network 인스턴스변수에 저장한다.

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

그리고 책에서 제공하는 load_mnist 함수를 이용하여 MNIST 데이터셋을 가져와서 훈련용/테스트용 Input값,

훈련용/테스트용 target값으로 나눈 뒤에 테스트용 Input값/target값인 x_test, t_test을 반환하는 get_data함수를

구현한다. 초기화 시 get_data 함수의 호출에 따라 이 (가공된) 테스트용 데이터가 인스턴스 변수에 저장된다.

>>> (x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, flatten=True, one_hot_label=False) >>> x_train.shape (60000, 784) >>> t_train.shape (60000,)

load_mnist 함수에서는 인수로 normalize, flatten, one_hot_label 여부를 지정하도록 하는데 각각

데이터의 정규화 / 평면화 / 원핫레이블 인코딩 여부를 의미한다. 여기서는 normalize와 flatten을 True로 설정하여

0 ~ 255인 데이터값을 0 ~ 1 사이로 정규화하고, (28, 28) shape를 784 크기로 길게 이어지도록 바꾼다.

def predict(self, x): a1 = np.dot(x, self.network['W1']) + self.network['b1'] z1 = sigmoid(a1) a2 = np.dot(z1, self.network['W2']) + self.network['b2'] z2 = sigmoid(a2) a3 = np.dot(z2, self.network['W3']) + self.network['b3'] y = softmax(a3) return y

마지막으로 예측을 수행하는 predict 함수에서는 미리 훈련된 가중치에 따라 순전파를 수행한다.

마지막 출력층에서 softmax 함수를 통해 각 0 ~ 9 까지 숫자의 확률을 반환한다.

>>> predictor = mnist_predictor() >>> predictor.x.shape (10000, 784) >>> predictor.t.shape (10000,)

해당 클래스는 테스트용 데이터를 가지고 있기때문에 각각 10000개의 이미지 (각 이미지당 784 사이즈)를 가지며

그에 따라 타깃값 또한 10000개이다.

>>> result = predictor.predict(predictor.x[11]) >>> result array([2.6870526e-03, 4.4876558e-04, 5.7756763e-02, 7.5509388e-04, 5.0751637e-03, 9.0242680e-03, 9.1198188e-01, 1.9680336e-05, 1.2216875e-02, 3.4506880e-05], dtype=float32)

predict 함수를 통해 x의 12번째 이미지를 예측하였다. softmax 함수에 따라 각 숫자당 확률을 반환한다.

>>> np.argmax(result) 6 >>> predictor.t[11] 6

numpy의 argmax 함수로 가장 큰 값의 index를 출력하였더니 6이 나왔다.

t(타깃값)의 12번째 값 역시 6인 것을 보아 올바르게 예측하였다.

>>> predictor.network['W1'].shape (784, 50) >>> predictor.network['W2'].shape (50, 100) >>> predictor.network['W3'].shape (100, 10)

여기서 각 가중치의 크기를 살펴보자. 먼저 W1의 경우 (784, 50)인 것으로 보아 한 이미지의 모든 픽셀(784개)에

대응하여 50개의 노드값을 만들어낸다. 첫번째 은닉층의 크기는 50이라고 할 수 있다.

다음으로 W2의 크기는(50, 100)으로, 50개인 은닉층 노드들에 대응하여 총 100개의 노드값을 만들어낸다.

두번째 은닉층의 크기는 100이다. 마지막으로 W3에 따라 이 100개의 은닉층 노드들이

최종적으로 10개의 노드들을 만들어 내고 이 값들이 Softmax 함수를 거친다.

정리하자면 해당 신경망에 784 사이즈의 이미지 하나가 들어가면 10개의 값이 출력된다.

그렇다면 이러한 처리과정을 여러 개의 이미지를 한번에 처리할 수 있도록 확장하면 어떨까?

>>> predictor.x[0:100].shape (100, 784)

x에 저장된 이미지 100개를 추출하였다.

>>> W1 = predictor.network['W1'] >>> W2 = predictor.network['W2'] >>> W3 = predictor.network['W3'] >>> x = predictor.x[0:100]

이 데이터 묶음과 가중치를 각각 저장하여서

>>> layer1 = np.dot(x, W1) >>> layer2 = np.dot(layer1, W2) >>> layer3 = np.dot(layer2, W3) >>> layer3.shape (100, 10)

(의사) 순전파 과정을 거치면 최종적으로 (100, 10) 크기의 결과가 출력된다.

이는 100개의 각 이미지마다 가지고 있는 10개의 확률값을 의미한다.

이렇게 데이터를 묶어서 처리하는 기법을 배치 처리라고한다.

batch_size = 100 accuracy_cnt = 0 for i in range(0, len(predictor.x), batch_size): predicted_val = predictor.predict(predictor.x[i:i+batch_size]) predicted_val = np.argmax(predicted_val, axis=1) accuracy_cnt += np.sum(predicted_val == predictor.t[i:i+batch_size]) print(accuracy_cnt/len(predictor.x))

만들어진 신경망을 평가해보도록 하자.

for문에서 전체 x 크기를 모두 진행하는데, 배치 사이즈(batch size)만큼씩 건너뛰도록 구현한다.

numpy의 argmax 함수에서 axis를 1로 지정해 각 가로방향의 축마다(이미지마다)의 가장 큰값의 인덱스를 찾는다.

모든 배치를 처리하고 옳게 예측한 횟수(accuracy_cnt)를 전체 데이터 크기로 나눠서 최종 정확도를 계산한다.

0.9352

우리는 이 예측기를 통해 93.52%의 확률로 손글씨를 예측할 수 있다.

from http://humankind.tistory.com/53 by ccl(A) rewrite - 2021-09-12 16:26:58