이전 포스트에서 신경망 학습이 어떠한 원리에 의해 이루어지는지 간략하게 살펴보았다. 이번 포스트에서는 제곱 오차(Square Error)와 제곱 오차를 기반으로 만든 손실 함수 오차제곱합(SSE)에 대해 알아보도록 하겠다.
1. 제곱오차(Square Error, SE)
- 자, 앞서 손실함수는 실제값과 예측값의 차이를 이용해서 가중치가 얼마나 적합하게 뽑혔는지를 평가하기 위해 만들어졌다고 했다.
- 그렇다면 말 그대로 실제값과 예측값을 뺀 편차를 이용하면 이를 평가할 수 있지 않겠는가?
- 이에, 통계학에서도 즐겨 사용하는 제곱 오차를 가져오게 되었다.
$$ SE = (y - \hat{y})^2 $$
- 위 수식에서 $y$는 실제 값, $\hat{k}$는 예측한 값이고, 이를 제곱한 이유는 분산을 구할 때처럼, 부호를 없애기 위해서이다.
- 예를 들어 실제 값이 4이고 예측값이 2일 때의 차이는 2, 실제값이 2이고 예측값이 4일 때 차이는 -2인데, 이 두 경우 모두 실제 값과 예측값의 크기의 차이는 2지만, 부호 때문에 서로 다르다고 인식할 수 있다. 편차에서 중요한 것은 두 값의 크기 차이지, 방향(부호)에는 의미가 없으므로, 절댓값을 씌우거나, 제곱하여 편차의 방향을 없앤다.
- 참고로 이 제곱오차는 후술 할 최적화 기법 중 가장 대표적인 경사하강법에서 중요한 부분이므로, 숙지하고 있도록 하자.
- 경사하강법은 미분을 통해 실시되는데, 만약 실제값과 오차값의 편차 제곱을 한 제곱오차(SE)가 아닌, 절댓값을 씌운 절대 오차 합계(SAE)를 사용하게 되면, 절댓값에 의해 구분되는 0에서 미분이 불가능하기 때문에 SAE는 사용해선 안된다.
(미분 조건은 좌미분 = 우미분이 동일해야 한다!)
2. 오차제곱합(Sum of Squares for Error, SSE)
- 자, 위에서 우리는 실제값과 예측값의 편차를 알기 위해 제곱오차를 사용하였다.
- 만약, 이 오차제곱들을 모두 합한다면, 딱 한 값으로 이 가중치가 적절한지 알 수 있지 않겠는가.
(어떠한 알고리즘을 판단할 때, 하나의 값인 스칼라로 만들어야 평가하기가 쉽다. 값이 하나란 의미는 판단하는 기준인 변수가 하나라는 소리이며, 변수의 수가 많아질수록, 그 알고리즘을 평가하는 것이 복잡해진다.) - 기본적으로 오차제곱합의 공식은 다음과 같다.
$$ SSE = \sum_{k}(y_k - \hat{y_k})^2 $$
- 그러나, 우리가 딥러닝에서 사용할 오차제곱합은 아래 공식으로 조금 다르다.
$$ E = \frac{1}{2}\sum_{k}(y_k - \hat{y_k})^2 $$
- 갑자기 쌩뚱맞게 $\frac{1}{2}$가 추가된 것을 볼 수 있다.
- 이는 델타 규칙(Delta Rule) 때문인데, 최적의 가중치를 찾아가는 최적화(Optimizer)에서 사용되는 경사하강법은 기울기를 기반으로 실시되며, 이 과정에서 발생할 수 있는 오류를 최소화시키기 위해 $\frac{1}{2}$를 곱하는 것이다.
- (en.wikipedia.org/wiki/Delta_rule)
3. 구현해보자.
>>> import numpy as np
>>> def SSE(real, pred):
>>> return 0.5 * np.sum((real - pred)**2)
# 예시 1.
>>> label = np.array([0, 0, 0, 0, 1, 0, 0, 0, 0, 0])
>>> predict = np.array([0.3, 0.05, 0.1, 0.1, 0.6, 0.05, 0.1, 0.2, 0.0, 0.1])
>>> SSE(label, predict)
0.1675
- 위 데이터는 0부터 9까지의 숫자를 분류한 신경망의 출력값이다.
- 위에서 label은 실제 값이고, predict는 예측된 값이다.
- 여기서 label이라는 배열을 보면, 5번째 자리만 1이고 나머지는 0인데, 이를 원-핫 벡터(One-Hot Vector)라고 한다.
- 이 예시를 기준으로 값을 조금씩 바꾸면서, 오차제곱합이 어떻게 변하는지 봐보자.
# 예시 2.
>>> label = np.array([0, 0, 0, 0, 1, 0, 0, 0, 0, 0])
>>> predict = np.array([0.3, 0.05, 0.2, 0.3, 0.4, 0.1, 0.1, 0.2, 0.8, 0.1])
>>> SSE(label, predict)
0.64625
- 첫 번째 예시에서는 실제 데이터에서 가장 큰 값의 위치와 예측 데이터에서 가장 큰 값의 위치가 동일했으며, 상대적으로 다른 위치의 값들이 그리 크지 않았다.
- 그러나 두 번째 예시에서는 예측 데이터와 실제 데이터의 값의 배치가 상당히 다르다.
- 그로 인해, 오차제곱합(SSE)가 0.1675에서 0.64625로 올라간 것을 볼 수 있다.
# 예시 3.
>>> label = np.array([0, 0, 0, 0, 1, 0, 0, 0, 0, 0])
>>> predict = np.array([0.0, 0.01, 0.0, 0.05, 0.85, 0.01, 0.0, 0.05, 0.1, 0.0])
>>> SSE(label, predict)
0.01885
- 세 번째 예시에서는 반대로 실제 데이터와 아주 가까운 형태로 예측 데이터를 만들어보았다.
- 그로 인해 오차제곱합이 0.01885로 0에 가깝게 떨어진 것을 볼 수 있다.
- 이러한, 실제 데이터와 예측 데이터의 편차의 제곱 합이 최소가 되는 점을 찾는 것이 학습의 목표가 된다.
원-핫 벡터란?
- 원-핫 벡터는 문자를 벡터화하는 전처리 방법 중 하나로, 0부터 9까지의 숫자를 원-핫 벡터를 사용하여 벡터화 한다면, 다음과 같이 할 수 있다.
>>> label_0 = np.array([1, 0, 0, 0, 0, 0, 0, 0, 0, 0])
>>> label_1 = np.array([0, 1, 0, 0, 0, 0, 0, 0, 0, 0])
>>> label_2 = np.array([0, 0, 1, 0, 0, 0, 0, 0, 0, 0])
>>> label_3 = np.array([0, 0, 0, 1, 0, 0, 0, 0, 0, 0])
>>> label_4 = np.array([0, 0, 0, 0, 1, 0, 0, 0, 0, 0])
>>> label_5 = np.array([0, 0, 0, 0, 0, 1, 0, 0, 0, 0])
>>> label_6 = np.array([0, 0, 0, 0, 0, 0, 1, 0, 0, 0])
>>> label_7 = np.array([0, 0, 0, 0, 0, 0, 0, 1, 0, 0])
>>> label_8 = np.array([0, 0, 0, 0, 0, 0, 0, 0, 1, 0])
>>> label_9 = np.array([0, 0, 0, 0, 0, 0, 0, 0, 0, 1])
- 원-핫 벡터는 먼저 유니크한 단어(숫자 역시 단어의 개념으로써 접근 가능하다!)의 넘버링된 사전을 만들고 총 단어의 수만큼 벡터 크기를 정하며, 각 단어에 넘버링된 위치만 1로 하고, 나머지는 다 0으로 채우는 방법이다.
- 그다지 어려운 내용은 아니나, 문자를 벡터로 바꾸는 벡터화에 있어서 기본이 되는 방법이며, 자주 사용되는 방법 중 하나이므로, 추후 임베딩을 학습 할 때, 세세히 다루도록 하겠다.
지금까지 손실함수에서 많이 사용되는 기법 중 하나인 오차제곱합(SSE)에 대해 학습해보앗다. 다음 포스트에서는 오차제곱(SE)에서 파생된 다른 손실함수인 평균제곱오차(MSE)와 평균제곱근오차(RMSE)에 대하여 학습해보도록 하겠다.
'Machine Learning > Deep Learning' 카테고리의 다른 글
딥러닝-5.2. 손실함수(3)-평균제곱근오차(RMSE) (0) | 2021.01.29 |
---|---|
딥러닝-5.1. 손실함수(2)-평균제곱오차(MSE) (0) | 2021.01.29 |
딥러닝-4.2. 인공신경망(3)-신경망 학습 (0) | 2021.01.28 |
딥러닝-4.1. 인공신경망(2)-신경망 연산(MLP) (0) | 2021.01.28 |
딥러닝-4.0. 인공신경망(1)-신경망 연산(SLP) (0) | 2021.01.28 |