"LLM 핵심 학습 (1/6) — 기본: 토큰화·임베딩·어텐션·위치 인코딩"

트랜스포머 한 블록의 데이터 흐름을 문자열 → 토큰 → 임베딩 → 어텐션 → 출력 표현까지 손으로 풀어 본다. 이번 편이 끝나면 "왜 \(\sqrt{d_k}\)로 나누는가", "RoPE는 무엇을 회전시키는가", "한국어가 영어보다 토큰이 많은 까닭은 무엇인가"가 한 줄로 설명될 수 있다.


0. 학습 목표

이 편을 마치면 다음을 직접 할 수 있다.

  • BPE 토크나이저가 어휘를 어떻게 학습하고 새 문자열을 어떻게 자르는지 종이 위에서 시뮬레이션한다.
  • 임베딩 행렬의 모양과 초기화, weight tying의 의미를 한 줄로 설명한다.
  • Scaled Dot-Product Attention의 식을 차원 단위로 추적하고 PyTorch 30줄로 재현한다.
  • Multi-Head Attention이 한 헤드와 비교해 무엇이 다른지, 헤드 수가 늘면 어디가 비싸지는지 답한다.
  • Sinusoidal·RoPE·ALiBi 세 위치 인코딩의 식과 외삽 성질을 구분한다.
  • 컨텍스트 확장이 왜 \(O(N^2)\)인지, "Lost in the Middle"이 왜 발생하는지 설명한다.

1. 핵심 요약

  • 트랜스포머의 입력은 문자열이 아니라 정수 ID 시퀀스다. 토크나이저가 문자열 → ID로 자른다.
  • 임베딩은 정수 ID를 \(d\)차원 벡터로 lookup하는 학습 가능한 테이블이다.
  • Self-Attention은 \(\text{softmax}(QK^\top / \sqrt{d_k}) V\) 한 줄이 핵심이고, 멀티헤드는 같은 연산을 여러 부분 공간에서 병렬화한 것이다.
  • 위치 정보는 임베딩에 직접 더하거나(Sinusoidal), Q·K에 회전을 가하거나(RoPE), 어텐션 스코어에 거리 패널티를 더해서(ALiBi) 주입한다.
  • 컨텍스트 길이 \(N\)에 대해 표준 어텐션은 \(O(N^2)\) 비용을 갖고, 긴 컨텍스트에서는 중간 위치 회상이 약해진다.

2. 직관: 트랜스포머 한 블록은 무엇을 하는가

자기지도 학습(self-supervised) LLM은 결국 다음 한 가지 게임을 푼다. 지금까지 본 토큰을 조건으로, 다음에 올 토큰의 확률 분포를 출력하라. 이 게임을 잘 하려면 모델은 "지금까지 본 토큰들 사이의 관계"를 효과적으로 정리해야 한다. RNN은 이 관계를 시간 순서로 누적했지만, 트랜스포머는 위치와 무관하게 토큰끼리 직접 가중치 있는 메시지를 주고받는다(Vaswani et al., 2017). 이 메시지 교환의 정량적 정의가 self-attention이다.

문자열은 그 자체로 모델에 들어가지 못한다. 컴퓨터 안에서 토큰화 단계가 "한국어 자모 → 서브워드 → 정수 ID"로 분해해 주고, 임베딩이 그 정수를 \(d\)차원 좌표로 옮겨 준다. 어텐션 블록은 이 좌표들 사이의 관계를 재가공해 새 좌표를 만든다. 한 트랜스포머 블록 = (어텐션 + 잔차 + 정규화 + MLP + 잔차 + 정규화)의 반복이고, 이 블록을 \(L\)층 쌓으면 한 모델이 된다. 이 편은 그중 가장 앞단 입력 표현 + 어텐션 + 위치 정보만 다룬다. 나머지(LayerNorm, MLP, Residual의 학습 동역학)는 5편 수학 직관에서 정리한다.


3. 토큰화 — 문자열을 정수 시퀀스로 자르는 법

3.1 정의와 표기

토큰화는 함수 \(T: \Sigma^* \to \mathbb{Z}^{*}\)다. 여기서 \(\Sigma\)는 유니코드 문자 집합(혹은 바이트 집합), 출력은 길이 가변의 정수 ID 시퀀스다. 어휘 \(V = \{0, 1, \dots, |V|-1\}\)는 토크나이저 학습 시 고정되고, 이후 모든 모델 가중치(임베딩 행렬 크기, 출력 헤드 크기)는 이 \(|V|\)에 종속된다.

대표 어휘 크기와 토크나이저는 다음과 같다.

  • GPT-2/3: BPE, \(|V| = 50{,}257\).
  • LLaMA 계열: SentencePiece BPE, \(|V| = 32{,}000\) (LLaMA 1·2) / \(|V| = 128{,}000\) (LLaMA 3).
  • Gemma: SentencePiece, \(|V| = 256{,}000\) (다국어 강화 목적).

3.2 Byte-Pair Encoding (BPE) — 학습 알고리즘

BPE는 원래 데이터 압축 알고리즘이고, NMT에 도입한 것이 Sennrich et al., 2016이다. 핵심 아이디어는 가장 자주 등장하는 인접 심볼 쌍을 새 심볼로 병합하는 것을 어휘가 차오를 때까지 반복하는 것이다.

입력: 문자 단위 토큰화된 코퍼스. 예: low, lower, newest, widest
초기 어휘: {l, o, w, e, r, n, s, t, i, d, </w>}
반복:
  1) 인접 토큰 쌍의 빈도를 센다.
  2) 가장 빈번한 쌍 (X, Y)를 골라 새 토큰 XY를 만든다.
  3) 코퍼스 전체에서 (X, Y) → XY로 치환한다.
어휘가 목표 크기에 도달하면 종료.

이 과정의 첫 몇 단계는 다음과 비슷하게 진행한다.

('e','s'): 9   →  병합 'es'
('es','t'): 9 →  병합 'est'
('l','o'): 7  →  병합 'lo'
('lo','w'): 7 →  병합 'low'

학습이 끝나면 병합 규칙의 순서가 곧 토크나이저 자체다. 추론 시에는 같은 규칙을 같은 순서로 적용해 새 문자열을 자른다.

3.3 인코딩: 새 문자열을 자르는 법

추론은 학습된 병합 규칙을 우선순위 큐로 적용하는 그리디 알고리즘이다.

def bpe_encode(text, merges, vocab):
    # merges: List[Tuple[str, str]]  # 학습된 병합 규칙 (학습 순서대로)
    tokens = list(text)  # 문자 단위로 시작
    while True:
        pairs = [(tokens[i], tokens[i+1]) for i in range(len(tokens)-1)]
        # 학습 규칙 중 가장 우선순위 높은 것을 찾는다
        best = None
        best_rank = float('inf')
        for i, p in enumerate(pairs):
            if p in merges and merges.index(p) < best_rank:
                best_rank = merges.index(p)
                best = (i, p)
        if best is None:
            break
        i, (a, b) = best
        tokens = tokens[:i] + [a+b] + tokens[i+2:]
    return [vocab[t] for t in tokens]

실제 구현은 O(N · merges)가 아니라 O(N log N) 정도가 되도록 heapq를 쓰지만, 학습 정신은 위 그대로다. 핵심은 문자열 길이가 같아도, 어휘에 자주 등장하는 패턴은 1토큰으로, 드문 패턴은 여러 서브워드로 분해된다는 점이다.

3.4 WordPiece vs SentencePiece vs Byte-level BPE

알고리즘 학습 기준 공백 처리 대표 모델
BPE (Sennrich 2016) 빈도 최대 쌍 병합 공백 = 단어 경계 보조 토큰 </w> GPT-2
WordPiece (Schuster & Nakajima 2012, BERT) 우도(likelihood) 증가 최대 쌍 병합 공백 = 단어 경계 BERT
SentencePiece (Kudo & Richardson 2018) 유니그램 언어 모델 또는 BPE를 원문에 직접 학습 공백을 별도 토큰 로 보존 LLaMA, T5
Byte-level BPE UTF-8 바이트 시퀀스에 BPE 적용 모든 유니코드 OOV 없음 GPT-2, GPT-3 이후

Byte-level BPE의 장점은 OOV가 절대 발생하지 않는다는 점이다. 어떤 유니코드 입력도 결국 1~4바이트로 분해되고, 그 바이트 256개를 기본 어휘로 깔아 두면 모든 입력이 정수 ID 시퀀스로 인코딩된다. 단점은 영어 외 언어(특히 CJK)의 평균 토큰 길이가 길어진다는 것이다. SentencePiece는 로 공백을 보존하기 때문에 다국어와 코드에 동시에 강하다.

3.5 한국어가 영어보다 토큰이 많은 이유

다국어 BPE 어휘에서는 빈번한 영어 단어("the", "ing", "tion")가 1토큰으로 통합되는 반면, 한국어 어절은 어휘에 들어가지 않아 자모 또는 음절 단위로 떨어지는 경우가 많다. 결과적으로 같은 의미를 표현할 때 한국어가 영어 대비 평균 1.5~2.5배 토큰을 소비한다(GPT-4 토크나이저 기준 실측). 한국어 전용 토크나이저로 학습한 모델(예: Polyglot-ko, KoGPT)은 이 비율이 크게 개선되지만, 영어 코퍼스 능력이 떨어진다는 트레이드오프가 있다.

3.6 다이어그램

diagram-1

3.7 한계와 실패 양상

  • 숫자 처리 취약. "12345"가 "1", "23", "45"로 갈리면 산술이 깨진다. 최근 모델은 "0"~"9" 각각 단일 토큰화하는 digit-level 전처리로 보강한다.
  • 고유명사 부정확성. 학습 어휘에 없는 이름이 자모 단위로 쪼개져 모델 내부에서 일관 식별이 어렵다.
  • 다국어 비용 비대칭. 청구·컨텍스트 한도 추정 시 영어 기준으로만 계산하면 한국어·일본어·CJK 코퍼스에서 실비용이 2배 가까이 차이 난다.

3.8 실습 과제

  1. 코퍼스 ["banana", "ban", "bandit", "anchor"]에 BPE를 5번 병합한 어휘를 손으로 만들어 본다.
  2. tiktoken(pip install tiktoken)으로 enc = tiktoken.get_encoding("o200k_base")를 사용해 같은 문장의 영어·한국어 토큰 수를 비교한다.
  3. "12,345,678"이 GPT-4 토크나이저에서 몇 토큰으로 잘리는지 측정하고, "12345678"과 비교한다.

4. 임베딩 — 정수 ID에서 \(d\)차원 벡터로

4.1 정의

토큰 ID \(t \in \{0, \dots, |V|-1\}\)에 대해 임베딩은

$$ E: t \mapsto E[t, :] \in \mathbb{R}^{d_{\text{model}}} $$

로 lookup된다. 임베딩 행렬 \(E \in \mathbb{R}^{|V| \times d_{\text{model}}}\)는 학습 가능한 파라미터다. 어휘 크기 \(|V| = 32{,}000\), \(d_{\text{model}} = 4096\)이면 임베딩만으로 \(32{,}000 \times 4096 \approx 1.3 \times 10^8\) 파라미터다. 이는 7B 파라미터 모델의 약 1.9%에 해당한다.

4.2 초기화

표준 트랜스포머 학습에서 임베딩 초기값은 \(\mathcal{N}(0, \sigma^2)\), 대표적으로 \(\sigma = 0.02\)를 쓴다. 너무 큰 초기값은 학습 초기 \(\sqrt{d_{\text{model}}}\) 스케일 효과와 결합해 그래디언트 폭주를 부른다(He et al., 2015의 He 초기화 관점에서 이는 입력 분산 제어 문제와 동치다).

4.3 Weight Tying — 입출력 임베딩 공유

언어 모델의 최종 출력은 \(h_T W_{\text{out}}^\top + b\) 꼴로, 여기서 \(W_{\text{out}} \in \mathbb{R}^{|V| \times d}\)이다. Press & Wolf, 2017은 입력 임베딩 \(E\)와 출력 행렬 \(W_{\text{out}}\)을 같은 텐서로 공유하면 파라미터를 절약하면서 perplexity가 감소함을 보였다.

self.embedding = nn.Embedding(vocab_size, d_model)
self.lm_head = nn.Linear(d_model, vocab_size, bias=False)
self.lm_head.weight = self.embedding.weight  # tied

GPT-2, T5는 weight tying을 사용한다. 일부 최신 모델(LLaMA 3 등)은 어휘 크기가 크고 \(W_{\text{out}}\)을 분리해 학습 안정성과 추론 효율을 우선한다. 어떤 결정이든 추론 시 출력 logits 계산은 동일한 \(O(d \cdot |V|)\)이다.

4.4 임베딩 공간의 기하

학습된 임베딩은 좌표적으로 의미를 담는 것이 아니라 다음 토큰 예측에 유용한 좌표를 학습한다. 따라서 "king − man + woman ≈ queen" 같은 word2vec 시대의 정합성은 LLM 임베딩에서는 부분적으로만 관측되며, 토큰 단위가 서브워드라 더 단편적으로 흩어진다. 실제 의미 임베딩이 필요하면 문맥적 임베딩(마지막 hidden state의 평균 또는 [EOS] 토큰의 hidden state)을 쓰는 것이 표준이다.

4.5 실습 과제

  1. nn.Embedding(50257, 1024)의 파라미터 수를 손으로 계산하고, weight tying 시 7B 모델 전체에서 차지하는 비율을 추정한다.
  2. 동일한 문장을 두 번 토큰화한 뒤 embedding lookup 결과의 평균 코사인 유사도가 1.0인지 확인한다(샘플링 없이 lookup 자체는 결정적이어야 한다).

5. Self-Attention — \(\text{softmax}(QK^\top / \sqrt{d_k})V\)

5.1 식 한 줄

길이 \(N\)의 시퀀스에 대해 임베딩 \(X \in \mathbb{R}^{N \times d_{\text{model}}}\)이 주어지면, 학습 가능한 행렬 \(W^Q, W^K, W^V \in \mathbb{R}^{d_{\text{model}} \times d_k}\) (편의상 \(d_v = d_k\))를 통해

$$ Q = X W^Q,\quad K = X W^K,\quad V = X W^V $$

를 만든다. 스케일드 닷프로덕트 어텐션은

$$ \mathrm{Attention}(Q, K, V) = \mathrm{softmax}\!\left( \frac{Q K^\top}{\sqrt{d_k}} \right) V \in \mathbb{R}^{N \times d_v} $$

이다(Vaswani et al., 2017, eq. 1). 이 한 줄이 트랜스포머의 알파이자 오메가다.

5.2 차원 추적

텐서 모양
\(X\) \(N \times d_{\text{model}}\)
\(Q, K, V\) \(N \times d_k\)
\(QK^\top\) \(N \times N\) — 어텐션 스코어
\(\mathrm{softmax}(QK^\top/\sqrt{d_k})\) \(N \times N\) — 가중치 (행 합 1)
출력 \(N \times d_v\)

행렬곱과 softmax 모두 \(N\)에 대해 \(O(N^2)\) 시간·메모리다. 이것이 컨텍스트 확장의 근본 비용이다.

5.3 왜 \(\sqrt{d_k}\)로 나누는가

\(Q, K\)의 성분이 평균 0, 분산 1인 i.i.d.라고 가정하면, \(QK^\top\)의 한 성분 \(\sum_{i=1}^{d_k} Q_{:,i} K_{:,i}\)는 분산 \(d_k\)를 갖는다. softmax는 입력이 커지면 포화되어, 가장 큰 성분에 가중치 1을 몰고 나머지는 0에 가까운 그래디언트를 갖게 만든다. \(\sqrt{d_k}\)로 나누면 분산이 1로 정규화돼 그래디언트가 살아남는다. 이는 작지만 결정적인 디테일로, 트랜스포머가 큰 \(d_k\)에서도 안정적으로 학습되도록 만드는 핵심이다.

5.4 Multi-Head Attention

한 헤드는 한 가지 관계(예: "주어-동사 일치")를 본다. 여러 부분 공간에서 병렬로 어텐션을 수행하면 통사·의미·위치 등 서로 다른 관계를 동시에 학습할 수 있다. \(h\)개의 헤드, 각 헤드 차원 \(d_k = d_{\text{model}}/h\)로 두고,

$$ \mathrm{head}_i = \mathrm{Attention}(X W^Q_i, X W^K_i, X W^V_i) $$ $$ \mathrm{MHA}(X) = \mathrm{Concat}(\mathrm{head}_1, \dots, \mathrm{head}_h) W^O $$

이다. \(W^O \in \mathbb{R}^{d_{\text{model}} \times d_{\text{model}}}\)는 헤드 출력 결합용. 총 파라미터는 한 헤드 짜리 어텐션과 같은 \(4 d_{\text{model}}^2\)이지만, 표현력은 헤드 수만큼 다양해진다(서로 다른 부분 공간에서의 학습 가능).

5.5 인과 마스크 (디코더)

GPT 계열은 미래 토큰을 볼 수 없어야 하므로, 어텐션 스코어 \(QK^\top/\sqrt{d_k}\)에 상삼각 부분을 \(-\infty\)로 채우는 마스크를 더한다. softmax 후 이 위치는 가중치 0이 된다.

점수 행렬 (3×3):                마스크 적용 후:
[ s11 s12 s13 ]                  [ s11 -inf -inf ]
[ s21 s22 s23 ]                  [ s21  s22 -inf ]
[ s31 s32 s33 ]                  [ s31  s32  s33 ]

5.6 PyTorch 미니 구현

아래 30줄로 Multi-Head Attention 한 블록을 재현할 수 있다.

import torch
import torch.nn as nn
import torch.nn.functional as F


class MultiHeadAttention(nn.Module):
    def __init__(self, d_model: int, n_heads: int, causal: bool = True):
        super().__init__()
        assert d_model % n_heads == 0
        self.d_model = d_model
        self.n_heads = n_heads
        self.d_k = d_model // n_heads
        self.causal = causal
        self.qkv = nn.Linear(d_model, 3 * d_model, bias=False)
        self.out = nn.Linear(d_model, d_model, bias=False)

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        B, N, _ = x.shape
        qkv = self.qkv(x)  # (B, N, 3 * d_model)
        q, k, v = qkv.chunk(3, dim=-1)  # 각 (B, N, d_model)
        # (B, N, h, d_k) -> (B, h, N, d_k)
        q = q.view(B, N, self.n_heads, self.d_k).transpose(1, 2)
        k = k.view(B, N, self.n_heads, self.d_k).transpose(1, 2)
        v = v.view(B, N, self.n_heads, self.d_k).transpose(1, 2)
        scores = (q @ k.transpose(-2, -1)) / (self.d_k ** 0.5)  # (B, h, N, N)
        if self.causal:
            mask = torch.triu(torch.ones(N, N, device=x.device), diagonal=1).bool()
            scores = scores.masked_fill(mask, float('-inf'))
        weights = F.softmax(scores, dim=-1)
        out = weights @ v  # (B, h, N, d_k)
        out = out.transpose(1, 2).contiguous().view(B, N, self.d_model)
        return self.out(out)


if __name__ == '__main__':
    mha = MultiHeadAttention(d_model=128, n_heads=8, causal=True)
    x = torch.randn(2, 10, 128)
    y = mha(x)
    print(y.shape)  # torch.Size([2, 10, 128])

이 코드를 실행하면 입력 \((B, N, d_{\text{model}}) = (2, 10, 128)\)이 같은 모양으로 나간다. 마스크된 위치의 가중치가 0인지 weights[0, 0, 0, 5:]를 출력해 확인하라.

5.7 다이어그램

diagram-2

5.8 변형과 사례

  • Grouped-Query Attention (GQA). KV 헤드 수를 Q 헤드 수보다 줄여 KV 캐시 메모리를 절약한다(Ainslie et al., 2023). LLaMA 2 70B는 8개의 KV 헤드를 64개의 Q 헤드가 공유한다.
  • Multi-Query Attention (MQA). KV 헤드를 1개로 극단화한 변형. 추론은 빠르지만 학습 안정성이 떨어진다.
  • FlashAttention (Dao et al., 2022). \(O(N^2)\) 시간은 그대로지만 I/O를 타일링해 메모리 대역폭 병목을 해소. 동일한 출력을 더 적은 메모리 통신으로 만든다.
  • Sliding Window Attention. 모든 토큰이 자기 주변 \(w\)개 토큰만 본다. Mistral 7B의 슬라이딩 윈도우(\(w = 4{,}096\))가 대표적.

5.9 한계와 실패 양상

  • \(O(N^2)\) 비용. 100K 컨텍스트는 1K 대비 어텐션 비용이 1만 배다. FlashAttention과 KV 캐시로 상수 계수를 줄이지만 비용 성장률 자체는 그대로다.
  • Lost in the Middle (Liu et al., 2023). 긴 컨텍스트에서 모델이 시작과 끝 근처 토큰을 잘 회상하고 중간을 흐리게 본다는 현상이 정량적으로 확인됐다. RAG로 중요한 정보를 앞이나 뒤에 배치하는 실무 가이드의 근거.
  • 숫자 토큰화와의 상호작용. 어텐션은 토큰을 단위로 보기 때문에 숫자가 잘못 잘리면 산술 정확도가 떨어진다.

5.10 실습 과제

  1. 위 미니 구현에 attn_mask 인자를 추가해 인과 마스크를 외부에서 주입할 수 있도록 바꿔 본다.
  2. n_heads=1n_heads=8로 같은 입력에 대해 출력을 비교하고, 출력 노름과 weights 분포 차이를 관찰한다.
  3. N=1024에서 scores = q @ k.T 한 텐서의 메모리(FP16 기준)를 계산하고, KV 캐시까지 포함한 메모리를 추정한다.

6. 위치 인코딩 — 순서 정보를 어디로 끼워 넣을 것인가

어텐션은 집합 연산이다. \(Q, K, V\)에 토큰의 위치 정보가 없으면 입력 순서를 뒤바꿔도 출력이 동일하다(set-equivariance). 따라서 위치 신호를 어딘가에 명시적으로 주입해야 한다.

6.1 Sinusoidal Positional Encoding (Vaswani 2017)

위치 \(p\), 차원 \(i\)에 대해

$$ \mathrm{PE}(p, 2i) = \sin\!\left(p / 10000^{2i/d_{\text{model}}}\right), $$ $$ \mathrm{PE}(p, 2i+1) = \cos\!\left(p / 10000^{2i/d_{\text{model}}}\right). $$

이 PE를 토큰 임베딩에 더한다. 장점은 학습 길이를 넘는 위치에서도 같은 공식으로 값을 만들 수 있다는 것(외삽 가능). 단점은 어텐션이 절대 위치만을 보기 때문에 상대 위치 정보를 학습하기 어렵다는 점이다.

6.2 Learned Positional Embedding

GPT-2는 위치 \(p \in \{0, \dots, P_{\max}-1\}\)에 대해 학습 가능한 \(P_{\max} \times d_{\text{model}}\) 행렬을 두고 lookup한다. 학습한 \(P_{\max}\) 이상의 길이로 외삽이 안 된다는 결정적인 단점이 있다.

6.3 Rotary Position Embedding (RoPE)

Su et al., 2021은 위치 정보를 임베딩에 더하지 않고, Q·K를 회전시키는 방식을 제안했다. 짝수 차원 쌍 \((2i, 2i+1)\)을 복소수처럼 묶고 위치 \(p\)와 주파수 \(\theta_i = 10000^{-2i/d}\)에 대해

$$ \begin{bmatrix} q'_{2i} \\ q'_{2i+1} \end{bmatrix} = R(p\theta_i) \begin{bmatrix} q_{2i} \\ q_{2i+1} \end{bmatrix},\quad R(\phi) = \begin{bmatrix} \cos\phi & -\sin\phi \\ \sin\phi & \cos\phi \end{bmatrix} $$

같은 회전을 \(K\)에도 적용한다. 그 결과 \(Q_p^\top K_q\)는 \(p - q\)에 의존하는 상대 위치 함수가 된다. 핵심 성질:

  • 상대 위치를 자연스럽게 표현.
  • 학습 길이를 넘는 외삽이 일정 범위까지 안정.
  • 어텐션 식 외부에 추가 파라미터가 필요 없다.

LLaMA, Qwen, Mistral 등 대다수 최신 오픈 모델이 RoPE를 사용한다. 컨텍스트 확장 시 주파수 base를 늘리는 트릭(예: 10000 → 500000)으로 더 긴 길이까지 외삽한다(NTK-aware scaling, YaRN 등).

def apply_rope(x: torch.Tensor, freqs: torch.Tensor) -> torch.Tensor:
    """x: (B, h, N, d_k) — d_k 짝수. freqs: (N, d_k/2)"""
    x_pair = x.reshape(*x.shape[:-1], -1, 2)
    cos = freqs.cos().unsqueeze(0).unsqueeze(0)  # (1, 1, N, d_k/2)
    sin = freqs.sin().unsqueeze(0).unsqueeze(0)
    x0, x1 = x_pair[..., 0], x_pair[..., 1]
    out0 = x0 * cos - x1 * sin
    out1 = x0 * sin + x1 * cos
    return torch.stack([out0, out1], dim=-1).flatten(-2)

6.4 ALiBi (Press et al., 2022)

ALiBi는 임베딩과 Q/K를 건드리지 않고, 어텐션 스코어에 거리 패널티를 더한다.

$$ \text{scores}_{ij} \leftarrow \text{scores}_{ij} - m \cdot |i - j| $$

\(m\)은 헤드별 음의 기울기 상수다. 학습 길이를 크게 넘어가도 외삽이 매우 잘 된다는 점이 강점이고, MosaicBERT, Falcon 등이 채택했다.

6.5 세 방식 한 줄 비교

방식 어디에 작용 외삽 대표 모델
Sinusoidal 임베딩에 덧셈 가능, 정확도 떨어짐 Transformer 원본
Learned 임베딩에 덧셈 불가 GPT-2
RoPE Q, K에 회전 양호 (base 조정 시 우수) LLaMA, Qwen
ALiBi 스코어에 패널티 매우 우수 Falcon, MosaicBERT

6.6 한계와 실패 양상

  • 모든 위치 인코딩은 학습 길이 분포 밖에서 일정 부분 무너진다. RoPE/ALiBi가 상대적으로 강할 뿐, 무한히 안전하지 않다.
  • 컨텍스트 확장은 위치 인코딩 한 가지만 바꿔서 되지 않는다. KV 캐시 한도, 데이터 분포, 평가 길이 모두 동반 조정이 필요하다.

6.7 실습 과제

  1. d_model=8, N=4에서 Sinusoidal PE 행렬을 직접 계산해 4×8 표로 그려 본다.
  2. apply_rope를 호출해 길이 1024 시퀀스의 Q·K를 회전시키고, 회전 전후의 \(Q_0^\top K_5\)와 \(Q_5^\top K_{10}\)이 (회전 후) 같은 \(p-q\)에 대해 동일한 값을 갖는지 확인한다.
  3. ALiBi의 기울기 \(m\)을 헤드 수 \(h\)에 대해 \(2^{-8/h}, 2^{-16/h}, \dots\)로 두는 표준 권장값을 도출한 뒤, 가까운 토큰에 더 큰 가중치가 가도록 잘 작동하는지 시뮬레이션한다.

7. 종합: 한 블록의 데이터 흐름

다이어그램으로 한 트랜스포머 블록(디코더 변형)을 정리한다.

diagram-3

블록을 \(L\)번 반복한 뒤 최종 hidden state에 unembedding을 곱해 logits을 만든다. logits에 softmax를 씌우면 다음 토큰 확률 분포가 된다. 분포에서 토큰을 어떻게 뽑을지는 3편 디코딩 챕터의 주제다.


8. 컨텍스트 확장에 대한 짧은 메모

긴 컨텍스트는 다음 세 가지를 동시에 비싸게 한다.

  1. 어텐션 행렬 \(O(N^2)\). FlashAttention/슬라이딩 윈도우로 상수 계수만 줄어든다.
  2. KV 캐시 메모리 \(O(N \cdot L \cdot 2 \cdot d_{\text{kv}})\). 100K 컨텍스트 + 7B 모델이면 GB 단위로 늘어난다. GQA가 \(d_{\text{kv}}\)를 줄여 완화.
  3. 품질. Lost in the Middle, 위치 인코딩 외삽 한계가 결합되어 중간 회상력이 떨어진다.

따라서 "1M 컨텍스트면 RAG가 불필요한가"에 대한 답은 불필요하지 않다이다. (a) 비용, (b) 중간 회상, (c) 신선도(stale data) 세 가지 이유로 RAG는 여전히 가성비가 높다(이 부분은 4편 고급에서 다시).


9. 정리된 결론 — 보지 않고 답해 보라

이 글이 답한 핵심 질문 5개. 먼저 답을 가린 채로 한 줄로 답해 본 뒤 정답과 비교.

Q1. BPE의 학습 절차를 4줄로 묘사하면?

정답 (1) 모든 단어를 문자 단위로 분해, (2) 가장 빈번한 문자 쌍을 찾아 새 토큰으로 병합, (3) 어휘 크기 도달까지 반복, (4) 학습된 머지 규칙을 추론 시 적용. 등장 빈도 + 결합 = 의미적으로 유의미한 subword. OOV 문제를 통계적으로 해결 (Sennrich et al. 2016). (본문 §3 토큰화)

Q2. 어휘 크기 \(|V|\)와 \(d_{\text{model}}\)이 임베딩 파라미터 수에 어떻게 연결되는가?

정답 임베딩 행렬 = \(|V| \times d_{\text{model}}\). 예: \(|V| = 50{,}000\), \(d_{\text{model}} = 4096\) → 약 2억 파라미터 — 모델 총 파라미터의 10~30%. 출력 unembed 행렬도 같은 크기 (보통 tied). 어휘를 늘리면 파라미터 + KV 메모리 + 학습 시간 이 모두 증가. (본문 §4 임베딩)

Q3. Attention의 \(\sqrt{d_k}\) 스케일을 빼면 무엇이 일어나나?

정답 \(Q K^{\top}\) 값의 분산이 \(d_k\)에 비례해 커져 softmax가 극단적 one-hot 에 가까워짐 → 그래디언트 폭락 (softmax saturation). softmax의 도함수가 큰 입력값에서 거의 0. 학습 초기에 손실이 정체. \(d_k\)가 클수록 영향 크므로 스케일링 필수. (본문 §5 attention)

Q4. Multi-Head Attention에서 헤드 수 \(h\)가 늘면 어디가 비싸지나? 파라미터·KV 메모리·둘 다?

정답 파라미터 총량 불변, KV 메모리 불변. 텐서 reshape 비용만 증가. \(d_{\text{model}}\)를 \(h\)개 헤드로 나눠서 사용 — 헤드당 \(d_k = d_{\text{model}}/h\). 총 가중치 양은 같고, 다양한 관계를 병렬 학습하는 표현력 증가가 본질. (본문 §5)

Q5. RoPE와 ALiBi는 어디에 물리적으로 끼어드나? "Lost in the Middle"이 왜 일어나나?

정답 RoPE: Q·K에 회전 변환을 적용 시점에 곱함. ALiBi: attention score 행렬에 거리 기반 bias를 더함. Lost in the Middle: attention이 시퀀스 중간 위치의 정보를 자주 누락 — 학습 분포가 시작·끝에 편향됨 (Liu et al. 2024). 두 위치 인코딩 모두 extrapolation (학습보다 긴 컨텍스트) 에서 다른 trade-off. RoPE는 길이 일반화 강함, ALiBi는 학습 외 길이에서도 부드럽게 작동. (본문 §6 위치 인코딩, §8 컨텍스트 확장)


5개를 한 줄로 답할 수 있었다면 트랜스포머 한 블록의 내부 흐름이 자리잡은 것이다. 막힌 질문은 본문 해당 챕터로 돌아가 재독.


10. 추가 학습

  • Vaswani et al., 2017, Attention Is All You Need. arXiv:1706.03762. 트랜스포머 원논문, 5.1절(Position-wise Feed-Forward)·5.3절(Positional Encoding) 직접 정독 권장.
  • Sennrich et al., 2016, Neural Machine Translation of Rare Words with Subword Units. arXiv:1508.07909. BPE 도입.
  • Kudo & Richardson, 2018, SentencePiece. arXiv:1808.06226.
  • Su et al., 2021, RoFormer: Enhanced Transformer with Rotary Position Embedding. arXiv:2104.09864. RoPE 원논문, 식 (15)·(16) 직접 추적.
  • Press et al., 2022, Train Short, Test Long: ALiBi. arXiv:2108.12409.
  • Ainslie et al., 2023, GQA. arXiv:2305.13245.
  • Dao et al., 2022, FlashAttention. arXiv:2205.14135.
  • Liu et al., 2023, Lost in the Middle. arXiv:2307.03172.
  • Karpathy, nanoGPT 저장소: github.com/karpathy/nanoGPT. 250줄 GPT 구현. 이 편의 모든 식이 거기 한 파일로 들어 있다.

이 글은 LLM 핵심 학습 시리즈의 1/6 편이다. 다음 편(2/6)은 파인튜닝의 LoRA·QLoRA·증류로 이어진다.

시리즈 전체 안내: 시리즈 목차

댓글

이 블로그의 인기 게시물

"LLM 핵심 학습 (2/6) — 파인튜닝: LoRA·QLoRA·증류·Adapter"

"ML 기초 학습 (1/9) — 머신러닝과 sklearn: 학습의 좌표계"