본문 바로가기
AI란 무엇인가/AI 기본

Positional Encoding&Layer Normalization

by brainstormingai 2026. 4. 4.

 

 

 
AI Architecture Series · Episode 06

Positional Encoding & Layer Normalization

Transformer가 "순서"를 인식하는 수학적 트릭, 그리고 깊은 신경망을 안정시키는 정규화의 비밀. 사인·코사인 파동에서 평균·분산 계산까지 — 공식을 코드와 시각화로 완전히 분해한다.

2025 · 읽기 약 16분 · #PE #LayerNorm #Transformer 수학
01 Part A · PE
 

왜 위치 정보가
필요한가

Self-Attention은 모든 토큰을 동시에(병렬로) 처리한다. 덕분에 빠르지만, 치명적인 문제가 생긴다 — 입력의 순서를 전혀 모른다는 것이다.

"나는 AI를 공부한다"와 "AI를 나는 공부한다"는 Attention 연산에서 동일하게 처리된다. 단어 집합(bag of words)과 다를 바 없다. 언어는 순서가 의미이므로, 이는 치명적이다.

PE 없을 때 — 순서 정보 소실 나는 AI를 공부 한다 ⇅ 순서 바꿔도... AI를 나는 한다 공부 Attention 결과 동일 ⚠ 순서 = 의미, 정보 손실 치명적 PE 추가 후 — 위치 고유 벡터 주입 나는 PE(0) AI를 PE(1) 공부 PE(2) 한다 PE(3) + + + + 각 위치마다 고유한 벡터 주입 ✓ 순서 바꾸면 다른 벡터 → 위치 구별 가능 PE 벡터 패턴:
Fig.1 — PE 없을 때의 순서 소실 문제(좌)와 PE 주입으로 위치 고유성 부여(우)

§
02 Sin · Cos 공식
 

사인·코사인이 만드는
위치의 지문

원논문 "Attention Is All You Need"가 제안한 PE 공식은 우아하게 단순하다.

// Positional Encoding 공식
PE(pos, 2i) = sin( pos / 100002i/d_model )
PE(pos, 2i+1) = cos( pos / 100002i/d_model )
pos
시퀀스에서 토큰의 위치 (0, 1, 2, ... seq_len-1)
i
임베딩 차원의 인덱스 (0, 1, 2, ... d_model/2-1)
d_model
임베딩 벡터의 총 차원 수 (GPT-3: 12288, BERT-base: 768)
10000
파장 스케일 상수 — 이 값으로 저주파~고주파 다양한 파동 생성

이 공식은 각 위치(pos)에 대해 d_model 차원의 벡터를 생성한다. 짝수 차원(0, 2, 4...)에는 sin, 홀수 차원(1, 3, 5...)에는 cos을 배치한다. 핵심은 차원마다 서로 다른 주파수를 사용한다는 것이다 — i가 커질수록 파장이 길어진다(주파수가 낮아진다).

0 +1 -1 pos → sin(pos/10000^0) — i=0, 고주파 sin(pos/10000^0.5) — i=64 cos(pos/10000^0) — i=0, 고주파 cos(pos/10000^0.5) — i=64 pos=18
Fig.2 — 위치(pos)에 따른 sin/cos 파형 (인터랙티브: 아래 슬라이더로 조절)
// Positional Encoding 파형 탐색기
슬라이더를 움직여 위치(pos)와 차원 인덱스(i)를 변경하세요
위치 pos: 10 차원 i: 0
 

§
03 PE 히트맵 시각화
 

PE 행렬을
히트맵으로 보다

실제 PE 행렬 [seq_len × d_model]을 2D 히트맵으로 시각화하면 그 구조가 아름답게 드러난다. 가로축(x)은 임베딩 차원, 세로축(y)은 시퀀스 위치다.

// Positional Encoding Matrix Heatmap
d_model 크기를 변경해 PE 패턴이 어떻게 달라지는지 확인하세요
d_model: 64 seq_len: 30
← 고주파 (차원 0~)                                              저주파 (차원 d_model~) →

§
04 4가지 핵심 성질
 

sin·cos를 선택한
4가지 이유

01

유계성 (Boundedness) — 값이 항상 [-1, 1] 범위

sin과 cos은 항상 -1에서 1 사이값을 가진다. 이는 임베딩 벡터와 더할 때 값의 폭발 없이 안정적인 덧셈이 가능하다. 랜덤 초기화나 지수 함수를 쓰면 값이 폭발하거나 소멸할 수 있다.

02

결정론적 고유성 — 모든 위치가 유일한 패턴

어떤 두 위치 pos₁ ≠ pos₂에 대해서도, 생성되는 PE 벡터는 다르다. 수백 개의 차원(sin·cos 쌍)이 복합적으로 조합되므로 충돌(collision) 없이 각 위치를 구분한다. 동시에 파라미터 없이 공식으로만 생성되므로 추가 학습 비용이 없다.

03

상대 위치 표현 — 선형 변환으로 오프셋 표현 가능

sin·cos의 삼각함수 덧셈 공식: sin(α+β) = sin(α)cos(β) + cos(α)sin(β). 이 성질 덕분에 PE(pos+k)를 PE(pos)의 선형 변환으로 표현할 수 있다. Attention이 상대적 위치 관계를 학습하는 데 유리하다.

04

길이 외삽 (Extrapolation) — 훈련보다 긴 시퀀스도 처리

공식 기반이므로 훈련 중 보지 못한 위치(예: 훈련 max=512인데 추론 시 pos=600)도 수식으로 계산 가능하다. 물론 성능은 저하될 수 있지만, 학습형 PE는 아예 처리 불가다. 이것이 RoPE(Rotary PE)가 등장한 배경이기도 하다.

삼각함수 덧셈 공식 → 상대 위치 표현의 수학적 근거 sin(pos + k) = sin(pos)·cos(k) + cos(pos)·sin(k) cos(pos + k) = cos(pos)·cos(k) - sin(pos)·sin(k) ↑ PE(pos+k)는 PE(pos)와 고정 상수 k에만 의존하는 선형 변환으로 표현된다 즉, Attention이 어떤 위치 쌍이든 "몇 칸 차이나는가"를 벡터 연산으로 일반화해 학습 가능 → RoPE(Rotary Positional Encoding)는 이 원리를 회전 행렬로 발전시켜 GPT-NeoX, LLaMA에서 채택
Fig.3 — 삼각함수 덧셈 공식이 PE에서 상대 위치 표현을 가능하게 하는 수학적 원리

§
05 학습형 vs 고정형 PE
 

고정 PE vs
학습형 PE 비교

구분 고정형 (Sinusoidal PE) 학습형 (Learned PE) RoPE / ALiBi
원리 sin/cos 공식으로 결정론적 생성 위치 임베딩 파라미터를 데이터에서 학습 회전 행렬 / 어텐션 편향으로 상대 위치 인코딩
파라미터 0개 추가 없음 max_len × d_model개 추가 최소 또는 0개
외삽성 이론적 가능 (성능 저하) 불가 훈련 길이 초과 시 오류 우수 특히 ALiBi
채택 모델 원본 Transformer, BERT(초기) BERT, GPT-2, GPT-3 LLaMA, Mistral, Falcon (RoPE)
성능 실용적으로 학습형과 유사 특정 태스크에서 약간 우수 긴 컨텍스트에서 최우수

§
06 Part B · Layer Norm
 

왜 정규화가
필요한가

깊은 신경망(예: 96층 GPT-3)을 학습할 때 나타나는 두 가지 고질적 문제가 있다. 기울기 폭발(Gradient Explosion)기울기 소실(Gradient Vanishing)이다.

정규화 없을 때 — 내부 공변량 이동 Layer 1 Layer 2 Layer 3 Layer 4 기울기 폭발! ∞ 기울기 소실! ≈0 ≈0 레이어가 깊을수록 값의 분포가 불안정 → 학습 실패 Layer Norm 후 — 안정적 분포 유지 L1 → L2 → L3 → L4 → L96 LN LN LN LN 각 층 출력 분포 (μ≈0, σ≈1 유지): 모든 층에서 μ≈0, σ≈1 → 안정적 학습 ✓
Fig.4 — 정규화 없을 때 기울기 폭발·소실(좌) vs Layer Norm으로 안정적 분포 유지(우)

§
07 μ · σ · γ · β 공식
 

Layer Normalization의
수학적 원리

// Layer Normalization 공식 (4단계)
① 평균: μ = (1/H) · Σᵢ xᵢ
② 분산: σ² = (1/H) · Σᵢ (xᵢ - μ
③ 정규화: x̂ᵢ = (xᵢ - μ) / √(σ² + ε)
④ 스케일: LN(x)ᵢ = γ · x̂ᵢ + β
H
레이어 내 뉴런(피처) 수 — d_model (예: 768, 4096)
μ
해당 레이어 입력 벡터의 평균 — 스칼라값
σ²
해당 레이어 입력 벡터의 분산 — 스칼라값
ε
수치 안정성을 위한 작은 상수 (보통 1e-5 또는 1e-6)
γ, β
학습 가능한 스케일·이동 파라미터 (각 H개) — 모델이 최적 분포 스스로 결정

Layer Norm의 핵심은 같은 레이어 내 모든 피처에 걸쳐 정규화한다는 것이다. 평균을 빼고(centering), 분산으로 나눠(scaling) 분포를 표준화한다. 이후 학습 파라미터 γ(감마)와 β(베타)로 모델이 원하는 스케일과 이동을 다시 자유롭게 결정하게 한다.

① 원본 입력 x μ≠0 분포 치우침 σ≠1 -μ ② 평균 제거 (x-μ) μ=0 ✓ 중심 이동 σ≠1 ÷σ ③ 표준정규화 x̂ μ=0 ✓ σ=1 ✓ 표준 정규화 γ,β ④ γ·x̂ + β (학습됨) 모델이 최적 분포 스스로 결정 ✓ γ=스케일 β=이동
Fig.5 — Layer Normalization 4단계: 원본 → 평균 제거 → 분산 정규화 → γ·β 재조정

§
08 LN 인터랙티브 시뮬레이터
 

Layer Norm을
직접 체험하다

입력 벡터의 분포를 직접 조절하고, Layer Normalization이 어떻게 표준화하는지 실시간으로 확인하세요.

// Layer Normalization 실시간 시뮬레이터
파라미터를 바꿔 정규화 전후 분포 변화를 확인하세요
입력 평균 μ: 2.5 입력 분산 σ: 2.0
γ (스케일): 1.0 β (이동): 0.0
BEFORE LAYER NORM
 
AFTER LAYER NORM (γ·x̂+β)
 

§
09 Batch Norm vs Layer Norm
 

Batch Norm vs
Layer Norm — 차원의 차이

Layer Norm 이전에는 Batch Normalization(BN)이 표준이었다. 두 방식의 핵심 차이는 "무엇에 걸쳐 정규화하느냐"다.

Batch Normalization 배치(세로) 방향으로 정규화 Batch↓ Feature → B₁ B₂ B₃ B₄ μ,σ 배치 방향 배치 내 같은 피처끼리 정규화 ⚠ 배치 크기에 의존 → 소배치·RNN에 불리 Layer Normalization 피처(가로) 방향으로 정규화 B₁ B₂ B₃ B₄ μ, σ — 피처 방향 각 샘플(토큰) 내 모든 피처를 정규화 ✓ 배치 크기 무관 → Transformer에 최적
Fig.6 — Batch Norm(열 방향 정규화)과 Layer Norm(행 방향 정규화)의 결정적 차이
구분 Batch Normalization Layer Normalization
정규화 축 배치 차원 (N) + 공간 차원 피처 차원 (H) — 레이어 내부
통계량 계산 같은 피처의 배치 전체 평균/분산 같은 샘플의 모든 피처 평균/분산
배치 의존성 강함 배치 크기가 작으면 불안정 없음 샘플 1개도 처리 가능
추론 시 동작 이동평균 통계량 사용 (학습-추론 불일치) 동일한 방식 (학습=추론)
RNN/Transformer 적합성 나쁨 시퀀스 길이 변화에 취약 최적 토큰별 독립 정규화
주요 사용처 CNN 이미지 처리 (ResNet, VGG) Transformer, RNN, BERT, GPT

§
10 PyTorch 구현
 

코드로 완성하는
PE & LN 구현

 
positional_encoding_and_layer_norm.py
import torch
import torch.nn as nn
import math

# ════════════════════════════════════════════════════
#  Part A: Positional Encoding (고정형 Sinusoidal)
#  PE(pos, 2i)   = sin(pos / 10000^(2i/d_model))
#  PE(pos, 2i+1) = cos(pos / 10000^(2i/d_model))
# ════════════════════════════════════════════════════
class SinusoidalPositionalEncoding(nn.Module):
    def __init__(self, d_model: int, max_len: int = 5000, dropout: float = 0.1):
        super().__init__()
        self.dropout = nn.Dropout(dropout)

        # PE 행렬 사전 계산: [max_len, d_model]
        pe = torch.zeros(max_len, d_model)

        # pos: [max_len, 1] — 위치 벡터
        pos = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)

        # 분모 계산: 10000^(2i/d_model) → 로그 공간에서 안정적 계산
        div_term = torch.exp(
            torch.arange(0, d_model, 2, dtype=torch.float) *
            -(math.log(10000.0) / d_model)
        )

        # 짝수 차원 = sin, 홀수 차원 = cos
        pe[:, 0::2] = torch.sin(pos * div_term)  # [max_len, d_model/2]
        pe[:, 1::2] = torch.cos(pos * div_term)  # [max_len, d_model/2]

        # buffer로 등록 (학습 파라미터 X, 저장은 O)
        self.register_buffer('pe', pe.unsqueeze(0))  # [1, max_len, d_model]

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        # x: [batch, seq_len, d_model]
        x = x + self.pe[:, :x.size(1), :]  # 임베딩에 PE 더하기
        return self.dropout(x)


# ════════════════════════════════════════════════════
#  Part B: Layer Normalization (수식 직접 구현)
#  LN(x) = γ · (x - μ) / √(σ² + ε) + β
# ════════════════════════════════════════════════════
class LayerNorm(nn.Module):
    def __init__(self, d_model: int, eps: float = 1e-6):
        super().__init__()
        self.eps = eps

        # γ(감마): 스케일 파라미터 — 초기값 1
        self.gamma = nn.Parameter(torch.ones(d_model))
        # β(베타): 이동 파라미터 — 초기값 0
        self.beta  = nn.Parameter(torch.zeros(d_model))

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        # x: [batch, seq_len, d_model]
        # 마지막 차원(d_model)에 대해 평균·분산 계산 — Layer Norm의 핵심!
        mean = x.mean(dim=-1, keepdim=True)          # μ: [B, S, 1]
        var  = x.var(dim=-1, keepdim=True, unbiased=False)  # σ²: [B, S, 1]

        # 정규화: x̂ = (x - μ) / √(σ² + ε)
        x_hat = (x - mean) / torch.sqrt(var + self.eps)

        # 스케일·이동: γ·x̂ + β
        return self.gamma * x_hat + self.beta


# ════════════════════════════════════════════════════
#  실전 Transformer Block에서의 사용 패턴
# ════════════════════════════════════════════════════
class TransformerBlock(nn.Module):
    """Pre-Norm 방식 (최신 LLM 표준: LLaMA, GPT-4)"""
    def __init__(self, d_model=512, n_heads=8, d_ffn=2048, dropout=0.1):
        super().__init__()
        self.norm1 = LayerNorm(d_model)  # Attention 전 정규화
        self.norm2 = LayerNorm(d_model)  # FFN 전 정규화
        self.attn  = nn.MultiheadAttention(d_model, n_heads, dropout=dropout, batch_first=True)
        self.ffn   = nn.Sequential(
            nn.Linear(d_model, d_ffn),
            nn.GELU(),                  # 최신 모델은 ReLU 대신 GELU/SwiGLU
            nn.Linear(d_ffn, d_model),
            nn.Dropout(dropout)
        )

    def forward(self, x):
        # Pre-Norm: LayerNorm → SubLayer → 잔차 연결
        # (원본 Post-Norm과 다름: 원본은 SubLayer → Add → LayerNorm)
        attn_out, _ = self.attn(self.norm1(x), self.norm1(x), self.norm1(x))
        x = x + attn_out                      # 잔차 연결
        x = x + self.ffn(self.norm2(x))       # 잔차 연결
        return x


# ════ 전체 파이프라인 사용 예시 ════
d_model, n_heads, seq_len, batch = 512, 8, 64, 4

# 1. 토큰 임베딩
embedding = nn.Embedding(50000, d_model)
token_ids  = torch.randint(0, 50000, (batch, seq_len))
x = embedding(token_ids)          # [4, 64, 512]

# 2. Positional Encoding 추가
pe  = SinusoidalPositionalEncoding(d_model)
x   = pe(x)                      # [4, 64, 512] — 순서 정보 주입

# 3. Transformer 블록 통과 (LayerNorm 내장)
block = TransformerBlock(d_model, n_heads)
out   = block(x)                  # [4, 64, 512]

print(f"PE 포함 입력:  {x.shape}")    # torch.Size([4, 64, 512])
print(f"블록 출력:     {out.shape}")  # torch.Size([4, 64, 512])
print(f"LN 파라미터:  γ={block.norm1.gamma.shape}, β={block.norm1.beta.shape}")
# LN 파라미터: γ=torch.Size([512]), β=torch.Size([512])

# torch.nn.LayerNorm으로도 동일하게 사용 가능:
ln = nn.LayerNorm(d_model, eps=1e-6)   # 내부적으로 동일한 수식
// 핵심 정리 · Series 06

수학이 만드는
AI의 질서

Positional Encoding은 병렬 처리의 단점(순서 무지)을 sin·cos의 수학적 파동으로 극복한다. 주파수가 다른 수백 개의 파동이 조합되어 각 위치마다 세상에 하나뿐인 "지문"을 만든다.

Layer Normalization은 수십~수백 층을 쌓아도 기울기가 소멸하거나 폭발하지 않도록 각 레이어의 출력을 안정화한다. 평균을 0으로, 분산을 1로 정규화하되 γ·β로 모델이 원하는 분포를 다시 찾을 자유를 준다.

이 두 기술이 없었다면 GPT-3의 96층, LLaMA의 80층은 존재할 수 없었다. 화려한 어텐션 뒤에서 조용히 질서를 지키는 수학의 힘이다.

#PositionalEncoding #LayerNormalization #Transformer수학 #SinusoidalPE #RoPE #BatchNorm #딥러닝수학 #GPT #LLaMA #PyTorch #AI아키텍처 #개발자블로그

가치 보강: 2026년 5월 23일 기준

이 글은 독자가 바로 적용할 수 있는 기준을 더하기 위해 2026년 5월 23일 기준으로 보강했습니다. 단순 정보 나열보다 실제 예시, 확인 순서, 관련 글 연결을 함께 보는 것이 블로그 글의 가치를 높입니다.

실전 적용 예시

상황 어떻게 보면 좋은가
처음 읽을 때 글의 결론과 적용 대상을 먼저 확인합니다.
실제로 쓸 때 내 상황에 맞는 예시만 골라 적용하고, 숫자나 정책은 원문을 확인합니다.
다음 행동 관련 글을 이어 읽어 주제 전체 흐름을 잡습니다.

읽고 바로 확인할 것

  • 내 상황에 적용 가능한 글인지 확인했는가?
  • 날짜, 정책, 요금, 게임 정보처럼 바뀌는 내용은 다시 확인했는가?
  • 관련 글을 함께 읽어 맥락을 보완했는가?
  • 글의 예시를 그대로 복사하지 않고 내 상황에 맞게 바꿨는가?

추가 참고자료: G, h

반응형