Group Normalization¶
Group Normalization (GN) 은
- Yuxin Wu와 Kaiming He가 2018년 ECCV에서 발표한 normalization 기법.
- 각 sample의 channel을 여러 group으로 나누고,
- 각 group 내부의 channel 및 spatial 축에 걸쳐 mean과 variance를 구하여 normalization함.
사전 지식¶
- Solutions for the Gradient Vanishing or Exploding Problem
- Batch Normalization, 2015
- Layer Normalization, 2016
특징¶
- Group 구성 예: 입력 shape이
(N, C, H, W) = (8, 32, 16, 16)이고 group 수가 8인 경우:- 각 sample의 32개 channel을 4개 channel씩 8개 group 으로 나눔.
- Group 수는 channel 수의 약수여야 함.
- 각 group에서 4 × 16 × 16 = 1,024개 값 으로 하나의 mean과 variance를 구하여 normalization함.
- Normalization 이후 학습 가능한 scale과 bias를 channel별로 적용함.
- Batch의 8개 sample은 서로 독립적으로 처리하며, 총 8 × 8 = 64개 group 의 통계를 각각 계산함.
- 각 sample의 32개 channel을 4개 channel씩 8개 group 으로 나눔.
- Batch size와 무관함: 각 sample에서 독립적으로 통계를 계산하므로, 작은 batch size에서도 안정적으로 동작 함.
- BN과의 차이:
- LN과 마찬가지로 batch 구성에 따른 통계 변동이 없으므로,
- BN의 batch 통계 변동에 따른 부수적인 regularization 효과는 GN도 없음.
- 대표적 활용:
- CNN 기반 object detection 및 segmentation: 고해상도 영상 등으로 batch size가 제한되는 경우에 활용됨.
- U-Net 기반 diffusion model: 이미지 생성 모델의 convolution block 등에서 사용됨.
- Group 수에 따른 관계:
- Group 수가 1인 경우:
(C, H, W)전체로 통계를 계산하므로, 같은 축을 사용하는 LN과 normalization 결과가 같음.- 단, GN의 scale과 bias는 channel별이므로, sample 별로 적용하는 LN과 파라미터 구조는 다를 수 있음.
- Group 수가 channel 수와 같은 경우: 각 channel의
(H, W)로 통계를 계산하므로, Instance Normalization과 normalization 범위가 같음.
- Group 수가 1인 경우:
일반적인 LN, Transformer의 LN, group 수가 1인 GN의 구분:¶
- 일반적인 LN: 각 sample에서 지정한 feature 축들 에 걸쳐 mean과 variance를 구하여 normalization함.
- Normalization 범위는 지정한 축에 따라 달라짐.
- 예를 들어 입력이
(N, L, C) = (8, 16, 32)이고(L, C)를 지정하면, 각 sample의 16 × 32 = 512개 값 으로 하나의 mean과 variance를 계산함. - Sample별로 독립적으로 처리하므로, 총 8개 의 mean과 variance를 각각 계산함.
- Transformer의 LN: 입력
(N, L, C)에서 마지막 feature 축인 C만 지정한 LN 임.- 입력이
(8, 16, 32)이면, 각 token의 32개 값 으로 하나의 mean과 variance를 계산함. - Sample과 token별로 독립적으로 처리하므로, 총 8 × 16 = 128개 의 mean과 variance를 각각 계산함.
- 다른 token의 값은 해당 token의 통계 계산에 포함하지 않음.
- 입력이
- Group 수가 1인 GN: 같은 데이터를
(N, C, L) = (8, 32, 16)로 배치하면, 각 sample의 32 × 16 = 512개 값 으로 하나의 mean과 variance를 계산함. * Sample별로 독립적으로 처리하므로, 총 8개 의 mean과 variance를 각각 계산함. * 모든 token이 하나의 mean과 variance를 공유함.
따라서 group 수가 1인 GN은 * 같은 축을 지정한 LN과 normalization 범위가 같지만, * Transformer의 token별 LN과는 다름.
단, 학습 가능한 scale과 bias는 GN에서 channel별로, LN에서 지정한 축의 element별로 적용하므로 파라미터 구조는 다를 수 있음.
PyTorch 예제¶
같은 sequence data에
BN, 일반 LN, Transformer의 LN, group 수가 1인 GN과 4인 GN 을 적용하고,
training mode와 eval mode의 동작도 비교하는 예제.
- 입력은
(N, L, C) = (8, 16, 32)임. - BN과 GN에는
transpose로(N, C, L)형태로 전달함. - Normalization 범위만 비교하기 위해 학습 가능한 scale과 bias는 비활성화함.
- BN과 GN은
affine=False, LN은 **elementwise_affine=False**로 지정함. - 기본값은 모두
True이며,False로 지정하면 scale (weight)과 bias (bias)를 생성하지 않음. - 이때
.weight와.bias는 모두None이며, mean과 variance를 이용한 normalization만 수행함.
- BN과 GN은
import torch
from torch import nn
torch.manual_seed(23)
N, L, C = 8, 16, 32
x = torch.randn(N, L, C)
x_ncl = x.transpose(1, 2) # (N, C, L)
# BN: channel별로 batch와 sequence 축을 함께 사용함.
# affine=False로 학습 가능한 scale과 bias를 생성하지 않음.
bn = nn.BatchNorm1d(C, affine=False)
# 일반 LN: 각 sample의 (L, C) 전체를 사용하도록 지정함.
# elementwise_affine=False로 학습 가능한 scale과 bias를 생성하지 않음.
ln_all = nn.LayerNorm((L, C), elementwise_affine=False)
# Transformer의 LN: 각 token의 C개 feature만 사용함.
ln_token = nn.LayerNorm(C, elementwise_affine=False)
# GN (1 group): 각 sample의 (C, L) 전체를 사용함.
gn1 = nn.GroupNorm(num_groups=1, num_channels=C, affine=False)
# GN (4 groups): channel을 8개씩 4개 group으로 나눔.
# 각 group의 8개 channel과 전체 sequence 위치를 함께 사용함.
G = 4
gn4 = nn.GroupNorm(num_groups=G, num_channels=C, affine=False)
# 각 layer에 필요한 입력 형태를 지정함.
cases = [
("BN", bn, x_ncl),
("LN (L, C)", ln_all, x),
("LN (token)", ln_token, x),
("GN (1 group)", gn1, x_ncl),
("GN (4 groups)", gn4, x_ncl),
]
# Training mode에서 출력값을 계산함.
train_outputs = {}
for name, layer, data in cases:
layer.train()
train_outputs[name] = layer(data)
# 출력 비교를 위해 (N, L, C) 형태로 맞춤.
y_bn = train_outputs["BN"].transpose(1, 2)
y_ln_all = train_outputs["LN (L, C)"]
y_ln_token = train_outputs["LN (token)"]
y_gn1 = train_outputs["GN (1 group)"].transpose(1, 2)
y_gn4_ncl = train_outputs["GN (4 groups)"]
y_gn4 = y_gn4_ncl.transpose(1, 2)
def report(name, y, dims):
mean = y.mean(dim=dims)
var = y.var(dim=dims, unbiased=False)
# 각 normalization 단위에서 계산한 mean과 variance를 확인함.
# mean의 절댓값과 variance의 1에 대한 차이의 최댓값을 출력함.
print(
f"{name:16s} "
f"통계 shape={tuple(mean.shape)}, "
f"max |mean|={mean.abs().max().item():.4f}, "
f"max |var - 1|={(var - 1).abs().max().item():.4f}"
)
# (N, L, C) 형태에서 각 방식의 normalization 축을 확인함.
report("BN", y_bn, (0, 1))
report("LN (L, C)", y_ln_all, (1, 2))
report("LN (token)", y_ln_token, (2,))
report("GN (1 group)", y_gn1, (1, 2))
# GN (4 groups)는 group별 통계를 확인하기 위해 reshape함.
# (N, C, L)에서 (N, G, C // G, L)로 변환함.
y_gn4_grouped = y_gn4_ncl.reshape(N, G, C // G, L)
report("GN (4 groups)", y_gn4_grouped, (2, 3))
print(
"LN (L, C)와 GN (1 group)의 결과 일치:",
torch.allclose(y_ln_all, y_gn1, atol=1e-6),
)
print(
"모든 layer의 scale과 bias가 None:",
all(
layer.weight is None and layer.bias is None
for _, layer, _ in cases
),
)
# 같은 입력에 대해 training mode와 eval mode의 출력을 비교함.
for name, layer, data in cases:
layer.eval()
y_eval = layer(data)
print(
f"{name}: train/eval 결과 일치:",
torch.allclose(train_outputs[name], y_eval, atol=1e-6),
)
# 예상 출력: 소수점 넷째 자리까지 표시한 예시이며, 실제 실행 확인 전임.
#
# BN 통계 shape=(32,), max |mean|=0.0000, max |var - 1|=0.0000
# LN (L, C) 통계 shape=(8,), max |mean|=0.0000, max |var - 1|=0.0000
# LN (token) 통계 shape=(8, 16), max |mean|=0.0000, max |var - 1|=0.0000
# GN (1 group) 통계 shape=(8,), max |mean|=0.0000, max |var - 1|=0.0000
# GN (4 groups) 통계 shape=(8, 4), max |mean|=0.0000, max |var - 1|=0.0000
# LN (L, C)와 GN (1 group)의 결과 일치: True
# 모든 layer의 scale과 bias가 None: True
# BN: train/eval 결과 일치: False
# LN (L, C): train/eval 결과 일치: True
# LN (token): train/eval 결과 일치: True
# GN (1 group): train/eval 결과 일치: True
# GN (4 groups): train/eval 결과 일치: True
#
# 통계 shape은 normalization에 사용한 축들을 축약한 후 남는 shape임.
# BN의 (32,)는 channel별 32쌍의 통계를 의미함.
# LN (L, C)와 GN (1 group)의 (8,)은 sample별 8쌍의 통계를 의미함.
# LN (token)의 (8, 16)은 sample별·token별 총 128쌍의 통계를 의미함.
# GN (4 groups)의 (8, 4)는 sample별·group별 총 32쌍의 통계를 의미함.
#
# max |mean|이 약 0이면 각 normalization 단위의 mean이 약 0임.
# max |var - 1|이 약 0이면 각 normalization 단위의 variance가 약 1임.
# 0.0000은 반올림된 표시이며, 정확히 0이라는 의미는 아님.
# eps 때문에 variance는 정확히 1이 되지는 않음.
#
# LN (L, C)와 GN (1 group)은 scale과 bias가 비활성화된 상태에서
# 동일한 값들을 사용하여 normalization하므로 결과가 허용 오차 내에서 같음.
#
# BN은 training mode에서 현재 batch의 통계를 사용하고,
# eval mode에서는 running statistics를 사용하므로 이 예제의 출력이 다름.
# LN과 GN은 두 mode 모두 현재 입력의 통계를 사용하므로 출력이 같음.
입력 데이터 shape: (N, L, C) = (8, 16, 32). BN과 GN에는 **(N, C, L) = (8, 32, 16)**으로 변환하여 전달함.
| 방식 | 통계 계산에 사용하는 값 | Mean·variance 쌍의 수 |
|---|---|---|
| BN | 각 channel에서 batch와 sequence 전체의 8 × 16 = 128개 값 | 32 |
일반 LN: (L, C) 지정 | 각 sample에서 16 × 32 = 512개 값 | 8 |
Transformer의 LN: C 지정 | 각 token에서 32개 값 | 128 |
| GN: group 수 1 | 각 sample에서 32 × 16 = 512개 값 | 8 |
| GN: group 수 4 | 각 sample의 각 group에서 8 × 16 = 128개 값 | 8 × 4 = 32 |
- 일반 LN과 Transformer의 LN은 같은
nn.LayerNorm이며,normalized_shape로 지정한 normalization 범위가 다름. - GN의 group 수가 4이면 channel 0–7, 8–15, 16–23, 24–31이 각각 하나의 group을 구성함.
- 각 group은 모든 sequence 위치를 함께 사용하므로 token별 normalization과 다름.
참고로, BN은 training mode와 eval mode가 다른 동작임:
| 방식 | Training mode | Eval mode |
|---|---|---|
| BN | 현재 batch의 mean과 variance를 사용함. | 누적한 running mean과 running variance를 사용함. |
| LN | 현재 입력에서 지정한 축의 mean과 variance를 사용함. | Training mode와 동일함. |
| GN | 현재 입력에서 group별 mean과 variance를 사용함. | Training mode와 동일함. |
- LN과 GN은 running statistics를 저장하지 않으므로, 같은 입력과 파라미터에 대해 두 mode의 출력이 같음.
- BN도
track_running_stats=False로 설정하면 두 mode 모두 현재 batch의 통계를 사용함. affine=False는 BN의 running statistics를 비활성화하는 설정이 아님: Scale과 bias의 생성 여부만 제어함.eval()은 scale과 bias를 비활성화하거나 gradient 계산을 끄는 기능이 아님.