Download src/dataset.py from adeena43/slm: direct link, hf CLI and curl.
- Browser
- Download file 2.86 kB
-
https://huggingface.co/spaces/adeena43/slm/resolve/main/src/dataset.py
- Command line
-
hf download hf://spaces/adeena43/slm/src/dataset.py
-
curl -L -o dataset.py https://huggingface.co/spaces/adeena43/slm/resolve/main/src/dataset.py
2.86 kB
| """Data pipeline: download, tokenise, split, and build (input, shifted-target) batches. | |
| For a window of tokens t[i], t[i+1], ..., t[i+T] we use | |
| x = t[i : i+T] (inputs) | |
| y = t[i+1 : i+T+1] (targets) -> y[k] is the token that FOLLOWS x[k]. | |
| """ | |
| from __future__ import annotations | |
| import urllib.request | |
| from pathlib import Path | |
| import torch | |
| from src.tokenizer import CharTokenizer | |
| from src.utils import resolve | |
| # Same Tiny Shakespeare text as the Hugging Face card (karpathy/tiny_shakespeare); this raw copy is easiest to fetch. | |
| DATA_URL = "https://raw.githubusercontent.com/karpathy/char-rnn/master/data/tinyshakespeare/input.txt" | |
| def load_text(path: str | Path = "data/input.txt", download: bool = True) -> str: | |
| """Read the corpus; download Tiny Shakespeare if the file is missing.""" | |
| p = resolve(path) | |
| if not p.exists(): | |
| if not download: | |
| raise FileNotFoundError(f"{p} not found. Download Tiny Shakespeare into data/input.txt") | |
| p.parent.mkdir(parents=True, exist_ok=True) | |
| print(f"Downloading Tiny Shakespeare to {p} ...") | |
| urllib.request.urlretrieve(DATA_URL, p) | |
| return p.read_text(encoding="utf-8") | |
| def build_splits(text: str, tokenizer: CharTokenizer, val_fraction: float = 0.1): | |
| """Contiguous split (no shuffling across the boundary): first 90% train, last 10% val.""" | |
| data = torch.tensor(tokenizer.encode(text), dtype=torch.long) | |
| n_train = int(len(data) * (1.0 - val_fraction)) | |
| return data[:n_train], data[n_train:] | |
| def get_batch(data: torch.Tensor, batch_size: int, context_length: int, device="cpu"): | |
| """Random windows. Returns x, y of shape (B, T) with y shifted one step to the right.""" | |
| max_start = len(data) - context_length - 1 | |
| ix = torch.randint(0, max_start + 1, (batch_size,)) | |
| x = torch.stack([data[i : i + context_length] for i in ix]) | |
| y = torch.stack([data[i + 1 : i + context_length + 1] for i in ix]) | |
| return x.to(device), y.to(device) | |
| def sequential_batches(data: torch.Tensor, batch_size: int, context_length: int, device="cpu"): | |
| """Non-overlapping windows covering the whole split (used for full-validation perplexity).""" | |
| n = (len(data) - 1) // context_length | |
| xs = torch.stack([data[i * context_length : (i + 1) * context_length] for i in range(n)]) | |
| ys = torch.stack([data[i * context_length + 1 : (i + 1) * context_length + 1] for i in range(n)]) | |
| for s in range(0, n, batch_size): | |
| yield xs[s : s + batch_size].to(device), ys[s : s + batch_size].to(device) | |
| def dataset_stats(text: str, tokenizer: CharTokenizer, train: torch.Tensor, val: torch.Tensor) -> dict: | |
| return { | |
| "num_characters": len(text), | |
| "num_lines": text.count("\n") + 1, | |
| "vocab_size": tokenizer.vocab_size, | |
| "train_tokens": int(len(train)), | |
| "val_tokens": int(len(val)), | |
| } | |