Byte Pair Encoding (BPE) 직접 구현하며 이해하기
요약
LLM의 핵심 전처리 단계인 토큰화 기술 중 하나인 Byte Pair Encoding(BPE)의 원리와 구현 방법을 다룹니다. 단어 단위와 문자 단위 토큰화의 한계를 분석하고, 서브워드 단위로 문제를 해결하는 BPE의 메커니즘을 설명합니다.
핵심 포인트
- 단어 단위 토큰화의 OOV 문제와 문자 단위의 긴 시퀀스 문제 해결
- 빈번한 인접 문자 쌍을 병합하여 서브워드 토큰을 생성하는 원리
- BPE를 통한 효율적인 어휘 사전 구축 및 트레이드오프 최적화
Byte Pair Encoding (BPE) 직접 구현하며 이해하기
서론: 토큰화 (Tokenization)가 존재하는 이유
GPT, LLaMA, 또는 Mistral과 같은 대규모 언어 모델 (LLMs)을 다룰 때, 가장 먼저 마주하는 숨겨진 단계 중 하나는 **토큰화 (tokenization)**입니다.
LLMs는 가공되지 않은 텍스트 (raw text)를 그대로 읽지 않습니다. 대신, 텍스트는 모델이 처리할 수 있는 수치적 표현인 **토큰 (tokens)**으로 먼저 변환되어야 합니다.
현대 자연어 처리 (NLP) 시스템에서 사용되는 가장 중요한 토큰화 기술 중 하나가 바로 **Byte Pair Encoding (BPE)**입니다.
이 글에서는 직관적인 이해부터 구현에 이르기까지 단계별로 분석하고, Python을 사용하여 BPE를 처음부터 직접 어떻게 구축했는지 보여드리겠습니다.
다음 내용을 다룹니다:
- 토큰화가 필요한 이유
- 단어 단위 (word-level) 및 문자 단위 (character-level) 토큰화가 실패하는 이유
- BPE가 이러한 트레이드오프 (trade-off)를 해결하는 방법
- BPE의 단계별 구현 방식
- 학습 (training) 대 추론 (inference) 동작
- 장점과 한계점
단어 단위 토큰화 (Word-Level Tokenization)의 문제점
가장 단순한 접근 방식은 텍스트를 단어별로 나누는 것입니다:
I love machine learning
다음과 같이 변환됩니다:
["I", "love", "machine", "learning"]
이 방식은 합리적으로 보이지만, 한 가지 큰 문제점을 깨닫기 전까지만 그렇습니다.
모델이 다음과 같은 단어를 보게 되면 어떻게 될까요?
machinelearning
또는:
hyperlearning
만약 이 단어들이 학습 데이터 (training data)에 없었다면, 이들은 어휘 사전 외 (Out-Of-Vocabulary, OOV) 토큰이 됩니다.
이는 심각한 한계점입니다. 언어는 끊임없이 진화하며, 우리는 가능한 모든 단어를 저장할 수 없기 때문입니다.
문자 단위 토큰화 (Character-Level Tokenization)의 문제점
OOV 문제를 해결하기 위해 모든 것을 문자로 나눌 수도 있습니다:
learning
다음과 같이 변환됩니다:
["l", "e", "a", "r", "n", "i", "n", "g"]
이 방식은 OOV 문제를 완전히 제거합니다.
하지만 새로운 문제들을 야기합니다:
- 시퀀스 (sequences)가 매우 길어짐
- 학습 (training)이 비효율적으로 변함
- 모델이 의미 있는 단어 구조를 잃어버림
따라서 우리는 다음과 같은 트레이드오프 (trade-off)를 마주하게 됩니다:
| 접근 방식 | 문제점 |
|---|---|
| 단어 단위 (Word-level) | OOV 문제 |
| 문자 단위 (Character-level) | 너무 긴 시퀀스 |
우리는 그 중간 단계의 무언가가 필요합니다.
Byte Pair Encoding (BPE)의 핵심 아이디어
BPE는 **텍스트 내의 공통 패턴 (common patterns in text)**을 학습함으로써 이 문제를 해결합니다.
단어(words)나 문자(characters)를 선택하는 대신, **서브워드 단위 (subword units)**를 구축합니다.
아이디어는 간단합니다:
- 문자로 시작합니다.
- 가장 빈번하게 나타나는 인접 쌍 (adjacent pair)을 찾습니다.
- 이를 새로운 토큰 (token)으로 병합 (merge)합니다.
- 반복합니다.
시간이 흐름에 따라 빈번한 패턴들은 하나의 토큰이 됩니다.
예시를 통한 직관적 이해
주어진 데이터:
l o w
l o w e r
l o w e s t
BPE는 빈도 패턴에 따라 다음과 같은 것들을 학습할 수 있습니다:
lo
low
lowe
1단계: 어휘 사전 (Vocabulary) 구축
단어 빈도수부터 시작합니다:
{
"low": 1,
"lower": 1,
...
그 다음 각 단어를 문자로 변환하고 단어 끝 표시자 (end-of-word marker)를 추가합니다:
{
('l', 'o', 'w', '</w>'): 1,
('l', 'o', 'w', 'e', 'r', '</w>'): 1,
...
</w> 표시자는 단어 경계 (word boundaries)를 구분하는 데 도움을 줍니다.
2단계: 인접 쌍 (Adjacent Pairs) 계산
모든 단어를 스캔하여 인접한 심볼 쌍 (symbol pairs)의 개수를 셉니다.
예를 들어:
l o w </w>
다음과 같은 결과를 생성합니다:
(l, o)
(o, w)
(w, </w>)
각 쌍은 단어 빈도수에 가중치를 두어 (weighted by word frequency) 계산됩니다.
이를 통해 코퍼스 (corpus) 내의 모든 쌍에 대한 전역 빈도 테이블 (global frequency table)을 얻을 수 있습니다.
3단계: 가장 빈번한 쌍 선택
가장 빈번하게 나타나는 인접 쌍을 선택합니다:
('l', 'o')
이것은 다음과 같은 병합 규칙 (merge rule)이 됩니다:
(l, o) → lo
4단계: 전체 어휘 사전에 걸쳐 병합 적용
다음의 모든 출현 사례를 교체합니다:
l o
다음으로:
lo
따라서:
('l','o','w','</w>')
다음이 됩니다:
('lo','w','</w>')
5단계: 프로세스 반복
쌍의 빈도를 다시 계산하고 병합을 계속합니다:
(l,o) → lo
(lo,w) → low
(low,e) → lowe
각 반복 (iteration)은 더 단순한 것들로부터 더 복잡한 토큰들을 구축합니다.
학습 (Training) vs 토큰화 (Tokenization) (핵심 개념)
이 부분이 많은 사람들이 혼란스러워하는 지점입니다.
학습 단계 (During Training)
BPE는:
- 쌍의 빈도를 계산합니다.
- 병합 규칙을 학습합니다.
- 어휘 사전 (vocabulary)을 구축합니다.
예시:
(l,o) → lo
(lo,w) → low
토큰화 단계 (During Tokenization / Inference)
토크나이저 (tokenizer)는:
- 아무것도 학습하지 않습니다.
- 빈도를 계산하지 않습니다.
- 오직 학습된 병합 규칙을 적용하기만 합니다.
예시:
입력:
lowly
시작:
l o w l y
병합 적용:
(l,o) → lo
(lo,w) → low
결과:
low l y
여기서는 학습이 일어나지 않으며, 오직 규칙 적용만 이루어집니다.
BPE가 매우 효과적인 이유
1. OOV (Out-of-Vocabulary) 문제 해결
보지 못한 단어라도 다음과 같이 표현할 수 있습니다:
playfulness → play + ful + ness
2. 더 작은 어휘 사전 (Vocabulary)
전체 단어를 저장하는 대신:
play
playing
player
...
우리는 다음을 재사용합니다:
play
ing
er
3. 더 나은 일반화 (Generalization)
공통된 어근을 가진 단어들은 토큰을 재사용합니다:
play
playing
player
이를 통해 모델은 연관된 단어들 사이에서 의미를 공유할 수 있습니다.
BPE의 한계
강력한 성능에도 불구하고, BPE에는 다음과 같은 한계가 있습니다:
- 학습 코퍼스 (Training Corpus)에 민감함
- 탐욕적 병합 전략 (Greedy Merging Strategy)
- 다국어 균형 측면에서 취약함
- 언어를 "이해"하는 것이 아니라, 오직 빈도 패턴만을 학습함
이러한 이유로 현대의 LLM (Large Language Models)은 주로 다음을 사용합니다:
- Byte-level BPE (GPT 스타일의 토크나이저)
- WordPiece (BERT)
- SentencePiece Unigram (LLaMA, T5)
나의 Python 구현 (From Scratch)
아래는 간소화된 나의 BPE 구현체입니다:
import sys
def main(av: list[str]) -> int:
...
이 구현체는 다음을 수행합니다:
- 어휘 사전 (Vocabulary) 구축
- 쌍 빈도 (Pair Frequencies) 계산
- 병합 규칙 (Merge Rules) 학습
- 반복적인 토큰 업데이트
핵심 통찰 (Key Insight)
BPE는 놀라울 정도로 단순합니다:
그것은 언어를 이해하는 것이 아니라, 어떤 문자 쌍이 가장 빈번하게 함께 나타나는지를 학습할 뿐입니다.
그럼에도 불구하고 이 단순한 아이디어는 모든 현대 LLM에서 사용될 만큼 강력합니다.
결론
Byte Pair Encoding은 현대 NLP (Natural Language Processing) 시스템의 핵심에 자리 잡고 있습니다.
BPE는 다음 사이의 균형을 제공합니다:
- 단어 단위 토큰화 (Word-level tokenization, 너무 경직됨)
- 문자 단위 토큰화 (Character-level tokenization, 너무 김)
빈번한 서브워드 (Subword) 패턴을 학습함으로써, BPE는 모델이 흔히 쓰이는 단어와 보지 못한 단어 모두를 효율적으로 표현할 수 있게 해줍니다.
BPE를 이해하는 것은 LLM이 실제로 언어를 어떻게 처리하는지 이해하기 위한 가장 좋은 입문 경로 중 하나입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기