2장. Transformer 입력 표현
서브워드 사전을 만드는 방법
신문에서 자주 함께 나오는 글자 쌍에 형광펜을 칠한다고 해 봅시다. "e 다음에 s" 가 가장 많이 나오면 둘을 묶어 "es" 를 한 글자처럼 취급합니다. 다시 세어 보니 "es 다음에 t" 가 가장 많으면 "est" 로 묶습니다. 이 일을 수만 번 되풀이하면 자주 쓰는 조각은 점점 길게 뭉치고, 드문 조각은 짧게 남습니다. 이것이 BPE 입니다.
세 방법 모두 "조각을 합쳐 사전을 키운다"는 생각은 같고, 어떤 쌍을 먼저 합칠지 고르는 기준과 띄어쓰기를 다루는 법이 다릅니다. Qwen 은 글자가 아니라 UTF-8 바이트에서 출발하는 Byte-level BPE 라서, 한글처럼 한 글자가 3바이트인 글자도 바이트 조각으로는 반드시 쪼갤 수 있습니다.
대표 알고리즘
| 알고리즘 | 핵심 아이디어 | 쓰는 모델 |
|---|---|---|
| BPE(Byte Pair Encoding) | 가장 자주 붙어 나오는 쌍을 되풀이해 합친다 | GPT 계열, Llama 3, Qwen(Byte-level BPE) |
| WordPiece | 합쳤을 때 말뭉치 가능도가 가장 커지는 쌍을 합친다 | BERT |
| SentencePiece(BPE/Unigram) | 공백까지 문자로 보고 언어와 상관없이 원문에서 바로 배운다 | T5, Llama 2 |
짧은 예시
# Counter = 개수를 세는 사전. pandas 의 value_counts 와 비슷하다
from collections import Counter
# 작은 말뭉치: 낱말을 글자로 띄어 쓰고, 값은 그 낱말이 나온 횟수
words = {"l o w": 5, "l o w e r": 2, "n e w e s t": 6, "w i d e s t": 3}
def pair_counts(words):
# 이웃한 두 조각의 쌍마다, 낱말이 나온 횟수만큼 더해 센다
counts = Counter()
for word, freq in words.items():
pieces = word.split()
for a, b in zip(pieces, pieces[1:]):
counts[(a, b)] += freq
return counts
def merge(words, pair):
# 고른 쌍(a, b)이 붙어 있는 곳을 한 조각 ab 로 합친다
a, b = pair
merged = {}
for word, freq in words.items():
pieces = word.split()
out, i = [], 0
while i < len(pieces):
if i < len(pieces) - 1 and pieces[i] == a and pieces[i + 1] == b:
out.append(a + b)
i += 2
else:
out.append(pieces[i])
i += 1
merged[" ".join(out)] = freq
return merged
for step in range(3):
# most_common(1) = 가장 많이 나온 쌍 하나
best, count = pair_counts(words).most_common(1)[0]
words = merge(words, best)
print(step + 1, best, count, words)
# 결과:
# 1 ('e', 's') 9 {'l o w': 5, 'l o w e r': 2, 'n e w es t': 6, 'w i d es t': 3}
# 2 ('es', 't') 9 {'l o w': 5, 'l o w e r': 2, 'n e w est': 6, 'w i d est': 3}
# 3 ('l', 'o') 7 {'lo w': 5, 'lo w e r': 2, 'n e w est': 6, 'w i d est': 3}이 코드가 뜻하는 것
형광펜 칠하기를 세 번 해 본 코드입니다. 첫 번째로 ("e", "s") 가 뽑힌 까닭을 손으로 세어 봅니다. "newest" 가 6번, "widest" 가 3번 나왔고 두 낱말에 모두 "e s" 가 한 번씩 들어 있으므로 6 + 3 = 9 입니다. 두 번째에는 방금 만든 "es" 가 "t" 와 9번 붙어 "est" 가 됩니다. 실제 토크나이저는 이 일을 사전이 수만~십수만 개가 될 때까지 되풀이합니다.
| 줄 | 하는 일 | 쉽게 말하면 |
|---|---|---|
pair_counts(words) | 이웃한 두 조각의 쌍을 횟수와 함께 센다 | 짝꿍 글자 집계표 |
most_common(1) | 가장 많이 나온 쌍을 고른다 | 1등 짝꿍 고르기 |
merge(words, best) | 그 쌍을 한 조각으로 합친다 | 형광펜으로 묶기 |
주의할 점
- 첫 번째 합치기에서 ("e", "s") 와 ("s", "t") 는 둘 다 9번입니다. 횟수가 같으면 먼저 센 쌍이 뽑힙니다. 실제 토크나이저도 정해진 규칙으로 동점을 가르므로, 같은 말뭉치라도 규칙이 다르면 사전이 달라집니다.
- 사전은 학습 말뭉치를 닮습니다. 영어가 많은 말뭉치로 만든 사전은 영어 낱말을 길게 뭉치고, 적게 나온 언어는 잘게 자릅니다. 다음 절의 "한국어 토큰 수" 이야기가 여기서 나옵니다.