트랜스포머·PEFT 2일 과정2장 · Transformer 입력 표현5 / 15← 이전목차다음 →MetaContents

2장. Transformer 입력 표현

서브워드 사전을 만드는 방법

신문에서 자주 함께 나오는 글자 쌍에 형광펜을 칠한다고 해 봅시다. "e 다음에 s" 가 가장 많이 나오면 둘을 묶어 "es" 를 한 글자처럼 취급합니다. 다시 세어 보니 "es 다음에 t" 가 가장 많으면 "est" 로 묶습니다. 이 일을 수만 번 되풀이하면 자주 쓰는 조각은 점점 길게 뭉치고, 드문 조각은 짧게 남습니다. 이것이 BPE 입니다.

BPE, WordPiece, SentencePiece 의 세 상자를 나란히 놓은 비교 그림

세 방법 모두 "조각을 합쳐 사전을 키운다"는 생각은 같고, 어떤 쌍을 먼저 합칠지 고르는 기준과 띄어쓰기를 다루는 법이 다릅니다. 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

짧은 예시

Python
# 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번입니다. 횟수가 같으면 먼저 센 쌍이 뽑힙니다. 실제 토크나이저도 정해진 규칙으로 동점을 가르므로, 같은 말뭉치라도 규칙이 다르면 사전이 달라집니다.
  • 사전은 학습 말뭉치를 닮습니다. 영어가 많은 말뭉치로 만든 사전은 영어 낱말을 길게 뭉치고, 적게 나온 언어는 잘게 자릅니다. 다음 절의 "한국어 토큰 수" 이야기가 여기서 나옵니다.
← 이전 절2장 · 토큰화: 문장을 레고 블록으로다음 절 →2장 · 토큰 수가 중요한 이유