MNIST Lab 기본 구현 9편
Dropout은 학습 중 일부 뉴런을 무작위로 끄는 계층이다. 핵심은 학습 모드에서는 mask를 만들고, backward에서는 같은 mask로 gradient도 막는 것이다.
1. Dropout이 막는 문제
모델이 특정 뉴런 몇 개에만 과하게 의존하면 훈련 데이터에는 잘 맞지만 새로운 데이터에는 약해질 수 있다. Dropout은 학습 때 일부 뉴런을 랜덤하게 꺼서 이런 의존을 줄인다.
1
학습 모드
랜덤 mask를 만들고 일부 뉴런 출력을 0으로 만든다.
2
역전파
꺼졌던 뉴런 위치는 gradient도 흐르지 않게 한다.
3
평가 모드
랜덤하게 끄지 않고 평균적인 출력 크기만 맞춘다.
2. 최종 구현 코드
class Dropout:
def __init__(self, drop_ratio=0.5):
self.drop_ratio = drop_ratio
def forward(self, x, train=True):
if train:
self.mask = np.random.rand(*x.shape) > self.drop_ratio
return x * self.mask
return x * (1 - self.drop_ratio)
def backward(self, dout):
return dout * self.mask
3. train과 inference의 차이
| 모드 | 동작 | 이유 |
|---|---|---|
| train=True | 무작위 mask로 일부 값을 0으로 만든다. | 특정 뉴런 의존을 줄이기 위해서다. |
| train=False | 랜덤으로 끄지 않고 1 - drop_ratio를 곱한다. |
학습 때 평균적으로 살아남던 비율과 출력 크기를 맞춘다. |
| backward | mask를 곱해 꺼진 위치 gradient를 막는다. | forward 때 계산에 참여하지 않은 뉴런은 갱신 신호도 받으면 안 된다. |
4. 테스트가 묻는 것
| 테스트 | 확인 조건 |
|---|---|
| test_dropout_forward_train_shape | 학습 모드 출력 shape가 입력과 같은가 |
| test_dropout_forward_inference_scale | 평가 모드에서 출력에 살아남는 비율이 곱해지는가 |
주의할 점
Dropout은 랜덤성이 있으므로 값 자체를 고정해서 테스트하기 어렵다. 그래서 학습 모드는 shape를 보고, 평가 모드는 deterministic한 scale 결과를 본다.
이번 글에서 기억할 것
Dropout은 학습 때 일부 뉴런과 그 gradient를 함께 막고, 평가 때는 평균적인 출력 크기를 맞추는 계층이다.
스스로 점검
- Dropout이 학습 모드와 평가 모드에서 다르게 동작해야 하는 이유는 무엇인가?
- forward에서 꺼진 뉴런은 backward에서 어떻게 처리되는가?
- 이 구현은 inverted dropout 방식인가, 기본 dropout 방식인가?
다음 글 예고
다음 글에서는 train/evaluate를 구현한다. 지금까지 만든 부품을 한 epoch 학습 루프로 연결한다.