💡 'Deep Learning from Scratch'를 참고하여 작성
신경망에서 학습(train)이란 학습 데이터로부터 가중치 매개변수(weight parameter)의 최적값(optimal value)을 자동으로 획득하는 것을 의미합니다. 이번 게시물에서는 신경망을 학습하기 위한 지표, 손실 함수에 대하여 알아보겠습니다.
1. 데이터와 학습
신경망은 데이터를 통해 학습합니다. 데이터를 이용하여 학습한다는 것은 데이터를 통해 가중치 매개변수의 값을 자동으로 결정한다는 뜻입니다. 이는 모든 매개변수를 수작업으로 결정해야 하는 어려움을 해결해줍니다.
1.1 데이터 주도 학습
기계 학습(machine learning)의 생명은 바로 데이터입니다. 데이터에서 답을 찾고 데이터에서 패턴을 발견하고 데이터로 이야기를 만드는 것이 바로 기계 학습입니다. 기계 학습을 통해 사람 중심의 접근 방식에서 데이터가 이끄는 접근 방식으로 변화하였습니다.
기계 학습은 사람이 알고리즘을 밑바닥부터 설계하는 대신, 주어진 데이터를 잘 활용하여 해결하는데 집중합니다. 예를 들어, 이미지에서 특징(feature)을 추출하고 그 특징의 패턴을 학습하는 방법이 있습니다. 여기서 특징은 입력 데이터에서 본질적인 데이터(중요한 데이터)를 정확하게 추출한 것을 말합니다. 특징을 이용하여 이미지 데이터를 벡터로 변환하고, 변환된 벡터를 가지고 지도 학습(supervised learning) 방식을 이용할 수 있습니다. 기계 학습은 크게 2가지 접근 방법으로 나뉩니다.
- 문제에 따라 사람이 적절한 특징을 생각해내고, 기계 학습을 활용한다.
- 데이터에 포함된 중요한 특징 모두 기계가 스스로 학습한다.
첫 번째 방법의 경우, 사람이 특징에 대해 적절하게 설계함으로써 좋은 결과를 얻을 수 있습니다. 이를 지도 학습이라 합니다. 두 번째 접근 방법을 흔히 딥러닝(deep-learning) 혹은 신경망(neural network)이라 부릅니다. 흔히 딥러닝을 end-to-end 기계 학습이라고도 합니다. 이는 '처음부터 끝까지' 데이터로부터 목표한 결과를 사람의 개입 없이 얻는다는 뜻을 담고 있습니다. 이러한 접근 방법은 아래의 그림 1을 확인하면 더 이해하기 쉽습니다.
1.2 학습 데이터와 테스트 데이터
기계 학습 문제는 데이터를 학습 데이터와 테스트 데이터로 나눠 아래와 같이 수행하는 것이 일반적입니다.
- 학습 데이터를 사용하여 학습하며 최적의 매개변수를 찾는다.
- 테스트 데이터를 사용하여 학습한 모델의 성능을 평가한다.
학습 데이터와 테스트 데이터로 나누는 이유는 범용적으로 사용할 모델을 만들기 위해서입니다. 범용 능력은 새로운 데이터에서도 문제를 올바르게 풀어내는 능력을 말합니다. 이 범용 능력을 획득하는 것이 기계 학습의 최종 목표입니다. 따라서 데이터셋(dataset) 하나로 학습과 평가(evaluation)를 수행하면 올바른 평가가 될 수 없습니다. 특히나 하나의 데이터셋에 지나치게 최적화된 상태를 과적합(overfitting)이라고 합니다. 과적합을 피하는 것은 기계 학습의 중요한 과제 가운데 하나입니다.
2. 손실 함수
신경망 학습에서 최적의 매개변수 값을 탐색하기 위해 사용하는 지표는 손실 함수입니다. 손실 함수는 신경망 성능의 '나쁨'을 나타내는 지표입니다. 현재의 신경망이 학습 데이터를 얼마나 잘 처리하지 못 하느냐를 나타냅니다.
2.1 오차제곱합(Sum of Squares for Error, SSE)
일반적으로 가장 많이 사용하는 손실 함수는 오차제곱합입니다. 수식은 아래와 같습니다.
$$ E=\frac{1}{2}\sum_{k}{(y_k-t_k)^2} $$
- $ y_k $ : 신경망의 출력(output), 추정값
- $ t_k $ : 정답 레이블, 참값
- $ k $ : 데이터의 차원 수
오차제곱합은 각 원소의 출력(추정값)과 정답 레이블(참값)의 차를 제곱하고 그 총합을 구합니다. 오차제곱합에 $ \frac{1}{2} $이 있는 것을 확인할 수 있습니다. 이는 경사하강법(gradient descent) 가운데 하나인 델타 규칙(Delta rule)을 계산하는 과정에서 오차(error)가 지나치게 커지는 것을 막기 위해서 입니다. $ \frac{1}{2} $가 고정은 아니지만, 델타 규칙을 도출하는 과정에서 손실 함수를 미분하는데 이를 편하게 계산하기 위해서로 보입니다. 이를 구현하면 아래와 같습니다.
def sum_squares_error(y: np.array, t: np.array) -> float:
return 0.5 * np.sum((y-t)**2)
이를 실제로 아래와 같이 사용할 수 있습니다.
y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
sum_squares_error(np.array(y), np.array(t))
-> 0.09750000000000003
입력값을 살펴보면, y는 소프트맥스 함수의 출력을 의미합니다. 소프트맥스 함수의 출력은 확률값으로 해석할 수 있었습니다. 즉, 해당 데이터가 2번 레이블일 확률은 0.6이라고 해석할 수 있습니다. t는 정답 레이블을 의미합니다. 참값에 해당하는 하나의 원소는 1로 표현하고 그 외는 0으로 표현하는 표기법인 원-핫 인코딩(one-hot encoding)을 사용했습니다.
2.2 평균제곱합(Mean of Squares for Error, MSE)
기존의 오차제곱합을 데이터의 크기만큼 나눠서 사용하는 손실 함수입니다. 수식은 아래와 같습니다.
$$ E=\frac{1}{k}\sum_{k}{(y_k-t_k)^2} $$
오차제곱합의 경우, 오차가 커질 때 데이터의 양이 많아서 커지는 것인지 확인하는데 어려움이 있습니다. 하지만 평균제곱합은 데이터의 차원 수만큼 오차를 나눠줌으로써 이러한 한계점을 극복할 수 있습니다. 이를 구현하면 아래와 같습니다.
def mean_squares_error(y: np.array, t: np.array) -> float:
return np.sum((y-t)**2) / len(y)
이는 아래와 같이 사용할 수 있습니다.
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]
mean_squares_error(np.array(y), np.array(t))
-> 0.019500000000000007
2.3 교차 엔트로피 오차(Cross Entropy Error, CEE)
교차 엔트로피 오차의 수식은 아래와 같습니다.
$$ E=-\sum_{k}{t_{k}\log{y_k}} $$
- $ y_k $ : 신경망의 출력
- $ t_k $ : 정답 레이블, 원-핫 인코딩 데이터
교차 엔트로피 오차는 $ \log $를 활용하여 정답일 때의 출력이 전체 값을 정하게 합니다. 정답에 해당하는 레이블의 소프트맥스 출력이 커질수록 0에 다가갑니다. 반대로 정답에 해당하는 레이블의 출력이 작아질수록 오차는 커집니다. 이를 구현하면 아래와 같습니다.
def cross_entropy_error(y: np.array, t: np.array) -> float:
delta = 1e-7
return -np.sum(t * np.log(y + delta))
코드의 마지막에 아주 작은 값인 delta를 더한 것을 확인할 수 있습니다. 이는 아주 작은 값을 더함으로써, $ \log $ 함수 내부가 0이 되어 무한대로 발산하는 것을 막기 위해서 입니다. 이를 사용한 결과는 아래와 같습니다.
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))
-> 0.510825457099338
3. 손실 함수의 이용
3.1 미니배치(mini-batch) 학습
기계 학습은 학습 데이터에 대한 솔실 함수의 값을 구하고, 그 값을 최대한 줄여주는 매개변수를 찾아냅니다. 이를 위해서는 모든 학습 데이터를 대상으로 손실 함수의 값을 구해야 합니다. 가령, 교차 엔트로피 오차를 이용해 모든 학습 데이터에 대한 손실 함수의 합을 구하는 방법을 생각해보겠습니다.
$$ E=-\frac{1}{N}\sum_{n}\sum_{k}{t_{nk}\log{y_{nk}}} $$
- $ N $ : 데이터의 개수
- $ t_{nk} $ : $ n $번째 데이터의 $ k $번째 값, 정답 레이블
- $ y_{nk} $ : 신경망의 출력
이전에 언급했던 데이터 하나에 대한 손실 함수를 $ N $개의 데이터로 확장했습니다. 또한, 마지막에 $ N $으로 나누어 정규화(normalization)를 진행하여 '평균 손실 함수'를 구했습니다. 이렇게 평균을 구하여 사용하면 학습 데이터와 상관 없이 언제든 통일된 지표를 얻을 수 있따는 장점이 있습니다.
하지만 모든 데이터를 대상으로 손실 함수의 합을 구하기에는 시간이 많이 필요합니다. 이를 해결하기 위해 데이터의 일부를 추려 전체의 '근사치'로 이용합니다. 신경망 학습에서도 학습 데이터로부터 일부만을 골라 학습을 수행합니다. 이 일부를 미니배치라 부릅니다. 무작위로 데이터를 선택하고 이를 이용해 학습하는 방법을 미니배치 학습이라 합니다.
3.2 교차 엔트로피 오차(배치)
배치 데이터를 지원하는 교차 엔트로피는 기존의 구현에서 조금만 수정하면 됩니다. 여기에서는 데이터가 하나인 경우와 배치로 묶어서 입력하는 경우 모두를 처리하도록 구현하였습니다.
def cross_entropy_error(y: np.array, t: np.array) -> np.array:
if y.ndim == 1:
t = t.reshape(1, t.size)
y = y.reshape(1, y.size)
batch_size = y.shape[0]
delta = 1e-7
return -np.sum(t * np.log(y + delta)) / batch_size
마지막에 배치의 크기로 나눔으로써 정규화를 진행하고, 각 데이터의 평균 교차 엔트로피 오차를 계산할 수 있습니다.
3.3 손실 함수를 지표로 사용하는 이유
기계 학습의 궁극적인 목적은 높은 '정확도'를 이끌어내는 매개변수 값을 찾는 것입니다. 그렇다면 왜 '정확도'라는 지표가 아닌, '손실 함수의 값'이라는 우회적인 방법을 택할까요?
신경망 학습에서 '미분'의 역할에 주목해보면 그 해답을 찾을 수 있습니다. 신경망 학습에서는 최적의 매개변수(가중치와 편향)를 탐색할 때, 손실 함수의 값을 가능한 작게 하는 매개변수 값을 찾습니다. 이때 기울기(gradient)를 계산하고, 이를 단서로 매개변수의 값을 서서히 갱신하는 과정을 반복하게 됩니다.
기울기를 구하기 위해서는 매개변수의 미분을 계산해야 합니다. 매개변수 가운데 가중치 매개변수 하나를 떠올려보겠습니다. 해당 가중치 매개변수에 대한 손실 함수의 미분은 '가중치 매개변수의 값을 아주 조금 변화시켰을 때, 손실 함수가 어떻게 변화하냐'를 의미합니다. 이에 대한 변화는 아래와 같습니다.
- 미분 값 : 양 → 가중치 : 음의 방향으로 변화
- 미분 값 : 음 → 가중치 : 양의 방향으로 변화
- 미분 값 : 0 →가중치 : 변화 없음
여기서 미분 값이 0이 되면 가중치 매개변수를 어느 쪽으로 움직여도 손실 함수의 값은 변하지 않습니다. 즉, 미분 값이 0일 때 가중치 매개변수의 갱신은 멈춥니다. 정확도를 지표로 삼아서 안 되는 이유는 정확도의 미분 값이 대부분의 장소에서 0이 되어 매개변수를 갱신할 수 없기 때문입니다. 정확도를 지표로 삼으면, 가중치 매개변수를 조금 수정하더라도 정확도가 개선되지 않고 일정하게 유지됩니다. 정확도는 매개변수의 미세한 변화에는 거의 반응하지 않고, 반응이 있더라도 그 값이 불연속적으로 갑자기 변화합니다. 이러한 이유 때문에 손실 함수를 신경망 학습의 지표로 사용합니다.
'기초 > 인공지능' 카테고리의 다른 글
옵티마이저(Optimizer) (2/2) (0) | 2021.09.02 |
---|---|
옵티마이저(Optimizer) (1/2) (1) | 2021.08.31 |
오차역전파(Back-Propagation) (0) | 2021.08.29 |
활성화 함수(Activation function) (0) | 2021.08.26 |
퍼셉트론(Percetron) (0) | 2021.08.25 |