BPE, WordPiece, Unigram(=SentencePiece Unigram) 세 가지 토크나이저를 같은 코퍼스, 같은 문장으로 비교
from tokenizers import Tokenizer, models, trainers, pre_tokenizers
# --------------------------
# 1. 샘플 코퍼스 준비
# --------------------------
corpus = [
"Machine learning is fascinating.",
"Deep learning models improve with data.",
"Large language models are powerful.",
"Tokenization affects model performance."
]
# ==========================
# 2. BPE Tokenizer
# ==========================
bpe_tokenizer = Tokenizer(models.BPE())
bpe_tokenizer.pre_tokenizer = pre_tokenizers.Whitespace()
bpe_trainer = trainers.BpeTrainer(
vocab_size=60,
special_tokens=["[PAD]", "[UNK]", "[CLS]", "[SEP]"]
)
bpe_tokenizer.train_from_iterator(corpus, bpe_trainer)
# ==========================
# 3. WordPiece Tokenizer
# ==========================
wp_tokenizer = Tokenizer(models.WordPiece(unk_token="[UNK]"))
wp_tokenizer.pre_tokenizer = pre_tokenizers.Whitespace()
wp_trainer = trainers.WordPieceTrainer(
vocab_size=60,
special_tokens=["[PAD]", "[UNK]", "[CLS]", "[SEP]"]
)
wp_tokenizer.train_from_iterator(corpus, wp_trainer)
# ==========================
# 4. Unigram Tokenizer (SentencePiece Unigram)
# ==========================
uni_tokenizer = Tokenizer(models.Unigram())
uni_tokenizer.pre_tokenizer = pre_tokenizers.Whitespace()
uni_trainer = trainers.UnigramTrainer(
vocab_size=60,
special_tokens=["[PAD]", "[UNK]", "[CLS]", "[SEP]"]
)
uni_tokenizer.train_from_iterator(corpus, uni_trainer)
# --------------------------
# 5. 비교 문장
# --------------------------
text = "Tokenization improves language models"
print("=== BPE ===")
print(bpe_tokenizer.encode(text).tokens)
print("\n=== WordPiece ===")
print(wp_tokenizer.encode(text).tokens)
print("\n=== Unigram ===")
print(uni_tokenizer.encode(text).tokens)
=== BPE ===
['To', 'k', 'en', 'i', 'z', 'at', 'i', 'o', 'n', 'i', 'm', 'p', 'r', 'o', 'v', 'e', 's', 'l', 'an', 'g', 'u', 'age', 'models']
=== WordPiece ===
['T', '##o', '##k', '##e', '##n', '##i', '##z', '##at', '##i', '##o', '##n', 'i', '##m', '##p', '##r', '##o', '##v', '##e', '##s', 'l', '##a', '##n', '##g', '##u', '##a', '##g', '##e', 'model', '##s']
=== Unigram ===
['T', 'o', 'k', 'e', 'ni', 'z', 'ati', 'o', 'n', 'i', 'm', 'p', 'r', 'o', 'v', 'e', 's', 'l', 'a', 'ng', 'u', 'a', 'g', 'e', 'model', 's']
| 항목 | BPE | WordPiece | Unigram (SentencePiece) |
|---|---|---|---|
| 학습 방식 | 자주 등장하는 byte pair 병합 | 확률 기반 최대 우도 서브워드 조합 | 전체 단어 분해 후보 세트를 만들고 확률적으로 최적화 |
| 서브워드 분해 특징 | 직관적, 병합 기반 | '##' 접두사로 subword 표시 | 가장 가능성 높은 조합 선택 (probabilistic) |
| OOV 처리 | 잘 처리함 | 잘 처리함 | 가장 강함 (여러 분해 후보 중 선택) |
| 장점 | 빠르고 직관적 | 안정적, BERT류에서 사용 | 소형 vocab으로도 강력, 다양한 분해 가능 |
| 단점 | 일부 비직관적 분해 발생 | deterministic이라 변화 적음 | 학습 난이도 약간 높음 |
| 대표 모델 | GPT 계열 | BERT, RoBERTa | SentencePiece(알파벳/한글 다 지원), T5 |
자주 등장하는 쌍을 계속 합쳐 나감
Token | ization | im | prove | s | language | models
접두사
##로 “중간 조각” 표시
Token | ##ization | im | ##proves | language | models
여러 후보 중 가장 확률 높은 조합을 선택
Token | ization | impro | ves | language | models
['To', 'k', 'en', 'i', 'z', 'at', ...]
✔ 자주 등장하는 문자 쌍만 병합됨
✔ 코퍼스가 너무 작아서 대부분 문자 단위 토큰
✔ 단어 내부에서 빈번한 조합만 조금씩 합쳐짐
→ BPE의 본질: 통계적으로 자주 나오는 조합부터 합친다
['T', '##o', '##k', '##e', '##n', ...]
✔ 대부분 문자 단위
✔ WordPiece는 “OOV를 피하도록” 가장 작은 단위로 나누는 경향
✔ 뒤에 붙은 ##는 이전 토큰의 연속(subword)
→ WordPiece의 본질: 안정적이고 규칙적인 분해, 항상 단어 시작/중간 구분 유지
['T', 'o', 'k', 'e', 'ni', 'z', 'ati', ...]
✔ 확률 기반 선택 모델 → 가장 가능성이 높은 subword 선택
✔ 그래서 문자 단위 + 2~3글자 subword가 섞여 있음
✔ 다양한 분해 후보 중 “전체 문장의 우도를 최대로 만드는 조합” 선택
→ Unigram의 본질: 확률적으로 가장 자연스러운 분해 선택 (가장 flexible)