본문 바로가기
Artificial Intelligence/60. Python

[PYTHON] Gradient 문제 해결을 위한 Batch vs Layer Normalization 2가지 수학적 차이와 7개 구현 방법

by Papa Martino V 2026. 4. 28.
728x90

Batch vs Layer Normalization
Batch vs Layer Normalization

 

 

딥러닝 모델이 깊어질수록 우리는 필연적으로 기울기 소실(Vanishing Gradient)기울기 폭주(Exploding Gradient)라는 거대한 벽에 부딪힙니다. 파이썬을 활용한 신경망 설계 과정에서 이 난제를 해결하기 위해 가장 보편적으로 사용되는 도구가 바로 Batch Normalization(BN)Layer Normalization(LN)입니다. 하지만 많은 개발자들이 이 두 기법을 단순히 '정규화 도구'로만 취급하며, 내부의 수학적 메커니즘과 데이터 처리 차이에 따른 성능 최적화 포인트를 놓치곤 합니다. 본 포스팅에서는 이 두 정규화 기법의 수학적 근본 원리를 비교하고, 실무에서 마주하는 다양한 네트워크 구조(CNN, RNN, Transformer)에 따라 어떤 기법을 선택해야 하는지 명확한 가이드를 제시합니다.


1. 정규화(Normalization)가 필요한 근본적인 이유

학습 과정에서 각 층의 입력 분포가 변하는 현상을 Internal Covariate Shift라고 합니다. 이전 층의 가중치가 업데이트됨에 따라 다음 층으로 들어오는 입력값이 요동치면, 신경망은 새로운 분포에 적응해야 하므로 학습 속도가 현저히 느려집니다. 특히 활성화 함수가 Sigmoid나 Tanh일 경우, 입력값이 커지거나 작아지면 미분값이 0에 수렴하여 학습이 중단되는 Vanishing Gradient 문제가 발생합니다.


2. Batch Norm vs Layer Norm: 2가지 핵심 수학적 차이

두 기법의 가장 큰 차이는 "어떤 차원(Dimension)을 기준으로 통계량을 계산하는가"에 있습니다. 다음 표는 두 방식의 메커니즘을 요약한 것입니다.

항목 Batch Normalization (BN) Layer Normalization (LN)
계산 기준 미니배치(Batch) 내 동일 채널의 모든 데이터 단일 샘플 내 모든 특성(Feature/Channel)
수학적 공식 $\mu_B = \frac{1}{m}\sum x_i$ (배치 전체 평균) $\mu_L = \frac{1}{H}\sum x_i$ (레이어 전체 평균)
배치 크기 의존성 높음 (작은 배치에서 성능 급감) 없음 (배치 크기와 무관하게 작동)
주요 적용 분야 Computer Vision (CNN, ResNet) NLP (RNN, Transformer, GPT)
학습/추론 차이 학습 시의 이동 평균을 저장하여 추론 시 사용 학습과 추론 시 동작 방식이 동일함

수학적 포인트: BN은 데이터 간의 관계를 고려하여 정규화하는 반면, LN은 데이터 각각을 독립적으로 정규화합니다. 따라서 시계열 데이터처럼 데이터의 길이가 가변적이거나 배치 크기를 크게 가져갈 수 없는 환경에서는 LN이 압도적으로 유리합니다.


3. 딥러닝 성능 개선을 위한 실무 Python Example (7가지)

PyTorch 프레임워크를 기준으로, 실무에서 즉시 적용 가능한 최적화 코드를 소개합니다.

Example 1: CNN에서의 Batch Normalization 적용 (ResNet 스타일)

import torch
import torch.nn as nn

class ConvBNRelu(nn.Module):
    def __init__(self, in_channels, out_channels):
        super(ConvBNRelu, self).__init__()
        # Conv 이후 ReLU 이전에 BN을 배치하는 것이 정석입니다.
        self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1)
        self.bn = nn.BatchNorm2d(out_channels)
        self.relu = nn.ReLU()

    def forward(self, x):
        return self.relu(self.bn(self.conv(x)))

# 실행: (Batch, Channel, Height, Width)
model = ConvBNRelu(3, 64)
input_tensor = torch.randn(16, 3, 32, 32)
output = model(input_tensor)

Example 2: Transformer Encoder에서의 Layer Normalization 구현

import torch.nn as nn

class TransformerBlock(nn.Module):
    def __init__(self, embed_dim, num_heads):
        super(TransformerBlock, self).__init__()
        self.ln1 = nn.LayerNorm(embed_dim)
        self.ln2 = nn.LayerNorm(embed_dim)
        self.mha = nn.MultiheadAttention(embed_dim, num_heads)
        self.ffn = nn.Sequential(
            nn.Linear(embed_dim, 4 * embed_dim),
            nn.ReLU(),
            nn.Linear(4 * embed_dim, embed_dim)
        )

    def forward(self, x):
        # Pre-LN 구조: 학습 안정성이 Post-LN보다 뛰어납니다.
        x = x + self.mha(self.ln1(x), self.ln1(x), self.ln1(x))[0]
        x = x + self.ffn(self.ln2(x))
        return x

Example 3: 가변 길이 시퀀스 처리를 위한 RNN과 LN의 결합

# RNN계열은 시점마다 통계량이 급변하므로 BN 대신 LN을 써야 합니다.
class LNLSTM(nn.Module):
    def __init__(self, input_size, hidden_size):
        super(LNLSTM, self).__init__()
        self.lstm = nn.LSTM(input_size, hidden_size, batch_first=True)
        self.ln = nn.LayerNorm(hidden_size)

    def forward(self, x):
        out, _ = self.lstm(x)
        # 마지막 시점 또는 모든 시점에 LN 적용
        return self.ln(out)

Example 4: 1D Convolution과 Batch Norm을 이용한 시그널 전처리

# 음성 인식이나 시계열 예측에서 1D Conv 사용 시
model_1d = nn.Sequential(
    nn.Conv1d(1, 16, kernel_size=5),
    nn.BatchNorm1d(16),
    nn.ReLU()
)
# (Batch, Channel, Length) 형식의 입력 지원

Example 5: 작은 배치 사이즈 문제 해결을 위한 Group Normalization

# 배치 크기가 1~2인 Object Detection 등의 태스크에서는 
# BN의 대안으로 Group Norm(GN)을 사용합니다.
# num_groups가 1이면 Layer Norm과 동일해집니다.
gn = nn.GroupNorm(num_groups=8, num_channels=64)
x = torch.randn(2, 64, 20, 20)
output = gn(x)

Example 6: Weight Initialization과 Normalization의 시너지 효과

# 가중치 초기화(Kaiming/Xavier)와 정규화를 병행하여 Gradient 문제를 원천 봉쇄합니다.
def init_weights(m):
    if isinstance(m, nn.Conv2d):
        nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
    elif isinstance(m, nn.BatchNorm2d):
        nn.init.constant_(m.weight, 1)
        nn.init.constant_(m.bias, 0)

model.apply(init_weights)

Example 7: 정규화 레이어의 학습 파라미터(Affine) 동결 해결 방법

# 전이 학습 시 BN의 통계량은 유지하고 감마/베타 파라미터만 학습하고 싶을 때
for name, param in model.named_parameters():
    if "bn" in name:
        param.requires_grad = True # BN 파라미터는 미세 조정이 필요한 경우가 많음
    else:
        param.requires_grad = False

4. 결론: 최적의 정규화 기법 선택 가이드

결국 방법의 선택은 모델의 구조와 데이터의 특성에 달려 있습니다. 정형 데이터나 이미지 데이터처럼 고정된 입력을 큰 배치로 처리할 때는 Batch Normalization이 학습 속도 면에서 유리합니다. 반면, 자연어 처리나 오디오 데이터처럼 길이가 가변적이고 배치 크기에 제약이 있는 경우에는 Layer Normalization이 기울기 문제를 해결하는 가장 안정적인 해결책이 됩니다.

최근의 트렌드는 Transformer의 성공 이후 Layer Norm이 대세로 자리 잡고 있으나, CNN 기반의 고성능 컴퓨터 비전 모델에서는 여전히 Batch Norm이 성능 우위를 점하는 경우가 많다는 차이를 기억하시기 바랍니다.


내용 출처 및 기술 자료

  • Ioffe, S., & Szegedy, C. (2015). Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift.
  • Ba, J. L., Kiros, J. R., & Hinton, G. E. (2016). Layer Normalization.
  • PyTorch Documentation: torch.nn.BatchNorm2d & torch.nn.LayerNorm.
  • He, K., et al. (2016). Deep Residual Learning for Image Recognition.
728x90