일부러 불완전하게 만드는 훈련
좋은 모델을 만들고 싶다면 보통 더 많은 뉴런, 더 많은 층, 더 긴 학습을 떠올립니다. 그런데 딥러닝에서는 조금 이상한 방법이 자주 등장합니다. 학습 중에 신경망의 일부 뉴런을 일부러 꺼 버리는 것입니다.
처음 들으면 이상합니다. 열심히 만든 신경망인데 왜 굳이 일부를 못 쓰게 할까요? 더 많은 뉴런이 있으면 더 많은 패턴을 배울 수 있을 것 같은데, 왜 학습 도중에 뉴런 출력을 0으로 만들어 버릴까요?
그 이유는 지난 장에서 본 과적합과 연결됩니다. 모델이 너무 똑똑해지면 훈련 데이터의 우연한 특징까지 외워 버릴 수 있습니다. 어떤 뉴런 몇 개가 훈련 데이터에서만 잘 통하는 단서에 강하게 반응하기 시작하면, 모델 전체가 그 뉴런들에게 지나치게 의존하게 됩니다. 훈련 데이터에서는 점수가 좋지만, 새로운 데이터가 들어오면 쉽게 흔들립니다.
드롭아웃(dropout)은 이 의존성을 줄이는 방법입니다. 학습하는 동안 뉴런 일부를 무작위로 쉬게 만들어, 신경망이 특정 뉴런이나 특정 특징 하나에만 기대지 않도록 훈련시킵니다. 마치 팀 프로젝트에서 항상 한 사람만 발표하게 두지 않고, 매번 다른 사람이 설명하도록 시키는 것과 비슷합니다. 처음에는 불편하지만, 시간이 지나면 팀 전체가 더 튼튼해집니다.

드롭아웃은 무엇을 0으로 만드는가
드롭아웃은 보통 층의 출력값에 적용됩니다. 어떤 층을 통과한 뒤 나온 뉴런의 활성값 중 일부를 확률적으로 0으로 만듭니다. 여기서 중요한 점은 가중치 자체를 삭제하는 것이 아니라, 학습 중 특정 forward pass에서 그 뉴런의 출력을 잠시 사용하지 않는다는 것입니다.
예를 들어 어떤 은닉층의 출력이 다음과 같다고 해 봅시다.
h = [0.8, 1.2, 0.0, 2.5, 1.7]여기에 드롭아웃 비율을 0.4로 적용하면, 각 값은 40% 확률로 0이 될 수 있습니다. 어떤 배치에서는 다음처럼 될 수 있습니다.
h_after_dropout = [0.8, 0.0, 0.0, 2.5, 0.0]다음 배치에서는 꺼지는 위치가 달라질 수 있습니다.
h_after_dropout = [0.0, 1.2, 0.0, 0.0, 1.7]이처럼 드롭아웃은 매번 같은 뉴런을 끄지 않습니다. 학습 단계에서 데이터를 통과시킬 때마다 무작위 마스크(mask)를 만들고, 그 마스크에 따라 일부 뉴런의 출력을 0으로 만듭니다. 여기서 마스크란 "이번 계산에서 어떤 뉴런을 살리고 어떤 뉴런을 끌 것인가"를 나타내는 0과 1의 배열이라고 생각하면 됩니다.
드롭아웃 비율을 p라고 할 때, PyTorch의 nn.Dropout(p=0.5)는 각 뉴런 출력을 50% 확률로 0으로 만듭니다. p=0.2라면 20% 확률로 끕니다. 많은 사람이 처음에 헷갈리는 부분이 있는데, p는 "살릴 확률"이 아니라 "끌 확률"입니다.
특정 뉴런에게만 기대지 않게 하기
신경망이 과적합될 때는 종종 특정 뉴런들이 너무 강력한 역할을 맡습니다. 예를 들어 숫자 분류 모델이 있다고 해 봅시다. 어떤 뉴런이 훈련 이미지의 특정 위치에 있는 진한 픽셀에 강하게 반응한다고 합시다. 훈련 데이터에서는 그 위치가 우연히 숫자 7과 자주 함께 나타났을 수 있습니다. 그러면 모델은 "아, 저 위치가 진하면 7이구나"라고 쉽게 배울 수 있습니다.
문제는 새로운 데이터에서는 그 규칙이 항상 맞지 않는다는 점입니다. 손글씨는 사람마다 다르고, 숫자의 위치도 조금씩 흔들립니다. 모델이 한두 개 단서에만 기대고 있다면 입력이 조금만 달라져도 예측이 무너집니다.
드롭아웃은 이런 상황을 방지합니다. 학습 중에 어떤 뉴런이 사라질지 모르기 때문에, 모델은 한 뉴런에게 모든 책임을 맡길 수 없습니다. 오늘은 중요한 뉴런이 살아 있지만, 다음 배치에서는 그 뉴런이 꺼질 수 있습니다. 그러면 다른 뉴런들도 비슷한 정보를 나누어 배워야 합니다.
이것을 조금 더 사람답게 표현하면, 드롭아웃은 신경망에게 이렇게 말하는 셈입니다.
"너는 지금 이 뉴런을 믿고 싶겠지만, 다음 순간에는 그 뉴런이 없을 수도 있어. 그러니 여러 단서를 함께 보고 판단하는 법을 배워."
이 훈련을 반복하면 모델은 더 분산된 표현을 배우게 됩니다. 하나의 특징, 하나의 뉴런, 하나의 우연한 패턴에 과하게 의존하지 않고, 여러 신호를 조합해 판단하려고 합니다. 이것이 드롭아웃이 정규화 기법으로 작동하는 핵심 직관입니다.
여러 작은 모델을 함께 훈련하는 느낌
드롭아웃을 이해하는 또 다른 좋은 비유는 "여러 작은 모델을 동시에 훈련하는 것"입니다.
원래 신경망 전체를 하나의 큰 모델이라고 해 봅시다. 그런데 학습 중에 드롭아웃을 적용하면 매번 일부 뉴런이 꺼집니다. 그러면 이번 배치에서 사용되는 신경망 구조와 다음 배치에서 사용되는 신경망 구조가 조금씩 달라집니다.
어떤 순간에는 1번, 3번, 5번 뉴런이 살아 있는 작은 모델이 학습됩니다. 다음 순간에는 2번, 4번, 5번 뉴런이 살아 있는 또 다른 작은 모델이 학습됩니다. 실제로 모델 파일이 여러 개 생기는 것은 아니지만, 학습 과정의 효과만 보면 매번 다른 부분 신경망(subnetwork)을 훈련하는 것처럼 보입니다.
이 직관은 매우 강력합니다. 여러 모델의 의견을 평균하면 보통 더 안정적인 예측을 얻을 수 있습니다. 머신러닝에서는 이런 방식을 앙상블(ensemble)이라고 부릅니다. 앙상블은 여러 모델을 따로 학습한 뒤 결과를 합치는 방법입니다. 드롭아웃은 하나의 신경망 안에서, 매번 다른 작은 모델을 훈련하는 듯한 효과를 냅니다.
물론 드롭아웃이 실제 앙상블과 완전히 같지는 않습니다. 하지만 독자가 기억해야 할 감각은 충분합니다. 드롭아웃은 모델이 한 가지 고정된 계산 경로에만 익숙해지지 않게 만들고, 다양한 경로로도 비슷한 답을 내도록 훈련시킵니다. 그래서 새로운 데이터가 들어왔을 때 예측이 조금 더 안정적이 됩니다.
PyTorch에서 드롭아웃 사용하기
PyTorch에서 드롭아웃은 nn.Dropout으로 쉽게 넣을 수 있습니다. 보통 선형층과 활성화 함수 뒤에 배치합니다.
import torch
import torch.nn as nn
class MLP(nn.Module):
def __init__(self):
super().__init__()
self.net = nn.Sequential(
nn.Linear(784, 256),
nn.ReLU(),
nn.Dropout(p=0.5),
nn.Linear(256, 128),
nn.ReLU(),
nn.Dropout(p=0.3),
nn.Linear(128, 10)
)
def forward(self, x):
return self.net(x)이 모델에서는 첫 번째 은닉층의 출력 중 50%를 학습 중 무작위로 0으로 만들고, 두 번째 은닉층의 출력 중 30%를 0으로 만듭니다. 마지막 출력층 뒤에는 드롭아웃을 넣지 않았습니다. 분류 문제에서 마지막 층은 각 클래스의 점수, 즉 로짓(logit)을 만드는 부분이므로 일반적으로 그 뒤에 드롭아웃을 적용하지 않습니다.
드롭아웃을 어디에 넣을지는 모델 구조에 따라 달라집니다. MLP에서는 은닉층 사이에 자주 넣습니다. CNN에서는 합성곱 층 뒤보다는 완전연결층 근처에 넣는 경우가 많았고, 더 현대적인 구조에서는 드롭아웃, 배치 정규화, 데이터 증강 등을 상황에 맞게 함께 사용합니다. 텍스트 모델이나 Transformer 계열에서도 드롭아웃은 여러 위치에서 사용됩니다. 예를 들어 임베딩 출력, Attention 결과, feed-forward 층 출력 등에 드롭아웃을 넣어 모델이 특정 표현에 과하게 의존하지 않게 만들 수 있습니다.
중요한 것은 "드롭아웃을 넣으면 무조건 좋아진다"가 아닙니다. 드롭아웃은 과적합을 줄이는 도구입니다. 모델이 이미 충분히 작은데 데이터도 어렵다면, 드롭아웃을 강하게 넣었을 때 오히려 학습이 느려지거나 성능이 떨어질 수 있습니다.
train 모드와 eval 모드가 다른 이유
드롭아웃에서 반드시 이해해야 할 부분이 있습니다. 드롭아웃은 학습할 때와 평가할 때 다르게 동작합니다.
학습 단계에서는 일부 뉴런 출력을 무작위로 0으로 만듭니다. 하지만 검증이나 테스트 단계에서는 드롭아웃을 끕니다. 평가할 때도 뉴런을 무작위로 끄면 같은 입력을 넣어도 매번 예측이 달라질 수 있습니다. 모델 성능을 안정적으로 측정하기 어렵고, 실제 서비스에서도 결과가 흔들릴 수 있습니다.
그래서 PyTorch 모델에는 train() 모드와 eval() 모드가 있습니다.
model.train()
# 드롭아웃이 활성화됨
# 학습 루프에서 사용
model.eval()
# 드롭아웃이 비활성화됨
# 검증, 테스트, 추론에서 사용이 차이는 매우 중요합니다. model.train()을 호출하면 nn.Dropout은 실제로 뉴런 출력을 무작위로 0으로 만듭니다. 반대로 model.eval()을 호출하면 드롭아웃은 아무것도 하지 않는 층처럼 동작합니다. 입력이 들어오면 그대로 다음 층으로 넘깁니다.
그렇다면 이런 의문이 생길 수 있습니다. 학습할 때는 뉴런의 절반을 껐는데, 평가할 때는 모든 뉴런을 사용합니다. 그러면 출력 크기가 갑자기 커지는 것 아닐까요?
PyTorch의 드롭아웃은 이 문제를 자동으로 처리합니다. 학습 중 살아남은 뉴런의 출력값을 적절히 키워서 평균적인 출력 크기를 맞춥니다. 예를 들어 p=0.5라면 절반 정도의 뉴런이 꺼지므로, 살아남은 출력은 대략 2배로 조정됩니다. 이런 방식을 inverted dropout이라고 부릅니다. 덕분에 평가 단계에서는 별도의 보정 없이 드롭아웃을 끄기만 하면 됩니다.
다음 코드를 보면 드롭아웃의 동작 차이를 직접 확인할 수 있습니다.
import torch
import torch.nn as nn
x = torch.ones(10)
dropout = nn.Dropout(p=0.5)
dropout.train()
print(dropout(x))
print(dropout(x))
dropout.eval()
print(dropout(x))
print(dropout(x))학습 모드에서는 출력 일부가 0이 되고, 실행할 때마다 0이 되는 위치가 달라질 수 있습니다. 평가 모드에서는 입력이 거의 그대로 출력됩니다. 이 차이를 모르면 검증 성능이 이상하게 흔들리거나, 저장한 모델을 불러와 추론할 때 기대와 다른 결과가 나올 수 있습니다.
실전 학습 루프에서는 보통 다음 흐름을 따릅니다.
for epoch in range(num_epochs):
model.train()
for x_batch, y_batch in train_loader:
optimizer.zero_grad()
logits = model(x_batch)
loss = loss_fn(logits, y_batch)
loss.backward()
optimizer.step()
model.eval()
with torch.no_grad():
for x_batch, y_batch in val_loader:
logits = model(x_batch)
val_loss = loss_fn(logits, y_batch)학습 루프에서는 model.train()을 호출해 드롭아웃을 켜고, 검증 루프에서는 model.eval()을 호출해 드롭아웃을 끕니다. torch.no_grad()는 기울기 계산을 막아 검증을 더 가볍게 만드는 역할을 합니다. 드롭아웃을 끄는 것은 torch.no_grad()가 아니라 model.eval()이라는 점도 구분해야 합니다.
드롭아웃 비율은 얼마나 잡아야 할까
드롭아웃 비율은 정답이 정해져 있지 않습니다. 다만 직관은 분명합니다. 비율이 너무 낮으면 정규화 효과가 약합니다. 비율이 너무 높으면 모델이 배울 수 있는 정보가 지나치게 많이 사라집니다.
p=0.1은 비교적 약한 드롭아웃입니다. 뉴런의 10%만 끄기 때문에 모델의 계산 흐름을 크게 방해하지 않습니다. p=0.5는 꽤 강한 드롭아웃입니다. 은닉층 절반이 매번 사라지는 셈이므로 모델은 훨씬 더 튼튼한 표현을 배워야 합니다. 하지만 작은 모델이나 어려운 문제에 p=0.7, p=0.8처럼 너무 큰 값을 넣으면 학습 자체가 어려워질 수 있습니다.
학생에게 공부를 시키기 위해 가끔 교과서를 덮게 하는 것은 도움이 될 수 있습니다. 하지만 매번 책의 80%를 가리고 시험을 보게 하면 제대로 배우기 어렵습니다. 드롭아웃도 마찬가지입니다. 적당한 불편함은 일반화에 도움이 되지만, 지나친 방해는 학습을 망칩니다.
실전에서는 보통 검증 성능을 보며 조절합니다. 훈련 정확도는 계속 높아지는데 검증 정확도가 떨어진다면 드롭아웃을 조금 키워 볼 수 있습니다. 반대로 훈련 성능도 잘 오르지 않는다면 드롭아웃이 너무 강하거나, 모델이 너무 작거나, 학습률 같은 다른 설정에 문제가 있을 수 있습니다.
드롭아웃 비율을 볼 때는 훈련 손실과 검증 손실을 함께 봐야 합니다. 드롭아웃을 넣으면 훈련이 조금 더 어려워지므로 훈련 손실이 이전보다 높게 보일 수 있습니다. 이것만 보고 나빠졌다고 판단하면 안 됩니다. 중요한 것은 최종적으로 새로운 데이터, 즉 검증 데이터나 테스트 데이터에서 더 잘 맞히는가입니다.
드롭아웃이 모든 문제의 해결책은 아니다
드롭아웃은 간단하고 강력하지만 만능은 아닙니다. 과적합을 줄이는 방법에는 여러 가지가 있습니다. 더 많은 데이터를 모으거나, 데이터 증강을 사용하거나, 모델 크기를 줄이거나, 가중치 감쇠를 적용하거나, 조기 종료를 사용할 수도 있습니다. 드롭아웃은 이 도구들 중 하나입니다.
특히 최근의 많은 모델에서는 드롭아웃을 무조건 크게 넣기보다, 데이터 규모와 모델 구조에 맞게 신중하게 사용합니다. 데이터가 아주 많고 모델이 적절히 설계되어 있다면 강한 드롭아웃이 필요하지 않을 수도 있습니다. 반대로 데이터가 작고 모델이 큰 경우에는 드롭아웃이 검증 성능을 안정시키는 데 큰 도움이 될 수 있습니다.
또 하나 기억할 점은 드롭아웃이 "나쁜 특징을 골라 제거하는 방법"은 아니라는 것입니다. 드롭아웃은 어떤 뉴런이 중요한지 판단해서 끄지 않습니다. 무작위로 끕니다. 바로 이 무작위성이 핵심입니다. 모델은 예측 과정에서 일부 정보가 사라지는 상황을 반복해서 겪으며, 더 넓고 튼튼한 판단 기준을 만들게 됩니다.
작은 불편함이 만드는 튼튼함
지금까지 우리는 신경망이 예측하고, 손실을 계산하고, 역전파로 가중치를 조정하는 흐름을 배웠습니다. 여기에 드롭아웃을 넣으면 학습 과정이 조금 더 거칠어집니다. 모델은 매번 완전한 신경망으로 편하게 예측하지 못합니다. 어떤 뉴런은 꺼지고, 어떤 단서는 사라집니다.
하지만 바로 그 불편함 때문에 모델은 더 좋은 습관을 갖게 됩니다. 특정 뉴런 하나에 매달리지 않고, 여러 특징을 함께 사용합니다. 훈련 데이터의 우연한 패턴을 외우기보다, 새로운 데이터에서도 통할 가능성이 높은 표현을 찾으려 합니다.
드롭아웃의 핵심은 한 문장으로 정리할 수 있습니다.
학습 중 일부 뉴런을 무작위로 끄면, 신경망은 특정 뉴런에만 기대지 않는 더 튼튼한 표현을 배운다.
다음 장에서는 또 다른 중요한 정규화 도구인 배치 정규화를 살펴봅니다. 드롭아웃이 일부 뉴런을 쉬게 하며 의존성을 줄이는 방법이라면, 배치 정규화는 층을 통과하는 값들의 분포를 다루어 학습을 더 안정적으로 만드는 방법입니다. 신경망을 잘 학습시키는 일은 단순히 층을 많이 쌓는 일이 아닙니다. 모델이 흔들리지 않고 배울 수 있도록 길을 닦아 주는 일이기도 합니다.
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.