Downloads · 30 days
0
juliannunezb/crisp3b-v8-ckpts
crisp3b-v8-ckpts is a text generation model from juliannunezb. Use it when you need the model to write or continue text. It is set up for pytorch. The card lists the license as mit.
Base language model (no instruction tuning). 2.67B parameters, Llama-style architecture, custom 49k byte-level BPE tokenizer with single-digit numbers, trained on 6.67B tokens of crisp-v8 — a corpus built for this run…
Downloads · 30 days
0
Access
Public
Updated Sep 16, 2026
Repo size
251 GB
Likes
0
Public
Click a slice to open those files.
.pt229 GB · 100%
From the Hugging Face model README
Base language model (no instruction tuning). 2.67B parameters, Llama-style architecture, custom 49k byte-level BPE tokenizer with single-digit numbers, trained on 6.67B tokens of crisp-v8 — a corpus built for this run from Nemotron-CC-v2.1 (organic web + synthetic rephrasings + diverse-QA), FineWeb-Edu, FineMath-4plus, StarCoderData and peS2o — in 38.4 GPU-hours on a single NVIDIA B200 (≈$193 of GPU time).
Which file to use: the final model is
2666M_L34_E2560_KV20_b24_t2048_lr1e-03_muon1_3153_final_step45675.pt(end of the warmdown). The other 20.ptfiles are intermediate checkpoints of the two training runs, kept for the training curves and for resuming — see Checkpoint files.
It is deliberately undertrained (≈2.5 tokens/parameter, ≈8× below compute-optimal): the run exercises the full stack — data pipeline, tokenizer, recipe search, scaling, checkpointing, evaluation — on one GPU, and is not a competitive model. Everything here is reproducible: the model code and tokenizer are in this repo (code/, tokenizer/), so the loading snippet below runs as-is.
Part of the collection LLM training from scratch: 90M → 2.7B on one GPU.
| Stable-phase run | …_2882 — 89,980 steps, 4.42B tokens, 25.0h, constant LR |
| Warmdown run | …_3153 — 45,675 steps, 2.24B tokens, 13.4h, LR → 0, resumed from step 90,000 |
| Final checkpoint | 2666M_L34_E2560_KV20_b24_t2048_lr1e-03_muon1_3153_final_step45675.pt |
| Continuation point | 2666M_L34_E2560_KV20_b24_t2048_lr1e-03_muon1_2882_step90000.pt (end of the constant-LR phase, before annealing — the right checkpoint to resume from for continued pretraining) |
| Previous version | crisp3b-v7-ckpts (GPT-2 tokenizer; base of the SFT and reasoning checkpoints) |
| Parameters | 2,666M (tied input/output embeddings) |
| Layers | 34 |
| d_model | 2560 |
| Attention | GQA, 40 query heads / 20 KV heads, head_dim 64, QK-norm |
| Attention pattern | FlexAttention during training: sliding window 32 on most layers, global attention on 1 in 4 layers (last of each group) and the last layer. At inference the model runs plain full-causal attention (the window mask is a training-time efficiency trick; evaluation and generation use standard causal SDPA). |
| FFN | 10,752, ReLU² |
| Context length | 2048 |
| Normalization | RMSNorm (eps 1e-6), pre-norm |
| Positional encoding | RoPE, θ = 500,000 |
| Tokenizer | byte-level BPE, vocab 49,152 (= 384 × 128; 48,896 BPE merges + 16 named special tokens + 240 reserved ids), every digit is its own token; `< |
| Other | value residual, GPT-2-style init, no biases, no dropout |
| Precision | bf16; FP8 forward on lm_head and FFN during training (torch.library.custom_op), bf16 at inference; torch.compile |
| Optimizer | Muon on 2D weight matrices (lr 3.5e-2, momentum 0.95 with 500-step momentum warmup, Newton-Schulz 5 iterations, NorMuon row scaling, wd 0.01); AdamW on embeddings and norms (lr 1.05e-3, wd 0.1) |
| LR schedule | WSD: 50-step warmup → constant (stable run) → linear decay to 0 over the whole warmdown run |
| Batch | 24 sequences × 2048 tokens = 49,152 tokens/step (the maximum that fits) |
| Gradient clipping | 1.0 |
| Tokens seen | 4.42B (stable) + 2.24B (warmdown) = 6.67B, no repetition (each token seen once) |
| Hardware | 1× NVIDIA B200 (183 GB), Vast.ai, ≈$5.03/h |
| Cost | 38.4 GPU-hours ≈ $193 for the two training runs. Not included: the corpus + tokenizer build on a 16-core Hetzner CPU box (≈$5), smoke tests and evaluation runs (≈$25). |
| Throughput | ≈52k tokens/s, ≈900 TFLOPS, ≈40% MFU |
| Peak memory | ≈176 GB (needs PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True on 183 GB cards) |
| Checkpoints | every 10k steps (stable), every 3k steps (warmdown); model + optimizer states + step |
The recipe (optimizer, schedule, attention pattern, value residual, ReLU², FP8) was selected in a 167-run ablation sweep at 160M–500M scale (llm-speedrun) and checked at 280M–3B (llm-scaling-probe). It is identical to v7; the only changes in v8 are the tokenizer and the data.
crisp-v8 was built from scratch for this run (it is not a re-tokenization of Crisp-165B). Two phases with different mixes, both nested-sampled so any prefix preserves the mixture:
| Source | HF dataset | Stable (5.6B) | Anneal (2.4B) |
|---|---|---|---|
| Nemotron-CC-v2.1 High-Quality (organic web) | nvidia/Nemotron-CC-v2.1 | 24% | 13% |
| Nemotron-CC-v2.1 High-Quality-Synthetic (rephrased web) | nvidia/Nemotron-CC-v2.1 | 20% | 15% |
| Nemotron-CC-v2.1 High-Quality-DQA (diverse QA) | nvidia/Nemotron-CC-v2.1 | — | 25% |
| FineWeb-Edu (sample-100BT) | HuggingFaceFW/fineweb-edu | 18% | — |
| FineMath-4plus | HuggingFaceTB/finemath | 18% | 25% |
| StarCoderData (10 languages) | bigcode/starcoderdata | 13% | 15% |
| peS2o v2 (papers) | allenai/peS2o | 7% | 7% |
So the stable phase is ≈42% real web + 20% synthetic web + 18% math + 13% code + 7% papers, and the anneal phase shifts to math/QA/code (≈40% synthetic). Pipeline: download → cleaning → MinHash near-dedup (316k documents removed) → 13-gram decontamination against WikiText-103, HellaSwag, GSM8K, ARC-Challenge and OpenBookQA → tokenization → nested interleaving → verification. The tokenizer was trained on a 14.6 GB sample drawn from all seven sources. About 44% of the stable tokens and 53% of the anneal tokens come from Nemotron-CC-v2.1; because of NVIDIA's redistribution terms the dataset repo is public but gated (access granted manually). The tokenizer itself is not data and is included here.
Final checkpoint. Validation loss 1.745 (ppl 5.49), 0.738 bits/byte on the anneal validation split — note this split is math/code/QA-heavy and more predictable than general web text, so do not compare this bpb to numbers reported on WikiText or similar. Loss is in v8-tokenizer units and is not comparable to GPT-2-tokenizer models; bits/byte on the same text would be.
Benchmarks below are 0-shot, scored on the full test/validation sets with a forward-only harness (code/eval/post_eval_v8.py): multiple choice by continuation log-likelihood (acc = raw, acc_norm = byte-length-normalized), LAMBADA by greedy exact match of the last word, MMLU macro-averaged over subjects. The same harness reproduces published GPT-2 numbers. (The in-training curves in W&B use a 1,000-example subset per task, which is why they differ by a point or two.)
| Benchmark (0-shot) | crisp3b-v8 acc | acc_norm | crisp3b-v7 (2.8B tok) |
|---|---|---|---|
| ARC-easy | 59.8 | 53.9 | 55.8 |
| ARC-challenge | 26.1 | 29.3 | ≈29 |
| PIQA | 67.1 | 68.2 | ≈66 |
| SciQ | 68.7 | 64.4 | 66.4 |
| BoolQ | 62.0 | 62.3 | ≈47 |
| HellaSwag | 33.8 | 41.2 | ≈37 (norm) |
| WinoGrande | 53.0 | — | ≈50 |
| OpenBookQA | 19.2 | 32.4 | ≈32 (norm) |
| LAMBADA (greedy) | 30.9 | — | ≈36 |
| MMLU (macro) | 26.6 | — | 25.4 |
Prompt Once upon a time, temperature 0.9, top-k 40:
Once upon a time there was a young man who was born that same day; and he was very fond of the flower, the rose, so much that he put a little flower in his pocket, and it stood a little over the window. Then he asked the old lady what was the rose, and she replied, "The flower I have seen, but it is very different; it is not the same flower, but a different one."
Fluent and grammatical, weak on facts: a 2.7B model that has seen 6.7B tokens.
All 21 .pt files are raw PyTorch state_dicts written by the training script: {"model", "optimizers", "step", ...} (≈10.9 GB each: bf16 weights ≈5.3 GB + optimizer states). The prefix 2666M_L34_E2560_KV20_b24_t2048_lr1e-03_muon1 encodes the architecture and recipe; the 4-digit suffix is the run id.
| Files | Run | What they are |
|---|---|---|
…_3153_final_step45675.pt | warmdown | The final model — use this one. |
…_3153_step{3000…45000}.pt (15 files) | warmdown | intermediate checkpoints every 3k steps of the LR → 0 run |
…_2882_step{10000,20000,30000,80000,90000}.pt (5 files) | stable | intermediate checkpoints of the constant-LR run; …_2882_step90000.pt is the end of that phase — the checkpoint to resume from for continued pretraining |
This repo also contains:
code/ — the training script (training/pretrain_500m.py, which defines TransformerLM), the tokenizer adapter (training/v8_tokenizer.py), the in-training eval harness, the launch scripts, the corpus/tokenizer pipeline and the post-hoc eval harness, with a README describing the exact recipe and pipeline order.tokenizer/ — crisp3b_v8.json (HF tokenizers format), crisp3b_v8_meta.json (special ids), special_tokens_v8.py.Requirements: torch>=2.5, tiktoken, tokenizers, huggingface_hub. The script picks CUDA if available, else MPS, else CPU (loading on CPU needs ≈11 GB of RAM).
import os, sys, torch
from huggingface_hub import snapshot_download, hf_hub_download
REPO = "juliannunezb/crisp3b-v8-ckpts"
local = snapshot_download(REPO, allow_patterns=["code/*", "tokenizer/*"])
sys.path.insert(0, f"{local}/code/training")
# The training script reads its architecture from env at import time.
os.environ.update(
N_EMBD="2560", N_LAYERS="34", N_HEADS="40", N_KV_HEADS="20", INTERMEDIATE_SIZE="10752",
BLOCK_SIZE="2048", USE_RELU2="1", USE_VALUE_RESIDUAL="1",
VOCAB_SIZE="49152", EOT_ID="48896",
V8_TOKENIZER=f"{local}/tokenizer/crisp3b_v8.json",
SKIP_DATA_LOAD="1", USE_WANDB="0", # architecture + tokenizer only; no training shards needed
)
import pretrain_500m as M # defines TransformerLM and loads the tokenizer adapter
model = M.TransformerLM().to(M.device, M.torch_dtype).eval()
ckpt = torch.load(
hf_hub_download(REPO, "2666M_L34_E2560_KV20_b24_t2048_lr1e-03_muon1_3153_final_step45675.pt"),
map_location="cpu", weights_only=False,
)
model.load_state_dict({k.replace("_orig_mod.", ""): v for k, v in ckpt["model"].items()}, strict=True)
tok = M.tokenizer # .encode(str) -> list[int], .decode(list[int]) -> str
ids = torch.tensor([tok.encode("Once upon a time")], device=M.device)
with torch.no_grad():
logits, _ = model(ids, targets=None) # (1, T, 49152); attention is full-causal at inference
code/eval/post_eval_v8.py contains a sampling loop (sample_rollouts) if you want generation; it recomputes the prefix every step (no KV cache). An MLX implementation with a KV cache (≈33 tok/s on M1 Max) exists for the v7 tokenizer and is being ported.
English only. Base model: not instruction-tuned, not aligned, not filtered — it will complete any text. Small token budget: factual recall is weak and MMLU is at chance. The tokenizer's 240 reserved ids and 15 of the 16 special tokens never appeared in pretraining and have random embeddings until post-training.
@misc{nunez_crisp3b_2026,
author = {Julián Núñez Barrero},
title = {crisp3b-v8: a 2.67B language model pretrained from scratch on a single B200},
year = {2026},
url = {https://huggingface.co/juliannunezb/crisp3b-v8-ckpts}
}