Download export.py from GeorgeCodesSometimes/sprout-instruct: direct link, hf CLI and curl.
- Browser
- Download file 7.45 kB
-
https://huggingface.co/GeorgeCodesSometimes/sprout-instruct/resolve/main/export.py
- Command line
-
hf download hf://GeorgeCodesSometimes/sprout-instruct/export.py
-
curl -L -o export.py https://huggingface.co/GeorgeCodesSometimes/sprout-instruct/resolve/main/export.py
7.45 kB
| """Export instruction-tuned weights and a standalone CPU demo, without uploading. | |
| python export.py --checkpoint checkpoints/sft/best.pt --out checkpoints/release | |
| """ | |
| import argparse | |
| import json | |
| from pathlib import Path | |
| import shutil | |
| import subprocess | |
| import torch | |
| from safetensors.torch import save_model | |
| from eval import load as load_checkpoint | |
| from inference import FIELDS, TEMPLATE, digest, load | |
| def main(): | |
| parser = argparse.ArgumentParser(description=__doc__) | |
| parser.add_argument("--checkpoint", default="checkpoints/sft/best.pt") | |
| parser.add_argument("--data", default="data") | |
| parser.add_argument("--out", default="checkpoints/release") | |
| args = parser.parse_args() | |
| out = Path(args.out) | |
| if out.exists() and any(out.iterdir()): | |
| raise ValueError("Output directory is not empty. Use a new directory.") | |
| torch.set_num_threads(4) | |
| model, tok, cfg, step = load_checkpoint(args.checkpoint, args.data, "cpu") | |
| checkpoint = torch.load(args.checkpoint, map_location="cpu", weights_only=False) | |
| if "sft" not in checkpoint or checkpoint["sft"]["template"] != TEMPLATE: | |
| raise ValueError("Export requires an instruction-tuned checkpoint with the story template.") | |
| out.mkdir(parents=True, exist_ok=True) | |
| save_model(model, str(out / "model.safetensors")) | |
| shutil.copyfile(Path(args.data) / "tokenizer.model", out / "tokenizer.model") | |
| architecture = {key: cfg[key] for key in ("n_layer", "dim", "n_head", "ctx", "vocab")} | |
| architecture["pos"] = cfg.get("pos", "rope") | |
| root = Path(__file__).resolve().parent | |
| commit = subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=root, text=True).strip() | |
| config = dict(format_version=1, model=architecture, template=TEMPLATE, fields=FIELDS, | |
| special_tokens=tok.special, dtype="float32", step=step, | |
| source_checkpoint_sha256=digest(args.checkpoint), source_commit=commit, | |
| training_fingerprint=checkpoint["fingerprint"], | |
| instruction_revision=checkpoint["sft"]["revision"], | |
| sha256={name: digest(out / name) for name in ("model.safetensors", "tokenizer.model")}) | |
| (out / "config.json").write_text(json.dumps(config, indent=2) + "\n") | |
| for name in ("model.py", "tokenizer.py", "inference.py", "app.py", "requirements.txt", "LICENSE"): | |
| shutil.copyfile(root / name, out / name) | |
| params = sum(p.numel() for p in model.parameters()) | |
| run = checkpoint.get("wandb_id") | |
| card = f"""--- | |
| license: mit | |
| language: | |
| - en | |
| datasets: | |
| - roneneldan/TinyStories | |
| - roneneldan/TinyStoriesInstruct | |
| pipeline_tag: text-generation | |
| tags: | |
| - pytorch | |
| - safetensors | |
| - custom-code | |
| --- | |
| # Sprout story model | |
| A decoder-only model trained from scratch on TinyStoriesV2-GPT4 and instruction tuned on TinyStoriesInstruct. It has {params:,} parameters, {cfg['n_layer']} layers, width {cfg['dim']}, {cfg['n_head']} attention heads, a {cfg['ctx']}-token context, {architecture['pos']} positions, RMSNorm, SwiGLU, tied embeddings, and a {cfg['vocab']}-token byte-level BPE vocabulary. | |
| [Try the live browser demo](https://huggingface.co/spaces/GeorgeCodesSometimes/sprout). The static demo runs float32 ONNX weights on your device; prompts stay local. | |
| This bundle contains float32 inference weights at instruction step {step:,}. It excludes optimizer state, training data, credentials, and training dependencies. The tied embedding/output tensor is stored once and restored with `safetensors.torch.load_model`. `config.json` records weight and tokenizer SHA-256 checksums, source checkpoint SHA-256, source code commit, and the original training fingerprint. | |
| ## Run locally | |
| Download all files from this model repository into an empty directory, then run: | |
| ```bash | |
| python -m pip install -r requirements.txt | |
| python app.py | |
| ``` | |
| Use the included loading module directly: | |
| ```python | |
| from inference import load, stream | |
| model, tok, config = load(".") | |
| for text in stream(model, tok, {{"Summary": "A little fox learns to be patient."}}): | |
| print(text, end="\\r", flush=True) | |
| ``` | |
| `load` also accepts a Hub model ID and an optional `revision` commit. The included code is the loading contract; this is not a Transformers `AutoModel` repository. Loading does not require any training corpus files. The demo uses the exact training template, keeps the full instruction in context, and supports streamed output, stopping, temperature, and top-k. | |
| ## Training and evaluation | |
| The source checkpoint's instruction validation loss is {checkpoint['best_val']:.6f}. The [training run](https://wandb.ai/george-nasseem/sprout/runs/{run}) records configuration and losses. See the [project README](https://github.com/gnasseem/sprout#instruction-tuning) for the fixed behavioral evaluation, comparison with the pretrained model, and base-validation regression. Low validation loss does not establish reliable constraint following. | |
| Pretraining uses the story-level seeded split in the project, with 537,686,596 training tokens and 5,402,089 validation tokens. Instruction preparation excludes malformed and duplicate stories, held-out story overlap, and conversations beyond the context limit. TinyStoriesInstruct is pinned to `{config['instruction_revision']}`. These are synthetic English stories generated by GPT models, not a general conversational corpus. | |
| ## Limitations and intended use | |
| An educational story-generation experiment. It often misses requested words and sentences, repeats itself, and loses narrative details. Instruction tuning regresses base-story validation loss. Long instructions leave less room for the story. It has no general chat or factual-answering capability, no content filter, and no suitability evaluation for children. Outputs can contain biases or disturbing story content from its synthetic source corpus. | |
| ## License and provenance | |
| Code and exported weights use the included MIT license. The upstream [TinyStories dataset card](https://huggingface.co/datasets/roneneldan/TinyStories) lists CDLA-Sharing-1.0; [TinyStoriesInstruct](https://huggingface.co/datasets/roneneldan/TinyStoriesInstruct) is the separate instruction corpus. No source stories are redistributed in this bundle. The dataset creators' [paper](https://arxiv.org/abs/2305.07759) describes TinyStories. | |
| """ | |
| (out / "README.md").write_text(card, encoding="utf-8") | |
| (out / "SPACE.md").write_text("""--- | |
| title: Sprout Story Workshop | |
| sdk: gradio | |
| sdk_version: 6.30.0 | |
| python_version: '3.11' | |
| app_file: app.py | |
| pinned: false | |
| license: mit | |
| --- | |
| # Sprout Story Workshop | |
| An experimental small story model. It can miss constraints, repeat itself, and lose story details. | |
| """) | |
| restored, restored_tok, _ = load(out) | |
| prefix = tok.encode(TEMPLATE.format(instruction="Write a short story.\nSummary: A fox finds a seed.")) | |
| x = torch.tensor([prefix]) | |
| before = model.generate(x, 64, temperature=0) | |
| after = restored.generate(x, 64, temperature=0) | |
| assert torch.equal(before, after), "export changed greedy generation" | |
| assert model.head.weight is model.tok_emb.weight | |
| assert restored.head.weight is restored.tok_emb.weight | |
| assert restored_tok.decode(after[0].tolist()) == tok.decode(before[0].tolist()) | |
| print(f"Exported {params:,} parameters; 64 greedy tokens match; tied weights and tokenizer match.") | |
| print(f"Bundle: {out.resolve()}; weights: {(out / 'model.safetensors').stat().st_size:,} bytes") | |
| if __name__ == "__main__": | |
| main() | |