Skip to main content

정규화 (Regularization)

학습 목표

  • 과적합(Overfitting)의 원인과 진단 방법을 이해한다
  • Dropout의 동작 원리와 학습/추론 시 차이를 설명할 수 있다
  • Batch Normalization과 Layer Normalization의 차이를 이해한다
  • 실무에서 정규화 기법을 조합하여 적용할 수 있다

왜 중요한가

딥러닝 모델은 파라미터가 매우 많아 학습 데이터를 완벽하게 암기할 수 있습니다. 이러한 과적합이 발생하면 학습 데이터에서는 높은 성능을 보이지만, 새로운 데이터에서는 성능이 급락합니다. 정규화 기법은 모델의 복잡도를 제약하여 일반화(Generalization) 성능을 높입니다.

Dropout

학습 시 뉴런을 확률 pp로 무작위 비활성화합니다. 매 학습 스텝마다 다른 부분 네트워크가 학습되어, 앙상블(Ensemble) 효과를 냅니다.
import torch
import torch.nn as nn

class MLPWithDropout(nn.Module):
    def __init__(self, input_dim, hidden_dim, num_classes, 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, num_classes),
        )

    def forward(self, x):
        return self.layers(x)

model = MLPWithDropout(784, 256, 10, dropout_rate=0.5)

# 학습 모드: Dropout 활성화
model.train()
output_train = model(torch.randn(1, 784))

# 추론 모드: Dropout 비활성화 (모든 뉴런 사용, 가중치 스케일링)
model.eval()
output_eval = model(torch.randn(1, 784))
학습 시에는 반드시 model.train()을, 추론 시에는 model.eval()을 호출하세요. Dropout과 BatchNorm은 학습/추론 모드에서 동작이 다릅니다.

Batch Normalization (배치 정규화)

미니배치 내에서 각 특성의 평균을 0, 분산을 1로 정규화합니다. 내부 공변량 이동(Internal Covariate Shift)을 줄여 학습을 안정화하고 가속합니다. x^i=xiμBσB2+ϵ\hat{x}_i = \frac{x_i - \mu_B}{\sqrt{\sigma_B^2 + \epsilon}} yi=γx^i+βy_i = \gamma \hat{x}_i + \beta
  • μB,σB2\mu_B, \sigma_B^2: 미니배치의 평균과 분산
  • γ,β\gamma, \beta: 학습 가능한 스케일/시프트 파라미터
  • ϵ\epsilon: 수치 안정성을 위한 작은 값
# 1D 데이터 (MLP, RNN)
bn1d = nn.BatchNorm1d(num_features=256)

# 2D 데이터 (CNN) - 채널 방향 정규화
bn2d = nn.BatchNorm2d(num_features=64)

# CNN 예시
cnn_block = nn.Sequential(
    nn.Conv2d(3, 64, kernel_size=3, padding=1),
    nn.BatchNorm2d(64),    # Conv 뒤에 BN 적용
    nn.ReLU(),
)
주의: 배치 크기가 너무 작으면(1~2) 통계량이 불안정해집니다. 추론 시에는 학습 중 계산된 이동 평균(Running Mean/Variance)을 사용합니다.

Layer Normalization (층 정규화)

배치가 아닌 각 샘플의 모든 특성에 대해 정규화합니다. 배치 크기에 의존하지 않으므로 시퀀스 모델과 Transformer에서 선호됩니다. x^i=xiμLσL2+ϵ\hat{x}_i = \frac{x_i - \mu_L}{\sqrt{\sigma_L^2 + \epsilon}}
  • μL,σL2\mu_L, \sigma_L^2: 한 샘플의 모든 특성에 대한 평균과 분산
# Layer Normalization
ln = nn.LayerNorm(normalized_shape=256)

# Transformer 블록에서의 사용 (Pre-LN 구조)
class TransformerBlock(nn.Module):
    def __init__(self, d_model):
        super().__init__()
        self.ln1 = nn.LayerNorm(d_model)
        self.ln2 = nn.LayerNorm(d_model)
        self.attn = nn.MultiheadAttention(d_model, num_heads=8)
        self.ffn = nn.Sequential(
            nn.Linear(d_model, d_model * 4),
            nn.GELU(),
            nn.Linear(d_model * 4, d_model),
        )

    def forward(self, x):
        # Pre-LN: 정규화 먼저, 그 다음 연산
        x = x + self.attn(self.ln1(x), self.ln1(x), self.ln1(x))[0]
        x = x + self.ffn(self.ln2(x))
        return x

BatchNorm vs LayerNorm

특성BatchNormLayerNorm
정규화 축배치 방향 (같은 특성끼리)특성 방향 (같은 샘플 내)
배치 크기 의존예 (작은 배치 시 불안정)아니오
학습/추론 차이있음 (Running Stats)없음
주 사용처CNNTransformer, RNN

Weight Decay (가중치 감쇠)

가중치의 크기를 직접 줄여 모델 복잡도를 제한합니다. L2 정규화와 개념적으로 유사하지만, AdamW에서는 분리된 형태로 적용됩니다. wt+1=(1λ)wtηwLw_{t+1} = (1 - \lambda)w_t - \eta \nabla_w \mathcal{L}
# AdamW에서의 Weight Decay
optimizer = torch.optim.AdamW(
    model.parameters(),
    lr=0.001,
    weight_decay=0.01  # 가중치 감쇠 계수
)

Early Stopping (조기 종료)

검증 손실(Validation Loss)이 더 이상 감소하지 않으면 학습을 중단합니다.
best_val_loss = float('inf')
patience = 10          # 개선 없이 기다리는 에포크 수
counter = 0

for epoch in range(1000):
    train_loss = train_one_epoch(model, train_loader)
    val_loss = evaluate(model, val_loader)

    if val_loss < best_val_loss:
        best_val_loss = val_loss
        counter = 0
        # 최적 모델 저장
        torch.save(model.state_dict(), 'best_model.pt')
    else:
        counter += 1
        if counter >= patience:
            print(f"조기 종료: epoch {epoch}")
            break

실무 조합 가이드

아키텍처권장 정규화 조합
MLPDropout(0.3~0.5) + Weight Decay
CNNBatchNorm + Dropout(0.2~0.3) + Weight Decay
TransformerLayerNorm + Dropout(0.1) + Weight Decay
RNN/LSTMLayerNorm + Dropout(0.2~0.5)
일반적으로 MLP에서 0.5, CNN에서 0.2~0.3, Transformer에서 0.1을 사용합니다. 데이터가 적으면 더 높은 Dropout을, 데이터가 많으면 더 낮은 Dropout을 적용합니다. 하이퍼파라미터 탐색으로 최적값을 찾는 것이 좋습니다.
사용할 수 있지만, BatchNorm 직후에 Dropout을 적용하면 학습이 불안정해질 수 있습니다. 일반적으로 Conv → BN → ReLU → Dropout 순서를 따르거나, 최근에는 BatchNorm이 충분한 정규화 효과를 제공하므로 Dropout을 제거하는 추세입니다.

체크리스트

  • 과적합의 증상(학습 손실 ↓ but 검증 손실 ↑)을 진단할 수 있다
  • Dropout이 학습/추론 모드에서 다르게 동작하는 이유를 안다
  • BatchNorm과 LayerNorm의 차이와 사용 시점을 구분할 수 있다
  • model.train()model.eval()의 중요성을 이해한다

다음 문서