ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [딥러닝 학습 팁] Drop out
    Data miner 2020. 12. 9. 18:30
    728x90
    • 특정 모델이 학습 데이터에 과학습 되었을 때만 사용하는 기술이다. 학습하는 데이터의 수가 적은 경우, 딥러닝 모델이 데이터의 노이즈 부분까지 세밀하게 학습하여 궁극적으로 테스트 데이터에서 좋은 성능을 내지 않을 수 있다. 테스트 데이터와 학습 데이터의 분포가 동일함에도 이러한 문제는 발생한다. 학습하는 데이터의 수가 충분하여 모델이 과학습 되지 않았다면, 굳이 사용하지 않아도 된다. 
    • 여러 모델의 결합이 머신러닝의 성능을 높여줄 수 있으나, 이러한 접근 방식은 비용이 많이 드는 방식이다. 여러 모델은 각기 다른 아키텍처를 가지고 있어야 하며, 다른 데이터에서 학습된 것이어야 하기 때문이다. Dropout은 이러한 이슈를 해소시켜준다. 
    • Dropout은 모델의 과학습을 방지하기 위한 Regularization의 방법 중에 하나이다. 이것은 아래의 그림과 같이, 무작위적으로 히든 레이어의 몇몇 뉴런들을 학습을 못하도록 하는 것이다. 특정 레이어의 뉴런 중에서 몇 개의 뉴런만을 학습할 것인지에 대한 정보를 keep_probability라는 변수에 저장한다. 만약 특정 뉴런들이 forward시에 모델 학습에 기여하지 않는다면, 자연스럽게 backward propagation 단계에서도 사용되지 않는다. 매회 모델 학습시마다(each iteration), Dropout으로 일부 뉴런만 학습한다. 

    • 이러한 방식이 Regularization에 효과적인 이유는 다음과 같다. 매회 학습 시마다, 레이어에서 일부 뉴런들만 가지고 모델을 학습시키는 것은 사실상 모델을 조금 변형하여 학습하는 것이다. 각 레이어의 뉴런 수가 줄어들기 때문이다. 또한, 레이어의 뉴런들로 국한시켜서 생각해볼 때, 특정 뉴런의 weight 값만 커지는 것을 방지할 수 있다. 레이어에 속한 뉴런들의 weight들의 값이 분산되어 학습될 수 있다.
    • 개별 히든 레이어에 따라서 뉴런을 얼만큼 학습시킬지, Keep_probability변수를 다르게 설정할 수 있다. 하지만, 이 경우 사용자가 관리해야 할 하이퍼파라미터가 많다. 일괄적으로 모든 히든레이어에 동일하게 적용되는 하나의 Keep_probability변수를 사용하는 경우가 일반적이다.
    • 보통, Keep_probability값이 높아지면, Regularization 효과는 줄어들고, 학습데이터에 대한 트레이닝 에러는 줄어든다. 
    • 이와 관련된 코드 내용은 다음과 같다. 코드를 볼 때 유의해야할 점은, D1이라는 임의의 메트릭스를 통해서 특정 레이어의 뉴런의 값을 제거할 것인지를 결정한다. 1) D1의 원소값들을 np.random.rand()로 0~1의 값으로 만들어준 다음에, keep_prob보다 작은 값에 대해서는 0, 그렇지 않은 값들은 1로 만들어주는 식으로 D1을 구성한다. 어떤 뉴런을 잠시 0으로 만들었는지에 대한 정보는 D1에 저장하여, cache값으로 리턴한다. 이후, 레이어의 전체 활성화된 기댓값을 keep_prob으로 나눠줌으로써 보강한다. 이를 inverted dropout이라고 일컫는다.
    • Dropout의 단점으로는 Cost-function이 깔끔하게 수학적으로 표현되지 않는다. 매회마다 특정 노드들이 제거되어 학습되기 때문이다.
    • 한편으로, 모델을 마지막으로 테스트 데이터에 대해서 평가할 때에는 Drop-out을 적용하지 않는다. 

     

    def forward_propagation_with_dropout(X, parameters, keep_prob=0.5):
    
    ...
    ...    
        
        # LINEAR -> RELU -> LINEAR -> RELU -> LINEAR -> SIGMOID
        
        Z1 = np.dot(W1, X) + b1
        A1 = relu(Z1)
        
        #임의의 initialize한 matrix를 만든다. 이를 활용하여 dropout할 것이기 때문에, matrix의 차원은 A1와 동일하게 만든다. 
    	D1 = np.random.rand(A1.shape[0], A1.shape[1])
        #임의의 initialize한 matrix의 원소는 0~1사이의 무작위 값을 가진다. 
        #D1의 각 원소들을 keep_prob의 값보다 작을 경우 1, 그렇지 않은 경우에는 0의 값을 가지도록 한다.
        D1 = (D1 < keep_prob).astype(int)
        # D1이 1인 값들에 대해서, A1 뉴런의 값을 살린다. 나머지값은 0으로 만든다.
        A1 = A1 * D1
        # 0으로 값이 사라진 뉴런 값들만큼, 남은 뉴런들에게 Keep_prob만큼 보강시켜준다. 이를 inverted dropout이라고 불리는 부분이다. 
        A1 = A1 / keep_prob                              
    
    	Z2 = np.dot(W2, A1) + b2
        A2 = relu(Z2)
        
    ...
        #위와 같은 방식으로 dropout을 진행한다.
    ...
        A3 = sigmoid(Z3)
        
        cache = (Z1, D1, A1, W1, b1, Z2, D2, A2, W2, b2, Z3, A3, W3, b3)
        
        return A3, cache
    def backward_propagation_with_dropout(X, Y, cache, keep_prob):
    
    ...
    ...
        
        m = X.shape[1]
        (Z1, D1, A1, W1, b1, Z2, D2, A2, W2, b2, Z3, A3, W3, b3) = cache
        
        dZ3 = A3 - Y
        dW3 = 1./m * np.dot(dZ3, A2.T)
        db3 = 1./m * np.sum(dZ3, axis=1, keepdims = True)
        dA2 = np.dot(W3.T, dZ3)
    
    	dA2 = dA2*D2             
        dA2 = dA2/keep_prob              
    
    	dZ2 = np.multiply(dA2, np.int64(A2 > 0))
        dW2 = 1./m * np.dot(dZ2, A1.T)
        db2 = 1./m * np.sum(dZ2, axis=1, keepdims = True)
        
        dA1 = np.dot(W2.T, dZ2)
        
    ...
    ...
        gradients = {"dZ3": dZ3, "dW3": dW3, "db3": db3,"dA2": dA2,
                     "dZ2": dZ2, "dW2": dW2, "db2": db2, "dA1": dA1, 
                     "dZ1": dZ1, "dW1": dW1, "db1": db1}
        
        return gradients

     

     

    출처;
    Srivastava, N., Hinton, G., Krizhevsky, A., Sutskever, I., & Salakhutdinov, R. (2014). Dropout: a simple way to prevent neural networks from overfitting. The journal of machine learning research, 15 (1), 1929-1958.


    Andrew Ng의 Improving Deep Neural Networks: Hyperparameter tuning, Regularization and Optimization 강의


Designed by Tistory.