from collections.abc import Mapping, Sequence
from os import PathLike
from pathlib import Path
from typing import Literal
from ._lib import BpeTrainer_Character_CharIdx, BpeTrainer_u8_Idx
CharLevel = Literal["char", "u8"]
OutputFormat = Literal["uni", "gpt2"]
InitialAlphabet = Literal["raw", "byte_level"]
TieBreak = Literal["smallest_pair_id", "largest_content"]
class BpeTrainer:
def __init__(
self,
special_tokens: Sequence[str],
*,
ch: CharLevel = "u8",
output_format: OutputFormat | None = None,
initial_alphabet: InitialAlphabet | None = None,
tie_break: TieBreak | None = None,
parallel_merge_min_occurs_in: int | None = None,
) -> None:
self._ch: CharLevel = ch
self.output_format: OutputFormat = "uni"
if ch == "char":
self._trainer = BpeTrainer_Character_CharIdx(
special_tokens=special_tokens,
initial_alphabet=initial_alphabet,
tie_break=tie_break,
parallel_merge_min_occurs_in=parallel_merge_min_occurs_in,
)
else:
self.output_format = output_format or "gpt2"
self._trainer = BpeTrainer_u8_Idx(
special_tokens=special_tokens,
initial_alphabet=initial_alphabet,
tie_break=tie_break,
parallel_merge_min_occurs_in=parallel_merge_min_occurs_in,
)
@property
def vocab_size(self) -> int:
return self._trainer.vocab_size()
@property
def char_level(self) -> CharLevel:
return self._ch
@property
def vocabs(self):
return self._trainer.get_vocabs()
def add_words(self, words: Mapping[str, int] | Sequence[tuple[str, int]]) -> None:
if isinstance(words, Mapping):
words = list(words.items())
self._trainer.add_words(words)
def init_training(self) -> None:
self._trainer.init_training()
def train(self, vocab_size: int) -> None:
try:
self._trainer.train_until(vocab_size)
except:
return
def step(self) -> int | None:
try:
return self._trainer.step() or self._trainer.vocab_size()
except:
return None
def save(self, name: str, *, outdir: str | PathLike = ".", output_format: OutputFormat | None = None) -> None:
vocab_path = Path(outdir) / f"vocab.{name}[{self.char_level}].json"
merges_path = Path(outdir) / f"merges.{name}[{self.char_level}].txt"
self.save_files(vocab_path, merges_path, output_format=output_format)
def save_files(
self,
vocab_path: str | PathLike,
merges_path: str | PathLike,
*,
output_format: OutputFormat | None = None,
) -> None:
spec = output_format or self.output_format
self._trainer.save_vocab(vocab_path, spec)
self._trainer.save_merges_txt(merges_path, spec)