1. 학습
■ 신경망 '학습'에서 학습은 train set으로 가중치 매개변수의 최적값을 자동으로 얻는 것을 의미한다.
■ 신경망이 학습할 수 있도록 기준이 되는 지표는 손실 함수이며, 이 손실 함수의 결과값을 최대한 작게 만드는 가중치를 찾아야 한다.
■ 딥러닝은 ML과 달리 '처음부터 끝까지', '데이터에서 출력까지' 사람의 개입 없이 주어진 데이터를 온전히 있는 그대로 학습하고 패턴을 발견하여 종단간(end-to-end) 기계학습이라고도 한다.
2. 손실 함수(loss function)
■ 딥러닝에서 train set으로 학습한 다음, 어떤 지표를 기준으로 가중치의 최적값을 자동으로 갱신해 어떤 지표가 최대한 작아지는 가중치를 찾는다. 이 어떤 지표가 바로 손실 함수이다.
■ 손실 함수는 사용자가 임의의 함수를 정의하여 사용할 수도 있다. 다만 기본적으로 회귀 문제인지 분류 문제인지에 따라 다른 손실 함수를 사용한다. 보통 오차제곱합, 교차 엔트로피를 사용한다.
■ 손실 함수 결과값은 신경망 성능이 얼마나 나쁜지를 나타내므로, 여기에 마이너스를 곱하면 성능이 얼마나 좋은지를 나타내게 된다.
2.1 오차제곱합(sum of squares for error, SSE)
■ 오차제곱합은 말 그대로 신경망의 출력값, 즉 신경망이 예측한 값에서 실제 정답을 뺀 값인 오차를 제곱하고, 모든 오차 제곱을 더한 값이다. 이를 수식으로 나타내면 다음과 같다.
\[ E = \dfrac {1}{2} \displaystyle \sum_{k} (y_{k} - t_{k})^2 \]
- \( k \)는 데이터의 차원 수 이며, \( y_k \)는 신경망의 출력값 \( t_k \)는 정답, 즉 실제 값이며, 델타 규칙(Delta Rule)에 의해 \( \dfrac {1}{2} \)을 곱해준다.
- 최적화(optimizer)를 통해 손실 함수 결과값을 최대한 작게 만드는 최적화된 가중치를 찾아가는 경사하강법 과정에서 발생할 수 있는 오류를 최소화시키기 위해 \( \dfrac {1}{2} \)을 곱하는 것이며, 반드시 \( \dfrac {1}{2} \)을 써야 하는 것은 아니다.
cf) 평균제곱오차(MSE)에서는 \( \dfrac {1}{N} \), ( \( N \)는 데이터 수)를 이용한다. 데이터가 늘어날수록 오차제곱합이 커질 수 있어 \( \dfrac {1}{N} \)을 곱해 N개의 데이터로 확장된 것과 상관없이 오차를 수치화하기 위해서이다.
cf) Delta Rule에서 Delta는 출력값과 정답과의 차이인 오차를 의미하며, Delta Rule이란 모든 입력값으로부터 얻어진 출력값과 정답과의 차이인 오차에서, 이 오차의 제곱합을 최소화하도록 가중치를 조정하는 방법을 의미한다.
■ 오차제곱합을 파이썬으로 구현하면 다음과 같다.
def SSE(y, t):
return 0.5 * sum((y - t)**2)
- 만약 t(정답)가 원핫 인코딩 형태로 t = [0, 1, 0, 0]이고 신경망 출력값 y가 출력층에서 소프트맥스 함수를 통해 각 클래스의 확률값으로 표현되어 y = [0, 0.7, 0.2, 0.1]이라 하자. 이 y와 t를 오차제곱합 함수에 적용시키면 다음과 같다.
t = [0, 1, 0, 0]
y = [0, 0.7, 0.2, 0.1]
SSE(np.array(y), np.array(t))
```#결과#```
0.07000000000000002
````````````
- y는 소프트맥스의 결과값이므로 예측 결과가 두 번째 클래스인 확률이 0.7로 가장 높아 잘 예측했다고 볼 수 있다. 이렇게 잘 예측한 결과에 대한 손실 함수인 SSE의 값은 작게 나타나며, 이는 y와 t간의 오차가 작다는 것을 알 수 있다.
- 반대로 신경망이 잘못 예측한 경우를 상정해서 SSE 값을 보면 다음과 같이 SSE 값이 잘 예측했을 때보다 값이 매우 커진 것을 볼 수 있다.
y2 = [0.7, 0.1, 0.2, 0]
SSE(np.array(y2), np.array(t))
```#결과#```
0.67
````````````
2.2 교차 엔트로피 오차(cross entropy error)
■ 교차 엔트로피의 수식은 다음과 같다.
\[ E = - \displaystyle \sum_{k} t_{k}log_{e}y_{k} \]
- \( y_k \)는 신경망의 출력, \( t_k \)는 원핫 인코딩된 정답 레이블로서 각 클래스의 원소 중 정답인 원소의 값만 1이고, 나머지 원소의 값은 0이다.
■ 교차 엔트로피 오차도 오차제곱합처럼 정답과 멀어지면 E가 커지고, 정답과 가까울수록 E가 작아지는데, 이는 수식에 \( y = e^x \)의 역함수인 \( y = lnx \)가 있기 때문이다.
- \( E = \displaystyle \sum_{k} t_{k} \left( -\ln{y_{k}} \right) \)라고 할 수 있다. \( y = lnx \)와 \( y = -lnx \)를 비교하면,
\( y = lnx \)는 \( x =1 \)이면 \( y = 0 \), \( x \to 0^+ \) (x가 0에 가까워질수록) \( y \to -\infty \), \( y = -lnx \)는 \( x =1 \)이면 \( y = 0 \), \( x \to 0^+ \) (x가 1에서 0으로 가까워질수록) \( y \to \infty \)이다.
- 즉, \( y = -lnx \)는 \( x \)가 작아질수록 \( lnx \)값은 기하급수적으로 증가한다. \( x \)가 작아진 것보다 \( lnx \)가 커지는 폭이 더 크므로 전체 엔트로피는 증가하게 된다.
- 따라서 \( y = -lnx \)의 성격을 생각해보면, 올바르게 예측한 경우 \( t_k \)의 원소가 1일 때(정답이면), \( y_k \)가 소프트맥스 함수의 결과라면 \( y_k \)의 정답을 예측한 원소는 \( y_k \)의 다른 원소에 비해 1에 가까울 것이다.
- 그렇다면 수식의 결과인 엔트로피 '오차'는 t = 1일 때, y의 값이 1에 가까우면 가까울수록 오차의 값, 즉 \( -ln y_k \)의 값이 0에 가까울 것이고, 반대로 잘못 예측한 경우 t = 1일 때, y의 값이 0에 가까우면 가까울수록 오차의 값인 \( -ln y_k \)의 값이 커지게 된다. 따라서 잘못 예측한 경우 오차의 값이 커지게 되는 것이다.
■ 파이썬에서는 다음과 같이 교차 엔트로피 오차를 구현할 수 있다.
def CEE(y, t):
epsilon = 1e-12
y_plus_epsilon = y + epsilon
return -np.sum(t * np.log(y_plus_epsilon))
- np.log를 계산할 때 \( y \)에 아주 작은 값인 엡실론을 더한 이유는 \( \ln(0) = -\infty, \quad -\ln(0) = \infty \)가 되기 때문에 np.log( )함수의 입력값이 0이 되는 것을 방지하기 위함이다. 또한, 당연히 기존 y값이 변동될 정도의 값을 더하면 결과의 변동이 발생하므로, 기존 \( y \)에 영향을 최소화하도록 아주 작은 값을 더해야 한다.
t = [0, 1, 0, 0]
y = [0, 0.7, 0.2, 0.1] # 올바르게 예측한 경우
y2 = [0.7, 0.1, 0.2, 0] # 제대로 예측하지 못한 경우
print(CEE(np.array(y), np.array(t))); print(CEE(np.array(y2), np.array(t)))
```#결과#```
0.3566749439373039
2.3025850929840455
`````````````
- y가 올바르게 예측된 경우, 그렇지 않은 경우와 비교해 오차의 값이 더 작은 것을 볼 수 있다.
3. 미니배치(mini-batch) 학습
■ 모든 train set을 사용할 경우, 교차 엔트로피 오차의 수식은 위의 교차 엔트로피 오차 식을 \( N \)개의 데이터로 확장한 식이다.
\[ E = -\dfrac {1}{N} \displaystyle \sum_{n} \sum_{k} t_{nk}log_{e}y_{nk} \]
- 데이터가 \( N \) 개라면, \( t_{nk} \)는 \( n \)번째 데이터의 \( k \)번째 값을 의미하며, \( \dfrac {1}{N} \)을 곱하는 이유는 \( N \)으로 나누어 '평균' 손실 함수를 계산하기 위함이다. 평균을 사용하면 train set의 개수와 상관없이 평균 손실 함수를 구할 수 있다.
- 다만, 이런 방식은 train set의 개수가 많으면 많을수록 손실 함수를 계산하는 시간이 늘어나게 된다. 따라서 전체 train set에서 일부만 무작위로 추출하여 근사치를 계산하는 것이 더 좋은 방법일 수 있다.
■ 신경망 학습에서도 train set의 일부만 선택하여 학습을 수행할 수 있는데, 이 일부를 미니배치라 하며, 미니배치로 학습하는 방법을 미니배치 학습이라 한다. 결과적으로 미니배치에 맞춰 손실 함수를 계산해야 한다.
■ 예를 들어 MNIST의 train set은 60,000개의 이미지 데이터로 구성되어 있어 전체 train set에 대한 평균 손실 함수를 구하기에 시간이 많이 소요될 수 있다. 따라서 train set 중 10장, 100장, 1000장, ... 등 무작위로 선택한 미니배치를 구성하여 미니배치 학습을 진행해서 근사치를 구하는 것이 효율적일 수 있다.
- 파이썬에서 미니배치를 구성하는 방법은 다음과 같이 train_size의 수 중에서 무작위로 지정한 배치 사이즈의 수만큼 숫자를 선택하고, 이 숫자를 미니배치를 구성할 데이터의 인덱스로 사용하면 된다.
train_size = x_train.shape[0] # 60000
batch_size = 100
batch_mask = np.random.choice(train_size, batch_size) # 60,000개의 숫자 중, 배치 사이즈로 저장할 100개를 무작위 선택
# 즉, batch_mask에는 60000까지의 숫자 중 100개의 랜덤한 숫자가 저장된다.
# batch_mask를 인덱스로 이용해 x_train, y_train에서 각각 100장의 이미지 데이터 추출
x_batch = x_train[batch_mask]
y_batch = y_train[batch_mask]
- 손실 함수도 미니배치를 고려해 다음과 같이 변경해야 한다.
def CEE(y, t):
epsilon = 1e-12
batch_size = y.shape[0] # 매개변수 y의 데이터의 수에 따라 배치 사이즈 설정
E = -np.sum(t * np.log(y + epsilon)) / batch_size
return E