토크나이저 커스터마이징

토크나이저 커스터마이징 (Customizing tokenizers)

토크나이저는 학습된 어휘에서 분리돼 있어요. 그래서 학습용 빈 토크나이저를 만들거나 나만의 어휘로 직접 토크나이저를 만들 수 있어요. 훈련과 커스텀 어휘, 서브클래싱 방법을 살펴볼게요.

출처: 문서

본문

토크나이저는 학습된 어휘에서 분리(decoupled)돼 있어요. 덕분에 학습용 빈 토크나이저를 초기화하거나 나만의 어휘로 직접 토크나이저를 만들 수 있어요. 밑바닥 토크나이제이션 파이프라인(normalizer, pre-tokenizer, 토크나이제이션 알고리즘)은 그대로 유지되므로 처음부터 다시 만들 필요가 없어요.

이 가이드는 커스텀 토크나이저를 훈련하고 만드는 방법을 보여줘요.

토크나이저 훈련하기 (Training a tokenizer)

빈 훈련용 토크나이저는 어휘를 새 목표 어휘로 교체해요. 금융 같은 새 도메인, 저자원 언어, 또는 코드에 적응할 때 유용해요.

빈 토크나이저를 만들고 데이터셋을 불러와요.

from datasets import load_dataset
from transformers import GemmaTokenizer

tokenizer = GemmaTokenizer()
dataset = load_dataset("Josephgflowers/Finance-Instruct-500k", split="train")

TokenizersBackend.train_new_from_iterator() 메서드로 토크나이저를 훈련해요. 이 메서드는 모든 것을 한 번에 메모리에 불러오는 대신 데이터셋에서 텍스트 청크를 반환하는 제너레이터 함수를 받아요. vocab_size 인자는 토크나이저의 어휘 크기를 설정해요.

def batch_iterator(batch_size=1000):
    for i in range(0, len(dataset), batch_size):
        yield dataset[i : i + batch_size]["assistant"]

trained_tokenizer = tokenizer.train_new_from_iterator(
    batch_iterator(),
    vocab_size=32000,
)
encoded = trained_tokenizer("The stock market rallied today.")
print(encoded["input_ids"])
[5866, 11503, 98, 5885, 8617, 13381, 30]

새 특수 토큰은 new_special_tokens 인자로 추가하거나, special_tokens_map을 사용해 기존 특수 토큰의 이름을 새 특수 토큰으로 바꿀 수 있어요.

새 금융 토크나이저는 save_pretrained()로 저장하거나, push_to_hub()로 저장·업로드해요. 이러면 새로 학습된 어휘, merge 규칙, 전체 파이프라인 설정을 담은 tokenizer.json 파일이 생성돼요.

trained_tokenizer.save_pretrained("./finance-gemma-tokenizer")
trained_tokenizer.push_to_hub("finance-gemma-tokenizer")

커스텀 어휘 (Custom vocabulary)

빈 토크나이저는 vocab과 merges 인자로 커스텀 어휘를 지원해요.

  • vocab은 토크나이저가 아는 토큰의 완전한 집합이고, 각 항목은 토큰을 입력 id에 매핑해요.
  • merges는 BPE 알고리즘이 인접 토큰을 어떻게 결합할지 정의해요.
from transformers import GemmaTokenizer

vocab={
    "<pad>": 0,
    "</s>": 1,
    "<s>": 2,
    "<unk>": 3,
    "<mask>": 4,
    "▁the": 5,
    "▁stock": 6,
    "▁market": 7,
    "▁": 8,
    "r": 9,
    "a": 10,
    "l": 11,
    "i": 12,
    "e": 13,
    "d": 14,
    "ra": 15,
    "li": 16,
    "lie": 17,
    "lied": 18,
    "ral": 19,
    "ralli": 20,
    "rallie": 21,
    "rallied": 22,
}
merges=[
    ("r", "a"),       # r + a → ra
    ("l", "i"),       # l + i → li
    ("li", "e"),      # li + e → lie
    ("lie", "d"),     # lie + d → lied
    ("ra", "l"),      # ra + l → ral
    ("ral", "li"),    # ral + li → ralli
    ("ralli", "e"),   # ralli + e → rallie
    ("rallie", "d"),  # rallie + d → rallied
]

tokenizer = GemmaTokenizer(vocab=vocab, merges=merges)
encoded = tokenizer("the stock market rallied")
print(encoded["input_ids"])

TokenizersBackend 서브클래싱 (Subclassing TokenizersBackend)

Tokenizers는 네 가지 다른 백엔드를 지원해요. 일반적으로 새 토크나이저를 정의할 때는 TokenizersBackend를 사용하는 게 더 빠르기 때문에 권장해요.

[!TIP] PythonBackend은 Rust, SentencePiece, mistral-common 같은 백엔드에 의존하지 않는 순수 Python 토크나이저예요. Rust 백엔드로 표현할 수 없는 아주 특수한 토크나이저를 만들 때만 PythonBackend를 사용해야 해요.

  1. 패딩 방향, 사용할 토크나이제이션 알고리즘 같은 클래스 속성으로 TokenizersBackend를 서브클래싱해요.
  2. __init__에서 토크나이제이션 파이프라인을 정의해요. 사용할 토크나이제이션 알고리즘, 알고리즘 전에 원시 텍스트를 어떻게 나눌지, 토큰을 텍스트로 다시 디코딩하는 방법을 포함해요.
from tokenizers import Tokenizer, decoders, pre_tokenizers
from tokenizers.models import BPE
from transformers import TokenizersBackend

class NewTokenizer(TokenizersBackend):
    padding_side = "left"
    model = BPE

    def __init__(
        self,
        vocab=None,
        merges=None,
        unk_token="<unk>",
        bos_token="<s>",
        eos_token="</s>",
        pad_token="<pad>",
    ):
        self._vocab = vocab or {
            str(unk_token): 0,
            str(bos_token): 1,
            str(eos_token): 2,
            str(pad_token): 3,
        }
        self._merges = merges or []

        self._tokenizer = Tokenizer(
            BPE(vocab=self._vocab, merges=self._merges, fuse_unk=True)
        )
        self._tokenizer.pre_tokenizer = pre_tokenizers.ByteLevel(add_prefix_space=False)
        self._tokenizer.decoder = decoders.ByteLevel()

        super().__init__(
            unk_token=unk_token,
            bos_token=bos_token,
            eos_token=eos_token,
            pad_token=pad_token,
        )

새 빈 토크나이저를 훈련하거나 저장해요.

tokenizer = NewTokenizer()

# train on new corpus
corpus = ["The stock market rallied today.", "Bond yields fell sharply."]
trained_tokenizer = tokenizer.train_new_from_iterator(corpus, vocab_size=32000)
# save tokenizer
trained_tokenizer.save_pretrained("./new-tokenizer")

더 알아보기 (Learn more)