Assignment 2-2: Dropout

ASSIGNMENT 글 목록
목차

Overview

드롭아웃의 개념과 효과, 순전파 및 역전파를 구현한다.

핵심 아이디어

드롭아웃(Dropout)은 훈련 시 drop probability qq만큼 뉴런의 출력을 무작위로 0으로 만드는 규제 기법이다.

pkeep=1qdropp_{\text{keep}} = 1-q_{\text{drop}}

각 activation에 적용할 mask는 베르누이 분포에서 뽑는다.

miBernoulli(p)m_i \sim \operatorname{Bernoulli}(p)
%% title: Inverted Dropout의 학습과 추론
%% caption: 학습 시에는 무작위 마스크를 적용하고 살아남은 활성값을 p로 나누며, 추론 시에는 모든 활성값을 그대로 사용한다.
flowchart LR
    h["Activation h"]
    subgraph train["Training"]
        mask["m ~ Bernoulli(p)"] --> scale["m ⊙ h / p"]
    end
    subgraph test["Inference"]
        identity["h 그대로 사용"]
    end
    h --> mask
    h --> identity
    scale --> expected1["E[output] = h"]
    identity --> expected2["output = h"]

Inverted Dropout

학습할 때 살아남은 활성값을 keep proability pp로 나누고 추론에서는 별도의 크기 조정을 하지 않는다.

h~train=mhp\tilde{h}_{\text{train}} = \frac{m \odot h}{p} h~test=h\tilde{h}_{test} = h

학습과 추론의 기댓값을 같게 만든다.

E[mhp]=h\mathbb{E} \left[ \frac{m \odot h}{p} \right] = h

Inverted Dropout에서 왜 pp로 나누는가?

Dropout에서는 학습 중 각 뉴런을 keep probability pp로 유지한다.

Dropout mask를 mm이라 하면

mBernoulli(p)m \sim \operatorname{Bernoulli}(p)

이고 기댓값은

E[m]=p\mathbb{E}[m] = p

이다.

pp로 나누지 않는 경우

입력 activation을 xx라고 하면 Dropout 출력은 다음과 같다.

y=mxy = mx

따라서 기댓값은 다음과 같다.

E[y]=xE[m]=px\mathbb{E}[y] = x\mathbb{E}[m] = px

즉, 학습 중 activation의 평균적인 크기가 원래 값의 pp배로 감소한다.

반면 inference에서는 Dropout을 사용하지 않으므로 다음과 같다.

yinference=xy_{\text{inference}} = x

따라서 training과 inference 사이에 activation scale 차이가 발생한다.

이를 방지하기 위해 학습 시 살아남은 값을 pp로 나눈다.

y=mxpy = \frac{mx}{p}

그러면

E[y]=xpE[m]=xpp=x\mathbb{E}[y] = \frac{x}{p}\mathbb{E}[m] = \frac{x}{p}p = x

가 되어 Dropout 전후의 기댓값이 유지된다.

즉, p로 나누는 이유는 Dropout으로 인해 감소하는 activation의 기댓값을 보정하여 훈련과 추론의 scale을 맞추기 위해서이다.

구현

forward

def dropout_forward(x, dropout_param):
    p, mode = dropout_param["p"], dropout_param["mode"]

    mask = None
    out = None

    if mode == "train":
        mask = np.random.binomial(1, p, size=x.shape)

        out = (x * mask) / p
    
    elif mode == "test":
        out = x
        cache = (dropout_param, mask)
        return out, cache
    
    cache = (dropout_param, mask)
    out = out.astype(x.dtype, copy=False)

    return out, cache

backward

def dropout_backward(dout, cache):
    dropout_param, mask = cache
    mode = dropout_param["mode"]

    dx = None

    if mode == "train":
        p = dropout_param["p"]
        return (mask / p) * dout 

    elif mode == "test":
        dx = dout
        return dx

Inverted dropout의 기울기는 같은 마스크를 사용한다.

Lh=mpLh~\frac{\partial L}{\partial h} = \frac{m}{p} \odot \frac{\partial L}{\partial \tilde{h}}

추가 개념들

Batch Normalization 유도 1

Dropout 유무에 따른 Training / Validation Accuracy 비교

Dropout을 적용하지 않은 모델은 training accuracy가 더 높게 나타났다. 반면 validation에서는 학습이 진행될수록 Dropout을 적용한 모델이 더 높은 평균 accuracy를 보였다.

이는 Dropout이 training data에 과도하게 맞춰지는 과적화(overfitting)을 줄이고, generalization 성능을 향상시키는 regularizer로 작동한다는 것을 보여준다.

Dropout은 학습 중 일부 뉴런의 출력을 무작위로 제거한다.

mBernoulli(p)m \sim \operatorname{Bernoulli}(p)

따라서 모델이 특정 뉴런이나 특정 feature 조합에 지나치게 의존하는 것을 방지하고, 더 분산된 representation을 학습하도록 만든다.

따라서 training accuracy는 다소 낮아질 수 있지만 validation 성능은 향상될 수 있다.