1. 가중치 초깃값
■ 너무 큰 가중치 (매개변수)값은 학습 과정에서 과적합을 발생시킬 수 있어서 초깃값을 최대한 작은 값에서 시작하거나 가중치 감소 기법을 통해 과적합을 억제해야 한다.
■ 주의할 점은 가중치 초깃값을 설정할 때 가중치를 균일한 값으로 설정해서는 안 된다.
■ 왜냐하면, 오차역전파 과정에서 모든 가중치 값이 동일하게 갱신되기 때문에 초깃값이 갱신을 거쳐도 여전히 동일한 값을 유지하게 만들어 가중치를 여러 개로 설정한 의미가 없어지기 때문이다. 따라서 초깃값은 무작위로 설정해야 한다.
■ 그러나 가중치를 랜덤하게 초기화해서 사용하면 다음 그림과 같이 신경망의 초기 Loss가 달라진다.
■ 즉, 신경망을 초기화할 때마다 위의 그림과 같이 Loss의 초기 위치가 A에서 E까지 달라질 수 있다. 그러므로 어떻게 초기화하냐에 따라 학습 속도가 달라질 수 있다.
3.1 은닉층의 활성화값 분포
■ 일반적으로 활성화 함수가 sigmoid나 tanh 등의 S자 형태의 곡선이라면 Xavier 초깃값, 활성화 함수가 ReLU일 때는 He 초깃값을 사용한다.
■ 먼저 시그모이드 함수를 사용하는 5층 신경망에 무작위 입력 데이터를 넣었을 때 각 층의 활성화값 분포는 다음과 같다.
def sigmoid(x):
return 1 / (1 + np.exp(-x))
x = np.random.randn(1000, 100)
node_num, hidden_layer_size = 100, 5 # 각 은닉층의 노드(뉴런) 수 및 은닉층 수
- (1) \( w \)를 표준편차가 1인 정규분포를 사용해 초기화했을 때, 각 은닉층의 활성화값(활성화 함수의 출력값) 분포는 다음과 같다.
w = np.random.randn(node_num, node_num)*1 # 각 은닉층 사이의 가중치 # 평균이 0이고 표준편차가 1인 정규분포
- 각 층의 활성화 값들은 모두 0과 1에 치우쳐져 있다. 이렇게 출력이 0에 또는 1에 가까워질수록 미분 값(기울기)은 0에 가까워진다.
- 왜냐하면, 시그모이드 함수를 \( h(x) = \dfrac{1}{1+e^{-x}} \)라 했을 때, 시그모이드 함수의 미분은 \( h'(x) = h(x)(1 - h(x)) \)이다.
- 따라서 시그모이드 함수 값(여기서는 은닉층의 활성화값)이 0이면 \( h'(0) = 0 \times (1 - 0) = 0 \), 0에 가까우면 \(
\displaystyle \lim_{x \to 0} h'(x) \approx 0
\), 시그모이드 함수 값이 1이면 \( h'(1) = 1 \times (1 - 1) = 0 \), 1에 가까우면 \(
\displaystyle \lim_{x \to 1} h'(x) \approx 0
\)
- 이렇게 시그모이드 함수의 출력값이 0이나 1에 가까워지면 기울기는 거의 0이 되기 때문에, 역전파 과정에서 가중치 업데이트를 위해 활성화 함수를 미분한 값을 곱해주는 특성상 기울기 값은 점차 작아지다가 0에 수렴해 학습이 잘 이뤄지지 않을 수 있다.
cf) 이런 문제로 인해 기울기 소실(gradient vanishing) 문제가 발생하는 것이다.
위의 그림과 같이 층이 깊어 신경망이 복잡할수록 기울기 소실은 더 심각한 문제가 될 수 있다.
- (2) 다음으로 가중치의 표준편차를 0.01로 변경해 각 층의 활성화값 분포를 보면
w = np.random.randn(node_num, node_num) * 0.01
- 각 층의 활성화 값들이 0.5 부근에 모여 있는 것을 볼 수 있다. 앞에서처럼 0과 1로 치우쳐 있지는 않으므로 역전파 과정에서 시그모이드 함수의 미분 값(기울기)를 계산할 때, 기울기 소실 문제가 발생하지는 않는다.
- 다만, 활성화 값들이 특정 값 근처에 모여 있기 때문에 신경망의 표현력이 저하되는 문제가 발생한다.
- 예를 들어, 여러 사진 중 고양이 사진을 분류하는 신경망에서 각 층의 활성화 값들이 0.5에 치우친 상황이라 가정해 보자.
- 각 층의 뉴런들이 고양이 사진을 보고 비슷한 값을 출력한다는 것은 신경망이 고양이 사진의 특징을 충분히 반영하지 못한 것이다.
- 고양이의 눈, 귀, 털 등 복잡한 패턴을 인식하고 구분해야 하는데 뉴런들이 모두 비슷한 활성화 값들을 출력한다면, 고양이의 서로 다른 특징들을 같은 특징으로 인식하는 것과 같다. 이렇게 학습된 신경망은 고양이를 개, 호랑이 등 다른 동물과 잘 구분하지 못하게 된다.
- (1), (2)의 예를 통해 알 수 있는 것은 각 층의 활성화 값은 골고루 분포되어야 한다는 것이다.
- 층과 층 사이에 다양한 데이터가 분포되어 있어야 기울기 소실 문제, 표현력 문제에 빠지지 않고 정상적인 신경망 학습이 이뤄진다.
- 신경망이 정상적인 활성화값 분포를 가진다면, 고양이 사진의 각 부분에 따라 뉴런들이 다르게 활성화된다.
- 예를 들어 고양이 귀에 민감한 뉴런들은 이 특징에 반응해 더 큰 활성화 값을 출력하고, 털 패턴에 반응하는 뉴런들은 다른 값으로 활성화될 것이다. 이런 식으로 여러 개의 뉴런이 서로 다른 특징을 학습하게 되면, 다른 동물과 고양이의 차이점을 더 명확하게 구분할 수 있게 된다.
- (3) Xavier 초깃값
- Xavier 초깃값은 정규 분포와 균등 분포 2가지 방법으로 나뉜다.
- 이전 은닉층의 노드가 \( n \) 개, 다음 은닉층의 노드가 \( m \) 개일 때, 정규 분포 방법은 \( \dfrac{2}{n + m} \)을 표준편차로 하는 정규 분포로 가중치를 초기화하고
\[
W \sim \mathcal{N} \left( 0, \operatorname{var}(W) \right), \quad \operatorname{var}(W) = \dfrac{2}{n + m}
\] - 균등 분포에서는 다음과 같이 가중치 매개변수 값의 하한 값, 상한 값을 설정해서 설정한 범위 안에서 균일하게 선택한 가중치로 초기화한다.\[
W \sim U\left( -\sqrt{\dfrac{6}{n + m}}, \; \sqrt{\dfrac{6}{n + m}} \right)
\] (* \( n \)을 입력 뉴런의 수 \( m \)을 출력 뉴런의 수로 봐도 된다.)
- Xavier 초깃값을 이용하는 딥러닝 프레임워크 중 카페(caffe) 프레임워크는 다음과 같이 이전 층의 노드 수만 고려하여 '앞 계층의 노드(뉴런)가 \( n \) 개라면 표준편차가 \( \dfrac{1}{\sqrt{n}} \)인 정규 분포'를 사용해 각 층의 활성화 값들을 더 광범위하게 분포시킨다.
- Xavier 초깃값을 사용하면 이전 층의 노드가 많을수록 가중치가 좁게 퍼진다. 이는 가중치 초깃값의 표준편차가 \( \dfrac{1}{\sqrt{n}} \)인 정규분포를 따르도록, 즉 가중치의 분산이 앞 층위 뉴런 수 \( 'n' \)에 비례하기 때문이다.
w = np.random.randn(node_num, node_num) / np.sqrt(node_num)
- 이렇게 Xavier 초깃값을 사용할 경우 앞의 (1), (2)와 반대로 가중치가 넓게 분포되어 있는 것을 볼 수 있다.
■ tanh 함수를 사용한 경우에도 시그모이드처럼 활성화값 분포가 치우치게 된다.
- 시그모이드 함수는 \( \dfrac{1}{1 + e^{-x} \)으로 함숫값의 범위가 0과 1 사이이다. 입력값 \( x \)가 큰 양수일수록 함수의 출력값은 1에, 큰 음수일수록 0에 가까워지기 때문에 활성화 값들이 0이나 1에 치우쳐 기울기 소실 문제를 발생시킨다.
- 하이퍼볼릭탄젠트 함수는 \( tanh(x) = \dfrac{e^x - e^{-x}}{e^x + e^{-x}} \)으로 함숫값의 범위는 -1과 1 사이이다. 큰 양수일수록 함수의 출력값은 1에, 큰 음수일수록 -1에 가까워지고 미분 시, \( tanh'(x) = 1 - tanh^2(x) = (1 - tanh(x))(1 + tanh(x)) \)이므로 활성화 값의 분포가 1이나 -1에 집중되어 시그모이드 함수처럼 기울기 소실 문제가 발생한다.
- 두 함수 모두 양수 또는 음수 입력값이 커질수록 활성화값이 한쪽 극단으로 몰리게 되는데 이는 두 함수가 S자 곡선 모양을 띄고 있어, 특정 구간에서 출력값이 포화되기 때문이다.
- 단, tanh 함수는 \( (x, y) \) = (0, 0) 에서 대칭이고 출력값이 -1에서 1 사이에 분포되며, 시그모이드는 (0, 0.5) 에서 대칭이고 출력값이 0에서 1 사이에 분포하기 때문에 tanh 함수의 활성화 값들이 시그모이드 활성화 값들 보다 값이 조금 더 고르게 분포할 가능성이 있다.
■ Xavier 초깃값은 활성화 함수가 선형인 것을 전제로 한다. 시그모이드와 tanh 함수는 \( x = 0 \)에서 좌우 대칭이기 때문에 함수의 중앙 부근에서 \( y = ax + b \) 형태의 선형 함수로 볼 수 있다.
따라서 활성화 함수로 sigmoid, tanh 함수를 사용할 경우 가중치 초깃값은 Xavier 초깃값이 적합하다.
■ 반면, ReLU는 \( f(x) = max(0, x) \)로 입력값 \(x \)가 0보다 크면, 그 값을 그대로 출력하고, 0 이하면 0을 출력한다. 이렇게 ReLU는 선형성을 가지지 않는 비선형 함수이기 때문에 Xavier의 가중치 초기화 방법이 적합하지 않으므로 ReLU에 특화된 초깃값을 사용해야 한다.
■ 이 특화된 가중치 초깃값을 'He 초깃값'이라 하며, He 초깃값도 정규 분포와 균등 분포, 2 가지 방법이 있다.
- 앞 층의 노드가 \( n \)개일 때, 표준편차가 \( \sqrt{\dfrac{2}{n}} \)인 정규 분포를 사용하고\[
W \sim \mathcal{N}\left(0, var(W)\right), \quad var(W) = \sqrt{\frac{2}{n}}
\] - 균등 분포일 때도 Xavier 균등 분포 방법처럼 가중치 매개변수 값의 하한, 상한 값을 설정해서 설정한 범위 안에서 가중치를 초기화한다.\[
W \sim U\left(-\sqrt{\frac{6}{n}}, \sqrt{\frac{6}{n}}\right)
\] ■ 표준편차가 0.01인 정규 분포, Xavier 초깃값, He 초깃값일 때 활성화 함수 ReLU의 출력 값들, 활성화 값들의 분포는 다음과 같다.
- 표준편차 0.01인 정규 분포를 가중치 초깃값으로 사용한 경우 각 층의 활성화 값들이 매우 작다. 순전파 과정에서 이렇게 작은 값을 전달하면 역전파 과정에서도 가중치의 기울기(미분 값)가 작아져 학습이 제대로 수행되지 않는다.
- Xavier 초깃값을 사용한 경우 층이 깊어질수록 조금씩 한 쪽으로 활성화 값의 분포가 치우쳐지는 것을 볼 수 있다. 따라서 신경망의 층이 깊은데 ReLU 활성화 함수를 사용하고 가중치 초깃값을 Xavier 초깃값으로 사용한다면 기울기 소실 문제가 발생할 것이다.
w = np.random.randn(node_num, node_num)* np.sqrt(2.0 / node_num)
- He 초깃값을 사용한 경우, 1층에서 5층의 활성화 값들이 모두 균일하게 분포되어 있는 것을 볼 수 있다. 따라서 신경망의 층이 깊어지더라도 분포가 균일하게 유지되므로 역전파 과정에서도 적절한 기울기가 유지될 것이다.
■ 정리하면, 활성화 함수로 sigmoid, tanh 등의 S자 곡선 함수를 사용할 경우 또는 선형 함수를 사용할 경우 가중치로 Xavier 초깃값을, 선형으로 분류, 해결되지 않는 복잡한 문제를 해결하고자 활성화 함수로 비선형 함수인 ReLU를 사용할 경우 He 초깃값을 사용하는 것이 일반적이다.
■ 정리하자면, Xavier(Glorot Initialization)는 활성화 함수가 없거나, tanh, sigmoid, softmax일 때, He는 활성화 함수가 ReLU, LeakyReLU, ELU 등 ReLU 계열의 비선형 함수일 때 사용하면 효과적이다.
2. 배치 정규화(Batch Normalization)
■ 신경망이 지니고 있는 단점으로 과적합과 기울기 소실(Gradient Vanishing) 외에도 Internal Covariate Shift 현상이 있다.
■ Internal Covariate Shift 란 다음 그림처럼 각 Layer마다 입력 데이터의 분포가 달라짐에 따라 학습 속도가 느려지는 현상을 말한다.
■ 배치 정규화는 Internal Covariate Shift 현상을 방지하기 위한 기법이다.
■ 즉, 배치 정규화를 적용하면 각 Layer마다 데이터의 분포가 달라짐에 따라 학습 속도가 느려지는 것을 방지하기 때문에 배치 정규화는 초깃값에 대한 민감도, 즉 가중치 초기화 방식에 대한 민감도가 낮다.
■따라서 배치 정규화는 신경망 모델의 학습 속도 향상에 기여한다. 이외에도 학습 안정화(기울기 소실, 폭주 문제 완화) , 과적합 방지(드롭아웃 같은 기법과 시너지 효과를 내거나 필요성 감소)에 기여한다.
■ 위와 같은 장점들이 작동하는 이유는 학습 과정에서 다양한 분포를 가지는 입력값을 표준 정규분포처럼 평균이 0, 분산이 1이 되게 정규화하기 때문에 각 Layer의 데이터 분포를 정규화해서 학습 속도를 빠르게 만들기 때문이다.
■ 배치 정규화의 목적은 각 층에서 활성화 값들이 적절한 분포를 가질 수 있도록 만드는 것으로, 다음 그림과 같이 신경망에 배치 정규화 계층을 넣어서 Affine 계층의 출력값을 배치 정규화 계층에서 입력값으로 받아 적절한 분포를 만든 다음, 이 값들을 활성화 함수 계층에서 입력값으로 사용하여 역전파 과정에서도 적절한 기울기가 유지되도록 하는 것이다. 이로 인해 다양한 가중치 초기화 방법을 시도할 필요성을 줄여준다.
■ 배치 정규화는 학습 시 미니배치 단위로 표준 정규화, 표준화(Standardization)라고도 불리는 Z - score Normalization처럼 데이터 분포가 평균이 0, 분산이 1이 되도록 정규화한다.
- \( m \) 개의 입력 데이터를 가지는 미니배치 B = {\( x_1, x_2, \ldots, x_m \)}가 있다고 할 때, 다음과 같이 B의 평균, 분산을 이용하여 정규화를 진행한다.
\(
\mu_B \leftarrow \dfrac{1}{m} \sum_{i=1}^{m} x_i, \quad
\sigma_B^2 \leftarrow \dfrac{1}{m} \sum_{i=1}^{m} (x_i - \mu_B)^2
\)
(* \( \mu_B \)는 미니배치 B에 대한 평균, \( \sigma_B^2 \)는 미니배치 B에 대한 분산)
\(
\hat{x}_i \leftarrow \dfrac{x_i - \mu_B}{\sqrt{\sigma_B^2 + \epsilon}}
\) (* 분모에 엡실론을 더하는 이유는 \( \sigma^2 = 0 \)일 때, 분모가 0이 되는 것을 방지하기 위해)
■ 정규화를 통해 미니배치 입력 데이터 \( x_1, x_2, \ldots, x_m \)을 평균이 0, 분산이 1인 \( \hat{x_1}, \hat{x_2}, \ldots, \hat{x_m} \)으로 변환하는 배치 정규화 계층을 활성화 함수 계층의 앞(혹은 뒤)에 위치시켜, 각 층에서 데이터의 분포를 덜 치우치게 만들어 줄 수 있다. 따라서 가중치 초깃값을 잘못 설정해 발생하는 기울기 소실 문제를 완화시킬 수 있다.
■ 마지막 식 \( y_i \leftarrow \gamma \hat{x_i} + \beta \)는 정규화된 입력 데이터 \( hat{x_i} \)에 대해 학습 가능한 변수인 scale 매개변수 \( \gamma \), shift 매개변수 \( \beta \) 값을 통해 (\( \gamma = 1, \beta = 0 \) 부터 시작) 배치 정규화의 최종 결과인 \( y_i \) 값을 적절한 값으로 조정해 나간다.
■ \( \gamma \)와 \( \beta \)를 사용하는 이유는 \( \gamma \)는 \( \hat{x_i} \)에 곱해 \( \hat{x_i} \)를 확대, \( \beta \)는 \( \gamma \hat{x_i} \)를 \( \beta \) 만큼 이동 시켜 정규화된 입력 데이터가 평균 0 & 분산 1 분포에 몰리는 것을 방지하기 때문이다.
■ 즉, 정규화 결과가 대부분 0에 가까운 값이 될 수 있어서 정규화하려는 의도와는 다르게 비선형성을 잃게 된다. 이를 방지하고자 스케일과 시프트의 역할을 해주는 \( \gamma \)와 \( \beta \)를 적용하는 것이며, \( \gamma \)와 \( \beta \)는 학습 과정에서 역전파를 통해 올바른 값으로 점차 갱신된다.
■ 결국, 역전파를 통해 갱신된 \( \gamma \)와 \( \beta \)는 학습(순전파 & 역전파)이 반복될수록 한 번의 학습에 사용되는 미니배치의 분포를 학습이 잘 되는 구간에 배치시키게 되므로, 모델의 정확도를 높이는 역할을 한다고 볼 수 있다.
■ 따라서 \( \gamma \)와 \( \beta \)의 역할은 입력 데이터가 특정 분포(올바른 학습에 도움이 안 되는 구간)에 몰리는 것을 방지하고 \( y_i \) 값을 적절한 값으로 조정 = 학습이 올바르게 되는 구간으로 분포 이동이다.
■ 예를 들어 다음 그림과 같이 활성화 함수로 비선형 함수인 ReLU 함수를 사용했을 때
- ReLU 함수는 입력 데이터의 값이 0 미만이면 0, 0 이상이면 그 값 그대로 출력하는 함수이다. 따라서 위와 같이 Layer 1에서 0 미만 값이 0으로 되는 것을 볼 수 있다.
- 그다음, Layer 1의 출력 결과와 가중치의 선형 결합을 통해 Layer 2의 분포가 우측으로 이동되었지만, Layer 3에서 배치 정규화를 통해 데이터 분포가 정규 분포와 비슷한 형태로 정규화되는 것을 볼 수 있다.
- 그리고 Layer 3의 분포는 ReLU 함수를 통해 Layer 4에서 데이터의 분포가 다시 0 미만 값은 0이 되는 것을 볼 수 있다.
- 만약, 배치 정규화를 적용하지 않고 바로 Layer 4에 Layer 2의 분포가 들어갔으면 ReLU 함수를 사용했기 때문에 Layer 2의 데이터 분포 형태가 Layer 4에 그대로 유지되었을 것이다.
- 즉, 배치 정규화를 사용하지 않았다면 은닉층을 깊게 쌓았을 때 비선형 활성 함수를 사용하는 의미가 없어질 가능성이 있다.
■ 따라서 배치 정규화는 데이터의 분포를 정규화해서 비선형 활성 함수의 의미를 살리는 기법으로도 볼 수 있다.
■ 단, 배치 정규화는 위의 계산 과정에서 볼 수 있듯이 미니배치 크기에 의존하므로 작은 미니배치에서는 배치 정규화가 극단적으로 적용될 수 있다.
3. 과적합(Overfitting) 억제 기법
■ 오버피팅은 학습 데이터에만 지나치게 적응되어 unseen 데이터에 대해서 모델의 성능이 크게 떨어졌을 때, 이를 '과적합 되었다.'라고 한다.
■ ML에서는 feature의 수가 많아 복잡한 모델이나 훈련 데이터 수가 적을 때 오버피팅이 발생하며, ML은 범용 성능을 지향하기 때문에 Nested CV, feature selection, feature importance, 통계적 방법 등 범용 성능 확인 및 불필요한 변수를 제거하는 다양한 방법들이 있다.
■ 신경망의 오버피팅도 주로 ① 매개변수가 많고 층이 깊어 표현력이 높은 모델(= 복잡한 모델) ② 훈련 데이터가 적은 경우 발생하며 대표적인 억제 기법으로 가중치 감소 기법과 드롭아웃이 있다.
3.1 가중치 감소(= 감쇠)(Weight Decay)
■ 가중치 감소는 학습 과정 중 가중치 크기에 비례하는 페널티를 부과하여 오버피팅을 억제하는 기법이다. 큰 가중치에 대해서는 이에 비례하는 큰 페널티가 부과된다. 즉, 신경망 모델이 과도하게 학습을 하지 않도록 적당한 페널티를 부여하는 것이다.
■ 오버피팅은 매개변수가 너무 많거나 매개변수의 값이 커서 복잡하고 표현력이 높은 모델에서 발생하는 경우가 많다.
■ 이 복잡한 모델을 좀 더 간단하게 만들기 위해, 가중치가 커지는 것을 억제하는 대표적인 가중치 규제(Regularization) 기법으로 L1 규제, L2 규제가 있다.
■ L1 규제는 손실 함수에 가중치 벡터의 L1 노름을, L2 규제는 손실 함수에 L2 노름을 추가하는 방법으로, Loss function + regularization term 으로 정규화 항을 더해 손실 함수에 가중치 규제를 적용한다.
■ 가중치 \( W = (w_1, w_2, \ldots, w_n) \)이 있다면, L1 노름은 가중치 \( w \)들의 절댓값의 합을 손실 함수에 더하는 방식으로 페널티를 부여한다.
■ 예를 들어 MSE를 손실 함수로 사용했을 때 L1 norm을 적용하면 다음과 같다. 이때, \( \lambda \)는 정규화의 세기를 조절하는 하이퍼파라미터로 상수 값을 가진다. \( \lambda \) 값을 크게 설정하면 할수록 가중치에 대한 페널티가 커지게 된다.\[
L_1 = MSE + \lambda \displaystyle\sum_{i=1}^{N} |w_i| = MSE + \lambda \left( |w_1| + |w_2| + \cdots + |w_n| \right)
\] - 이렇게 모든 가중치들의 절댓값 합계를 정규화 항으로 사용한다.
- 식을 보면 모든 가중치들을 0으로 만들면 손실 함수의 값이 최솟값이 될 것 같지만 그렇지 않다.
- 가중치가 모두 0 이면 순전파 과정에서부터 올바른 학습이 이뤄질 수 없기 때문이다.
- Affine 계층을 생각해 보면 된다. Y = XW + B에서 W가 모두 0 이면 편향인 B만 남게 된다.
- L1은 단순히 가중치 값들을 작게 만드는 것이 아니라 가중치 절댓값 합계를 줄이면서 손실 함수의 값이 최소가 되게 하는 균형 잡힌 가중치를 찾아가는 방법이다.
- 예를 들어 \( \hat{y} \)가 1차 함수 \( \hat{y} = w \cdot x + b \) 형태라면,
-\(
L_1 = MSE + \lambda |W| = (\hat{y} - y)^2 + \lambda |W| = (w \cdot x + b - y)^2 + \lambda |w|
\)이고
- \( L_1 \)을 편미분하면, \(
\dfrac{\partial L_1}{\partial W} = \dfrac{\partial}{\partial W} \left( (w \cdot x + b - y)^2 + \lambda |W| \right) + \lambda \cdot \dfrac{|w|}{\partial W} = 2 \cdot x \cdot (w \cdot x + b - y) + \lambda \cdot \dfrac{\partial |w|}{\partial W} \) (\( \lambda \)는 상수)
- 이때 \( |w| \)의 형태는 다음 그래프와 같기 때문에
\( y = |w| \)를 미분한 \( \dfrac{|w|}{\partial w} \) 그래프는 다음 그래프와 같다.
따라서 \(
\dfrac{\partial L_1}{\partial w} = 2x(w \cdot x + b - y) + \lambda \cdot \dfrac{|w|}{\partial w}
\)는 \( w \) 범위에 따라 \( w \geq 0 \)이면 \( \lambda \cdot \dfrac{|w|}{\partial w} = \lambda \cdot 1 \), \( w \) < 0 이면 \( \lambda \cdot \dfrac{|w|}{\partial w} = \lambda \cdot -1 \)가 되기 때문에 \(
\dfrac{\partial L_1}{\partial W} =
\begin{cases}
2x(w \cdot x + b - y) + \lambda, & \text{if } w \geq 0 \\[8pt]
2x(w \cdot x + b - y) - \lambda, & \text{if } w < 0
\end{cases}
\)으로 나타낼 수 있다.
■ 따라서 손실 함수가 MSE인 경우 가중치를 갱신할 때, \(
w_{new} \leftarrow w - \eta \cdot \dfrac{\partial L_1}{\partial w} =
\begin{cases}
w - \eta \cdot \left( 2x(w \cdot x + b - y) + \lambda \right), & \text{if } w \geq 0 \\[8pt]
w - \eta \cdot \left( 2x(w \cdot x + b - y) - \lambda \right), & \text{if } w < 0
\end{cases}
\)의 방식으로 가중치를 갱신한다.
- 즉, 가중치 \( w \)가 양수일 경우 \( w_{new} \)는 \( \lambda \) 값에 비례하여 작아지게 되고, (\( - \eta \lambda \))
- 가중치 \( w \)가 음수일 경우 \( w_{new} \)는 \( \lambda \) 값에 비례하여 커지게 된다. (\( + \eta \lambda \))
■ 이런 가중치 갱신 방법으로 학습을 진행하게 되면 \( L_1 \)은 가중치들의 절댓값 합을 최소로 만들려고 하기 때문에 가중치 \( w \) 값들은 0에 가까이 작아지거나 특정 가중치는 0이 된다. 즉, 불필요한 가중치들은 학습 과정에서 제거되기 때문에 결과적으로 모델의 복잡성이 줄어들게 된다.
■ 따라서 \( L_1 \)은 어떤 특성들이 모델에 영향을 주는지 확인하고자 할 때 유용하게 쓰일 수 있다.
■ \( L_2 \)는 모든 가중치 \( w \)들의 제곱합을 손실 함수에 더하는 방식으로 페널티를 준다.
■ 예를 들어 위의 예시에서 \( L_2 = MSE + \lambda w^2 \)으로 나타낼 수 있다.
- 위의 예시와 마찬가지로 \( \hat{y} = w \cdot x + b \)라고 한다면, \(
L_2 = MSE + \lambda w^2 = (w \cdot x + b - y)^2 + \lambda W^2
\)이며
- \(
\dfrac{\partial L_2}{\partial w} = \dfrac{\partial}{\partial w} \left( (w \cdot x + b - y)^2 \right) + \lambda \cdot \dfrac{w^2}{\partial w} = 2x (w \cdot x + b - y) + 2 \lambda w
\)이다.
■ 따라서 가중치를 갱신할 때, \(
w_{new} \leftarrow w - \eta \cdot \dfrac{\partial L_2}{\partial w} = w_ - \eta \cdot \left( 2x (w \cdot x + b - y) + 2 \lambda w \right)
\)의 방식으로 가중치를 갱신한다.
- \( \dfrac{\partial L_2}{\partial w} \) 앞에 \( - \eta \)가 있기 때문에 가중치 \( w \)가 양수일 경우 새로운 가중치 \( w_{new} \)는 크기가 줄어들고
- 반대로 \( w \)가 음수인 경우 \( w_{new} \)의 크기가 커지게 된다.
■ 이런 가중치 갱신 방법으로 학습을 진행하게 되면 \( L_2 \)는 \( L_1 \)과 달리 가중치들의 제곱을 최소화로 만들려고 하기 때문에 특정 가중치를 완전히 0으로 만들지 않고 모든 가중치들을 0에 가까워지도록 만든다.
■ 일반적으로 \( L_1 \) 보다는 \( L_2 \)가 좀 더 안정적으로 작동하는데, 그 이유는 \( L_2 \)는 \( w_{new} \)를 구하기 위해 \( 2 ' \lambda w ' \) 즉, 가중치의 크기를 반영한다.
■ 따라서 가중치 크기와 상관없이 가중치별로 변화를 주는 \( L_1 \)에 비해 \( L_2 \)는 가중치 \( w \)가 작으면 작은 페널티를, 가중치 \( w \)가 크면 큰 페널티를 주는, 가중치 \( w \)의 크기에 상응하는 페널티를 주기 때문에 좀 더 안정적으로 작동한다.
■ 예를 들어 가중치 매개변수가 \( w_1, w_2 \) 2개인 신경망 모델에 대해 'a'라는 학습 데이터를 훈련시켰을 때, 다음과 같이 \( w_1w_2 \) 평면에서 손실 함수를 봤을 때 'A' 지점에 global minimum으로 수렴했다고 가정해보자.
- 단, 이 global minimum은 학습 데이터가 'a'인 경우에만 A 지점으로 수렴할 뿐, 'b'라는 새로운 학습 데이터로 훈련시켰을 때 A 지점이 아닌 다른 지점 'B'에서 global minimum으로 수렴할 수 있다. 즉, 오버피팅이 발생할 수 있다.
- 따라서 모델을 만들고 unseen 데이터로 예측할 때, unseen 데이터에 대해 정확하게 예측하지 못한다.
■ 만약, 기존 손실 함수에 \( L_2 \) 노름을 더해주게 된다면, 2차원 벡터 \( W = (w_1, w_2) \)일 때 \( L_2 \) 노름은 각 원소의 제곱들을 더한 것이므로 \( \lambda W^2 = \lambda ||W||^2 \), \( L_2 \) 노름은 \( ||W||^2 = \sqrt{w_{1}^2 + w_{2}^2} \)이 된다.
■ 따라서 새로운 \( Loss = \) 기존 손실 함수 \( + \lambda ||W||^2 \)이며, \( \lambda ||W||^2 \) 항은 \(
\lambda \sqrt{w_{1}^2 + w_{2}^2}
\) 이므로 원점에서 global minimum을 가지게 된다.
■ 이렇게 \( L_2 \) 노름을 더한 경우, 학습 과정에서 기존 손실 함수와 \( L_2 \) 노름을 더한 새로운 Loss를 계산하게 된다. 따라서 새로운 Loss 형태는 다음과 같이 두 그래프를 합친 형태가 되어서
기존에 global minimum \( A \)에 수렴하지 않고(= 오버피팅이 발생하지 않고), 새로운 지점 \( A' \)에 수렴하게 만들어 학습 데이터 a 외에도 학습 데이터 b, c, d, ... 등을 훈련시켰을 때, 그 새로운 수렴 지점 \( A' \)에 근접하게 수렴할 수 있게 된다.
■ 즉, 기존 손실 함수에 \( L_2 \) 노름을 더하는 것은 서로 다른 학습 데이터를 사용하더라도 위의 오른쪽 그림과 같이 각각의 학습데이터의 global minimum 지점에 근접하게 수렴할 수 있도록 하는 역할을 하게 된다.
3.2 드롭아웃(Dropout)
■ 가중치 감소 방법은 구현이 간단하고 일정 수준의 과적합은 억제할 수 있으나, 신경망 모델이 복잡해지면 가중치 감소만 적용해서 과적합을 충분히 억제하기 어렵다.
■ 이럴 때, 드롭아웃 기법을 이용한다.
■ 드롭아웃은 신경망 학습 과정에서 과적합을 방지하기 위해 학습 단계마다 일부 뉴런(노드)을 무작위로 삭제(비활성화)하는 기법이다.
■ 드롭아웃을 얼마나 적용할지 확률 값을 지정해야 하며, 이는 Input Layer와 Hidden Layer에도 적용할 수 있다. 또한, 매 Epoch마다 랜덤하게 드롭아웃한다.
■ 이렇게 훈련 과정에서 랜덤하게 선택한 뉴런을 일시적으로 비활성화시켜 삭제된 뉴런이 다음 층에 있는 뉴런에 신호를 전달하지 못하게 만든다.
■ 이 과정을 데이터가 전달될 때마다 반복하여 신경망이 데이터의 다양한 패턴을 더 강건하게 학습할 수 있도록 돕는다.
■ 훈련 때는 데이터가 전달될 때마다 랜덤으로 뉴런을 삭제하지만, 시험(test) 단계에서는 모든 뉴런을 활성화한다. 그리고 훈련 시 일부 뉴런이 비활성화되었기 때문에, 시험 시 각 뉴런의 출력에 삭제한 비율을 곱하여 출력한다.
■ 예를 들어 뉼너 삭제 비율이 0.5 였다면 테스트 단계에서 각 뉴런의 출력에 0.5를 곱해준다.
■ 이렇게 시험 단계에서 뉴런의 출력에 훈련 과정에서 삭제한 비율을 곱해 출력하는 이유는 ML의 앙상블 효과를 하나의 네트워크에 구현하기 위해서이다.
- ML에서 앙상블은 여러 개 모델의 출력을 평균이나 투표(voting) 방식으로 최종 결과를 도출하는 기법으로 단일 모델을 사용했을 때보다 더 좋은 성능을 보인다.
- 예를 들어 5개 모델의 결과를 평균낸 값을 최종 결과 값으로 사용하거나 예측, 분류 문제에서 투표를 통해 최종 결과를 도출한다.
- 이를 신경망의 맥락에서 보면 드롭아웃은 학습 과정에서 하나의 네트워크 안에 있는 뉴런을 매 Epoch 단계마다 무작위로 삭제하여 훈련하기 때문에, 신경망의 한 Epoch를 하나의 모델이라고 생각하면, 매번 비슷한 구조를 가진 서로 다른 신경망 모델을 학습하는 것으로 볼 수 있으며,
- 테스트 단계에서 뉴런의 출력에 삭제한 비율을 곱하는 것은 여러 신경망의 평균적인 출력을 반영하는 것으로 볼 수 있다. 이는 마치 머신러닝의 앙상블 학습에서 여러 모델의 평균을 내는 것과 같은 효과를 얻을 수 있다.
■ 따라서 드롭아웃은 한 층의 모든 뉴런이 다음 층의 모든 뉴런과 연결된 구조인 완전 연결 계층(Fully Connected Layers)에서 사용하기 적합하며, CNN이나 다른 유형의 신경망에서도 적용할 수 있다.
■ 드롭아웃 값은 하이퍼파라미터이기 때문에 사용자가 설정한 값에 따라 신경망 모델의 성능이 달라질 수 있다.
cf) 상황에 따라 다르겠지만, 보통 0.2 또는 0.5의 값을 사용했을 때 좋은 성능을 나타내는 것으로 알려져 있다.
class Dropout:
def __init__(self, dropout_ratio = 0.5):
self.dropout_ratio = dropout_ratio
self.mask = None
def forward(self, x, train_flag = True):
if train_flag:
self.mask = np.random.rand(*x.shape) > self.dropout_ratio
return x * self.mask
else:
return x*(1.0 - self.dropout_ratio)
def backward(self, dout):
return dout*self.mask
- 학습 시 train_flag = True로 설정해 입력 데이터에 드롭아웃을 적용하고, 시험 시 train_flag = False로 설정해 드롭아웃을 적용하지 않고 x*(1 - 드롭아웃 비율)을 통해 출력값을 드롭아웃 비율에 맞게 조정한다.
- self.mask는 입력 데이터 x와 동일한 형태의 배열을 생성하며, 그 값이 드롭아웃 비율보다 큰 원소만 True로 설정해서 x*mask를 반환하여 마스크가 False인 위치의 뉴런은 비활성화한다.
- 역전파 과정에서 드롭아웃이 적용된 기울기 dout을 받고, 순전파 때 비활성화된 뉴런은 역전파에서도 비활성화해야 하므로 순전파에서 사용된 마스크를 그대로 사용하여 dout * mask를 반환한다.(=기울기를 전파한다.) 따라서 비활성화된 뉴런은 가중치 업데이트가 되지 않는다.
4. 하이퍼파라미터 값 설정 방법
■ 신경망의 성능을 높이기 위해서 입력, 은닉, 출력 층의 뉴런 수, 배치 크기, 학습률, 가중치 감소 등의 하이퍼파라미터뿐만 아니라 optimizer의 하이퍼파라미터도 적절한 값으로 설정해 줘야 한다.
■ 잘못된 값(너무 작은 값 혹은 너무 큰 값)으로 설정하게 되면, 하이퍼파라미터를 기본 값으로 설정했을 때보다 모델 성능이 더 저하되기 때문이다.
■ 이 하이퍼파라미터 값을 지정한 범위 내에서 비교적 정확한 값을 찾으려면 그리드 서치를 이용하면 되지만, 탐색하고자 하는 하이퍼파라미터의 개수가 많을수록, 탐색하고자 하는 하이퍼파라미터 값의 범위가 넓을수록 탐색해야 하는 차원의 크기가 커지므로, 이에 비례하는 탐색 시간을 갖게 된다. 또한 오랜 시간 끝에 적절한 값을 찾았다 하더라도 성능 개선 효과가 미미할 수 있다.
■ 따라서 보통의 경우 최대한 효율적으로 하이퍼파라미터 값을 탐색하는 것이 바람직하며, 그 방법으로 검증 데이터를 사용하거나 무작위로 하이퍼파라미터 값을 탐색하는 방법이 있다.
4.1 검증 데이터(validation data)
■ 하이퍼파라미터 값을 검증할 때 시험 데이터(test data) 대신 검증 데이터를 사용해야 한다.
■ 시험 데이터 영역에서 하이퍼파라미터 값을 탐색하면, 탐색된 하이퍼파라미터 값은 시험 데이터에 맞춰진 즉, 시험 데이터에 적합된 하이퍼파라미터 값이므로, 시험 데이터에만 적합하도록 하이퍼파라미터 값이 조정된다.
■ 따라서, 추후 이 값들을 이용해 시험 데이터로 모델 평가를 하면 시험 데이터에서 과적합이 발생한다.
■ 그러므로 하이퍼파라미터 탐색용 데이터가 별도로 필요하며, 이 데이터가 검증 데이터이다.
■ 정리하자면, 훈련 데이터는 매개변수(가중치와 편향) 학습에, 검증 데이터는 하이퍼파라미터를 탐색하고 그 성능을 평가할 때, 테스트 데이터는 훈련된 모델의 범용 성능을 평가할 때 사용한다.
import numpy as np
import tensorflow as tf
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
x_train.shape, y_train.shape
```#결과#```
((60000, 28, 28), (60000,))
````````````
plt.figure(figsize=(10, 5))
for i in range(10):
plt.subplot(2, 5, i + 1)
plt.imshow(x_train[i], cmap='gray')
plt.axis('off')
plt.tight_layout()
plt.show()
y_train[:10]
```#결과#```
array([5, 0, 4, 1, 9, 2, 1, 3, 1, 4], dtype=uint8)
````````````
- 이렇게 x_train과 y_train은 라벨 관계가 인덱스로 묶여있다. 즉, 위의 x_train[:10]의 정답은 y_train[:10]이다.
- 이 인덱스 관계를 무시하고 데이터를 셔플하면 이 라벨 관계가 무너지기 때문에, 다음과 같이 두 데이터의 라벨 관계를 유지하면서 데이터를 섞어야 한다.
indices = np.arange(len(x_train))
np.random.shuffle(indices)
x_train = x_train[indices]
y_train = y_train[indices]
# 셔플 후 x_train 이미지 출력
plt.figure(figsize=(10, 5))
for i in range(10):
plt.subplot(2, 5, i + 1)
plt.imshow(x_train[i], cmap='gray')
plt.axis('off')
plt.tight_layout()
plt.show()
# 셔플 후 y_train
y_train[:10]
```#결과#```
array([3, 1, 3, 4, 6, 7, 1, 0, 2, 2], dtype=uint8)
````````````
valid_rate = 0.2 # 검증 데이터 비율
valid_num = int(x_train.shape[0]*valid_rate)
valid_num
```#결과#```
12000
````````````
x_val = x_train[:valid_num] # 앞에서부터 12000 개
y_val = y_train[:valid_num] # 앞에서부터 12000 개
x_train = x_train[valid_num: ] # 다시 train set 조정
y_train = y_train[valid_num: ]
x_train.shape, y_train.shape, x_val.shape, y_val.shape
```#결과#```
((48000, 28, 28), (48000,), (12000, 28, 28), (12000,))
````````````
plt.figure(figsize=(10, 5))
for i in range(10):
plt.subplot(2, 5, i + 1)
plt.imshow(x_train[i], cmap='gray')
plt.axis('off')
plt.tight_layout()
plt.show()
y_train[:10]
```#결과#```
array([5, 7, 6, 4, 5, 6, 1, 6, 5, 9], dtype=uint8)
````````````
4.2 하이퍼파라미터 최적화
■ 하이퍼파라미터 최적화는 탐색할 범위를 설정하고 범위를 조금씩 줄여가며 성능을 평가하면서 최적값을 찾는다.
■ 탐색에 소요되는 시간을 단축하려면 학습을 위한 에폭을 줄여 1회 평가에 소요되는 시간을 단축하는 것이 좋다.
■ 탐색 방법으로 그리드 서치 외에 랜덤 서치, 베이지안 최적화가 있다.
참고) 딥러닝의 발전을 이끈 알고리즘
'딥러닝' 카테고리의 다른 글
단어의 의미를 파악하는 방법 (2) (0) | 2024.11.21 |
---|---|
단어의 의미를 파악하는 방법 (1) (0) | 2024.11.20 |
매개변수 갱신 방법(1) (0) | 2024.11.01 |
합성곱 신경망(CNN) (1) (0) | 2024.11.01 |
오차역전파 (3) (0) | 2024.09.24 |