본문 바로가기
Data & Research

[Deep Learning] Batch/Layer Normalization

by 물박사의 저장공간 2025. 7. 2.

2025.03.15 - [Data & Research] - [ML & DL 기초] Table of Contents


1. Batch Normalization

Batch Normalization의 핵심 아이디어는 내부 공변량 변화(Internal Covariate Shift) 문제를 해결하는 것입니다. 공변량 변화란 모델의 입력 데이터 분포가 훈련 시와 테스트 시에 달라지는 현상을 말합니다. 딥러닝 모델은 여러 층으로 구성되어 있는데, 학습 과정에서 이전 층의 파라미터가 변하면 현재 층에 들어오는 입력값의 분포가 계속해서 바뀌게 됩니다. 그런데, 이것은 딥러닝 학습의 입장에서 꽤나 문제가 된다는 건데요.

1) 학습 속도 저하: 각 층은 계속해서 변하는 새로운 분포의 입력에 적응해야 하므로 학습률(learning rate)을 크게 잡기 어렵고, 결국 전체적인 학습 속도가 느려집니다.

2) 활성화 함수 포화(Saturation) 문제: Sigmoid나 Tanh와 같은 활성화 함수는 입력값이 너무 크거나 작아지면 기울기(gradient)가 0에 가까워지는 포화 영역에 들어갑니다. 내부 공변량 변화로 인해 층의 입력값이 이 포화 영역으로 밀려 들어가면 기울기 소실(Vanishing Gradient) 문제가 발생하여 학습이 멈출 수 있습니다.

배치 정규화는 바로 이 각 층의 입력 분포를 학습 과정 내내 안정적으로 유지시켜 내부 공변량 변화를 줄이는 것을 목표로 합니다. 말 그대로 Batch 단위에서 평균과 분산을 구하면

$$\mu_{\mathcal{B}} = \frac{1}{m} \sum_{i=1}^{m} x_i$$

$$\sigma_{\mathcal{B}}^2 = \frac{1}{m} \sum_{i=1}^{m} (x_i - \mu_{\mathcal{B}})^2$$

이를 이용해서 input을 normalization 해주는 겁니다. 

$$\hat{x}_i = \frac{x_i - \mu_{\mathcal{B}}}{\sqrt{\sigma_{\mathcal{B}}^2 + \epsilon}}$$

그런데, 여기서 만약 모든 층의 입력을 단순히 평균 0, 분산 1로 강제하면 활성화 함수의 비선형 표현력이 약화될 수 있습니다. 예를 들어, Sigmoid 함수는 입력이 0 근처일 때 거의 선형(linear)처럼 동작하기 때문입니다. 이 문제를 해결하기 위해, 모델이 정규화된 분포를 스스로 조절할 수 있도록 학습 가능한 파라미터를 부여해줍니다. 

$$y_i = \gamma \hat{x}_i + \beta$$

여기서 \( \gamma\)와 \(\beta\)는 학습가능한 parameter입니다. 이러한 방식으로 정규화하기 때문에 Batch의 size를 너무 작게하면 방법 자체가 너무 불안정(batch가 전체 분포에서 너무 동떨어질 수도 있겠죠?)해져서 성능이 저하될 수 있습니다. 

 

그렇다면 추론시에는 어떻게 작동할까요? 테스트나 실제 추론 시점에는 미니배치 단위로 평균과 분산을 구하는 것이 비효율적이며, 입력 데이터가 하나씩 들어올 경우엔 아예 그것이 불가능합니다. 따라서 학습 과정 동안 계산했던 각 Mini-batch의 평균과 분산을 지수 이동 평균(Exponential Moving Average)으로 저장해 둔 모집단 통계량(population statistics)을 사용합니다.

# Keras 예시
x = layers.BatchNormalization()(x)

# Pytorch 예시 : 입력으로 들어오는 채널(channel)의 개수를 인자로 명시
self.bn1 = nn.BatchNorm2d(16) # model.train()과 model.eval() 모드 전환주의

 

https://medium.com/techspace-usict/normalization-techniques-in-deep-neural-networks-9121bf100d8

 

2. Layer Normalization 

layer normalization은 batch의 크기가 GPU 메모리 한계 등으로 매우 작다든가 언어모델처럼 input의 크기가 제각각인 경우에 사용하게 됩니다(이런 경우는 각 타임스텝마다 데이터의 개수가 달라 배치 통계량을 일관성 있게 적용하기 어렵고, 전체 시퀀스에 대한 통계량을 한 번에 구하는 것도 비효율적입니다). 결국 이러한 문제의 근본 원인은 normalization이 batch의 크기에 의존한다는 점이라고 볼 수 있습니다. 그래서 "배치 내 다른 데이터는 신경 쓰지 말고, 오직 현재 처리 중인 데이터 샘플 하나에 대해서만 정규화를 수행하자." 는 아이디어에서 나온 것이 Layer normalization이죠. 정규화에 필요한 평균과 분산을 하나의 데이터 샘플 내에 있는 모든 뉴런들로부터 계산합니다.

$$\mu = \frac{1}{H} \sum_{i=1}^{H} h_i$$

$$\sigma^2 = \frac{1}{H} \sum_{i=1}^{H} (h_i - \mu)^2$$

$$\hat{h}_i = \frac{h_i - \mu}{\sqrt{\sigma^2 + \epsilon}}$$

Batch normalization과 마찬가지로 학습가능한 parameter를 부여해줍니다.

$$y_i = \gamma \hat{h}_i + \beta$$

# Keras 예시
x = layers.LayerNormalization()(x) # LayerNorm

# Pytorch 예시 : 정규화를 적용할 차원의 크기를 normalized_shape 인자로 직접 지정
self.ln1 = nn.LayerNorm([16, 28, 28]) # CNN의 경우 [채널, 높이, 너비]