[Transformer 원리] Day 1: 토큰화와 임베딩 - 텍스트를 벡터로 바꾸기
이 글은 AI(Claude)의 도움을 받아 작성하고, 작성자가 검토·편집했습니다.
서론: LLM의 안을 들여다보기
ChatGPT 같은 LLM은 마법처럼 보이지만, 내부는 명확한 수학 연산의 연속이다. 그 핵심 구조가 2017년 “Attention Is All You Need”에서 나온 Transformer다. 이 시리즈는 토큰화부터 추론 최적화까지, Transformer가 텍스트를 처리하는 전 과정을 5일에 걸쳐 따라간다. 첫날은 모든 것의 출발점 — 텍스트를 모델이 다룰 수 있는 숫자로 바꾸는 단계다.
1. 왜 토큰화가 필요한가
신경망은 텍스트가 아니라 숫자만 다룬다. 텍스트를 어떻게 쪼개 숫자로 만들지가 첫 결정이다.
1
2
3
4
5
6
7
8
9
방법 1: 문자 단위 ("h","e","l","l","o")
✅ 어휘 작음 ❌ 시퀀스가 너무 길어짐, 의미 단위 약함
방법 2: 단어 단위 ("hello", "world")
✅ 의미 단위 ❌ 어휘 폭발(수백만 단어), 신조어·오타 처리 불가
방법 3: 서브워드 (BPE 등) ← 현대 LLM의 선택
"tokenization" → "token" + "ization"
✅ 어휘 크기와 표현력의 균형, 미등록 단어도 조각으로 처리
2. BPE (Byte Pair Encoding)
가장 널리 쓰이는 서브워드 알고리즘. 자주 함께 나오는 바이트 쌍을 반복 병합한다.
1
2
3
4
5
6
7
8
학습 과정:
초기: 모든 문자를 개별 토큰으로
반복: 가장 빈번한 인접 쌍을 하나로 병합
"l","o","w" ... "lo"가 자주 나옴 → "lo" 병합
"lo","w" → "low" 병합 ...
→ 빈도 높은 단어는 통째로, 드문 단어는 조각으로 남음
결과 어휘(vocab): 보통 3만~10만 개 토큰
1
2
3
4
5
6
7
8
9
# tiktoken으로 실제 토큰화 확인 (OpenAI 계열)
import tiktoken
enc = tiktoken.get_encoding("cl100k_base")
ids = enc.encode("Transformer는 강력하다")
print(ids) # [12345, 678, ...] 정수 ID 시퀀스
print([enc.decode([i]) for i in ids]) # 각 ID가 어떤 조각인지
# 영어는 보통 1단어≈1토큰, 한국어는 글자당 여러 토큰이 되기도
토큰 수가 곧 비용·컨텍스트 한도와 직결되므로, 토큰화 효율은 실무에서 중요하다.
3. 토큰 ID에서 임베딩 벡터로
토큰 ID는 그저 사전의 색인 번호일 뿐, 의미가 없다. 임베딩이 각 ID를 의미를 담은 고차원 벡터로 바꾼다.
1
2
3
4
5
6
7
임베딩 행렬: [어휘 크기 V] × [차원 d]
예: V=100,000, d=4096
토큰 ID 12345 → 행렬의 12345번째 행 = 4096차원 벡터
이 벡터들은 학습으로 결정됨:
의미가 비슷한 토큰("king", "queen")은 벡터 공간에서 가까워짐
유명한 관계: vec("king") - vec("man") + vec("woman") ≈ vec("queen")
1
2
3
4
import torch.nn as nn
embedding = nn.Embedding(num_embeddings=100_000, embedding_dim=4096)
# 토큰 ID 텐서 → 임베딩 벡터 (학습 가능한 파라미터)
vectors = embedding(token_ids) # shape: [시퀀스 길이, 4096]
4. 순서 정보가 사라지는 문제
Transformer의 어텐션(Day 2)은 토큰들을 동시에 본다. RNN처럼 순서대로 읽지 않는다. 그래서 “고양이가 개를 쫓다”와 “개가 고양이를 쫓다”를 구분하지 못한다 — 순서 정보가 없기 때문이다.
1
2
3
해법: 위치 정보를 임베딩에 더한다 (Positional Encoding)
최종 입력 = 토큰 임베딩 + 위치 임베딩
→ 같은 단어라도 위치가 다르면 다른 벡터가 됨
5. 위치 인코딩의 방식들
1
2
3
4
5
6
7
8
9
10
1. 사인파 위치 인코딩 (원논문):
위치마다 서로 다른 주파수의 sin/cos 값을 부여
✅ 학습 불필요, 임의 길이로 외삽 가능
2. 학습된 위치 임베딩 (BERT, GPT 초기):
위치도 학습 파라미터로 → 단순하나 최대 길이 고정
3. RoPE (회전 위치 인코딩) — 현대 LLM의 대세:
벡터를 위치에 따라 회전시켜 "상대적 위치"를 자연스럽게 인코딩
✅ 긴 컨텍스트 확장에 유리 (LLaMA, 최신 모델 다수가 채택)
1
2
3
4
5
6
7
8
# 사인파 위치 인코딩의 핵심 아이디어
import numpy as np
def positional_encoding(pos, d):
pe = np.zeros(d)
for i in range(0, d, 2):
pe[i] = np.sin(pos / 10000 ** (i / d))
pe[i+1] = np.cos(pos / 10000 ** (i / d))
return pe # 각 위치가 고유한 패턴을 가짐
6. 정리: 입력 파이프라인
1
2
3
4
5
6
7
8
9
"안녕하세요" (텍스트)
↓ 토큰화 (BPE)
[15496, 23821] (토큰 ID)
↓ 임베딩 룩업
[[0.2, -0.5, ...], [0.8, 0.1, ...]] (의미 벡터, d=4096)
↓ + 위치 인코딩
[[0.3, -0.4, ...], [0.9, 0.3, ...]] (위치까지 담긴 최종 입력)
↓
[Transformer 블록들] ← Day 2~3
이 최종 벡터 시퀀스가 어텐션 레이어로 들어간다. “텍스트 → 의미와 위치를 담은 벡터”가 Day 1의 전부다.
7. Day 1 체크리스트
- 신경망이 숫자만 다루므로 토큰화가 첫 단계임을 이해했다.
- 서브워드(BPE)가 어휘 크기와 표현력의 균형점인 이유를 안다.
- 토큰 ID가 임베딩 행렬을 통해 의미 벡터로 변환됨을 파악했다.
- 어텐션이 순서를 모르기에 위치 인코딩이 필요함을 이해했다.
- 사인파·학습·RoPE 위치 인코딩의 차이를 구분했다.
다음 편 예고
입력 벡터가 준비됐다. 이제 Transformer의 심장이 등장한다. Day 2에서는 셀프 어텐션(self-attention) — 각 토큰이 다른 모든 토큰을 “주목”해 문맥을 파악하는 메커니즘을 Query·Key·Value로 풀어낸다.