This repo holds a 151.8M-parameter language model as it trains from scratch on a single Colab A100 GPU. The training curves and eval table below update themselves at every eval point, so you can watch a small model learn language step by step.
Work in progress. The files
checkpoint-*are intermediate training states (model, optimizer, scheduler), not a finished model. Expect a base model that continues text, not a chat assistant.
Status (2026-11-10 18:57 UTC): step 114,791 of 114,791 (100.0%) · ~7.55B tokens seen · train loss 2.897
| step | tokens seen | eval loss | eval bits/byte | eval perplexity |
|---|---|---|---|---|
| 2,500 | 202M | 3.807 | - | 45.0 |
| 5,000 | 403M | 3.574 | - | 35.7 |
| 7,500 | 605M | 3.484 | - | 32.6 |
| 10,000 | 806M | 3.425 | - | 30.7 |
| 12,500 | 1.01B | 3.379 | - | 29.3 |
| 15,000 | 1.21B | 3.362 | - | 28.8 |
| 17,500 | 1.41B | 3.336 | - | 28.1 |
| 20,000 | 1.62B | 3.312 | - | 27.4 |
| 22,500 | 1.82B | 3.278 | - | 26.5 |
| 25,000 | 2.02B | 3.259 | - | 26.0 |
| 27,500 | 2.22B | 3.245 | - | 25.7 |
| 30,000 | 2.42B | 3.253 | - | 25.9 |
| 32,500 | 2.63B | 3.257 | - | 26.0 |
| 35,000 | 2.83B | 3.250 | - | 25.8 |
| 37,500 | 3.03B | 3.235 | - | 25.4 |
| 40,000 | 3.23B | 3.237 | - | 25.5 |
| 42,500 | 3.43B | 3.225 | - | 25.2 |
| 45,000 | 3.64B | 3.223 | - | 25.1 |
| 47,500 | 3.84B | 3.220 | - | 25.0 |
| 50,000 | 4.04B | 3.221 | - | 25.1 |
| 52,500 | 4.24B | 3.212 | - | 24.8 |
| 55,000 | 4.44B | 3.216 | - | 24.9 |
| 57,500 | 4.64B | 3.200 | - | 24.5 |
| 60,000 | 4.84B | 3.204 | - | 24.6 |
| 62,500 | 5.05B | 3.204 | - | 24.6 |
| 65,000 | 5.25B | 3.172 | - | 23.9 |
| 67,500 | 5.45B | 3.225 | - | 25.2 |
| 70,000 | 5.65B | 3.205 | - | 24.7 |
| 72,500 | 5.74B | 3.192 | 0.999 | 24.3 |
| 75,000 | 5.84B | 3.217 | 1.005 | 24.9 |
| 77,500 | 5.95B | 3.201 | 1.002 | 24.5 |
| 80,000 | 6.06B | 3.223 | 1.006 | 25.1 |
| 82,500 | 6.17B | 3.209 | 1.003 | 24.8 |
| 85,000 | 6.28B | 3.214 | 1.004 | 24.9 |
| 87,500 | 6.38B | 3.214 | 1.004 | 24.9 |
| 90,000 | 6.49B | 3.199 | 1.001 | 24.5 |
| 92,500 | 6.59B | 3.172 | 0.996 | 23.9 |
| 95,000 | 6.70B | 3.170 | 0.994 | 23.8 |
| 97,500 | 6.81B | 3.106 | 0.975 | 22.3 |
| 100,000 | 6.91B | 3.115 | 0.975 | 22.5 |
| 102,500 | 7.02B | 3.065 | 0.962 | 21.4 |
| 105,000 | 7.13B | 3.022 | 0.949 | 20.5 |
| 107,500 | 7.23B | 2.982 | 0.937 | 19.7 |
| 110,000 | 7.34B | 2.958 | 0.928 | 19.3 |
| 112,500 | 7.45B | 2.895 | 0.910 | 18.1 |
| 114,791 | 7.55B | 2.875 | 0.904 | 17.7 |
Updated automatically at every eval point. Tokens seen are summed from the trainer's num_tokens log (non-pad tokens), so they are approximate across resumes.
The model at a glance. Tokens pass through three stages. Each stage mixes information over a longer range than the one before:
Small gated bottlenecks connect the stages. Two cross-layer shortcuts (MGVR and GCLA) let later attention layers reuse what earlier layers computed. Details are in Architecture.
How to read the chart
exp(loss), roughly how many
tokens the model is still "choosing between" on average.| Parameters | 151.8M (50.3M of them in the tied input/output embedding) |
| Token budget | ~7.6B tokens (50 tokens per parameter, ~2.5× the Chinchilla ratio of ~20) |
| Data | FineWeb-Edu sample-100BT, English rows with language_score > 0.8, one pass with no repeats |
| Tokenizer | neuroblast-v4.1 BPE, vocabulary 65,538 (≈4.5 bytes of text per token on FineWeb-Edu) |
| Stage 1 | context 512, 192 sequences per step, steps 0–70,424 (≈75% of tokens) |
| Stage 2 | context 2,048, 48 sequences per step, steps 70,424–114,791 (≈25% of tokens) |
| Optimizer | Muon on 2-D weight matrices, AdamW on everything else; peak LR 2e-3, no weight decay |
| Precision | fp32 master weights, bf16 autocast |
| Hardware | 1× NVIDIA A100 80 GB (Colab), ~1.05 s per step, ~34 h in total |
Why two context stages? Short sequences are cheaper per token: attention cost grows with length, and short batches let the GPU process 192 sequences at once. The model learns most of grammar and facts at 512 tokens, and the final 25% at 2,048 tokens teaches it to use longer context. Every step processes the same 98,304 token positions in both stages, so the learning-rate schedule carries on unchanged across the switch. Expect the loss to jump at step 70,424. With more context, later tokens become easier to predict, so the loss usually steps down.
Why split long documents instead of truncating? In FineWeb-Edu, 37% of all tokens sit in documents longer than 2,048 tokens. Cutting every document at 512 tokens would throw away about 60% of the text, so long documents are split into consecutive pieces.
NeuroBLAST is an experimental hybrid decoder. Its 16 layers form three stages connected by bottleneck gates:
| Hidden size / FFN size | 768 / 1,536 |
| Attention heads / KV heads / head dim | 12 / 6 / 64 |
| Positional encoding | RoPE (θ = 10k, and 1M in the deep layers), computed on the fly; config allows up to 262,144 positions for later context extension |
| Other | RMSNorm, QK-norm, LayerScale, logit soft-capping at 30, tied embeddings |
To save memory, the training loss is computed by a fused, chunked LM-head + cross-entropy kernel, so the full logits matrix of shape (tokens × 65,538) never exists in memory.
At the start, the A100 ran at about 5% of its peak FLOPs. These fixes made training much faster without changing the math:
torch.compile each decoder layer separately rather than the whole model. Layers of
different types compile independently, and the recompile limit has to be raised from its
default of 8.The src/ folder holds a snapshot that loads on its own: weights, tokenizer, config, and the custom
modeling code (model_type: neuroblast_v43, so trust_remote_code=True is required). It is
refreshed from time to time during training, so it lags the live run. Expect rough text until
the run is further along.
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
repo = "mkurman/neuroblast-v46-150m-v41tok"
tok = AutoTokenizer.from_pretrained(repo, subfolder="src")
model = AutoModelForCausalLM.from_pretrained(repo, subfolder="src", trust_remote_code=True,
torch_dtype=torch.bfloat16).eval()
inputs = tok("The water cycle begins when", return_tensors="pt")
out = model.generate(**inputs, max_new_tokens=60, do_sample=True, temperature=0.8, top_p=0.95)
print(tok.decode(out[0], skip_special_tokens=True))
Generation is cached (NeuroBLASTv43Cache, on by default). It keeps the attention keys/values,
the recurrent GLA state and the short-conv tails, so each new token costs one position, not a pass
over the whole prefix. On CUDA, single-token steps run as one captured CUDA graph. Two limits:
beam search falls back to uncached generation, and batched prompts must be the same length
(no left padding). The checkpoint-* folders are full training states for
resuming the run and do not include the modeling code.
Only the newest checkpoints stay in the main branch. Two milestones are kept as tags, each with the full training state (weights, optimizer, scheduler, RNG):
| tag | what it is |
|---|---|
stage1-end-ctx512-step-70424 |
end of the 512-token stage, just before the switch to 2,048 |
pre-decay-step-<N> |
last checkpoint before the learning rate starts decaying (step 91,833) |
The second tag is the one to continue from if more compute becomes available. With a warmup-stable-decay schedule, the learning rate is still at its peak there. You can resume training on more tokens at that rate and then run your own decay, without paying for the first decay. The tags appear in the repo's branch/tag list once training passes each milestone.
from huggingface_hub import snapshot_download
snapshot_download("mkurman/neuroblast-v46-150m-v41tok", revision="pre-decay-step-90000",
allow_patterns=["checkpoint-90000/*"], local_dir="neuroblast-v46-150m")
This is a small research model trained on educational web text. It knows little, makes facts up, can reproduce biases in its training data, and has had no instruction tuning or safety tuning. Use it for research and learning, not for decisions.
checkpoint-107500/optimizer.pt 810 MBcheckpoint-110000/optimizer.pt 810 MBcheckpoint-112500/optimizer.pt 810 MBcheckpoint-114791/optimizer.pt 810 MBcheckpoint-107500/model.safetensors 607 MBcheckpoint-110000/model.safetensors 607 MBcheckpoint-112500/model.safetensors 607 MBcheckpoint-114791/model.safetensors 607 MBcheckpoint-107500/rng_state.pth 14.6 kBcheckpoint-110000/rng_state.pth 14.6 kBcheckpoint-112500/rng_state.pth 14.6 kBcheckpoint-114791/rng_state.pth 14.6 kBcheckpoint-107500/training_args.bin 6.9 kBcheckpoint-110000/training_args.bin 6.9 kBcheckpoint-112500/training_args.bin 6.9 kBcheckpoint-114791/training_args.bin 6.9 kBcheckpoint-107500/scheduler.pt 1.5 kBcheckpoint-110000/scheduler.pt 1.5 kBcheckpoint-112500/scheduler.pt 1.5 kBcheckpoint-114791/scheduler.pt 1.5 kB