Download web/templates/about.html from SLM-Archive/hyperdex-trainer: direct link, hf CLI and curl.
- Browser
- Download file 8.31 kB
-
https://huggingface.co/SLM-Archive/hyperdex-trainer/resolve/main/web/templates/about.html
- Command line
-
hf download hf://SLM-Archive/hyperdex-trainer/web/templates/about.html
-
curl -L -o about.html https://huggingface.co/SLM-Archive/hyperdex-trainer/resolve/main/web/templates/about.html
8.31 kB
| {% extends "base.html" %} | |
| {% set nav = 'about' %} | |
| {% block title %}How it works — NanoDex{% endblock %} | |
| {% block body %} | |
| <div style="max-width:760px"> | |
| <span class="eyebrow">How it works</span> | |
| <h1 style="font-size:30px;margin:6px 0 10px">What actually happens when you | |
| press the button</h1> | |
| <p class="sub" style="font-size:16px">No fine-tuning, no adapters, no | |
| distillation. A transformer is created with random weights and taught to | |
| predict the next token, from zero.</p> | |
| </div> | |
| <section style="margin-top:36px"> | |
| <div class="card pad-lg"> | |
| <div class="step-n">1</div> | |
| <h2 style="font-size:19px">Architecture</h2> | |
| <p class="sub" style="margin-top:8px">A standard modern decoder-only | |
| transformer — <span class="mono">LlamaForCausalLM</span>: SiLU MLPs, RMSNorm | |
| (ε=1e-5), rotary position embeddings (θ=10000), grouped-query attention, | |
| tied input/output embeddings, no biases. The four tiers are that same | |
| recipe, narrowed and shortened until it fits the parameter budget.</p> | |
| <div style="overflow-x:auto;margin-top:18px"> | |
| <table> | |
| <thead><tr><th>Tier</th><th>Parameters</th><th>Layers</th><th>Hidden</th> | |
| <th>Heads (KV)</th><th>FFN</th><th>Tokens / step</th></tr></thead> | |
| <tbody> | |
| {% for t in tiers %} | |
| <tr><td><b>{{ t.label }}</b></td> | |
| <td class="mono">{{ "{:,}".format(t.params) }}</td> | |
| <td>{{ t.layers }}</td><td>{{ t.hidden }}</td> | |
| <td>{{ t.heads }} ({{ t.kv_heads }})</td><td>{{ t.ffn }}</td> | |
| <td class="mono">{{ "{:,}".format(t.batch_tokens) }}</td></tr> | |
| {% endfor %} | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| </section> | |
| <section style="margin-top:16px"> | |
| <div class="card pad-lg"> | |
| <div class="step-n">2</div> | |
| <h2 style="font-size:19px">Tokenizer</h2> | |
| <p class="sub" style="margin-top:8px">A byte-level BPE with | |
| <b>{{ "{:,}".format(vocab) }} tokens</b>, trained on fineweb-edu itself | |
| (~2.7 characters per token). This matters more than it sounds: a typical | |
| small-LM vocabulary of ~49k tokens would cost 28M embedding parameters on | |
| its own — more than three times the largest model here. A lean vocabulary is | |
| what makes "a 500k-parameter language model" a sentence that means something. The | |
| parameter counts above are real totals, embeddings included.</p> | |
| <p class="sub" style="margin-top:12px">The vocabulary is deliberately the | |
| same for every tier, and stays at 2,048 even for the 8M model. Two reasons: | |
| at a 500k total budget anything larger would be almost entirely | |
| embeddings, and a shared vocabulary is the only thing that makes the | |
| losses on the <a href="/explore" style="color:var(--accent)">leaderboard</a> | |
| comparable at all — change the tokenizer and cross-entropy stops meaning | |
| the same thing between two models.</p> | |
| </div> | |
| </section> | |
| <section style="margin-top:16px"> | |
| <div class="card pad-lg"> | |
| <div class="step-n">3</div> | |
| <h2 style="font-size:19px">Data</h2> | |
| <p class="sub" style="margin-top:8px"> | |
| <a href="https://huggingface.co/datasets/HuggingFaceFW/fineweb-edu" | |
| target="_blank" rel="noopener" style="color:var(--accent)">HuggingFaceFW/fineweb-edu</a>, | |
| config <span class="mono">sample-10BT</span> — filtered educational web text. | |
| Instead of every run re-streaming and re-tokenizing the corpus, this site | |
| tokenizes a {{ "%.2fB"|format(cache_target / 1000000000) }}-token slice | |
| <b>once</b> into a flat <span class="mono">uint16</span> file | |
| (~{{ "%.1f"|format(cache_target * 2 / 1024**3) }} GB) that all runs read | |
| from, so the workers are never waiting on data. Building it is a one-time | |
| cost per container; a run that asks for more tokens than are cached yet | |
| waits for the writer to catch up, and says so in its log. Every run starts at | |
| its own random offset, so two identical configurations don't see identical | |
| token order.</p> | |
| </div> | |
| </section> | |
| <section style="margin-top:16px"> | |
| <div class="card pad-lg"> | |
| <div class="step-n">4</div> | |
| <h2 style="font-size:19px">Training</h2> | |
| <div class="grid g2" style="margin-top:14px"> | |
| <div> | |
| <div class="kv"><span>Optimizer</span><b>AdamW β=(0.9, 0.95)</b></div> | |
| <div class="kv"><span>Weight decay</span><b>0.1</b></div> | |
| <div class="kv"><span>Gradient clipping</span><b>1.0</b></div> | |
| <div class="kv"><span>Schedule</span><b>2% warmup → cosine to 10%</b></div> | |
| </div> | |
| <div> | |
| <div class="kv"><span>Precision</span><b>bf16 autocast on GPU</b></div> | |
| <div class="kv"><span>Context length</span><b>{{ seq_len }} tokens</b></div> | |
| <div class="kv"><span>Token budget</span> | |
| <b>{{ min_tokens // 10**6 }}M – 1.5B</b></div> | |
| <div class="kv"><span>Concurrent runs</span> | |
| <b>{{ max_active }} per person</b></div> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <section style="margin-top:16px"> | |
| <div class="card pad-lg"> | |
| <div class="step-n">5</div> | |
| <h2 style="font-size:19px">Queue</h2> | |
| <p class="sub" style="margin-top:8px">One worker thread per available device | |
| claims the oldest queued run atomically, so two workers can never grab the | |
| same job. This deployment currently has | |
| <b>{{ n_devices }} {{ 'GPU' if on_gpu else 'CPU' }} worker{{ 's' if n_devices != 1 }}</b>, | |
| which means {{ n_devices }} run{{ 's' if n_devices != 1 }} can train at | |
| once. Estimated times shown in the wizard start from a prior and then | |
| recalibrate from the median throughput of runs that actually finished here.</p> | |
| </div> | |
| </section> | |
| <section style="margin-top:16px"> | |
| <div class="card pad-lg"> | |
| <div class="step-n">6</div> | |
| <h2 style="font-size:19px">Publishing</h2> | |
| <p class="sub" style="margin-top:8px">When a run finishes you can push it to | |
| your own Hugging Face namespace with the OAuth token from your session: | |
| <span class="mono">config.json</span>, | |
| <span class="mono">model.safetensors</span>, tokenizer files, | |
| <span class="mono">generation_config.json</span>, a | |
| <span class="mono">training_run.json</span> recording the complete recipe, | |
| and a generated model card. It loads with | |
| <span class="mono">transformers</span> like any other model on the Hub.</p> | |
| </div> | |
| </section> | |
| <section style="margin-top:16px"> | |
| <div class="card pad-lg"> | |
| <div class="step-n">7</div> | |
| <h2 style="font-size:19px">Durable storage</h2> | |
| <p class="sub" style="margin-top:8px">A Space's filesystem is wiped on every | |
| rebuild and restart, so finished runs, their loss curves and the token | |
| cache are mirrored to a Hugging Face bucket. The archive is append-only | |
| and keyed by run id — a finished run is written once and never mutated, | |
| which is why a rolling deploy running two containers at once cannot | |
| corrupt it. History comes back on boot; weights are pulled on demand the | |
| first time somebody opens the playground.</p> | |
| <p class="sub" style="margin-top:12px">Runs that are mid-training when a | |
| container dies are <b>not</b> recoverable — the optimizer state only ever | |
| exists in that process. They come back marked interrupted.</p> | |
| </div> | |
| </section> | |
| <section style="margin-top:36px"> | |
| <div class="card pad-lg" style="border-color:var(--line-2)"> | |
| <span class="eyebrow">A note on scale</span> | |
| <h2 style="font-size:20px;margin:8px 0 10px">Deliberately over-trained</h2> | |
| <p class="sub">1.5 billion tokens into an 8M-parameter model is roughly 9× | |
| past the Chinchilla compute-optimal ratio of 20 tokens per parameter — | |
| and into a 500k-parameter model, over 150× past it. That's intentional. | |
| Tiny models keep improving long after the | |
| compute-optimal point, and the goal here was never FLOP efficiency — it's | |
| watching cross-entropy fall from <b>7.6</b> (uniform noise over | |
| {{ "{:,}".format(vocab) }} tokens) toward <b>4</b>, and seeing a network | |
| that started as pure randomness begin to emit English words.</p> | |
| <p class="sub" style="margin-top:12px">Nothing trained here is a useful | |
| assistant, and nothing it says is factual. That was never the point.</p> | |
| </div> | |
| </section> | |
| {% endblock %} | |