오버피팅 억제법

신경망 학습을 하다보면 오버피팅이 일어날 수 있다. 오버피팅이란 신경망이 훈련 데이터에만 지나치게 적응되어 그 외의 데이터에는 제대로 대응하지 못하는 상태를 말한다. 오버피팅은 주로
  • 매개변수가 많고 표현력이 높은 모델
  • 훈련 데이터가 적은 경우
위 경우에 일어난다. 이를 방지하기 위한 방법을 알아보자.
  1. 가중치 감소
  2. 가중치 매개변수의 값이 작아지도록 학습하는 방법이다. 오버피팅은 가중치 매개변수의 값이 클 때 발생하는 경우가 많기때문에, 학습 과정에서 큰 가중치에 대해서는 그에 상응하는 페널티를 적용해 오버피팅을 억제시키겠다는 것이다. 그럼 어떻게 작게 만들 수 있을까?
    우선 초기값을 아예 작게 만드는 방법을 생각해 볼 수 있다. 그렇다고 아예 0으로 설정해버리면 학습이 되지 않는다.(정확히는 가중치들을 다 같은 값으로 설정하는 경우) 왜냐하면 오차역전파법에서 가중치의 값들이 똑같이 갱신되기 때문이다.(*연산과 +연산에 있어서 역으로 미분값이 전파될 때를 생각하면 알 수 있다.)
    따라서 초깃값을 무작위로 설정해 줘야 하고 이를 위해 다음과 같이 표준편차가 0.1, 0.01과 같은 정규분포를 사용해 초기화 한다. 간단한 가중치 초기화하는 코드를 보자.
    def __init__(self, input_size, hidden_size, output_size, weight_init_std=0.01):
            # 가중치 초기화
            self.params = {}
            self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size)
            self.params['b1'] = np.zeros(hidden_size)
            self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size)
            self.params['b2'] = np.zeros(output_size)
    
    이 표준편차가 적절한 값이 아니면 활성화 값들이 일정값에 치우치는 현상이 발생할 수 있다. 이를 기울기 소실이라 부른다.
    이때문에 적절한 표준편차값으로 설정되는 알려진 값들이 있고 그것들은 다음과 같다.
    • Xavier초깃값
    • 앞 계층의 노드가 n개 일 때 표준편차는 $\frac{1}{\sqrt{n}}$으로 설정. sigmoid함수나 tanh함수같은 좌우 대칭인, 중앙 부근이 선형인 함수에 적합.
    • He 초깃값
    • 앞 계층의 노드가 n개 일 때 표준편차는 $\frac{2}{\sqrt{n}}$으로 설정. ReLU같이 중앙 부근이 선형이 아니고 음의 영역이 0이라 더 넓게 분포시키기 위한 함수에 적합.
    가중치의 적절한 초깃값 설정을 어떻게 해야할 지 알았으니, 페널티를 주는 방법을 알아보자. 이 방법으로 가중치의 제곱 norm(L2 norm)을 손실 함수에 더하는 방법이 있다. 가중치를 W라 하고 정규화의 세기를 조절하기 위한 하이퍼파라미터를 $\lambda$라 하면 $\frac{1}{2}\lambda W^2$을 손실함수에 더해준다.($\frac{1}{2}$는 미분결과를 조절하기 위한 상수임) 그리고 가중치의 기울기를 구하는 계산에서는 오차역전파법에 따른 결과인(=미분한) $\lambda W$를 더한다. 이를 통해 가중치가 갱신되는 과정에서 하이퍼파라미터 $\lambda$값에 따라 페널티가 적용된다. 구현 코드를 통해 보면 다음과 같다.
    def loss(self, x, t):
            """손실 함수를 구한다.
            
            Parameters
            ----------
            x : 입력 데이터
            t : 정답 레이블 
            
            Returns
            -------
            손실 함수의 값
            """
            y = self.predict(x)
    
            weight_decay = 0
            for idx in range(1, self.hidden_layer_num + 2):
                W = self.params['W' + str(idx)]
                weight_decay += 0.5 * self.weight_decay_lambda * np.sum(W ** 2)
    
            return self.last_layer.forward(y, t) + weight_decay
    
    위에서 알 수 있듯이 손실 함수를 구하는 과정, 즉 순전파때는 $\frac{1}{2}\lambda W^2$를 더해나감을 볼 수 있다. 그럼 역전파 과정을 보자.
    def gradient(self, x, t):
            """기울기를 구한다(오차역전파법).
    
            Parameters
            ----------
            x : 입력 데이터
            t : 정답 레이블
            
            Returns
            -------
            각 층의 기울기를 담은 딕셔너리(dictionary) 변수
                grads['W1']、grads['W2']、... 각 층의 가중치
                grads['b1']、grads['b2']、... 각 층의 편향
            """
            # forward
            self.loss(x, t)
    
            # backward
            dout = 1
            dout = self.last_layer.backward(dout)
    
            layers = list(self.layers.values())
            layers.reverse()
            for layer in layers:
                dout = layer.backward(dout)
    
            # 결과 저장
            grads = {}
            for idx in range(1, self.hidden_layer_num+2):
                grads['W' + str(idx)] = self.layers['Affine' + str(idx)].dW + self.weight_decay_lambda * self.layers['Affine' + str(idx)].W
                grads['b' + str(idx)] = self.layers['Affine' + str(idx)].db
    
            return grads
    
    self.weight_decay_lambda 값과 W를 곱한값을 역전파과정 때 W미분값에 더해서 갱신해줌을 볼 수 있다.
  3. Batch Normalization = 배치 정규화
  4. 가중치 감소에서 표준편차를 적절히 설정하지 못하면 활성화값이 분포가 잘 되지 않고 치우칠 수 있다했다. Batch Normalization은 여기서 활성화값이 적당히 퍼지도록 강제하는 방법이다. 이런 배치 정규화를 통해서 다음과 같은 장점을 취할 수 있다.
    • 학습을 빨리 진행할 수 있음.(학습 속도 개선)
    • 초깃값에 크게 의존하지 않음.(초깃값 선택 고민하지 않아도 됨.)
    • 오버피팅 억제함.
    배치 정규화는 이름과 같이 학습 시 미니배치를 단위로 정규화한다. 데이터 분포가 평균이 0, 분산이 1이 되도록 하는데 수식으로 나타내면 다음과 같다. $$ \mu_B \gets \frac{1}{m} \sum_{i=1}^m x_i \\ \sigma_B^2 \gets \frac{1}{m} \sum_{i=1}^m (x_i - \mu_B)^2 \\ \hat{x}_i \gets \frac{x_i - \mu_B}{\sqrt{\sigma_B^2 + \epsilon}} $$ 이렇게 입력 데이터를 정규화하는 처리를 활성화 함수 앞(혹은 뒤)에 삽입함으로써 데이터 분포가 덜 치우치게 할 수 있다. 그 뒤 정규화한 데이터($\hat{x}_i$)에 고유한 확대=scale와 이동=shift변환을 수행한다. 식으로는 아래와 같고, $\gamma$가 확대를, $\beta$가 이동을 담당한다. $$ y_i \gets \gamma \hat{x}_i + \beta $$ 각각 값은 1과 0부터 시작해(1배 확대와 0이동, 즉 처음은 원본그대로 시작) 학습하며 적합한 값으로 조정되간다. 사실 이부분이 오차역전파법게시글에서 계산그래프를 소개할 때 링크를 단 부분이다. 저 게시글에 있는 사진이 배치정규화의 수식을 계산그래프로 옮긴 것이며, 역전파 유도과정또한 링크단 부분에 있다.
  5. Dropout
  6. 가중치 감소는 간단하게 구현할 수 있지만 이것 만으로는 복잡한 신경망 모델에 대해서는 대응하기 어려워진다. 따라서 Dropout이라는 기법을 이용한다.
    Dropout은 뉴런을 임의로 삭제하면서 학습한다. 훈련 때 무작위로 삭제된 은닉층의 뉴런은 신호를 전달하지 않게된다. 단, 시험 때는 모든 뉴런에 신호를 전달하고 각 뉴런의 출력에 훈련 때 삭제한 비율을 곱하여 출력한다. 이런 방법은 기계학습에서의 앙상블 학습 = ensemble learning과 일맥상통한다. Dropout이 학습 때 뉴런을 무작위로 삭제하는 행위를 매번 다른 모델을 학습시키는 것으로 해석할 수 있기 때문이다. 그리고 추론 때는 삭제한 비율을 곱함으로써 앙상블 학습에서의 여러모델 평균내는 것과 같은 효과를 얻는다. 따라서 Dropout은 앙상블 학습과 같은 효과를 하나의 네트워크로 구현했다고 생각할 수 있다. 더 자세한 분석은 이 곳을 보면 도움이 될 것 같다.

댓글

이 블로그의 인기 게시물

Loss Function

Backpropagation

SGD = Stochastic Gradient Descent