Machine Learning

Normalization and Standardization

아르카눔 2025. 4. 16. 13:12

여기서는 머신러닝이나 딥러닝 뿐만 아니라 통계학에서도 많이 거론되는 정규화에 대해서 다루고자 한다.

 

우선 Normalization과 Standardization의 차이를 구분하고,

 

Batch Norm, Group Norm, Layer Norm, Instance Norm, 그리고 최근 LLM에서 많이 쓰는 RMS Norm까지 정리한다.

 

그리고 PyTorch 코드도 간단하게 알아본다. 

 

Normalization과 Standardization

 

Normalization

 

 min-max scaling이라고 보면 된다.

 

데이터 $x = \left[ x_1, ..., x_n \right] $ 이라고 하자.

 

정규화된 데이터 $x'$는 다음과 같다.

 

$x' = \frac{ x- x_{min} }{ x_{max} - x_{min} }$ 

 

Standardization

 

$x' = \frac{ x- \mu }{  \sigma  }$ 

$\mu$는 $x$의 mean (실무에서는 당연히 estimated mena)이고 $\sigma$는 variance (estimated variance)다. 

통계학에서 정규분포를 따르는 데이터를 변형해서 Standard Normal distribution으로 변환할 때 만이 보던 바로 그 식이다.

 

 

Normalization Methods

Batch Norm, Layer Norm, Instance Norm, Group Norm, RMSNorm을 설명한다. 

 

Group Normalization 논문 (링크)에서 나온 아래 그림이 아마 제일 유명한 레퍼런스일텐데 나도 그걸 사용해서 설명한다.

 

그리고 블로그 (링크)에서 찾은 트위터에서 차원으로 잘 설명된 자료 (링크)가 있길래 같이 비교한다.

 

기본적으로 H x W x C 차원의 이미지 데이터를 가정하고 Batch의 차원은 N 이라고 생각하면 이해할 수 있다. 

 

 

 

Batch Norm:

C개의 채널 마다 H, W, N을 모두 모아서 mean, variance를 구한다.

 

Layer Norm:

N개의 Batch 마다의 H, W, C를 모두 모아서 mean, variance를 구한다.

 

Instance Norm:

C 개의 채널마다, N 배치 마다 H, W를 모아서 mean, variance를 구한다. 따라서 C x N개의 mean, variance를 구해야 한다. 

 

Group Norm:

N 개의 배치마다, 그룹으로 묶인 몇개의 C 마다 H, W를 모아서 mean, variance를 구한다. 

 

RMS Norm:

Layer Norm과 같은 차원의 동작을 수행하지만 정규화의 계산식이 다르다.

 

$\bar{x_i} = x_i / RMS(x)$. 이때 $RMS(x) = \sqrt{ \frac{1}{n} \sum_i^n x_i^2 }$

 

기존의 Layer Norm은 mean과 variance를 모두 계산한다.

 

이는 location shift와 scaling을 모두 수행한다는 의미인데,

mean을 0으로 변형하면 이 식이 바로 RMS Norm이다.

즉 location shift를 제외하고 scaling만 사용한다. 

 

 

 

Batch Norm은 컴퓨터 비전 분야에서 가장 표준적인 방법이다.

 

Layer Norm와 RMS Norm을 자연어처리 분야에서 많이 쓰이는 방법이다. 

 

NLP에서 입력 차원은 N x d_model 이므로 여기에 Layer Norm을 적용하면 토큰의 임베딩에 정규화를 수행하는 것과 같다.

 

만약 NLP에 Batch Norm을 한다면 모든 token들의 임베딩 벡터의 $i$번째 엘레멘트끼리의 정규화를 수행하는 작업이 된다.

 

 

PyTorch 코드 구현

 

PyTorch Classes

 

PyTorch에서는 아래의 명령어들로 활용 가능하다.

 

nn.BatchNorm2d

nn.GroupNorm

nn.InstanceNorm2d

nn.LayerNorm

nn.RMSNorm

 

만약 직접 구현한다면 torch.mean과 torch.var를 dimension을 신경써서 구하면 된다.

 

Batch Norm에 대한 PyTorch 코드 예제다.

 

데이터 생성 

import torch
import torch.nn as nn

# N x C x H x W
x = torch.randn(4, 3, 224, 224)

x.shape
>> torch.Size([4, 3, 224, 224])

x[0]
>> 
tensor([[[-3.7272e-04,  6.1750e-01,  1.3545e+00,  ...,  3.9810e-01,
           1.1899e+00,  1.1262e+00],
         [ 1.1369e+00,  1.0826e+00, -1.1815e+00,  ..., -6.3049e-01,
          -3.9884e-01, -1.1682e+00],
...
          -1.6697e-01,  1.1093e+00],
         [-7.8097e-01,  1.8390e-02,  5.5789e-01,  ..., -1.0922e-01,
          -1.7502e+00,  7.4393e-01],
         [ 7.7623e-01, -3.6990e-01,  1.7729e+00,  ...,  5.1041e-01,
           9.0382e-01, -1.3995e+00]]])

 

 

nn.BatchNorm2d 사용

 

# Batch Norm

bn = nn.BatchNorm2d(3)
# bn(x) = (x - mean) / sqrt(var + eps)
# mean = 1/N * sum(x)
# var = 1/N * sum((x - mean)^2)
# eps = 1e-5
# bn(x) = (x - mean) / sqrt(var + eps)

x_bn = bn(x)
x_bn.shape

>> torch.Size([4, 3, 224, 224])

x_bn[0]  

>> tensor([[[-2.9264e-03,  6.1561e-01,  1.3534e+00,  ...,  3.9597e-01,
           1.1886e+00,  1.1249e+00],
         [ 1.1356e+00,  1.0812e+00, -1.1853e+00,  ..., -6.3373e-01,
          -4.0183e-01, -1.1720e+00],
         [ 7.7980e-02, -7.6128e-01,  1.3583e+00,  ..., -2.6688e-01,
           6.0352e-02, -1.2925e+00],
         ...,
...
          -1.6306e-01,  1.1129e+00],
         [-7.7687e-01,  2.2250e-02,  5.6159e-01,  ..., -1.0532e-01,
          -1.7458e+00,  7.4758e-01],
         [ 7.7987e-01, -3.6593e-01,  1.7763e+00,  ...,  5.1412e-01,
           9.0742e-01, -1.3953e+00]]], grad_fn=<SelectBackward0>)

 

Batch Norm 직접 구현 by torch.mean과 torch.var

 

# N x C x H x W 이므로 C를 제외한 모든 차원에 대해서 계산한다
# sum over N, H, W 
bn_mean = torch.mean(x, dim=(0, 2, 3), keepdim=True)
bn_var = torch.var(x, dim=(0, 2, 3), keepdim=True)
eps = 1e-5
x_bn_manual = (x - bn_mean) / torch.sqrt(bn_var + eps)
x_bn_manual.shape

>> torch.Size([4, 3, 224, 224])

x_bn_manual[0]

>> tensor([[[-2.9264e-03,  6.1561e-01,  1.3534e+00,  ...,  3.9597e-01,
           1.1886e+00,  1.1249e+00],
         [ 1.1356e+00,  1.0812e+00, -1.1853e+00,  ..., -6.3372e-01,
          -4.0183e-01, -1.1720e+00],
         [ 7.7980e-02, -7.6128e-01,  1.3583e+00,  ..., -2.6688e-01,
           6.0352e-02, -1.2925e+00],
         ...,
...
          -1.6306e-01,  1.1129e+00],
         [-7.7687e-01,  2.2250e-02,  5.6159e-01,  ..., -1.0532e-01,
          -1.7458e+00,  7.4758e-01],
         [ 7.7987e-01, -3.6593e-01,  1.7763e+00,  ...,  5.1412e-01,
           9.0742e-01, -1.3952e+00]]])

 

 

두 코드의 결과가 동일함을 알 수 있다. 

 

N x C x H x W 이므로 C를 제외한 모든 차원에 대해서 평균과 분산을 계산한다

그리고 위 코드를 보면 중간에 eps가 있는데 variance가 0이 되는걸 방지하기 위해서 집어넣는 작은 상수다.

 

그리고 torch.mean과 torch.var에 keep_dim = True 옵션을 활성화했는데 이는 False로 설정하면 torch tensor의 차원이 squeeze 되어 에러가 발생할 수 있기 때문이다.

 

물론 broadcating이 되어서 자동으로 계산될 수 있지만 명시적으로 차원을 표시하는게 더 낫기 때문이다. 

 

 

 

 

 

References:

https://stats.stackexchange.com/questions/10289/whats-the-difference-between-normalization-and-standardization

https://sonsnotation.blogspot.com/2020/11/8-normalization.html

https://dongsarchive.tistory.com/74

https://hxngiee.github.io/2021-05-07-PositionalNormalization/

https://velog.io/@harms/Batch-Norm-%EC%A0%95%EB%B3%B5%ED%95%98%EA%B8%B0

https://pytorch.org/docs/stable/generated/torch.mean.html

https://pytorch.org/docs/stable/generated/torch.var.html

https://pytorch.org/docs/stable/nn.html#normalization-layers