2021. 9. 12. 15:44ㆍDeep Learning
본 게시글은 한빛미디어 『밑바닥부터 시작하는 딥러닝, 사이토 고키, 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%의 확률로 손글씨를 예측할 수 있다.
'Deep Learning' 카테고리의 다른 글
[밑바닥딥러닝] 6. 수치 미분, 편미분 (0) | 2021.10.01 |
---|---|
[밑바닥딥러닝] 5. 신경망 학습 - 손실 함수, 미니 배치 학습 (0) | 2021.09.30 |
[밑바닥딥러닝] 3. 신경망 구현, 소프트맥스(softmax) (0) | 2021.09.12 |
[밑바닥딥러닝] 2. 신경망(neural network), 활성화함수(Activation function) (0) | 2021.09.10 |
[밑바닥딥러닝] 1.퍼셉트론 (0) | 2021.09.10 |