Deep learning/모델 구현

4. 신경망 학습 (1) - 손실함수

jwjwvison 2021. 4. 19. 22:25

이 포스팅은 밑바닥부터 시작하는 딥러닝을 공부하고 정리한것 입니다.


 기계학습 문제는 데이터를 훈련 데이터와 시험 데이터로 나눠 학습과 실험을 수행하는 것이 일반적이다. 그 이유는 우리가 원하는 것은 범용적으로 사용할 수 있는 모델이기 때문이다. 이 범용 능력을 제대로 평가하기 위해 훈련 데이터와 시험 데이터를 분리하는 것이다. 범용 능력은 아직 보지 못한 데이터(훈련 데이터에 포함되지 않은 데이터)로도 문제를 올바르게 풀어내는 능력이다. 그래서 데이터셋 하나로만 매개변수의 학습과 평가를 수행하면 올바른 평가가 될 수 없다. 참고로 한 데이터셋에만 지나치게 최적화된 상태를 오버피팅(overfitting)라고 한다. 

 

 

손실함수

 신경망은 지표를 기준으로 최적의 매개변수 값을 탐색한다. 이를 손실 함수(loss function)라고 한다. 이 손실함수는 임의의 함수를 사용할 수도 있지만 일반적으로는 오차제곱합과 교차 엔트로피 오차를 사용한다.

 

 1. 오차제곱합

  가장 많이 쓰이는 손실함수는 오차제곱합(sum of squares for error, SSE)이다.

여기서 yk는 신경망의 출력(신경망이 추정한 값), tk는 정답 레이블, k는 데이터의 차원 수를 나타낸다.

import numpy as np

y=[0.1,0.05,0.6,0.0,0.05,0.1,0.0,0.1,0.0,0.0] #2일 확률이 가장 높다고 추정함
t=[0,0,1,0,0,0,0,0,0,0]

def sum_squares_error(y,t):
    return 0.5 * np.sum((y-t)**2)
    
sum_squares_error(np.array(y),np.array(t))

y=[0.1,0.05,0.1,0.0,0.05,0.1,0.0,0.6,0.0,0.0]  #7일 확률이 가장 높다고 추정함
sum_squares_error(np.array(y),np.array(t))

 

 2. 교차 엔트로피 오차

  또 다른 손실 함수로서 교차 엔트로피 오차(cross entropy error, CEE)도 자주 사용한다.

 여기서 log는 ln이다. 이식은 실질적으로 정답일 때의 추정(tk가 1일 때의 yk)의 자연로그를 계산하는 식이 된다.

def cross_entropy_error(y,t):
    delta=1e-7
    return -np.sum(t * np.log(y + delta))
    
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]
cross_entropy_error(np.array(y),np.array(t))

y=[0.1,0.05,0.1,0.0,0.05,0.1,0.0,0.6,0.0,0.0]
cross_entropy_error(np.array(y),np.array(t))

 여기서 np.log를 계산할 때 아주 작은 값인 delta를 더했다. 이는 np.log()함수에 0을 입력하면 마이너스 무한대를 뜻하는 -lnf가 되어 더 이상 계산을 진행할 수 없게 되기 때문이다. 아주 작은 값을 더해서 절대 0이 되지 않도록 한 것이다.

 

 3. 미니배치 학습

  기계학습 문제는 모든 훈련 데이터를 대상으로 손실 함수를 구해야 한다.

 마지막에 N으로 나누어 정규화하고 있는데 N으로 나눔으로써 '평균 손실 함수'를 구하는 것이다. 이렇게 평균을 구해 사용하면 훈련 데이터 개수와 상관없이 언제든 통일된 지표를 얻을 수 있다.

 그런데 MNSIT 데이터셋은 훈련 데이터가 60,000개 였다. 그래서 모든 데이터를 대상으로 손실 함수의 합을 구하려면 시간이 좀 걸린다. 이런 경우 데이터 일부를 추려 전체의 '근사치'로 이용할 수 있다. 신경망 학습에서도 훈련 데이터로부터 일부만 골라 학습을 수행한다. 이 일부를 미니배치(mini-batch)라고 한다. 가령 60,000장의 훈련 데이터 중에서 100장을 무작위로 뽑아 그 100장만을 사용하여 학습하는 것이다. 이러한 학습 방법을 미니배치 학습이라고 한다.

import sys,os
sys.path.append(os.pardir)
import numpy as np
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)
print(t_train.shape)

 그러면 이 훈련 데이터에서 무작위로 10장만 빼내려면 어떻게 해야할까?

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

 

 4. (배치용) 교차 엔트로피 오차 구현하기

def cross_entropy_error(y,t):
    if y.dim == 1:
        t=t.shape(1,t_size)
        y=y.shape(1,y_size)
        
    batch_size=y.shape[0]
    return -np.sum(t * np.log(y+ 1e-7)) / batch_size

 

 5. 왜 손실 함수를 설정하는가?

  정확도라는 지표를 놔두고 손실 함수의 값이라는 우회적인 방법을 택하는 이유는 무엇일까? 이 의문은 신경망 학습에서의 '미분'의 역할에 주목한다면 해결된다. 가령 여기에 가상의 신경망이 있고 그 신경망의 어느 한 가중치 매개변수에 주목한다고 하자 이때 그 가중치 매개변수의 손실 함수의 미분이란 '가중치 매개변수의 값을 아주 조금 변화 시켰을 때, 손실 함수가 어떻게 변하냐' 라는 의미이다. 만약 이 미분값이 음수면 그 가중치 매개변수를 양의 방향으로 변화시켜 손실 함수의 값을 줄일 수 있다. 반대로 미분 값이 양수면 가중치 매개변수를 음의 방향으로 변화시켜서 손실 함수 값을 줄일 수 있다. 그러나 미분 값이 0이면 가중치 매개변수를 어느 쪽으로 움직여도 손실 함수의 값은 줄어들지 않는다. 그래서 가중치 매개변수의 갱신은 거기서 멈춘다.

 

 정확도를 지표로 삼아서 안 되는 이유는 미분 값이 대부분의 장소에서 0이 되어 매개변수를 갱신할 수 없기 때문이다. 이 이유는 예를 들어서 한 신경망이 100장의 훈련 데이터 중 32장을 올바로 인식한다고 하자. 그렇다면 정확도는 32퍼센트 이다. 만약 정확도가 지표였다면 가중치 매개변수의 값을 조금 바꾼다고 해도 정확도는 32% 그대로일 것이다. 만약 혹, 정확도가 개선된다 하더라고 그 값은 32.0123 등이 아닌 33이나 34% 처럼 불연속적인 띄엄띄엄한 값으로 바뀔 것이다. 하지만 손실 함수는 연속적으로 변화한다.

 

 정확도는 매개변수의 미소한 변화에는 거의 반응을 보이지 않고, 반응이 있더라도 그 값이 불연속적으로 갑자기 변화한다. 이는 계단 함수를 활성화 함수로 사용하지 않는 이유와도 들어맞는다. 만약 사용한다면 지금까지 설명한 것과 같은 이유로 신경망 학습이 잘 이뤄지지 않는다.

 

'Deep learning > 모델 구현' 카테고리의 다른 글

6. 신경망 학습 (3) - 학습 알고리즘 구현하기  (0) 2021.04.20
5. 신경망 학습 (2) - 기울기  (0) 2021.04.20
3. 신경망(2) - 손글씨 숫자 인식  (0) 2021.04.19
2. 신경망 (1)  (0) 2021.04.19
1. 퍼셉트론  (0) 2021.04.17