드롭아웃
Dropout
학습 시 뉴런을 무작위로 비활성화하는 정규화 기법. 과적합 방지에 효과적.
Dropout
학습 시 뉴런을 무작위로 비활성화하는 정규화 기법. 과적합 방지에 효과적.
드롭아웃(Dropout)은 신경망의 학습 과정에서 무작위로 일부 뉴런을 비활성화하여 과적합을 방지하는 정규화 기법입니다. 2012년 Hinton 교수팀이 제안한 이후 딥러닝의 기본 구성 요소로 자리잡았습니다. 핵심 아이디어는 특정 뉴런에 과도하게 의존하는 것을 방지하여 더 robust한 특징을 학습하도록 유도하는 것입니다.
학습 시 각 뉴런은 지정된 확률(보통 0.2~0.5)로 비활성화됩니다. 예를 들어 dropout rate가 0.5라면 절반의 뉴런이 매 학습 스텝마다 무작위로 꺼집니다. 이로 인해 네트워크는 마치 수많은 서브 네트워크의 앙상블처럼 동작하게 됩니다. 추론 시에는 모든 뉴런을 활성화하되, 출력에 (1 - dropout_rate)를 곱하여 학습 시와 기대값을 맞춥니다(Inverted Dropout은 학습 시 스케일링).
2024-2025년 기준으로 표준 Dropout 외에도 다양한 변형이 실무에서 활용됩니다. DropBlock은 CNN의 공간적 특성을 고려해 연속된 영역을 드롭하고, DropConnect는 가중치를 드롭합니다. Transformer 아키텍처에서는 Attention Dropout, Stochastic Depth(Layer Dropout)가 널리 사용됩니다. 특히 대형 언어 모델(LLM)에서는 학습 안정성을 위해 0.1 정도의 낮은 dropout rate가 표준입니다.
현대 딥러닝에서 Dropout의 역할은 아키텍처에 따라 달라집니다. CNN에서는 BatchNorm이 강력한 정규화 효과를 제공하므로 Dropout을 생략하거나 낮은 비율만 적용하는 경향이 있습니다. 반면 Transformer와 MLP에서는 여전히 필수적인 정규화 기법입니다. 최근 연구에서는 학습 단계에 따라 dropout rate를 동적으로 조절하는 Scheduled Dropout도 효과적임이 입증되었습니다.
PyTorch 드롭아웃 기본 사용법
import torch
import torch.nn as nn
# 1. 기본 Dropout 레이어
class MLPWithDropout(nn.Module):
def __init__(self, input_dim, hidden_dim, output_dim, dropout_rate=0.5):
super().__init__()
self.layers = nn.Sequential(
nn.Linear(input_dim, hidden_dim),
nn.ReLU(),
nn.Dropout(p=dropout_rate), # 학습 시 50% 뉴런 비활성화
nn.Linear(hidden_dim, hidden_dim),
nn.ReLU(),
nn.Dropout(p=dropout_rate),
nn.Linear(hidden_dim, output_dim)
)
def forward(self, x):
return self.layers(x)
# 2. 학습/추론 모드 전환 (매우 중요!)
model = MLPWithDropout(784, 256, 10)
# 학습 시: Dropout 활성화
model.train()
train_output = model(train_data)
# 추론 시: Dropout 비활성화 (모든 뉴런 사용)
model.eval()
with torch.no_grad():
test_output = model(test_data)
# 3. 2D Dropout (CNN용) - 채널 전체를 드롭
class CNNWithDropout(nn.Module):
def __init__(self):
super().__init__()
self.conv_layers = nn.Sequential(
nn.Conv2d(3, 64, 3, padding=1),
nn.BatchNorm2d(64),
nn.ReLU(),
nn.Dropout2d(p=0.2), # 공간 전체가 아닌 채널 단위 드롭
nn.MaxPool2d(2)
)
def forward(self, x):
return self.conv_layers(x)
Transformer에서의 Dropout (실무 패턴)
import torch.nn as nn
import torch.nn.functional as F
class TransformerBlockWithDropout(nn.Module):
"""GPT/BERT 스타일 Transformer 블록의 Dropout 적용 위치"""
def __init__(self, d_model=768, n_heads=12, dropout=0.1):
super().__init__()
self.attention = nn.MultiheadAttention(d_model, n_heads, dropout=dropout)
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
self.ffn = nn.Sequential(
nn.Linear(d_model, d_model * 4),
nn.GELU(),
nn.Dropout(dropout), # FFN 중간에 Dropout
nn.Linear(d_model * 4, d_model),
nn.Dropout(dropout) # FFN 출력에 Dropout
)
self.dropout = nn.Dropout(dropout)
def forward(self, x, mask=None):
# Attention 후 Dropout + Residual
attn_out, _ = self.attention(x, x, x, attn_mask=mask)
x = self.norm1(x + self.dropout(attn_out))
# FFN 후 Residual
ffn_out = self.ffn(x)
x = self.norm2(x + ffn_out)
return x
# Stochastic Depth (Layer Dropout) - 레이어 전체를 드롭
class StochasticDepthBlock(nn.Module):
def __init__(self, block, drop_prob=0.1):
super().__init__()
self.block = block
self.drop_prob = drop_prob
def forward(self, x):
if self.training and torch.rand(1) < self.drop_prob:
return x # 레이어 스킵 (residual만 통과)
return self.block(x) + x
# 학습 진행에 따른 Dropout 스케줄링
class ScheduledDropout(nn.Module):
def __init__(self, max_dropout=0.5, warmup_steps=1000):
super().__init__()
self.max_dropout = max_dropout
self.warmup_steps = warmup_steps
self.current_step = 0
def forward(self, x):
if self.training:
# 초기에는 낮은 dropout, 점진적 증가
p = self.max_dropout * min(1.0, self.current_step / self.warmup_steps)
return F.dropout(x, p=p, training=True)
return x
def step(self):
self.current_step += 1
| Dropout 유형 | 권장 비율 | 주요 적용 대상 | 효과 |
|---|---|---|---|
| Standard Dropout | 0.2 ~ 0.5 | FC Layer, MLP | 과적합 방지, 1-3% 정확도 향상 |
| Dropout2d | 0.1 ~ 0.3 | CNN (BatchNorm 없을 때) | 공간적 상관관계 보존 |
| Attention Dropout | 0.1 | Transformer Attention | 학습 안정화 |
| Stochastic Depth | 0.1 ~ 0.3 | 깊은 ResNet, ViT | 학습 속도 향상, 메모리 절약 |
| DropConnect | 0.5 | 특수 상황 | 더 강한 정규화 |
실무 가이드: BatchNorm을 사용하는 CNN에서는 Dropout을 최소화하거나 생략하세요. Transformer에서는 0.1이 표준이며, 과적합이 심하면 0.2까지 올릴 수 있습니다. 매우 작은 데이터셋에서는 0.5까지 높은 dropout rate가 효과적입니다.
"추론 코드에서 model.eval() 호출이 빠졌네요. 이러면 Dropout이 계속 활성화되어 추론 결과가 매번 달라집니다. 배포 전에 반드시 수정해야 합니다."
"검증 손실이 계속 증가하는데 학습 손실은 감소합니다. 전형적인 과적합 증상이니 Dropout rate를 0.3에서 0.5로 높이고, 데이터 증강도 추가해보죠."
"Dropout은 학습 시 랜덤하게 뉴런을 비활성화하여 co-adaptation(뉴런 간 과도한 의존)을 방지합니다. 결과적으로 각 뉴런이 더 독립적이고 robust한 특징을 학습하게 됩니다. 추론 시에는 모든 뉴런을 사용하되 출력을 스케일링하여 기대값을 맞춥니다."
가장 흔한 실수입니다. model.eval()을 호출하지 않으면 추론 시에도 Dropout이 활성화되어 같은 입력에 대해 매번 다른 출력이 나옵니다. BatchNorm도 마찬가지로 eval 모드에서 다르게 동작하므로 반드시 모드를 전환하세요.
BatchNorm은 이미 강력한 정규화 효과를 제공합니다. BatchNorm 직후에 Dropout을 적용하면 정규화가 과도해져 오히려 학습이 불안정해질 수 있습니다. CNN에서는 FC 레이어에만 Dropout을 적용하는 것이 일반적입니다.
1) 항상 학습 전 model.train(), 추론 전 model.eval()을 호출하세요. 2) CNN에서는 BatchNorm을 사용한다면 Dropout을 Classifier(FC) 부분에만 적용하세요. 3) Transformer에서는 Attention과 FFN 출력에 0.1 정도의 낮은 Dropout을 적용하세요. 4) 과적합 여부를 모니터링하며 dropout rate를 조절하세요.