| """ |
| Self-contained loader for this checkpoint. |
| |
| Reconstructs the model architecture and tokenizer entirely from |
| config.json (no hardcoded hyperparameters here), loads the trained |
| weights, and generates a sample -- proving this folder is sufficient on |
| its own, with no dependency on the original training repo. |
| |
| Usage: |
| python load_model.py |
| """ |
|
|
| from __future__ import annotations |
|
|
| import json |
| from pathlib import Path |
|
|
| import torch |
|
|
| from bigram import BigramLanguageModel |
|
|
| HERE = Path(__file__).resolve().parent |
|
|
|
|
| def _load_weights(path: Path) -> dict[str, torch.Tensor]: |
| if path.suffix == ".safetensors": |
| from safetensors.torch import load_file |
|
|
| return load_file(path) |
| return torch.load(path, map_location="cpu", weights_only=True) |
|
|
|
|
| def load_model_and_tokenizer(dir_path: Path = HERE): |
| config = json.loads((dir_path / "config.json").read_text()) |
|
|
| if config["tokenizer_type"] == "char": |
| from tokenizer import CharTokenizer as TokenizerClass |
| elif config["tokenizer_type"] == "bpe": |
| from bpe_tokenizer import BPETokenizer as TokenizerClass |
| else: |
| raise ValueError(f"Unknown tokenizer_type: {config['tokenizer_type']!r}") |
| tokenizer = TokenizerClass.load(dir_path / config["tokenizer_file"]) |
|
|
| model = BigramLanguageModel( |
| vocab_size=config["vocab_size"], |
| block_size=config["block_size"], |
| n_embd=config["n_embd"], |
| n_head=config["n_head"], |
| n_layer=config["n_layer"], |
| ) |
| state_dict = _load_weights(dir_path / config["weights_file"]) |
| model.load_state_dict(state_dict) |
| model.eval() |
|
|
| return model, tokenizer, config |
|
|
|
|
| def main() -> None: |
| model, tokenizer, config = load_model_and_tokenizer() |
|
|
| n_params = sum(p.numel() for p in model.parameters()) |
| print(f"Loaded model: {n_params:,} parameters (config.json says {config['parameter_count']:,})") |
| assert n_params == config["parameter_count"], "Reconstructed model doesn't match config!" |
|
|
| context = torch.zeros((1, 1), dtype=torch.long) |
| print("\n----- generated sample -----") |
| sample = tokenizer.decode(model.generate(context, max_new_tokens=400)[0].tolist()) |
| print(sample) |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|