
현대 딥러닝의 심장부인 Transformer 아키텍처를 공부하다 보면 한 가지 의구심이 생깁니다. CNN(Convolutional Neural Networks) 시대의 영웅이었던 Batch Normalization(BN)은 왜 Transformer에서 자취를 감추고, Layer Normalization(LN)이 그 자리를 대신하게 되었을까요? 본 포스팅에서는 두 기법의 구조적 차이와 더불어, 실무 개발자가 Transformer 계열 모델을 설계할 때 직면하는 수렴 문제를 해결하는 7가지 실전 파이썬 코드 가이드를 제공합니다.
1. Batch vs Layer Normalization: 구조적 차이와 Transformer의 선택
정규화(Normalization)는 내부 공변량 변화(Internal Covariate Shift)를 제어하고 학습 속도를 가속화하는 핵심 요소입니다. 하지만 데이터의 특성(이미지 vs 시퀀스)에 따라 적합한 방식이 극명하게 갈립니다.
정규화 기법별 메커니즘 비교
| 비교 항목 | Batch Normalization (BN) | Layer Normalization (LN) |
|---|---|---|
| 계산 차원 | 배치(Batch) 방향 (N 차원) | 특징(Feature) 방향 (C 차원) |
| 시퀀스 길이 의존성 | 길이가 다르면 계산이 매우 복잡함 | 시퀀스 길이에 독립적임 |
| 배치 크기 영향 | 작은 배치 크기에서 성능 급감 | 배치 크기에 영향을 받지 않음 |
| 학습/추론 모드 | Running Mean/Var 관리 필요 | 동일한 수식 적용 (단순함) |
| Transformer 적합성 | 부적합 (시퀀스 변동성 때문) | 최적 (가장 널리 사용됨) |
왜 Transformer는 BN을 외면할까요? 가장 큰 이유는 자연어 데이터의 가변 길이(Variable Length) 때문입니다. 문장마다 길이가 다른 텍스트 데이터에서 배치를 가로질러 통계량을 계산하는 BN은 계산적 오버헤드가 크고, 특정 타임스텝의 통계량이 배치 내 다른 문장에 의해 심하게 왜곡될 수 있습니다. 반면 LN은 각 샘플(문장) 내부에서 독립적으로 정규화를 수행하므로 훨씬 안정적입니다.
2. 실무 적용을 위한 Normalization 전략 및 해결 방법 Example (7가지)
PyTorch 환경에서 Transformer 구조를 설계하거나 커스터마이징할 때, 정규화 층을 올바르게 배치하고 활용하는 실전 코드 예제를 살펴봅니다.
Example 1: PyTorch에서의 기본적인 Layer Normalization 구현
import torch
import torch.nn as nn
# 입력 차원: [Batch_size, Sequence_length, Embedding_dim]
batch_size, seq_len, d_model = 32, 512, 768
x = torch.randn(batch_size, seq_len, d_model)
# LN은 마지막 차원(Embedding)을 기준으로 정규화 수행
ln = nn.LayerNorm(d_model)
output = ln(x)
print(f"LN Output Shape: {output.shape}") # [32, 512, 768]
Example 2: Post-LN vs Pre-LN 구조적 차이와 구현
최근에는 학습 안정성을 위해 Residual Connection 이전에 LN을 배치하는 Pre-LN 방식이 선호됩니다.
class TransformerBlockPreLN(nn.Module):
def __init__(self, d_model, nhead):
super().__init__()
self.ln1 = nn.LayerNorm(d_model)
self.attn = nn.MultiheadAttention(d_model, nhead)
self.ln2 = nn.LayerNorm(d_model)
self.ff = nn.Sequential(nn.Linear(d_model, d_model * 4), nn.ReLU(), nn.Linear(d_model * 4, d_model))
def forward(self, x):
# Pre-LN: 정규화 후 Attention 적용
x = x + self.attn(self.ln1(x), self.ln1(x), self.ln1(x))[0]
x = x + self.ff(self.ln2(x))
return x
Example 3: 가변 길이 시퀀스를 위한 Masked Layer Norm 해결 방법
패딩(Padding) 토큰이 정규화 통계에 영향을 주지 않도록 마스킹 처리가 필요할 때 유용합니다.
def masked_layer_norm(x, mask, eps=1e-5):
# mask: [batch, seq_len, 1], 1 for valid, 0 for padding
mean = (x * mask).sum(dim=-1, keepdim=True) / mask.sum(dim=-1, keepdim=True)
var = ((x - mean)**2 * mask).sum(dim=-1, keepdim=True) / mask.sum(dim=-1, keepdim=True)
return (x - mean) / torch.sqrt(var + eps)
Example 4: RMSNorm (Root Mean Square Layer Normalization) 적용
Llama 2 등 최신 LLM에서 평균 계산을 생략하여 속도를 높인 RMSNorm이 자주 사용됩니다.
class RMSNorm(nn.Module):
def __init__(self, dim, eps=1e-6):
super().__init__()
self.eps = eps
self.weight = nn.Parameter(torch.ones(dim))
def forward(self, x):
# 평균을 빼지 않고 제곱평균제곱근만 사용
norm_x = x * torch.rsqrt(x.pow(2).mean(-1, keepdim=True) + self.eps)
return norm_x * self.weight
Example 5: 특정 레이어의 가중치 초기화(Weight Initialization)와 LN의 조화
def init_weights(m):
if isinstance(m, nn.Linear):
nn.init.xavier_uniform_(m.weight)
elif isinstance(m, nn.LayerNorm):
nn.init.constant_(m.bias, 0)
nn.init.constant_(m.weight, 1.0)
model = nn.Sequential(nn.Linear(768, 768), nn.LayerNorm(768))
model.apply(init_weights)
Example 6: CNN-Transformer 하이브리드 모델에서의 정규화 통합
class HybridNorm(nn.Module):
def __init__(self, channels, d_model):
super().__init__()
self.bn = nn.BatchNorm2d(channels) # 이미지 feature용
self.ln = nn.LayerNorm(d_model) # 시퀀스 feature용
def forward(self, img_feat, seq_feat):
# 각 도메인에 맞는 최적의 정규화 적용
return self.bn(img_feat), self.ln(seq_feat)
Example 7: 정규화 계수(Gain/Bias)를 활용한 동적 조정
# LN은 내부적으로 learnable 파라미터를 가짐
ln = nn.LayerNorm(768, elementwise_affine=True)
# 특정 학습 단계에서 정규화 강도를 수동 조절하고 싶을 때 파라미터에 접근
with torch.no_grad():
ln.weight.fill_(0.5) # 정규화된 값의 스케일을 절반으로 축소
3. 심화 분석: 왜 BN은 자연어 처리(NLP)에서 실패했을까?
단순히 성능의 문제가 아닙니다. 근본적으로 Statistics Stability(통계적 안정성)의 차이입니다. 자연어는 문장마다 정보의 밀도가 다릅니다. 짧은 문장과 긴 문장이 섞인 배치에서 BN을 사용하면, 패딩 토큰의 0 값이 평균과 분산을 왜곡시킵니다. 또한 RNN이나 Transformer처럼 순차적인 의존성이 강한 모델에서 BN은 각 시점(Time-step)마다 별도의 통계량을 유지해야 하는 번거로움이 있습니다. 반면 Layer Normalization은 한 샘플 안에서 모든 단어 임베딩의 정보를 모아 정규화하므로, 문장의 길이나 배치의 상태에 관계없이 각 토큰이 일관된 스케일을 유지할 수 있게 도와줍니다.
4. 결론 및 향후 전망
Transformer 구조에서 Layer Normalization은 표준으로 자리 잡았지만, 연구는 멈추지 않습니다. RMSNorm은 효율성을, Group Normalization은 소량 배치에서의 안정성을 공략하며 계속 발전하고 있습니다. 개발자는 모델의 목적과 하드웨어 자원을 고려하여 최적의 정규화 기법을 선택해야 합니다.