토크나이저 커스터마이징
토크나이저 커스터마이징 (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를 사용해야 해요.
- 패딩 방향, 사용할 토크나이제이션 알고리즘 같은 클래스 속성으로 TokenizersBackend를 서브클래싱해요.
__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")