특정 모델이 학습 데이터에 과학습 되었을 때만 사용하는 기술이다. 학습하는 데이터의 수가 적은 경우, 딥러닝 모델이 데이터의 노이즈 부분까지 세밀하게 학습하여 궁극적으로 테스트 데이터에서 좋은 성능을 내지 않을 수 있다. 테스트 데이터와 학습 데이터의 분포가 동일함에도 이러한 문제는 발생한다. 학습하는 데이터의 수가 충분하여 모델이 과학습 되지 않았다면, 굳이 사용하지 않아도 된다.
여러 모델의 결합이 머신러닝의 성능을 높여줄 수 있으나, 이러한 접근 방식은 비용이 많이 드는 방식이다. 여러 모델은 각기 다른 아키텍처를 가지고 있어야 하며, 다른 데이터에서 학습된 것이어야 하기 때문이다. 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
출처; 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 강의