본문 바로가기

책/밑바닥부터 시작하는 딥러닝

6장 학습 관련 기술들 (2)

가중치의 초깃값

은닉층의 활성화값 분포

가중치의 초깃값에 따라 은닉층의 활성화값들이 어떻게 변하는지 알아보자.

 

시그모이드 활성화 함수를 사용하는 5층 신경망에 무작위로 생성한 입력 데이터를 흘려 각 층의 활성화 값 분포를 히스토그램으로 그려보자.

 

 

표준편차가 1인 정규분포로 초기화

- 각 층의 활성화 값들이 0과 1에 치우져 분포되어 있다.

- 시그모이드 함수는 출력이 0 또는 1에 가까워 질수록 gradient가 0에 다가간다. 그러면 역전파의 기울기가 점점 작아지다가 사라지게 된다. 이것이 gradient vanishing으로 알려진 문제이다.

 

표준편차를 0.01로 바꿔보자

- 0과 1에 치우치진 않아서 기울기 소실 문제가 일어나지는 않지만 0.5 부근에 집중되어 있다.

- 활성화 값들이 한 쪽으로 치우졌다는 것은 표현련 관점에서 문제가 있다. 다수의 뉴런이 같은 값을 출력하고 있으니 뉴런을 여러 개 둔 의미가 사라진다. 즉, 뉴런이 100개인 것과 10개인 것이 별반 차이가 없다.

 

각 층의 활성화 값은 적당히 고루 분포되어야 한다. 그래야 신경망 학습이 효율적으로 이루어지기 때문이다.

 

Xavier 초기값

앞 계층의 노드가 n개라면 표준편차가 $1/{\sqrt{n}}$인 정규분포를 사용

층이 깊어지면서 형태가 일그러지지만, 앞에서 본 방식보다는 확실히 고루 분포되어 있다. 사실 이 부분은 활성화 함수를 tanh로 바꿔주면 개선된다. 

이것도 좀 치우친 것 같긴한데..

 

ReLU를 사용할 때의 가중치 초깃값

Xavier 초기값은 활성홤 함수가 선형인 것은 전제로 이끈 결과이다. 시그모이드와 tanh는 좌우 대칭이라 중앙 부근이 선형이라고 볼 수 있다.

ReLU를 사용할 때는 He 초기값을 사용하는 것이 좋다. 이는 표준편차가 $\sqrt{2/n}$이다.

 

- 표준편차가 0.01인 정규분포를 사용하는 경우 활성화값들이 매우 작다. 이는 역전파시 가중치의 기울기 역시 매우 작아진다는 뜻이고 실제로 학습이 거의 이뤄지지 않는다.

- He 초기값은 매우 균일한 분포를 이루고 있다.

 

배치 정규화

가중치의 초기값을 적절히 설정하면 각 층의 활성화값 분포가 적당히 퍼지면서 학습이 원활하게 수행된다고 배웠다. 배치 정규화는 각 층이 활성화를 적당히 퍼뜨리도록 강제하는 기법이다.

위와 같이 신경망에 배치 정규화 계층을 삽입하여 사용, 수식은 아래와 같다.

입력 데이터의 평균과 분산을 구해서 평균이 0, 분산이 1이 되도록 정규화 한다.

또 여기에다가 고유한 scale과 shift 변환을 수행한다. 수식은 다음과 같다.

감마와 베타는 학습되는 파라미터이다.

 

바른 학습을 위해

오버피팅

- 매개변수가 많고 표현력이 높은 모델

- 훈련 데이터가 적음

인 경우에 주로 발생한다. 이를 억제하는 방법을 알아보자

 

가중치 감소

학습 과정에서 큰 가중치에 대해서는 큰 페널티를 부과하여 오버피팅을 억제하는 방법이다.

구체적으로는 모든 계층에 대한 각각의 규제를 손실함수에 더하는 것이다. 규제는 L1, L2, Max norm이 있다.

    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

드롭아웃

임의로 뉴런을 삭제하면서 학습하는 방법이다.

간단한 구현은 다음과 같다.

class Dropout:
    def __init__(self, dropout_ratio=0.5):
        self.dropout_ratio = dropout_ratio
        self.mask = None
    
    def forward(self, x, train_flg=True):
        if train_flg:
            self.mask = np.random.rand(*x.shape) > self.dropout_ratio # rand() 메서드는 [0, 1)에서 균일한 분포를 가짐
            return x * self.mask
        else:
            return x * (1.0 - self.dropout_ratio)
    
    def backward(self, dout):
        return dout * self.mask

훈련 때는 mask의 값이 True인 부분만 통과한다. 역전파 때도 마찬가지

 

적절한 하이퍼파라미터 값 찾기

그리드 서치, 랜덤 서치, 베이지안 최적화 등이 있으며 나는 주로 랜덤 서치로 적당한 범위를 찾고 그 안에서 베이지안 최적화를 사용한다.

' > 밑바닥부터 시작하는 딥러닝' 카테고리의 다른 글

7장 CNN (2)  (0) 2023.09.22
7장 CNN (1)  (0) 2023.09.21
6장 학습 관련 기술들 (1)  (0) 2023.09.20
5장 오차역전파법 (2)  (0) 2023.09.19
5장 오차역전파법 (1)  (0) 2023.09.19