BPE
BPE (Byte Pair Encoding)
1. 개요
BPE(Byte Pair Encoding)는 자연어 처리(NLP)에서 텍스트를 효율적으로 분절하기 위해 사용되는 서브워드(Subword) 토큰화 알고리즘으로, 빈도 기반의 문자 쌍 병합을 통해 단어와 문자 단위의 중간 형태인 서브워드 어휘집을 구축하는 기법이다.
전통적인 NLP에서는 단어 단위(Word-level) 또는 문자 단위(Character-level)의 토큰화를 사용했으나, 각각 '어휘집 크기의 폭발적 증가 및 미등록 단어(OOV) 발생'과 '시퀀스 길이의 과도한 증가 및 개별 문자의 의미 부족'이라는 치명적인 한계가 있었다. BPE는 자주 등장하는 문자열 조합을 하나의 단위로 묶음으로써, 희귀한 단어는 쪼개어 표현하고 빈번한 단어는 하나의 토큰으로 유지하여 이 두 방식의 과정 BPE 알고리즘을 적용하여 어휘집을 구축하기 위해서는 먼저 학습 코퍼스(Corpus)를 전처리하는 과정이 필요하다.
- 텍스트 정제 및 정규화: 특수문자 제거, 대소문자 통합 등 분석 목적에 맞는 정규화 과정을 거친다.
- 단어 빈도수 계산: 코퍼스 내의 모든 단어의 출현 빈도를 계산하여 사전(Dictionary) 형태로 저장한다.
- 단어 분절(Character-level Split): 모든 단어를 문자 단위로 쪼개고, 단어의 끝을 알리는 특수 기호(예:
</w>또는_)를 추가한다. 이는 나중에 병합된 서브워드가 단어의 중간에 위치하는지 끝에 위치하는지를 구분하기 위함이다.
예시 데이터 준비:
- 입력 문장: "low low low low low lowest lowest lowest lowest lowest lowest lower lower lower"
- 빈도 사전: {'low': 5, 'lowest': 6, 'lower': 3}
- 초기 분절 상태:
- l o w </w> : 5회
- l o w e s t </w> : 6회
- l o w e r </w> : 3회
초기 어휘집(Base Vocabulary) 정의: BPE의 초기 어휘집은 코퍼스에 등장하는 모든 고유 문자의 집합으로 구성된다. 이후 병합 과정을 통해 생성된 새로운 유닛들이 이 초기 어휘집에 순차적으로 추가된다.
3. 동작 원리
BPE는 가장 빈번하게 등장하는 두 개의 연속된 유닛(문자 또는 서브워드)을 하나의 새로운 유닛으로 병합하는 과정을 반복한다.
3.1 단계별 프로세스
- 빈도 분석: 현재 상태에서 인접한 모든 유닛(Unit) 쌍의 빈도를 계산한다.
- 최대 빈도 쌍 선택: 가장 많이 등장한 유닛 쌍을 선택한다.
- 병합(Merge): 선택된 유닛 쌍을 하나의 새로운 유닛으로 합쳐 어휘집에 추가하고, 전체 데이터셋의 해당 쌍을 교체한다.
- 반복: 사용자가 설정한 어휘집 크기(Vocabulary Size)에 도달할 때까지 1~3단계를 반복한다. 최종적으로 완성된 유닛들의 집합을 토큰(Token)이라 정의한다.
3.2 토큰화 과정 도식도
graph TD
A[초기 상태: 문자 단위 분절] --> B{가장 빈번한 쌍 찾기}
B --> C[쌍 병합: 'e' + 's' -> 'es']
C --> D{반복 조건 확인}
D -- "어휘집 크기 미달" --> B
D -- "어휘집 크기 도달" --> E[최종 서브워드 어휘집 완성]
E --> F[새로운 텍스트 적용: 분절 및 인덱싱]
3.3 시뮬레이션 코드 (Python)
import collections
# 초기 데이터: 단어와 빈도
vocab = {'l o w </w>': 5, 'l o w e s t </w>': 6, 'l o w e r </w>': 3}
def get_stats(vocab):
pairs = collections.defaultdict(int)
for word, freq in vocab.items():
symbols = word.split()
for i in range(len(symbols)-1):
pairs[symbols[i], symbols[i+1]] += freq
return pairs
def merge_vocab(pair, v):
new_vocab = {}
# 리스트 기반 병합 로직: 단순 replace의 오작동을 방지하고 토큰 단위로 병합
for word in v:
symbols = word.split()
i = 0
new_symbols = []
while i < len(symbols):
if i < len(symbols) - 1 and (symbols[i], symbols[i+1]) == pair:
new_symbols.append(symbols[i] + symbols[i+1])
i += 2
else:
new_symbols.append(symbols[i])
i += 1
new_vocab[' '.join(new_symbols)] = v[word]
return new_vocab
# BPE 반복 수행
num_merges = 5
for i in range(num_merges):
pairs = get_stats(vocab)
if not pairs: break
best = max(pairs, key=pairs.get)
vocab = merge_vocab(best, vocab)
print(f"Merge {i+1}: {best} -> {''.join(best)}")
print("Final Vocab:", vocab)
3.4 추론(Inference) 시의 토큰화 과정
학습 단계에서 구축된 어휘집뿐만 아니라, 병합 규칙의 순서(Merge Order)를 저장하는 것이 중요하다. 새로운 텍스트가 입력되면 다음과 같은 과정을 거친다. 1. 입력 텍스트를 문자 단위로 분절한다. 2. 학습 시 결정된 병합 규칙을 순서대로 적용한다. (가장 먼저 병합되었던 쌍부터 순차적으로 적용) 3. 더 이상 적용할 규칙이 없거나 모든 유닛이 어휘집에 존재할 때까지 반복하여 최종 토큰 시퀀스를 생성한다.
4. BPE의 특징 및 장점
BPE는 단어 단위의 효율성과 문자 단위의 유연성을 동시에 확보한다.
4.1 OOV(Out-of-Vocabulary) 문제 해결
단어 단위 토큰화에서는 학습 데이터에 없던 단어가 등장하면 [UNK](Unknown) 토큰으로 처리하여 정보를 손실한다. 반면 BPE는 단어를 더 작은 서브워드 단위로 쪼개어 표현하므로, 처음 보는 단어라도 이미 학습된 서브워드들의 조합으로 표현할 수 있어 OOV 문제를 획기적으로 줄인다.
4.2 토큰화 방식 비교
| 비교 항목 | Word-level | Character-level | BPE (Subword) |
|---|---|---|---|
| 어휘집 크기 | 매우 큼 (수만~수백만) | 매우 작음 (수백 개) | 조절 가능 (중간 수준) |
| OOV 문제 | 심각함 | 없음 | 거의 없음 |
| 시퀀스 길이 | 짧음 | 매우 김 | 적절함 |
| 의미 단위 | 단어 수준의 의미 보존 | 의미 파악 어려움 | 형태소적 의미 일부 보존 |
4.3 서브워드 알고리즘 상세 비교
| 구분 | BPE | WordPiece | Unigram (SentencePiece) |
|---|---|---|---|
| 병합 기준 | 빈도수 (Frequency) | 우도 (Likelihood) | 확률 (Probability) |
| 접근 방식 | 상향식 (Bottom-up) | 상향식 (Bottom-up) | 하향식 (Top-down) |
| 특징 | 가장 빈번한 쌍 병합 | 전체 우도를 높이는 쌍 병합 | 손실이 적은 토큰 유지/제거 |
| 주요 모델 | GPT, RoBERTa | BERT | ALBERT, T5 |
5. BPE의 한계점 및 분석
5.1 한계점
- 빈도 기반의 맹점: 단순히 빈도수만 고려하므로, 언어학적인 형태소 경계를 정확히 반영하지 못할 수 있다.
- 데이터 의존성: 학습 코퍼스의 도메인에 따라 어휘집이 결정되므로, 도메인이 다른 텍스트 처리 시 효율성이 떨어진다.
- 비가역적 병합: 한 번 병합된 토큰은 다시 쪼개지지 않으므로, 초기 병합 단계의 결정이 최종 품질을 결정한다.
5.2 시간 복잡도 분석
- 학습 단계: 매 반복마다 모든 단어 쌍의 빈도를 계산해야 하므로, 코퍼스의 크기를 $N$, 반복 횟수를 $M$이라 할 때 대략 $O(M \cdot N)$의 복잡도를 가진다. 다만, 실제 구현에서는 우선순위 큐(Priority Queue) 등을 사용하여 최적화한다.
- 추론 단계: 저장된 병합 규칙의 개수 $R$에 대해 입력 텍스트의 길이를 $L$이라 하면, $O(R \cdot L)$의 복잡도로 토큰화가 수행된다.
6. 변형 및 발전된 알고리즘
- WordPiece: Google에서 개발하였으며, 단순 빈도가 아닌 우도(Likelihood)를 기반으로 병합한다. (BERT에서 사용)
- Unigram Language Model: BPE와 반대로 큰 어휘집에서 시작하여, 손실이 가장 적은 토큰을 제거해 나가는 하향식(Top-down) 방식을 취한다. (SentencePiece에서 사용)
- Byte-level BPE (BBPE): 유니코드 문자 단위가 아닌 바이트(Byte) 단위로 BPE를 수행한다. 이를 통해 모든 가능한 문자열을 표현할 수 있으며, 어휘집 크기를 고정하면서도 모든 언어를 처리할 수 있다. (GPT-2 이후 모델에서 사용)
7. 실제 활용 사례 및 예시
7.1 학습된 어휘집 예시
BPE 학습 결과 생성될 수 있는 어휘집의 예시는 다음과 같다.
- Base Vocab: a, b, c, ..., z, 0, 1, ..., </w>
- Merged Vocab (추가된 토큰):
- th (t + h)
- he (h + e)
- the (th + e)
- in (i + n)
- ing (in + g)
- tion (t + ion)
7.2 LLM 적용 사례
- GPT 시리즈: Byte-level BPE를 사용하여 전 세계 다양한 언어와 특수문자를 효율적으로 처리한다. 이는 모델이 학습하지 못한 희귀한 유니코드 문자라도 바이트 단위로 쪼개어 처리함으로써
[UNK]토큰 발생을 완전히 제거한다. - RoBERTa: GPT-2의 BBPE 방식을 채택하여 더 넓은 범위의 텍스트 데이터를 학습하고 일반화 성능을 높였다.
- 수치화 과정:
텍스트$\rightarrow$BPE 토큰화$\rightarrow$어휘집 인덱스 변환(Integer ID)$\rightarrow$임베딩 벡터(Embedding Vector)순으로 변환되어 모델의 입력값으로 사용된다.
이 문서는 AI 모델(gemma-4-31b)에 의해 생성된 콘텐츠입니다.
주의사항: AI가 생성한 내용은 부정확하거나 편향된 정보를 포함할 수 있습니다. 중요한 결정을 내리기 전에 반드시 신뢰할 수 있는 출처를 통해 정보를 확인하시기 바랍니다.