Download src/text8.py from GOVINDFROM/parallel-consistent-masked-diffusion: direct link, hf CLI and curl.
- Browser
- Download file 3.46 kB
-
https://huggingface.co/GOVINDFROM/parallel-consistent-masked-diffusion/resolve/main/src/text8.py
- Command line
-
hf download hf://GOVINDFROM/parallel-consistent-masked-diffusion/src/text8.py
-
curl -L -o text8.py https://huggingface.co/GOVINDFROM/parallel-consistent-masked-diffusion/resolve/main/src/text8.py
3.46 kB
| """text8: the standard character-level language-modelling benchmark. | |
| 100M characters of cleaned English Wikipedia, vocabulary of 27 symbols (a-z and | |
| space). The conventional split is the first 90M characters for training, the | |
| next 5M for validation and the last 5M for test; it is used unchanged here so | |
| bits-per-character is comparable with published numbers. | |
| This is the task that separates the two failure modes of parallel decoding. | |
| Addition has unique answers, so every conditional is a point mass and all of the | |
| damage comes from marginals that were never trained at high mask ratios. Natural | |
| text has both kinds of position at once: closing a word, a suffix or a function | |
| word is nearly deterministic given the context, while the choice of the next | |
| content word carries real entropy. A method that claims to work "across all | |
| things" has to handle a sequence in which both appear together. | |
| """ | |
| import os | |
| import numpy as np | |
| URL = "http://mattmahoney.net/dc/text8.zip" | |
| VOCAB = " abcdefghijklmnopqrstuvwxyz" # 27 symbols, exactly as distributed | |
| # the conventional text8 split, in characters | |
| N_TRAIN, N_VALID, N_TEST = 90_000_000, 5_000_000, 5_000_000 | |
| class Text8Tokenizer: | |
| """Character vocabulary plus the absorbing [MASK] state. | |
| [MASK] is appended after the 27 real characters, so ids 0..26 are the data | |
| symbols and id 27 is the absorbing state. No padding symbol is needed: every | |
| training sequence is a fixed-length contiguous window of the corpus. | |
| """ | |
| def __init__(self): | |
| self.itos = list(VOCAB) + ["[MASK]"] | |
| self.stoi = {c: i for i, c in enumerate(self.itos)} | |
| self.mask = self.stoi["[MASK]"] | |
| self.pad = self.mask # unused; kept for interface compatibility | |
| def __len__(self): | |
| return len(self.itos) | |
| def encode(self, s): | |
| return np.array([self.stoi[c] for c in s if c in self.stoi], dtype=np.int64) | |
| def decode(self, ids): | |
| return "".join(self.itos[int(i)] for i in ids) | |
| def _raw(root): | |
| path = os.path.join(root, "text8") | |
| if not os.path.exists(path): | |
| raise FileNotFoundError( | |
| f"{path} not found. Fetch it once with:\n" | |
| f" mkdir -p {root} && cd {root} && curl -sLO {URL} && unzip -q text8.zip") | |
| with open(path, "r") as f: | |
| return f.read() | |
| def load_text8(root="data/text8", seq_len=256): | |
| """Return (train, valid, test) integer arrays of shape (n_windows, seq_len). | |
| Windows are non-overlapping and contiguous, which keeps the evaluation | |
| splits strictly disjoint from training: no window can straddle a split | |
| boundary, so no test character is ever seen during training. | |
| """ | |
| tok = Text8Tokenizer() | |
| text = _raw(root) | |
| if len(text) < N_TRAIN + N_VALID + N_TEST: | |
| raise ValueError(f"text8 is {len(text)} chars, expected 100,000,000") | |
| ids = tok.encode(text) | |
| tr = ids[:N_TRAIN] | |
| va = ids[N_TRAIN:N_TRAIN + N_VALID] | |
| te = ids[N_TRAIN + N_VALID:N_TRAIN + N_VALID + N_TEST] | |
| def windows(a): | |
| n = len(a) // seq_len | |
| return a[:n * seq_len].reshape(n, seq_len) | |
| return windows(tr), windows(va), windows(te), tok | |
| def batches(arr, bs, seed=0, shuffle=True): | |
| """Infinite batch iterator over pre-windowed data.""" | |
| rng = np.random.default_rng(seed) | |
| n = len(arr) | |
| while True: | |
| idx = rng.permutation(n) if shuffle else np.arange(n) | |
| for i in range(0, n - bs + 1, bs): | |
| yield arr[idx[i:i + bs]] | |