토크나이제이션과 NLP 기초: BPE, SentencePiece, 그리고 WordPiece
토크나이제이션은 텍스트를 모델이 처리할 수 있는 하위 단위(토큰)로 분할하는 과정이다. 이는 현대 LLM의 기반을 이루며, 모델 성능에 직접적인 영향을 준다.
토크나이제이션이란?
토크나이제이션은 원시 텍스트를 숫자 표현으로 변환하는 첫 단계이다:
"Hello world!" → ["Hello", "world", "!"] → [1234, 5678, 99]
토크나이제이션 수준
- 문자 단위: 모든 문자가 하나의 토큰.
- 단어 단위: 모든 단어가 하나의 토큰.
- 서브워드 단위: 단어를 더 작은 단위로 분해(현대적 접근 방식).
단어 단위 토크나이제이션
단순한 접근
1def word_tokenize(text): 2 return text.split() 3 4# Example 5text = "Artificial intelligence is shaping the future" 6tokens = word_tokenize(text) 7# ['Artificial', 'intelligence', 'is', 'shaping', 'the', 'future']
문제점
- OOV (Out of Vocabulary): 학습 중 보지 못한 단어가 등장할 수 있음.
- 거대한 어휘집: 수십만 개의 단어를 관리하는 것은 비효율적.
- 높은 형태적 다양성: 터키어와 같은 언어는 접미사 때문에 단어 변형 수가 매우 많음.
- 복합어 문제: "Artificial intelligence"가 하나의 개념인지 두 개인지 판단해야 함.
문자 단위 토크나이제이션
1def char_tokenize(text): 2 return list(text) 3 4# Example 5text = "Hello" 6tokens = char_tokenize(text) 7# ['H', 'e', 'l', 'l', 'o']
장점
- OOV 문제가 없음.
- 작은 어휘집 크기(약 100자).
단점
- 시퀀스 길이가 매우 길어짐.
- 토큰 수준의 의미 손실.
- 모델 계산 비용 증가.
서브워드 토크나이제이션
현대 LLM이 선택하는 방식: 단어 단위와 문자 단위의 균형.
"tokenization" → ["token", "ization"] "unhappiness" → ["un", "happiness"] 혹은 ["un", "happy", "ness"]
BPE (Byte Pair Encoding)
가장 널리 사용되는 서브워드 토크나이제이션 알고리즘.
BPE 알고리즘
- 텍스트를 개별 문자로 분리.
- 가장 빈도 높은 인접 문자 쌍을 찾음.
- 해당 쌍을 하나의 새로운 토큰으로 병합.
- 원하는 어휘집 크기에 도달할 때까지 반복.
BPE 예시
1Starting vocabulary: ['l', 'o', 'w', 'e', 'r', 'n', 's', 't', 'i', 'd'] 2Corpus: "low lower newest lowest widest" 3 4Step 1: Most frequent pair 'e' + 's' → 'es' 5Step 2: Most frequent pair 'es' + 't' → 'est' 6Step 3: Most frequent pair 'l' + 'o' → 'lo' 7Step 4: Most frequent pair 'lo' + 'w' → 'low' 8... 9 10Final Result: ['low', 'est', 'er', 'new', 'wid', ...]
BPE 구현
1def get_stats(vocab): 2 pairs = {} 3 for word, freq in vocab.items(): 4 symbols = word.split() 5 for i in range(len(symbols) - 1): 6 pair = (symbols[i], symbols[i + 1]) 7 pairs[pair] = pairs.get(pair, 0) + freq 8 return pairs 9 10def merge_vocab(pair, vocab): 11 new_vocab = {} 12 bigram = ' '.join(pair) 13 replacement = ''.join(pair) 14 for word in vocab: 15 new_word = word.replace(bigram, replacement) 16 new_vocab[new_word] = vocab[word] 17 return new_vocab 18 19def train_bpe(corpus, num_merges): 20 vocab = get_initial_vocab(corpus) 21 22 for i in range(num_merges): 23 pairs = get_stats(vocab) 24 if not pairs: 25 break 26 best_pair = max(pairs, key=pairs.get) 27 vocab = merge_vocab(best_pair, vocab) 28 29 return vocab
WordPiece
Google이 개발했으며 BERT와 같은 모델에서 사용되는 알고리즘.
BPE vs WordPiece
| Feature | BPE | WordPiece |
|---|---|---|
| Merge Criterion | Frequency | Likelihood |
| Prefix | None | ## (중간 서브워드에 사용) |
| Used In | GPT, LLaMA | BERT, DistilBERT |
WordPiece 예시
1"tokenization" → ["token", "##ization"] 2"playing" → ["play", "##ing"] 3## SentencePiece 4 5Google에서 개발한 언어 비의존적 토크나이저. 6 7### Features 8 9- **Language Independent:** 공백을 단어 구분자로 가정하지 않음. 10- **Byte-level:** 원시 텍스트를 직접 처리. 11- **BPE + Unigram:** 여러 알고리즘 지원. 12- **Reversible:** 완전한 디토크나이징 가능. 13 14### SentencePiece Usage 15 16```python 17import sentencepiece as spm 18 19# Training the model 20spm.SentencePieceTrainer.train( 21 input='corpus.txt', 22 model_prefix='tokenizer', 23 vocab_size=32000, 24 model_type='bpe' # or 'unigram' 25) 26 27# Loading and using the model 28sp = spm.SentencePieceProcessor() 29sp.load('tokenizer.model') 30 31# Encode 32tokens = sp.encode('Hello world', out_type=str) 33# ['▁Hello', '▁world'] 34 35ids = sp.encode('Hello world', out_type=int) 36# [1234, 5678, 9012] 37 38# Decode 39text = sp.decode(ids) 40# 'Hello world'
▁ (Underscore) Symbol
SentencePiece는 단어의 시작을 ▁로 표시함:
"Hello world" → ["▁Hello", "▁world"] "New York" → ["▁New", "▁York"]
Tiktoken (OpenAI)
OpenAI가 사용하는 특수화된 BPE 구현.
1import tiktoken 2 3# Loading the encoder 4enc = tiktoken.encoding_for_model("gpt-4") 5 6# Encode 7tokens = enc.encode("Hello world!") 8# [12345, 67890, 999] 9 10# Decode 11text = enc.decode(tokens) 12# "Hello world!" 13 14# Check token count 15print(len(tokens)) # 3
Model-Encoder Mappings
| Model | Encoder | Vocab Size |
|---|---|---|
| GPT-4 | cl100k_base | 100,277 |
| GPT-3.5 | cl100k_base | 100,277 |
| GPT-3 | p50k_base | 50,281 |
| Codex | p50k_edit | 50,281 |
Hugging Face Tokenizers
1from transformers import AutoTokenizer 2 3# Loading the tokenizer 4tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased") 5 6# Encode 7encoded = tokenizer("Hello, world!", return_tensors="pt") 8# { 9# 'input_ids': tensor([[101, 7592, 1010, 2088, 999, 102]]), 10# 'attention_mask': tensor([[1, 1, 1, 1, 1, 1]]) 11# } 12 13# Decode 14text = tokenizer.decode(encoded['input_ids'][0]) 15# "[CLS] hello, world! [SEP]" 16 17# Token List 18tokens = tokenizer.tokenize("Hello, world!") 19# ['hello', ',', 'world', '!']
Fast Tokenizers
1from tokenizers import Tokenizer, models, trainers, pre_tokenizers 2 3# Creating a new tokenizer 4tokenizer = Tokenizer(models.BPE()) 5tokenizer.pre_tokenizer = pre_tokenizers.Whitespace() 6 7trainer = trainers.BpeTrainer( 8 vocab_size=30000, 9 special_tokens=["[PAD]", "[UNK]", "[CLS]", "[SEP]", "[MASK]"] 10) 11 12tokenizer.train(files=["corpus.txt"], trainer=trainer) 13tokenizer.save("my_tokenizer.json")
Special Tokens
Common Special Tokens
| Token | Description | Use Case |
|---|---|---|
| [CLS] | 시퀀스 시작 | BERT 분류 작업 |
| [SEP] | 세그먼트 구분 | 문장 쌍 분리 |
| [PAD] | 패딩 | 배치 처리 정렬 |
| [UNK] | 미지 단어 | 사전 밖 단어 처리 |
| [MASK] | 마스크 | Masked Language Modeling (MLM) |
| <|endoftext|> | 시퀀스 종료 | GPT 생성 작업 |
Chat Tokens
1<|system|>You are a helpful assistant<|end|> 2<|user|>Hello!<|end|> 3<|assistant|>Hello! How can I help you today?<|end|>
Tokenization Challenges in Turkish
Morphological Richness
1"gelebileceklermiş" (they were said to be able to come) → 단일 단어지만 복잡한 구조 2gel (come) + ebil (can) + ecek (will) + ler (they) + miş (reportedly) 3 4Tokenization: 5- Poor: ["gelebileceklermiş"] (단일 토큰, 매우 희귀) 6- Good: ["gel", "ebil", "ecek", "ler", "miş"]
Solutions
- Turkish-optimized tokenizer training.
- Integration of morphological analysis.
- Suffix-aware BPE application.
토큰 제한 및 관리
컨텍스트 윈도우
| 모델 | 컨텍스트 길이 (토큰) | 단어 환산 (~) |
|---|---|---|
| GPT-3.5 | 16K | ~12,000 |
| GPT-4 | 128K | ~96,000 |
| Claude 3 | 200K | ~150,000 |
토큰 수 추정
1def estimate_tokens(text): 2 # Rough estimate: 1 token ≈ 4 characters (English) 3 # For Turkish: 1 token ≈ 3 characters 4 return len(text) // 3 5 6# More accurate calculation 7def count_tokens(text, model="gpt-4"): 8 enc = tiktoken.encoding_for_model(model) 9 return len(enc.encode(text))
결론
토큰화는 NLP와 LLM의 근본적인 구성 요소입니다. BPE, WordPiece, SentencePiece와 같은 서브워드 기반 기법은 현대 언어 모델의 성공에 중요한 역할을 합니다. 올바른 토크나이저를 선택하고 적절히 구성하는 것은 모델의 최종 성능에 직접적인 영향을 미칩니다.
Veni AI는 터키어 NLP 솔루션에 특화된 토큰화 전략을 제공합니다.
