토큰화는 대형 언어 모델(LLM)이 텍스트를 처리하기 위해 반드시 거쳐야 하는 첫 번째 단계입니다. 이 과정은 사람이 읽을 수 있는 텍스트를 모델이 이해할 수 있는 숫자 형태로 변환합니다. 흔히 간과되지만, 이 단계는 모델의 성능과 효율성, 그리고 실행 비용에 직접적인 영향을 미칩니다.
토큰화의 핵심 역할
토큰화는 입력된 텍스트를 작은 단위인 ‘토큰’으로 분해하는 작업입니다. 이러한 토큰은 이후 모델 내부에서 정수 ID로 매핑되어 처리됩니다. 이 방식은 다음과 같은 여러 요소에 영향을 줍니다:
- 모델의 성능 및 문제 해결 범위 (예: 미등록어(OOV) 문제)
- 컨텍스트 윈도우 제한
- 다국어 처리 능력
- API 호출 비용 (대부분의 LLM은 토큰 수에 따라 요금 부과)
주요 토큰화 알고리즘
문자 기반 토큰화 (Character-Level)
가장 기본적인 방법으로, 각 문자를 개별 토큰으로 취급합니다.
def tokenize_chars(input_text):
return list(input_text)
tokens = tokenize_chars("Hello")
# 결과: ['H', 'e', 'l', 'l', 'o']
장점은 구현이 간단하고 고정된 어휘 크기를 가지며 미등록어 문제가 발생하지 않는다는 점입니다. 하지만 시퀀스 길이가 길어져 계산 효율성이 떨어지고, 의미 있는 단위를 잡아내지 못할 수 있습니다.
단어 기반 토큰화 (Word-Level)
공백이나 구두점을 기준으로 텍스트를 나눕니다.
def split_words(input_text):
return input_text.split()
tokens = split_words("Hello, world!")
# 결과: ['Hello,', 'world!']
직관적이며 시퀀스 길이는 짧지만, 새로운 단어나 형태 변화가 많은 언어에서는 한계가 있습니다.
서브워드 토큰화 (Subword Tokenization)
현대 LLM 대부분이 사용하는 방식으로, 단어와 문자 사이의 균형을 맞춥니다.
BPE (Byte Pair Encoding)
GPT 모델에서 활용되며, 자주 등장하는 문자 쌍을 반복적으로 병합하여 어휘를 구성합니다.
WordPiece
BERT에서 사용되며, 두 토큰을 결합했을 때의 확률을 기반으로 병합 여부를 결정합니다.
# 예시
"embedding" → ["em", "##bed", "##ding"]
"transformer" → ["trans", "##form", "##er"]
SentencePiece / Unigram LM
T5나 다국어 모델에서 주로 사용되며, 통계적 언어 모델을 기반으로 최적의 토큰 집합을 찾습니다.
# 예시
# 영어: "Hello world!"
# SentencePiece: ["▁He", "llo", "▁world", "!"]
# 일본어: "こんにちは世界"
# SentencePiece: ["こん", "にち", "は", "世界"]
바이트 기반 토큰화 (Byte-Level)
GPT-4 등에서 사용되며 모든 문자열을 바이트 단위로 인코딩한 후 BPE를 적용합니다.
def encode_bytes(text):
return list(text.encode('utf-8'))
tokens = encode_bytes("Hello 🌍")
# 결과: [72, 101, 108, 108, 111, 32, 240, 159, 140, 141]
비트 기반 토큰화 (Bit-Level)
특정 논문에서는 UTF-8 바이트보다 더 작은 단위인 비트 수준에서 병합을 수행하여 시퀀스 길이를 줄이는 방법을 제안했습니다. 특히 CJK 문자나 이모지가 많은 경우 유용합니다.
실제 토크나이저 비교
같은 문장을 다른 토크나이저로 처리한 예시입니다.
입력: The tokenization process transforms text into numerical tokens.
- GPT-2/3/4 (BPE): 9개 토큰
- BERT (WordPiece): 12개 토큰
- T5 (SentencePiece): 10개 토큰
숫자 표현 방식
입력: The price is $1,234.56
GPT-4: ['The', 'Ġprice', 'Ġis', 'Ġ$', '1', ',', '234', '.', '56'] → 9개 토큰
Claude: ['The', ' price', ' is', ' $', '1', ',', '234', '.', '56'] → 9개 토큰
이모지 처리
입력: I love coding! 👨💻
GPT-4: 16개 토큰 (UTF-8 바이트)
Claude: 8개 토큰
코드 토큰화
code = "def hello_world():\n print('Hello, world!')"
GPT-4: 18개 토큰
CodeLlama: 13개 토큰 (코드 최적화됨)
토큰화의 도전 과제
- 언어 편향: 영어 중심으로 학습된 토크나이저는 다른 언어에서는 비효율적일 수 있음
- 비정형 텍스트: 반복되는 문자나 특수 패턴은 예상치 못한 토큰 수를 생성함
- 기술적 콘텐츠: 코드 내 특수 문자는 토큰화 과정에서 낭비를 초래할 수 있음
효율적인 토큰 사용 전략
- 배치 요청: 여러 작업을 하나의 요청으로 묶어 토큰 낭비를 줄임
- 프롬프트 최적화: 불필요한 설명을 줄이고 핵심만 전달
- 전처리: 중복 공백 제거, 장황한 표현 간결하게 수정
import re
def optimize_text(raw_text):
raw_text = re.sub(r'\s+', ' ', raw_text).strip()
replacements = {
"in order to": "to",
"a large number of": "many",
"due to the fact that": "because"
}
for old, new in replacements.items():
raw_text = re.sub(r'\b' + old + r'\b', new, raw_text, flags=re.IGNORECASE)
return raw_text
토크나이저 구현 예제
# OpenAI (GPT-4)
from tiktoken import get_encoding
enc = get_encoding("cl100k_base")
len(enc.encode("Hello, world!")) # 4
# Hugging Face
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("gpt2")
len(tokenizer("Hello, world!")["input_ids"]) # 5
# SentencePiece
import sentencepiece as spm
sp = spm.SentencePieceProcessor(model_file='t5.model')
len(sp.encode("Hello, world!", out_type=str)) # 5