Download tokenizer.py from Aashw1n/openwebtext-103M: direct link, hf CLI and curl.
- Browser
- Download file 4.9 kB
-
https://huggingface.co/Aashw1n/openwebtext-103M/resolve/main/tokenizer.py
- Command line
-
hf download hf://Aashw1n/openwebtext-103M/tokenizer.py
-
curl -L -o tokenizer.py https://huggingface.co/Aashw1n/openwebtext-103M/resolve/main/tokenizer.py
4.9 kB
| from pathlib import Path | |
| import regex as re | |
| import json | |
| from collections import defaultdict | |
| from collections.abc import Iterable, Iterator | |
| def gpt2_bytes_to_unicode() -> dict[int, str]: | |
| bs = (list(range(ord("!"), ord("~") + 1)) | |
| + list(range(ord("¡"), ord("¬") + 1)) | |
| + list(range(ord("®"), ord("ÿ") + 1))) | |
| cs = bs[:] | |
| n = 0 | |
| for i in range(256): | |
| if i not in bs: | |
| bs.append(i) | |
| cs.append(256+n) | |
| n += 1 | |
| return {b:chr(c) for b,c in zip(bs,cs)} | |
| def gpt2_unicode_to_bytes() -> dict[str,int]: | |
| return {v:k for k,v in gpt2_bytes_to_unicode().items()} | |
| PAT = r"""'(?:[sdmt]|ll|ve|re)| ?\p{L}+| ?\p{N}+| ?[^\s\p{L}\p{N}]+|\s+(?!\S)|\s+""" | |
| class Tokenizer: | |
| def __init__(self, vocab: dict[int, bytes], merges: list[tuple[bytes,bytes]], special_tokens: list[str]| None = None): | |
| self.vocab = vocab | |
| self.merges = merges | |
| self.special_tokens = special_tokens or [] | |
| self.byte_to_id = {v: k for k, v in self.vocab.items()} # for encoding | |
| self.merge_rank = {pair: i for i, pair in enumerate(merges)} | |
| # Load vocab from file. | |
| def from_file(cls, vocab_filepath: str | Path, merges_filepath: str| Path, special_tokens: list[str] | None = None) -> "Tokenizer": | |
| # populate vocab | |
| byte_decoder = gpt2_unicode_to_bytes() | |
| to_bytes = lambda s: bytes(byte_decoder[ch] for ch in s) | |
| with open(vocab_filepath, encoding = "utf-8") as f: | |
| raw = json.load(f) | |
| vocab = {k:to_bytes(v) for v,k in raw.items()} | |
| # Populate merges | |
| merges = [] | |
| with open(merges_filepath, encoding = "utf-8") as f: | |
| for line in f: | |
| line = line.rstrip("\n") | |
| a , b = line.split(" ") | |
| merges.append((to_bytes(a), to_bytes(b))) | |
| return cls(vocab, merges, special_tokens) | |
| def encode(self, string: str) -> list[int]: | |
| # Segment on special tokens first. | |
| if self.special_tokens: | |
| specials = sorted(self.special_tokens, key = len, reverse=True) | |
| segment_pattern = re.compile("(" + "|".join(re.escape(t) for t in specials) + ")") | |
| segments = re.split(segment_pattern, string) | |
| else: | |
| segments = [string] | |
| result = [] | |
| chunk_pattern = re.compile(PAT) | |
| for segment in segments: | |
| if segment in self.special_tokens: | |
| result.append(self.byte_to_id[segment.encode("utf-8")]) | |
| else: | |
| for match in chunk_pattern.finditer(segment): | |
| token = match.group() | |
| byte_list = [bytes([x]) for x in token.encode("utf-8")] | |
| new_list = byte_list.copy() | |
| # Apply merges | |
| while True: | |
| # If we don't have enough for a pair, we break off | |
| if len(new_list) < 2: | |
| break | |
| # Get all pairs | |
| pairs = [(i1,i2) for i1,i2 in zip(new_list, new_list[1:])] | |
| # Get all ranks in the merge list. | |
| rank = {p: self.merge_rank[p] for p in pairs if p in self.merge_rank} | |
| if not rank: | |
| break | |
| min_pair = min(rank, key=rank.get) | |
| # We apply merge now | |
| merge_applied_list = [] | |
| i = 0 | |
| while i < len(new_list): | |
| if i < len(new_list) - 1 and new_list[i] == min_pair[0] and new_list[i+1] == min_pair[1]: | |
| merge_applied_list.append(min_pair[0] + min_pair[1]) # Concatenate/merge | |
| i += 2 | |
| else: | |
| merge_applied_list.append(new_list[i]) | |
| i += 1 | |
| new_list = merge_applied_list | |
| for tok in new_list: | |
| result.append(self.byte_to_id[tok]) | |
| return result | |
| def encode_iterable(self, iterable: Iterable[str]) -> Iterator[int]: | |
| for chunk in iterable: | |
| yield from self.encode(chunk) | |
| def decode(self, byte_array: list[int]) -> str: | |
| decoded: bytes = b"" | |
| for i in byte_array: | |
| decoded += self.vocab[i] | |
| return decoded.decode("utf-8", errors="replace") | |
| if __name__ == "__main__": | |
| #tokenizer = Tokenizer.from_file( "tests/fixtures/train-bpe-reference-vocab.json","tests/fixtures/train-bpe-reference-merges.txt") | |
| #print(tokenizer.encode("hello my friend")) | |
| #print(tokenizer.decode([259, 76, 491, 486, 377, 73, 69, 269])) | |
| ... |