slm / src /dataset.py
adeena43's picture
deploy minigpt
8ce1de8 verified
Raw History Blame Contribute Delete
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)),
}