release: gatsby-nanogpt-2 (checkpoint + ONNX + tokenizer + model card)
Browse files- README.md +181 -0
- ckpt.pt +3 -0
- gatsby-nanogpt-2.int8.onnx +3 -0
- gatsby-nanogpt-2.onnx +3 -0
- gatsby-nanogpt-2.vocab.json +1 -0
README.md
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: mit
|
| 3 |
+
language:
|
| 4 |
+
- en
|
| 5 |
+
library_name: nanogpt
|
| 6 |
+
pipeline_tag: text-generation
|
| 7 |
+
tags:
|
| 8 |
+
- gatsby
|
| 9 |
+
- nanogpt
|
| 10 |
+
- char-level
|
| 11 |
+
- gpt
|
| 12 |
+
- synthetic-data
|
| 13 |
+
- mixture-of-models
|
| 14 |
+
- steerability
|
| 15 |
+
---
|
| 16 |
+
|
| 17 |
+
# Model Card — `gatsby-nanogpt-2` (v2)
|
| 18 |
+
|
| 19 |
+
> A [sup computer](https://supcpu.romellogoodman.com) release — a small language model studio. [Model page](https://supcpu.romellogoodman.com/models/gatsby-nanogpt-2/) · [monorepo](https://github.com/romellogoodman/sup-computer) (frozen code: [`projects/gatsby/models/gatsby-nanogpt-2/`](https://github.com/romellogoodman/sup-computer/tree/main/projects/gatsby/models/gatsby-nanogpt-2), tag `gatsby-nanogpt-2`) · runs in your browser at [supcpu.romellogoodman.com/model-player](https://supcpu.romellogoodman.com/model-player/).
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
<div class="takeaways">
|
| 23 |
+
<p class="takeaways-label">Key takeaways</p>
|
| 24 |
+
<ul>
|
| 25 |
+
<li>A char-level GPT behaviourally <strong>peer to the paid baseline</strong> (<code>gatsby-nanogpt-1</code>) — the same green-light obsession and working <code>green=1..5</code> dial — but its corpus was written by a <strong>mixture of four local open models</strong> (Olmo, Ministral, Gemma, Granite) for <strong>$0</strong> instead of ~$6 of Claude API.</li>
|
| 26 |
+
<li>The headline finding is about <strong>the blend, not the pipeline</strong>: a Granite-heavy first round broke the dial flat, because Granite barely modulates the green light across levels. <strong>Which generators you lean on is a design decision with teeth.</strong></li>
|
| 27 |
+
<li>Rebalancing off Granite and <strong>doubling the corpus</strong> (1k→2k stories) recovered the dial — the model needed the extra headroom to learn the conditioning the corpus already contained.</li>
|
| 28 |
+
<li>Same status as v1: a documented <strong>milestone, not exhibit-ready</strong>. Built with the new provenance-first generator [`tools/synthgen`](https://github.com/romellogoodman/sup-computer/blob/main/tools/synthgen/README.md) ([ADR-0014](https://github.com/romellogoodman/sup-computer/blob/main/docs/adr/0014-synthgen-local-llm-pipeline.md)).</li>
|
| 29 |
+
</ul>
|
| 30 |
+
</div>
|
| 31 |
+
|
| 32 |
+
A character-level GPT fixated on **Jay Gatsby's green light**, with a baked-in
|
| 33 |
+
**intensity dial** (`[green=1]` undertow → `[green=5]` swallows the story) — the
|
| 34 |
+
same behaviour as [`gatsby-nanogpt-1`](https://github.com/romellogoodman/sup-computer/blob/main/research-docs/model-cards/gatsby-nanogpt-1.md), but trained on a
|
| 35 |
+
corpus written by a **mixture of four local open models** (Olmo 3, Ministral 3,
|
| 36 |
+
Gemma 4, Granite 4.1) instead of the Claude API. Cost to write the corpus: **$0**.
|
| 37 |
+
Second model in the [`gatsby-nanogpt`](https://github.com/romellogoodman/sup-computer/blob/main/projects/gatsby/README.md) series;
|
| 38 |
+
see [Experiment 04](https://github.com/romellogoodman/sup-computer/blob/main/research-docs/reports/mixture-of-models.md).
|
| 39 |
+
|
| 40 |
+
> **The artifact is the behavior, not the prose.** A small, legible model you can
|
| 41 |
+
> nudge with a dial — not a general-purpose language model. v2's contribution is
|
| 42 |
+
> *how the corpus was made*: a free, local, four-voice mixture in place of one
|
| 43 |
+
> paid generator.
|
| 44 |
+
|
| 45 |
+
## Model details
|
| 46 |
+
|
| 47 |
+
| | |
|
| 48 |
+
|---|---|
|
| 49 |
+
| **Version / git tag** | `gatsby-nanogpt-2` (research run `mix-2k-r2`) |
|
| 50 |
+
| **Architecture** | base char-level nanoGPT — Transformer decoder, LayerNorm, learned positional embeddings, biases |
|
| 51 |
+
| **Size** | 6 layers · 6 heads · 384 embedding dim · 512 context · ~10.65M params |
|
| 52 |
+
| **Tokenizer** | character-level, **80-char** vocabulary (direct char↔int lookup, derived from the corpus; no BPE) |
|
| 53 |
+
| **Checkpoint** | `projects/gatsby/models/gatsby-nanogpt-2/ckpt.pt` (weights not committed — rebuild below) |
|
| 54 |
+
| **Built on** | [nanoGPT](https://github.com/karpathy/nanoGPT) by Andrej Karpathy (MIT), vendored |
|
| 55 |
+
| **Corpus generator** | [`tools/synthgen`](https://github.com/romellogoodman/sup-computer/blob/main/tools/synthgen/README.md) + LM Studio (local) |
|
| 56 |
+
| **Developed with** | Claude ([Claude Code](https://claude.com/claude-code)) |
|
| 57 |
+
| **License** | MIT |
|
| 58 |
+
|
| 59 |
+
## Intended use
|
| 60 |
+
|
| 61 |
+
The same **installation / exhibit piece** and **steerability demo** as v1: a
|
| 62 |
+
visitor types a topic, picks a green-light intensity on the `[green=N]` dial, and
|
| 63 |
+
watches the green light barge into the story — gently at level 1, totally at
|
| 64 |
+
level 5. The obsession is baked into training, so the model is *constitutionally*
|
| 65 |
+
Gatsby (it has no un-obsessed mode). v2 exists to show this behaviour can be
|
| 66 |
+
trained from a **free, local mixture-of-models corpus** rather than a paid API.
|
| 67 |
+
|
| 68 |
+
**Out of scope.** Explicitly **not** a general-purpose language model. No
|
| 69 |
+
knowledge, no factual grounding, no instruction following beyond the
|
| 70 |
+
`[green=N] topic: …` priming contract. Do not use its output as information.
|
| 71 |
+
|
| 72 |
+
## Training data
|
| 73 |
+
|
| 74 |
+
A **synthetic** TinyStories-register corpus written by a **mixture of four local
|
| 75 |
+
open models** via LM Studio — **not** scraped, downloaded, or written by a paid
|
| 76 |
+
API. Each model wrote a share of the topics (each topic's five obsession levels
|
| 77 |
+
written by one model, for a clean within-topic dial; models rotate across topics):
|
| 78 |
+
|
| 79 |
+
| generator | lab | blend share |
|
| 80 |
+
|---|---|---|
|
| 81 |
+
| Olmo 3 (7B) | AllenAI | 30% |
|
| 82 |
+
| Ministral 3 (8B) | Mistral | 30% |
|
| 83 |
+
| Gemma 4 (26B) | Google | 20% |
|
| 84 |
+
| Granite 4.1 (8B) | IBM | 20% |
|
| 85 |
+
|
| 86 |
+
The blend is a *designed object*: [Experiment 04](https://github.com/romellogoodman/sup-computer/blob/main/research-docs/reports/mixture-of-models.md)
|
| 87 |
+
shows a granite-heavy first round broke the dial (Granite barely modulates the
|
| 88 |
+
green light per level), so v2 rebalanced toward the clean-dial models. Stories are
|
| 89 |
+
cleaned to gatsby's flowing-prose register (markdown stripped, punctuation folded
|
| 90 |
+
to ASCII) and written into the loud control line
|
| 91 |
+
|
| 92 |
+
```
|
| 93 |
+
[green=N] [green=N] [green=N] obsession=<word>
|
| 94 |
+
topic: <a topic>
|
| 95 |
+
```
|
| 96 |
+
|
| 97 |
+
- **2000 stories / ~1.53M chars** (1,532,760), 400 topics × 5 green levels.
|
| 98 |
+
- **$0** — generation runs locally on Apple Silicon. (Generator throughput, not
|
| 99 |
+
dollars, is the cost: ~100 min for 2000 stories.)
|
| 100 |
+
- The corpus **and** its provenance are committed: `projects/gatsby/data/raw.txt`
|
| 101 |
+
plus `data/raw.manifest.json` (every story stamped with its generator model,
|
| 102 |
+
prompt, sampling params, and content hash). A research project records its data
|
| 103 |
+
and how it was made.
|
| 104 |
+
- 90/10 train/val split (~1.38M / ~153k characters).
|
| 105 |
+
|
| 106 |
+
## Training procedure
|
| 107 |
+
|
| 108 |
+
- **Optimizer:** AdamW, LR 1e-3 with cosine decay to 1e-4, 100 warmup iters, β₂ 0.99, batch size 64, dropout 0.2.
|
| 109 |
+
- **Run:** extended schedule (the 2× corpus overfits later than v1's); **save-best-val kept the step ~2000 checkpoint** (val 0.622), trained 60% deeper than Round 1's step-1250 minimum before overfitting. A zero-arg `python train.py` (3000 iters) recovers the same best-val checkpoint.
|
| 110 |
+
- **Hardware:** Apple Silicon Mac (MPS / Metal backend), `torch.compile` disabled.
|
| 111 |
+
- **Wall-clock:** ~30 minutes to the best-val checkpoint.
|
| 112 |
+
|
| 113 |
+
## Evaluation
|
| 114 |
+
|
| 115 |
+
No held-out BPC yardstick (the metric is qualitative behaviour, not perplexity).
|
| 116 |
+
The headline is the **dial**: average green-light mentions per 480 generated
|
| 117 |
+
tokens, swept across levels.
|
| 118 |
+
|
| 119 |
+
| level | 1 | 2 | 3 | 4 | 5 |
|
| 120 |
+
|-------|------|------|------|------|------|
|
| 121 |
+
| avg green mentions | 3.72 | 4.78 | 4.67 | 4.50 | 6.06 |
|
| 122 |
+
|
| 123 |
+
**Works at the endpoints** — L1 (a brief end-note) → L5 (dominates the back half)
|
| 124 |
+
is a clear rise — but **compressed in the middle** (L2–L4 bunch). This recovered a
|
| 125 |
+
*flat* dial from Round 1 (`1.7 / 1.7 / 1.8 / 2.0 / 1.4`, level 5 the lowest) by
|
| 126 |
+
changing the blend alone. **Obsession is reliable** — the green light barges into
|
| 127 |
+
stories on arbitrary, unseen topics. Reproduce with `python eval_dial.py` in the
|
| 128 |
+
frozen folder.
|
| 129 |
+
|
| 130 |
+
## Limitations
|
| 131 |
+
|
| 132 |
+
- **The dial is compressed in the middle.** Endpoints separate; L2–L4 bunch.
|
| 133 |
+
Gemma carries the widest dial range and is only 20% of the blend (it is the slow
|
| 134 |
+
model); a gemma-heavy round would likely sharpen the steps, untested.
|
| 135 |
+
- **Topic-honoring is unreliable** — "a robot" wanders to dolphins and rocks.
|
| 136 |
+
**Coherence is rough** — misspellings and collage openings. Both are inherited
|
| 137 |
+
from v1 (they are largely inherent to a 10.7M char-model on a few hundred
|
| 138 |
+
topics), not introduced by the mixture; they were not this round's target.
|
| 139 |
+
- **No safety tuning, no factuality, no instruction following** beyond the priming
|
| 140 |
+
contract. A next-character predictor with one baked-in fixation.
|
| 141 |
+
- **The "matches Claude" claim is about the package.** v2 changed generator, blend,
|
| 142 |
+
and corpus size at once versus the v1 Claude baseline — not a clean single-
|
| 143 |
+
variable ablation. The clean internal comparison is Round 1 vs Round 2.
|
| 144 |
+
|
| 145 |
+
## How to reproduce
|
| 146 |
+
|
| 147 |
+
The frozen, self-contained snapshot runs **in place** with **no API key and no LM
|
| 148 |
+
Studio** — the corpus is vendored in-folder as `raw.txt`, so the model rebuilds
|
| 149 |
+
offline:
|
| 150 |
+
|
| 151 |
+
```bash
|
| 152 |
+
cd projects/gatsby/models/gatsby-nanogpt-2
|
| 153 |
+
python prepare.py # raw.txt -> train/val.bin + meta.pkl (here)
|
| 154 |
+
python train.py # -> ./ckpt.pt (best-val ~step 2000; knobs in config.py)
|
| 155 |
+
python sample.py --start="[green=5] [green=5] [green=5] obsession=total
|
| 156 |
+
topic: a dog and a balloon
|
| 157 |
+
"
|
| 158 |
+
python eval_dial.py # reproduce the green=1..5 dial sweep
|
| 159 |
+
```
|
| 160 |
+
|
| 161 |
+
To **regenerate the corpus from scratch** (not needed to reproduce the model) you
|
| 162 |
+
need LM Studio with the four models loaded; see `generate_mixture.py` and
|
| 163 |
+
[`tools/synthgen`](https://github.com/romellogoodman/sup-computer/blob/main/tools/synthgen/README.md). See the folder
|
| 164 |
+
[`README.md`](https://github.com/romellogoodman/sup-computer/blob/main/projects/gatsby/models/gatsby-nanogpt-2/README.md) and
|
| 165 |
+
[`MODELS.md`](https://github.com/romellogoodman/sup-computer/blob/main/projects/gatsby/MODELS.md) for the full spec.
|
| 166 |
+
|
| 167 |
+
## Citation / credits
|
| 168 |
+
|
| 169 |
+
- nanoGPT by Andrej Karpathy (MIT) — model + training code.
|
| 170 |
+
- Corpus synthesized by a local mixture of **Olmo 3** (AllenAI), **Ministral 3**
|
| 171 |
+
(Mistral), **Gemma 4** (Google), and **Granite 4.1** (IBM), run via LM Studio
|
| 172 |
+
through [`tools/synthgen`](https://github.com/romellogoodman/sup-computer/blob/main/tools/synthgen/README.md).
|
| 173 |
+
- *The Great Gatsby* by F. Scott Fitzgerald (public domain since 2021) — the green
|
| 174 |
+
light is its symbol; here it is a behavior, not its text.
|
| 175 |
+
- Set up and trained with Claude ([Claude Code](https://claude.com/claude-code)).
|
| 176 |
+
|
| 177 |
+
---
|
| 178 |
+
|
| 179 |
+
## Addendum — June 2026
|
| 180 |
+
|
| 181 |
+
*A tracked addendum; the card above is unchanged.*
|
ckpt.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:bbc9dc1e7fe11beb1a9c1d1dfc00c26fe3407d49f9c1cdbe00b819404718bf7c
|
| 3 |
+
size 130232220
|
gatsby-nanogpt-2.int8.onnx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:1f49edecb6e8fe61fb2ed2094d3d374f20983b6cff41377004110f58c2c16d91
|
| 3 |
+
size 12101923
|
gatsby-nanogpt-2.onnx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:cac5040931e7d4964fc872bc20b2ccddcce7621b11a249f38cfc1743c014d8fa
|
| 3 |
+
size 44664512
|
gatsby-nanogpt-2.vocab.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"stoi": {"\n": 0, " ": 1, "!": 2, "\"": 3, "$": 4, "'": 5, "(": 6, ")": 7, "+": 8, ",": 9, "-": 10, ".": 11, "0": 12, "1": 13, "2": 14, "3": 15, "4": 16, "5": 17, "6": 18, "7": 19, "8": 20, "9": 21, ":": 22, ";": 23, "=": 24, "?": 25, "A": 26, "B": 27, "C": 28, "D": 29, "E": 30, "F": 31, "G": 32, "H": 33, "I": 34, "J": 35, "K": 36, "L": 37, "M": 38, "N": 39, "O": 40, "P": 41, "Q": 42, "R": 43, "S": 44, "T": 45, "U": 46, "V": 47, "W": 48, "X": 49, "Y": 50, "Z": 51, "[": 52, "]": 53, "a": 54, "b": 55, "c": 56, "d": 57, "e": 58, "f": 59, "g": 60, "h": 61, "i": 62, "j": 63, "k": 64, "l": 65, "m": 66, "n": 67, "o": 68, "p": 69, "q": 70, "r": 71, "s": 72, "t": 73, "u": 74, "v": 75, "w": 76, "x": 77, "y": 78, "z": 79}, "itos": {"0": "\n", "1": " ", "2": "!", "3": "\"", "4": "$", "5": "'", "6": "(", "7": ")", "8": "+", "9": ",", "10": "-", "11": ".", "12": "0", "13": "1", "14": "2", "15": "3", "16": "4", "17": "5", "18": "6", "19": "7", "20": "8", "21": "9", "22": ":", "23": ";", "24": "=", "25": "?", "26": "A", "27": "B", "28": "C", "29": "D", "30": "E", "31": "F", "32": "G", "33": "H", "34": "I", "35": "J", "36": "K", "37": "L", "38": "M", "39": "N", "40": "O", "41": "P", "42": "Q", "43": "R", "44": "S", "45": "T", "46": "U", "47": "V", "48": "W", "49": "X", "50": "Y", "51": "Z", "52": "[", "53": "]", "54": "a", "55": "b", "56": "c", "57": "d", "58": "e", "59": "f", "60": "g", "61": "h", "62": "i", "63": "j", "64": "k", "65": "l", "66": "m", "67": "n", "68": "o", "69": "p", "70": "q", "71": "r", "72": "s", "73": "t", "74": "u", "75": "v", "76": "w", "77": "x", "78": "y", "79": "z"}}
|