프로젝트/딥러닝

[CH.4]신경망 학습하기-1 (손실함수, 교차엔트로피오차)

냥냥친구 2020. 2. 2. 11:31

이번 챕터에서는 신경망 학습 방법에 대해 알아본다. 우리는 "5"라고 쓴 손글씨 이미지를 입력하면 컴퓨터가 "5"라고 인식하는 모델을 만들고 싶다. 이 모델을 만들기 위해서 신경망을 학습시킬 것이다. (이를 모델링이라고도 한다.) 신경망은 학습을 통해 손글씨 값을 가장 잘 인식하는 가중치와 편향의 최적값을 찾아준다. 우리는 최적값을 가지고 숫자를 얼마나 잘 맞추는지 성능을 테스트 할 것이다.

 

우리가 사용할 데이터는 MNIST 패키지의 손글씨 이미지이다. 이미지는 다음과 같다.

손글씨 숫자 5

손글씨로 쓴 숫자 5의 이미지이다. 이미지를 넘파이 배열로 변환하여 학습시킬 것이다. 해당 이미지는 28 * 28 사이즈로, 픽셀별로 쪼개어 배열로 만든다. 회색조 이미지에서 각 픽셀은 색상에 따라 0에서 255까지의 값을 취한다. 위 이미지를 배열로 만들면 아래와 같으며 배열의 shape는 (1,784)이다.

손글씨 이미지를 배열로 변환

하나의 데이터는 위의 배열과 같다. 이제 신경망 학습법을 살펴보자.

(데이터 전처리는 따로 또 포스팅할 예정입니다.)

 

신경망 학습 절차는 아래와 같다. 우리는 이 과정을 짚어보며 신경망 학습 방법을 이해할 것이다.

  1.  훈련데이터와 시험데이터 분리
  2. 훈련데이터 중 배치 돌릴 배치 데이터 랜덤 선택
  3. 배치 데이터로 손실함수 값 구하기
  4. 경사하강법으로 가중치 값 개선하기
  5. 2,3,4 반복하며 최적값 찾기
  6. 테스트 데이터로 성능 테스트 해보기

 

 

1. 훈련데이터와 시험데이터 분리

 MNIST 패키지의 손글씨 이미지는 7만장이다. 7만장을 훈련데이터와 시험데이터로 나눠 학습과 성능 테스트를 수행할 것이다. 훈련 데이터를 사용하여 최적의 매개변수를 찾은 다음, 시험데이터로 성능 테스트를 진행한다. 이는 범용능력을 위한 것으로, 다른 데이터가 들어왔을 때도 효과적인 값을 출력하는지 테스트하기 위함이다. 만약, 학습모델이 훈련데이터는 정확히 맞추더라도 시험데이터가 들어왔을 때 엉망이라면, 이 모델은 다른 데이터에는 사용할 수 없을 것이다. 이처럼 특정데이텅에 맞춰서 만들어진 모델을 오버피팅이라고 하며, 이 문제를 방지하기 위해 훈련데이터와 시험데이터를 분리한다. 여기서는 훈련데이터 6만장, 시험데이터를 1만장으로 분리할 것이다. 데이터를 분리하는 코드는 아래와 같다.

import sys, os
import numpy as np

path = "./deep-learning-from-scratch-master"
sys.path.append(path)

from dataset.mnist import load_mnist

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

print("x_train shape is: "+ str(x_train.shape)) #훈련데이터
print("t_train shape is: "+ str(t_train.shape)) #훈련데이터 레이블
print("x_test shape is: "+ str(x_test.shape))   #시험데이터
print("t_test shape is: "+ str(t_test.shape))   #시험데이터 레이블

훈련데이터, 시험데이터  shape

여기서 x_train과 x_test는 훈련데이터, 시험데이터이다. 한장 데이터의 shape는 (1, 784)인데, 6만장이 있으므로 (60000, 784)이다. 시험데이터는 1만장이므로 (10000,784)이다. 그리고 t_train과 t_test는 각각 훈련데이터 레이블과 시험데이터 레이블이다. 레이블은 정답표를 배열로 나타낸 것이다. 만약 특정 손글씨 숫자의 실제값이 "2"라면, 레이블은 [0,0,1,0,0,0,0,0,0,0]가 된다. 맨 앞에서부터 0,1,2 ...9까지 총 10개의 정답표이며 실제 값에만 1을 표시한다. 하나의 정답 레이블의 shape는 (1,10)이며 이미지만큼 정답표가 있기 때문에 (60000,10),(10000,10)이다.

 

 

 2. 훈련데이터 중 배치 돌릴 배치 데이터 랜덤 선택

 훈련 데이터 x_train은 6만개이다. 6만개 전체를 학습 한 번에 전부 사용하면 시간이 너무 오래 걸린다. 더 많은 데이터의 경우 그 시간은 더 오래 걸릴 것이다. 이런 경우 데이터 일부를 추려 전체의 근사치로 사용한다. 가령 6만개의 훈련 데이터 중에서 100개를 무작위로 뽑아 그 100개 만을 사용하여 학습하고 다시 또 100개를 추출하여 학습하는 것을 반복한다. 이러한 학습 방법을 미니 배치 학습이라고 한다. 이렇게 무작위로 추출한 데이터를 배치 데이터라고 부르겠다. 훈련 데이터에서 지정한 수의 데이터를 무작위로 골라오는 코드를 작성해 보자.

train_size = x_train.shape[0]
batch_size = 100
batch_mask = np.random.choice(train_size, batch_size)
x_batch = x_train[batch_mask]
t_batch = t_train[batch_mask]

print(batch_mask)

6만 개중에서 100개 랜덤 추출하기

 3. 배치 데이터로 손실함수 값 구하기

학습에 사용할 데이터 추출까지 모두 끝났다. 이제는 이 배치 데이터를 가지고 이미지 데이터의 숫자값을 예측한 후, 손실함수 값을 구할 것이다. 먼저 이미지 데이터 예측부터 살펴보자. 

W = np.random.randn(784,10)
def predict(x):
    return np.dot(x, W)

W는 가중치 매개변수로, 신경망의 최종목표인 가중치 최적값 찾기가 바로 이 W 변수이다. 신경망은 학습을 통해 W변수의 최적값을 찾을 것이다. 첫 예측에는 가중치 값이 없으므로 정규분포 값으로 랜덤추출하였다. 입력할 배치 데이터의 shape는 (100, 784)이므로 이미지당 예측값을 추정하기 위해서 가중치는 (784,10)형태여야 한다. 이는 행렬 곱을 위해서는 앞 행렬의 열과 뒤 행렬의 행이 같아야 하는 계산 방식 때문이다. 이렇게 하면 예측값의 shape는 (100,10)이며 이는 원소가 10개인 리스트가 100행이 있음을 의미한다. 다시 말해 predict()함수의 리턴 값의 shape는 (100,10)이 된다.

앞서 구한 배치 데이터(x_batch)를 가지고 predict 리턴 값의 한 줄을 출력해보자.

predict(x_batch)[0]

추정치

위 배열이 신경망이 이미지 데이터를 보고 생성한 추정치이다. 이 추정치가 100개가 있다. 추정치를 설명하면, 이미지 데이터가 0일 가능성이 14, 1일 가능성이 11, 9일 가능성이 10을 나타낸다. 그러면 최종 선택은 요소 중 가장 큰 값으로 숫자를 추정한다.

np.argmax(predict(x_batch)[0])

최종적으로 신경망이 추정한 값

여기서는 0일 때의 추정치가 값이 제일크기 때문에 이미지 데이터를 0으로 추정했다고 본다. 여기까지가 신경망의 예측이다. 이제 우리는 손실함수로 이 신경망의 성능이 얼마나 나쁜지 확인할 것이다.

 

손실함수

먼저, 손실함수란 신경망 성능의 '나쁨'을 나타내는 지표이다. 성능의 나쁨을 나타내는 손실함수의 값이 가장 작은 곳에 가중치 최적값이 있다. 신경망은 손실함수가 최저가 되게 만드는 가중치 값을 찾는다. 손실함수로는 오차제곱합과 교차 엔트로피 오차가 있는데 가장 유명한 교차 엔트로피 오차만 살펴 보겠다.

 

교차 엔트로피 오차

교차엔트로피오차 식은 다음과 같다. 

교차 엔트로피 오차 식

 

여기서 log는 자연로그이며 yk는 신경망이 학습을 통해 이미지를 추정한 값, tk는 앞서 살펴본 정답레이블이다. 예를 들면 다음과 같다.

t = [0,0,1,0,0,0,0,0,0,0]
y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
#참고 : t, y값은 예시를 위해 직접 작성한 값입니다.

 변수 t는 정답레이블로, 손글씨 데이터의 실제 숫자가 "2"임을 의미한다. 이는 원-핫 인코딩 방식으로 정답에 해당하는 값만 1로 나타낸 것이다. 그리고 변수 y는 신경망이 학습을 통해 이미지를 추정한 값이다. 0.6이 제일 높으므로 신경망은 손글씨 이미지가 "2"라고 추정하고 있다.

교차 엔트로피 오차 식을 다시 살펴보면, 정답레이블(tk)을 곱하기 때문에 답이 아닌경우(=tk가 0인 경우) 는 값이 0이고 정답인 경우에만 값이 있으므로 실질적으로 정답일 때의 자연로그를 계산하는 식이 된다. 교차 엔트로피 오차 수식은 다음과 같이 구현한다.

import numpy as np
def cross_entropy_error(y, t):
    delta = 1e-7
    return -np.sum(t * np.log(y + delta))
    
    
cross_entropy_error(np.array(y), np.array(t))

2가 정답일 때 신경망의 추정치는 0.6이며 이 때의 교차 엔트로피 오차는 약 0.51이다. 하나 더 살펴보자.

y = [0.1, 0.05, 0.2, 0.1, 0.05, 0.1, 0.1, 0.1, 0.1, 0.1]

cross_entropy_error(np.array(y), np.array(t))

이번에는 2가 정답일 때 2에 대한 신경망의 추정치는 0.2이다. 이 때의 교차 엔트로피 오차는 1.61이다. 추정치가 정답과 멀어질수록 오차값이 큰 것을 알 수 있다.

 

여기까지 전체 데이터를 훈련데이터와 시험데이터로 분리하고 훈련데이터에서 배치 데이터 100개을 랜덤 추출했다. 그리고 이 배치 데이터를 예측하여 예측 값에 대한 손실함수를 구하는 것까지 살펴보았다.

다음 챕터에서 이 손실함수 예측값을 갖고 가중치 매개변수 최적값을 구하는 방법을 살펴볼 것이다. 그럼 안뇽~!