🤖 AI/ML

Dropout

드롭아웃

학습 시 뉴런을 무작위로 비활성화하는 정규화 기법. 과적합 방지.

📖 상세 설명

Dropout은 신경망 학습 시 각 훈련 반복에서 뉴런을 무작위로 비활성화(출력을 0으로 설정)하는 정규화 기법입니다. 2012년 Geoffrey Hinton 팀이 발표한 이 기법은 신경망의 과적합(overfitting)을 방지하는 가장 효과적이고 널리 사용되는 방법 중 하나로, AlexNet 이후 거의 모든 딥러닝 모델의 표준 구성요소가 되었습니다.

Dropout의 핵심 아이디어는 앙상블 학습에서 영감을 받았습니다. 매 학습 스텝마다 다른 뉴런 조합을 사용하면, 결과적으로 2^n개의 서로 다른 "얇은(thinned)" 네트워크를 동시에 학습하는 효과가 있습니다. 이는 단일 뉴런에 대한 과도한 의존을 줄이고, 더 강건한 특징 표현을 학습하도록 유도합니다.

동작 원리는 단순합니다. 학습 시 각 뉴런을 확률 p(보통 0.5 또는 0.2)로 비활성화합니다. 추론 시에는 모든 뉴런을 사용하되, 출력에 (1-p)를 곱하거나(표준 dropout), 학습 시 출력을 1/(1-p)로 스케일링(inverted dropout)하여 기대값을 맞춥니다. 현대 프레임워크는 대부분 inverted dropout을 사용하여 추론 시 별도 처리가 필요 없습니다.

실무에서 Dropout은 특히 Fully Connected 레이어에서 효과적이며, CNN의 마지막 FC 레이어나 RNN/LSTM의 은닉층에 주로 적용됩니다. 다만 BatchNorm과 함께 사용 시 효과가 감소할 수 있고, Transformer 아키텍처에서는 Attention 후에 적용하는 것이 일반적입니다. ResNet 이후 현대 CNN에서는 BatchNorm이 충분한 정규화를 제공하여 Dropout 사용이 줄었지만, FC 레이어나 언어 모델에서는 여전히 필수적입니다.

💻 코드 예제

# PyTorch에서 Dropout 사용 예제
import torch
import torch.nn as nn

class MLPWithDropout(nn.Module):
    """Dropout이 적용된 MLP 분류기"""
    def __init__(self, input_size, hidden_size, num_classes, dropout_rate=0.5):
        super().__init__()
        self.layer1 = nn.Linear(input_size, hidden_size)
        self.layer2 = nn.Linear(hidden_size, hidden_size)
        self.layer3 = nn.Linear(hidden_size, num_classes)
        self.relu = nn.ReLU()
        # Dropout 레이어 (학습 시에만 활성화)
        self.dropout = nn.Dropout(p=dropout_rate)

    def forward(self, x):
        x = self.relu(self.layer1(x))
        x = self.dropout(x)  # 첫 번째 hidden layer 후 dropout
        x = self.relu(self.layer2(x))
        x = self.dropout(x)  # 두 번째 hidden layer 후 dropout
        x = self.layer3(x)   # 출력층에는 dropout 적용 안 함
        return x

# 모델 생성
model = MLPWithDropout(784, 256, 10, dropout_rate=0.5)

# 학습 모드 vs 평가 모드
model.train()  # Dropout 활성화
output_train = model(torch.randn(32, 784))

model.eval()   # Dropout 비활성화 (모든 뉴런 사용)
output_eval = model(torch.randn(32, 784))

# 2D Dropout (CNN용)
class CNNWithDropout(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv = nn.Conv2d(3, 64, 3, padding=1)
        self.dropout2d = nn.Dropout2d(p=0.25)  # 채널 단위 dropout
        self.fc = nn.Linear(64 * 32 * 32, 10)
        self.dropout = nn.Dropout(p=0.5)

    def forward(self, x):
        x = torch.relu(self.conv(x))
        x = self.dropout2d(x)  # Conv 후 2D dropout
        x = x.view(x.size(0), -1)
        x = self.dropout(x)    # FC 전 dropout
        return self.fc(x)

print("Dropout 모델 구성 완료!")

🗣️ 실무에서 이렇게 말하세요

모델 과적합 문제 해결 논의에서

"훈련 정확도는 98%인데 검증 정확도가 85%면 심한 과적합입니다. FC 레이어에 Dropout 0.5 추가하고, 데이터 증강도 같이 적용해보세요. Dropout 단독으로도 보통 2-4% 검증 정확도 개선 효과가 있습니다."

코드 리뷰에서

"이 부분에서 model.eval() 호출이 빠져있네요. 추론 시에는 반드시 eval 모드로 전환해야 Dropout이 비활성화됩니다. 그렇지 않으면 매 추론마다 다른 결과가 나오고, 정확도도 떨어집니다."

기술 면접에서

"Inverted Dropout은 학습 시 출력을 1/(1-p)로 스케일업하여 추론 시 별도 처리가 필요 없게 합니다. p=0.5면 학습 때 출력을 2배로 곱하고, 추론 때는 그대로 사용하면 기대값이 동일하게 유지됩니다."

⚠️ 흔한 실수 & 주의사항

⚠️
추론 시 반드시 eval() 모드 전환

PyTorch에서 model.eval()을 호출하지 않으면 추론 시에도 Dropout이 활성화되어 일관되지 않은 결과와 성능 저하가 발생합니다. with torch.no_grad()와 함께 사용하세요.

⚠️
BatchNorm과의 상호작용

BatchNorm 레이어 바로 뒤에 Dropout을 적용하면 추론 시 분포 불일치로 성능이 저하될 수 있습니다. 일반적으로 Conv-BN-ReLU-Dropout 순서보다 Conv-BN-ReLU만 사용하는 것이 권장됩니다.

⚠️
Dropout 비율 조정의 중요성

너무 높은 Dropout(0.7 이상)은 학습을 방해하고, 너무 낮은 값(0.1 이하)은 정규화 효과가 미미합니다. FC 레이어는 0.5, Conv 레이어는 0.2-0.3에서 시작하여 조정하세요.

🔗 관련 용어

📚 더 배우기