Ch.3 텍스트를 숫자로 — 토큰화의 세계
BPE 토크나이저 직접 만들기
GPT는 '단어' 단위가 아니라 '서브워드' 단위로 읽습니다
영어 'unhappiness'를 통째로 외우면 비효율적입니다. 'un' + 'happi' + 'ness'로 쪼개면 다른 단어에도 재활용할 수 있죠. BPE가 바로 이 분해를 자동으로 수행합니다.
어디서 쪼개야 최적일까? BPE는 어떤 기준으로 결정할까?
BPE 알고리즘 — 가장 자주 붙어 다니는 쌍을 반복적으로 합치는 방법입니다.
핵심 개념
BPE
빈번한 문자 쌍을 반복 병합하여 서브워드를 만드는 알고리즘
토큰
텍스트를 모델이 처리할 수 있는 최소 단위로 쪼갠 조각
핵심 내용
BPE는 글자부터 시작해서 자주 나오는 쌍을 반복 병합합니다
BPE (Byte Pair Encoding) 알고리즘 ① 모든 단어를 글자 단위로 분리 ② 가장 빈번한 연속 쌍(pair)을 찾는다 ③ 그 쌍을 하나의 새 토큰으로 병합 ④ 원하는 어휘 크기가 될 때까지 ②~③ 반복
GPT-4는 약 10만 개의 BPE 토큰을 사용합니다. 처음엔 256개 바이트로 시작해서 수만 번 병합한 결과입니다.
글자들이 하나씩 합쳐져서 서브워드가 되는 과정입니다
예시: "low lower lowest" 초기: [l, o, w, e, r, s, t] 병합1: (l, o) → lo ← 가장 빈번한 쌍 병합2: (lo, w) → low 병합3: (e, r) → er 병합4: (e, s) → es 결과: [low, er, es, t] — 4개 서브워드로 3단어를 표현!
BPE의 핵심인 쌍 빈도 계산과 병합 과정을 추적합니다
BPE 알고리즘을 직접 구현해봅시다!
from collections import Counter
def get_pairs(tokens):
pairs = Counter()
for i in range(len(tokens) - 1):
pairs[(tokens[i], tokens[i+1])] += 1
return pairs
def merge(tokens, pair):
merged = pair[0] + pair[1]
new_tokens = []
i = 0
while i < len(tokens):
if i < len(tokens)-1 and tokens[i] == pair[0] and tokens[i+1] == pair[1]:
new_tokens.append(merged)
i += 2
else:
new_tokens.append(tokens[i])
i += 1
return new_tokens
text = "low low lower lowest"
tokens = list(text.replace(" ", " _ "))
print(f"초기 토큰 ({len(set(tokens))}종): {tokens}
")
for step in range(5):
pairs = get_pairs(tokens)
if not pairs: break
best = pairs.most_common(1)[0][0]
tokens = merge(tokens, best)
print(f"병합 {step+1}: {best[0]}+{best[1]} → {best[0]+best[1]}")
print(f" 토큰: {tokens}")
print(f" 고유: {len(set(tokens))}
")병합을 많이 할수록 어휘 사전 크기는 커지고 토큰 수는 줄어듭니다
GPT-2: 50,257개 토큰 (약 50,000번 병합) GPT-4: ~100,000개 토큰 어휘가 크면: 한 토큰이 더 많은 의미를 담아 효율적 어휘가 작으면: 메모리 절약, 하지만 문장이 길어짐
BPE 병합 과정을 한 단계씩 추적하며 어휘가 어떻게 만들어지는지 관찰합니다
# 간단한 BPE 병합 시뮬레이션
from collections import Counter
# 초기 토큰 시퀀스 (문자 단위)
tokens = list("abababcab")
print(f"초기 토큰: {tokens}")
print(f"초기 어휘: {sorted(set(tokens))}\n")
# BPE 병합 3라운드 실행
for round_num in range(1, 4):
# 연속 쌍 빈도 계산
pairs = Counter()
for i in range(len(tokens) - 1):
pair = (tokens[i], tokens[i + 1])
pairs[pair] += 1
if not pairs:
break
best_pair, count = pairs.most_common(1)[0]
merged = best_pair[0] + best_pair[1]
print(f"[라운드 {round_num}] 최빈 쌍: {best_pair} ({count}회)")
# 병합 실행
new_tokens = []
i = 0
while i < len(tokens):
if i < len(tokens) - 1 and (tokens[i], tokens[i+1]) == best_pair:
new_tokens.append(merged)
i += 2
else:
new_tokens.append(tokens[i])
i += 1
tokens = new_tokens
print(f" 결과: {tokens}")
print(f" 어휘: {sorted(set(tokens))}\n")토큰: [a, b, a, b, c, a, b] 가장 먼저 병합될 쌍은?
BPE에서 병합 횟수를 늘리면 하나의 토큰이 표현하는 의미가 커진다
BPE는 왜 단어 단위가 아닌 서브워드 단위로 토큰화하나요?
BPE에서 어휘 사전(vocabulary) 크기가 커지면 어떤 트레이드오프가 있나요?
GPT-4의 토크나이저는 약 10만 개의 어휘 사전을 사용한다.
텍스트 → 토큰 → 숫자 LLM 파이프라인의 2단계 완료!
[[전처리]]: split, join, replace로 텍스트 정제 [[어휘 사전]]: 딕셔너리로 단어 → 정수 매핑 BPE: 서브워드 단위 토큰화 — GPT의 핵심 다음 챕터: 숫자가 된 토큰을 벡터(임베딩)로 변환합니다!
토큰화의 세계
핵심 용어
BPE
빈번한 문자 쌍을 반복 병합하여 서브워드를 만드는 알고리즘
토큰
텍스트를 모델이 처리할 수 있는 최소 단위로 쪼갠 조각
정리 노트
BPE 알고리즘 핵심 정리
BPE 알고리즘 단계
- 1단계
- 모든 단어를 글자 단위로 분리 (초기 어휘 = 개별 문자)
- 2단계
- 가장 빈번한 연속 쌍을 찾는다 (예: l+o → lo)
- 3단계
- 해당 쌍을 하나의 새 토큰으로 병합
- 4단계
- 원하는 어휘 크기까지 2~3단계 반복
어휘 크기와 트레이드오프
- 어휘가 크면
- 한 토큰이 더 많은 의미를 담아 효율적 (GPT-4: ~10만 개)
- 어휘가 작으면
- 메모리 절약, 하지만 문장이 더 많은 토큰으로 쪼개짐
- 서브워드의 장점
- 처음 보는 단어도 부분적으로 표현 가능 (OOV 해결)
GPT-2는 약 5만 번, GPT-4는 약 10만 번 병합하여 어휘 사전을 구축합니다!
시각 자료
핵심 정리
- 1split/join/replace — 텍스트 전처리의 3대 도구
- 2딕셔너리 + Counter — 어휘 사전 구축
- 3BPE — 서브워드 토큰화, GPT의 핵심 알고리즘
퀴즈와 인터랙션으로 더 깊이 학습하세요
play_circle인터랙티브 레슨 시작