Download src/oxmini/tokenizer.py from Shivam3002/OxMini: direct link, hf CLI and curl.
- Browser
- Download file 2.78 kB
-
https://huggingface.co/Shivam3002/OxMini/resolve/main/src/oxmini/tokenizer.py
- Command line
-
hf download hf://Shivam3002/OxMini/src/oxmini/tokenizer.py
-
curl -L -o tokenizer.py https://huggingface.co/Shivam3002/OxMini/resolve/main/src/oxmini/tokenizer.py
2.78 kB
| """A deliberately small character-level tokenizer.""" | |
| from __future__ import annotations | |
| import json | |
| from pathlib import Path | |
| from typing import Iterable | |
| class CharTokenizer: | |
| """Maps individual Unicode characters to integer token IDs.""" | |
| unk_token = "<unk>" | |
| def __init__(self, itos: Iterable[str]) -> None: | |
| values = list(itos) | |
| # Reserve ID 0 for out-of-vocabulary input. In a deterministic corpus | |
| # split this normally remains unused, but it makes interactive prompts | |
| # safe when they contain a character absent from training. | |
| if not values or values[0] != self.unk_token: | |
| values = [self.unk_token, *[item for item in values if item != self.unk_token]] | |
| if len(values) != len(set(values)): | |
| raise ValueError("token vocabulary contains duplicates") | |
| self.itos = values | |
| self.stoi = {token: index for index, token in enumerate(values)} | |
| def build(cls, text: str) -> "CharTokenizer": | |
| if not text: | |
| raise ValueError("cannot build a tokenizer from empty text") | |
| # Sorting gives a stable vocabulary independent of set iteration order. | |
| return cls(sorted(set(text))) | |
| def vocab_size(self) -> int: | |
| return len(self.itos) | |
| def encode(self, text: str) -> list[int]: | |
| unk_id = self.stoi[self.unk_token] | |
| return [self.stoi.get(char, unk_id) for char in text] | |
| def decode(self, ids: Iterable[int]) -> str: | |
| pieces: list[str] = [] | |
| for token_id in ids: | |
| if not 0 <= int(token_id) < self.vocab_size: | |
| raise ValueError(f"token ID {token_id} is outside the vocabulary") | |
| token = self.itos[int(token_id)] | |
| pieces.append("�" if token == self.unk_token else token) | |
| return "".join(pieces) | |
| def to_dict(self) -> dict[str, object]: | |
| return { | |
| "type": "character-level", | |
| "unk_token": self.unk_token, | |
| "vocab_size": self.vocab_size, | |
| "itos": self.itos, | |
| "stoi": self.stoi, | |
| } | |
| def save(self, path: str | Path) -> None: | |
| path = Path(path) | |
| path.parent.mkdir(parents=True, exist_ok=True) | |
| with path.open("w", encoding="utf-8") as handle: | |
| json.dump(self.to_dict(), handle, ensure_ascii=False, indent=2, sort_keys=True) | |
| handle.write("\n") | |
| def load(cls, path: str | Path) -> "CharTokenizer": | |
| with Path(path).open("r", encoding="utf-8") as handle: | |
| values = json.load(handle) | |
| if values.get("type") != "character-level" or not isinstance(values.get("itos"), list): | |
| raise ValueError(f"unsupported tokenizer file: {path}") | |
| return cls(values["itos"]) | |