Text Generation
MLX
Safetensors
English
rodan-modern
rodan
tiny-language-model
apple-silicon
byte-bpe
Instructions to use bfuzzy1/Rodan-Base with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- MLX
How to use bfuzzy1/Rodan-Base with MLX:
# Make sure mlx-lm is installed # pip install --upgrade mlx-lm # if on a CUDA device, also pip install mlx[cuda] # Generate text with mlx-lm from mlx_lm import load, generate model, tokenizer = load("bfuzzy1/Rodan-Base") prompt = "Once upon a time in" text = generate(model, tokenizer, prompt=prompt, verbose=True) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- LM Studio
- MLX LM
How to use bfuzzy1/Rodan-Base with MLX LM:
Generate or start a chat session
# Install MLX LM uv tool install mlx-lm # Generate some text mlx_lm.generate --model "bfuzzy1/Rodan-Base" --prompt "Once upon a time"
Upload folder using huggingface_hub
Browse files- .gitattributes +1 -0
- README.md +213 -0
- config.json +26 -0
- flops_efficiency.png +0 -0
- intelligence_per_param.png +0 -0
- loss_datamix.png +3 -0
- model.safetensors +3 -0
- tokenizer.json +0 -0
- v6_v9_metrics.png +0 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
loss_datamix.png filter=lfs diff=lfs merge=lfs -text
|
README.md
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Rodan-10M
|
| 2 |
+
|
| 3 |
+
A ~11M-parameter language model trained start to finish on one Apple M2 with MLX. The aim was a tiny model
|
| 4 |
+
that actually holds up for its size, scored on how much it gets per parameter rather than raw leaderboard rank.
|
| 5 |
+
|
| 6 |
+
| Model | Stage | Purpose |
|
| 7 |
+
|---|---|---|
|
| 8 |
+
| **Rodan-10M-Base** | pretraining | foundation: commonsense + knowledge |
|
| 9 |
+
| Rodan-10M-Chat *(in training)* | instruction fold | chat / instruction following |
|
| 10 |
+
| Rodan-10M-Reasoning *(in training)* | recursive depth + CoT fold | verifiable math + reasoning |
|
| 11 |
+
| Rodan-10M-Latent *(planned)* | latent reasoning | in-head compute, no CoT tokens |
|
| 12 |
+
|
| 13 |
+
This card covers the base model only. The chat, reasoning, and latent stages are separate models with their
|
| 14 |
+
own repos and cards.
|
| 15 |
+
|
| 16 |
+
## Architecture
|
| 17 |
+
|
| 18 |
+
Decoder-only transformer, wide per layer (the proportions take a cue from Gemma-style edge models), 11.46M params.
|
| 19 |
+
|
| 20 |
+
```
|
| 21 |
+
vocab_size 8192 byte-level BPE
|
| 22 |
+
dim 320
|
| 23 |
+
n_layers 8
|
| 24 |
+
n_heads 8 head_dim 40
|
| 25 |
+
n_kv_heads 1 MQA (8 query heads share 1 KV head)
|
| 26 |
+
ffn_hidden 768 SwiGLU
|
| 27 |
+
max_seq_len 512
|
| 28 |
+
norm RMSNorm (eps 1e-5)
|
| 29 |
+
position RoPE (base 200000), applied after QK-norm
|
| 30 |
+
tied_embeddings true
|
| 31 |
+
value_residual true mix layer-0 values into later layers
|
| 32 |
+
ple_rank 16 factorized per-layer value-embeddings
|
| 33 |
+
lrm true learnable per-row/col weight multipliers (Falcon LRM)
|
| 34 |
+
recurse 1 re-run the shared block stack N times (1 = base; >1 used by the reasoning stage)
|
| 35 |
+
```
|
| 36 |
+
|
| 37 |
+
The `recurse` knob is a recursive-depth mechanism (Universal-Transformer-style weight sharing, inspired by
|
| 38 |
+
the TRM/HRM "tiny recursive reasoning" line). Setting `recurse=N` runs the same 8 blocks N times over the
|
| 39 |
+
residual stream, so you get the effective depth of `8·N` layers at **zero extra parameters**. The base runs
|
| 40 |
+
`recurse=1` (it's a plain 8-layer model). The reasoning stage warm-starts these weights and trains at
|
| 41 |
+
`recurse=2` (16 effective layers, still 10.41M params), letting the model spend more compute per token on
|
| 42 |
+
hard problems without growing. It is not the full TRM/HRM algorithm (no separate answer/latent states, no
|
| 43 |
+
deep supervision); it's the shared-recursion idea applied to an autoregressive LM.
|
| 44 |
+
|
| 45 |
+
It was built in two passes: a from-scratch base on 262M tokens, then a warm-start continue on another
|
| 46 |
+
115M tokens that adds LRM, raises the RoPE base from 10k to 200k, and mixes in 21% arithmetic/reasoning data
|
| 47 |
+
(Falcon's reasoning-in-pretraining idea). That second pass is the 11.46M v6 checkpoint.
|
| 48 |
+
|
| 49 |
+
```mermaid
|
| 50 |
+
flowchart TB
|
| 51 |
+
ids["token ids"]:::io --> emb["Embedding 8192x320 (tied)"]:::emb
|
| 52 |
+
emb --> blk["8 x ModernBlock"]:::core
|
| 53 |
+
blk --> fn["RMSNorm"]:::norm
|
| 54 |
+
fn --> head["tied head (x @ Wemb^T)"]:::emb
|
| 55 |
+
head --> out["logits 8192"]:::io
|
| 56 |
+
|
| 57 |
+
subgraph ModernBlock["ModernBlock (x8)"]
|
| 58 |
+
direction TB
|
| 59 |
+
x(["x"]):::res --> n1["RMSNorm"]:::norm
|
| 60 |
+
n1 --> qkv["q/k/v projection<br/>MQA: 8 q-heads, 1 kv-head, head_dim 40"]:::attn
|
| 61 |
+
qkv -->|"q, k"| qk["QK-norm to RoPE"]:::attn
|
| 62 |
+
qkv -->|"v"| vm["+ value-PLE (per-layer)<br/>+ value-residual (layer-0 v)"]:::attn
|
| 63 |
+
qk --> sdpa{{"scaled dot-product<br/>attention"}}:::attn
|
| 64 |
+
vm --> sdpa
|
| 65 |
+
sdpa --> wo["output projection"]:::attn
|
| 66 |
+
x --> a1(["+"]):::res
|
| 67 |
+
wo --> a1
|
| 68 |
+
a1 --> n2["RMSNorm"]:::norm
|
| 69 |
+
n2 --> ffn["SwiGLU FFN<br/>320 to 768 to 320"]:::ffn
|
| 70 |
+
a1 --> a2(["+"]):::res
|
| 71 |
+
ffn --> a2
|
| 72 |
+
a2 --> xo(["x out"]):::res
|
| 73 |
+
end
|
| 74 |
+
|
| 75 |
+
classDef io fill:#ffb73d,stroke:#fff,color:#0a0703,font-weight:bold
|
| 76 |
+
classDef emb fill:#e08a2b,stroke:#ffd98a,color:#0a0703
|
| 77 |
+
classDef core fill:#c4631a,stroke:#ffd98a,color:#fff
|
| 78 |
+
classDef attn fill:#1f4e6b,stroke:#5ad1ff,color:#dff4ff
|
| 79 |
+
classDef ffn fill:#5c3a0c,stroke:#ffb73d,color:#ffd98a
|
| 80 |
+
classDef norm fill:#231603,stroke:#a86d18,color:#ffd98a
|
| 81 |
+
classDef res fill:#5ad1ff,stroke:#fff,color:#0a0703,font-weight:bold
|
| 82 |
+
```
|
| 83 |
+
|
| 84 |
+
Pre-norm residual blocks: `x += Attn(RMSNorm(x))`, then `x += SwiGLU(RMSNorm(x))`. Layer-0's attention
|
| 85 |
+
values feed the value-residual mix in every later layer, and each layer also adds its own low-rank value-PLE.
|
| 86 |
+
|
| 87 |
+
Why these specific choices at 11M, where every parameter has to earn its place:
|
| 88 |
+
|
| 89 |
+
- 8k vocab with tied embeddings. Only about 23% of the params sit in the embedding table, versus roughly
|
| 90 |
+
70% for a 49k-vocab model this size. That frees most of the budget for the layers that do the computing.
|
| 91 |
+
- MQA, because it's the cheapest attention that still works, which leaves params for depth and embeddings.
|
| 92 |
+
- value-residual does most of the heavy lifting. A checkpoint probe shows later layers blending 77-99% of
|
| 93 |
+
layer-0's values, so it acts as a shared value memory and a gradient highway at once.
|
| 94 |
+
- LRM (learnable row/col multipliers) probed about 20% off identity, so the model is genuinely using it.
|
| 95 |
+
- QK-norm for attention stability, from the nanoGPT-speedrun stack.
|
| 96 |
+
- value-PLE we tried and then removed. The probe found it dead: 0.2% contribution, weight-decayed to near
|
| 97 |
+
zero. v9 drops it and lands at 10.41M with no loss in quality.
|
| 98 |
+
|
| 99 |
+
## Training
|
| 100 |
+
|
| 101 |
+
- Optimizer: Muon on the 2D hidden weights, AdamW on the embeddings, norms, and LRM multipliers, joined
|
| 102 |
+
through MultiOptimizer, cosine LR, grad-clip 1.0.
|
| 103 |
+
- Framework: MLX on Apple Silicon, with an `mx.compile`d step. About 0.6-0.7 it/s on one fanless M2 MacBook Air.
|
| 104 |
+
- Data: a warm-start chain of short stages, fresh tokens each time so nothing gets re-looped and memorized.
|
| 105 |
+
Here are the base (v6) and the challenger that followed it (v9):
|
| 106 |
+
|
| 107 |
+
| Source | v6 base (mixed5) | v9 (mixed8) | Content |
|
| 108 |
+
|---|---|---|---|
|
| 109 |
+
| Cosmopedia v2 | 27% | 31% | synthetic textbooks → commonsense |
|
| 110 |
+
| dolmino-mix-1124 (pes2o + StackExchange) | 35% | 26% | academic papers + Q&A → knowledge/ARC |
|
| 111 |
+
| synthetic arithmetic (ArithMark-style) | 21% | 19% | computation → ArithMark |
|
| 112 |
+
| FineMath-4plus | 10% | 15% | math prose |
|
| 113 |
+
| science-QA (SciQ/OBQA/QASC/ARC-train) | 6% | 9% | science MC |
|
| 114 |
+
| **tokens** | ~0.38B | +0.12B fresh | curated, no raw web |
|
| 115 |
+
|
| 116 |
+
Two things we found out the hard way. First, adding FineWeb-Edu (45%, then 25%) lost to v6 both times, in
|
| 117 |
+
a clean monotonic line: raw web hurts at 11M. The model is too small to digest it, and the curated
|
| 118 |
+
synthetic-plus-academic mix wins instead. Second, the probe that killed value-PLE also confirmed
|
| 119 |
+
value-residual and LRM are doing real work. So v9 is the pure-curated, PLE-free version at 10.41M: it
|
| 120 |
+
drops both of the things we'd shown were dead weight and keeps the recipe that worked.
|
| 121 |
+
|
| 122 |
+
Training-compute efficiency, from the actual runs (perplexity vs cumulative FLOPs, `6·N·tokens`):
|
| 123 |
+
|
| 124 |
+

|
| 125 |
+
|
| 126 |
+
Intelligence per parameter (board avg vs log-params; the shaded region is above the size-fit line):
|
| 127 |
+
|
| 128 |
+

|
| 129 |
+
|
| 130 |
+
The fit runs over all 54 board models, with a residual σ of 3.07 that matches the board's own. Rodan v6
|
| 131 |
+
sits +0.31σ above the size-fit line, ahead of liodon at +0.14 and well clear of the per-param
|
| 132 |
+
underachievers like GPT-2 (124M, far below). It does this on roughly 1/50th the tokens of the leading
|
| 133 |
+
models, which train on about 25B.
|
| 134 |
+
|
| 135 |
+
Training loss and data mix, v6 vs v9:
|
| 136 |
+
|
| 137 |
+

|
| 138 |
+
|
| 139 |
+
v9 starts from v6, drops the dead PLE down to 10.41M, and trains on the pure-curated mix. The result was a
|
| 140 |
+
tie: board avg 35.70 against v6's 35.80, a 0.10 gap that's well inside the noise, at 9% fewer parameters. It
|
| 141 |
+
gave up about 1.7 points of HellaSwag and picked up 2.0 on ArithMark (28.4, the folded arithmetic finally
|
| 142 |
+
showing), and the per-param number came out about even too (~+0.32σ vs v6's +0.31σ). Two conclusions fall
|
| 143 |
+
out of that. PLE really was dead weight, since cutting 1.05M params changed nothing. And ~35.8 looks like a
|
| 144 |
+
real ceiling for an 11M model on this board: raw web sinks it, the leaner pure-curated mix holds it, and
|
| 145 |
+
nothing we tried pushed past it. So v6 stays the packaged base, and the next gains have to come from
|
| 146 |
+
capability stages rather than more base pretraining. Unique tokens stay around 0.5B the whole way, about
|
| 147 |
+
1/50th of what the leaders use.
|
| 148 |
+
|
| 149 |
+
## Evaluation
|
| 150 |
+
|
| 151 |
+
Zero-shot through lm-eval-harness, with a custom MLX backend for `loglikelihood`. We use acc_norm for the
|
| 152 |
+
length-sensitive multiple-choice tasks (HellaSwag, ARC, OpenBookQA) and plain acc otherwise.
|
| 153 |
+
|
| 154 |
+
Zero-shot, limit 1000 examples per task. Board avg = (HellaSwag + (ARC-E + ARC-C)/2 + PIQA + ArithMark) / 4.
|
| 155 |
+
|
| 156 |
+
| Task | Metric | Score | Random |
|
| 157 |
+
|---|---|---|---|
|
| 158 |
+
| SciQ | acc | 67.5 | 25 |
|
| 159 |
+
| PIQA | acc | 56.0 | 50 |
|
| 160 |
+
| COPA | acc | 55.0 | 50 |
|
| 161 |
+
| ARC-Easy | acc_norm | 35.6 | 25 |
|
| 162 |
+
| HellaSwag | acc_norm | 31.8 | 25 |
|
| 163 |
+
| OpenBookQA | acc_norm | 27.0 | 25 |
|
| 164 |
+
| ArithMark-2 | acc | 26.4 | 25 |
|
| 165 |
+
| ARC-Challenge | acc_norm | 22.4 | 25 |
|
| 166 |
+
| Winogrande | acc | 49.8 | 50 |
|
| 167 |
+
| LogicMark | acc | 44.8 | 25 |
|
| 168 |
+
| BoolQ | acc | 37.6 | ~50 |
|
| 169 |
+
| CommonsenseQA | acc | 20.7 | 20 |
|
| 170 |
+
| **Board avg (÷4)** | | **35.80** | |
|
| 171 |
+
|
| 172 |
+
For context, it beats the <10M leader on about 1/65th the tokens:
|
| 173 |
+
|
| 174 |
+
| Model | Params | Tokens | Board avg (÷4) |
|
| 175 |
+
|---|---|---|---|
|
| 176 |
+
| **Rodan-10M-Base (v6)** | 11.46M | ~0.38B | **35.80** |
|
| 177 |
+
| Liodon SLM-10M | 10M | 25B | 35.09 |
|
| 178 |
+
| GPT-S-5M (Axiomic) | 5.2M | 25B | 34.75 |
|
| 179 |
+
|
| 180 |
+

|
| 181 |
+
|
| 182 |
+
v6 lands around rank 22 of 54 and +0.31σ above the size-fit line, ahead of liodon at +0.14. The v9
|
| 183 |
+
challenger (PLE-free, 10.41M, pure-curated) tied it: 35.70 board avg at 9% fewer params, and about even on
|
| 184 |
+
per-param too (~+0.32σ). v9 confirmed the ~11M ceiling and that PLE was dead weight, but since it didn't
|
| 185 |
+
move the board, v6 stays the base. From here the work moves to the capability stages.
|
| 186 |
+
|
| 187 |
+
What the model is actually like: it holds up well for 11M on commonsense and science multiple-choice. SciQ
|
| 188 |
+
(67.5) beats GPT-2-124M, and PIQA (56.0), ARC-Easy (35.6), HellaSwag (31.8), and COPA (55.0) are all clearly
|
| 189 |
+
above random. Arithmetic has crept off the random floor (ArithMark 26.4) thanks to the folded-in computation
|
| 190 |
+
data, though it's a modest lift and actually generating arithmetic is still weak. On the harder abstract
|
| 191 |
+
reasoning tasks (Winogrande, CommonsenseQA, ARC-Challenge, OpenBookQA) and on open-ended generation it's near
|
| 192 |
+
chance, partly a capacity ceiling at this size and partly loglikelihood length-bias. It's a solid base for
|
| 193 |
+
discrimination; the deeper reasoning is the job of the later reasoning and latent stages.
|
| 194 |
+
|
| 195 |
+
## Limitations
|
| 196 |
+
|
| 197 |
+
- English only, ~11M params. This is a research and teaching base, not something to put in front of users or
|
| 198 |
+
trust for facts.
|
| 199 |
+
- It's reliable only on the easy commonsense and science multiple-choice where it beats random. On abstract
|
| 200 |
+
reasoning (Winogrande, CommonsenseQA, ARC-Challenge) and arithmetic it's at chance.
|
| 201 |
+
- No instruction tuning or safety alignment yet. It completes text; it does not follow instructions.
|
| 202 |
+
- Trained on about one epoch of a curated mix, so coverage of rare facts is thin compared to models trained
|
| 203 |
+
on far more tokens.
|
| 204 |
+
|
| 205 |
+
## Files
|
| 206 |
+
|
| 207 |
+
A standard model repo: `model.safetensors` (weights), `tokenizer.json` (8k byte-level BPE), `config.json`.
|
| 208 |
+
Trained on a single Apple M2 with MLX in about six hours.
|
| 209 |
+
|
| 210 |
+
## License
|
| 211 |
+
|
| 212 |
+
Weights are open. Data falls under the respective dataset licenses (Cosmopedia, dolmino-mix ODC-By, AllenAI
|
| 213 |
+
QA sets, FineMath).
|
config.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"model_type": "rodan-modern",
|
| 3 |
+
"architecture": "ModernLM",
|
| 4 |
+
"framework": "mlx",
|
| 5 |
+
"params": 11460000,
|
| 6 |
+
"vocab_size": 8192,
|
| 7 |
+
"dim": 320,
|
| 8 |
+
"n_layers": 8,
|
| 9 |
+
"n_heads": 8,
|
| 10 |
+
"n_kv_heads": 1,
|
| 11 |
+
"head_dim": 40,
|
| 12 |
+
"ffn_hidden": 768,
|
| 13 |
+
"max_len": 512,
|
| 14 |
+
"rope_base": 200000.0,
|
| 15 |
+
"norm": "rmsnorm",
|
| 16 |
+
"norm_eps": 1e-5,
|
| 17 |
+
"activation": "swiglu",
|
| 18 |
+
"qk_norm": true,
|
| 19 |
+
"tied_embeddings": true,
|
| 20 |
+
"value_residual": true,
|
| 21 |
+
"ple_rank": 16,
|
| 22 |
+
"lrm": true,
|
| 23 |
+
"attention": "mqa",
|
| 24 |
+
"tokenizer": "byte-level BPE (8k), eot id 0",
|
| 25 |
+
"notes": "Custom MLX decoder-only transformer. Load with model_opt.ModernLM(ModernConfig(**fields)) + load_weights('model.safetensors'). See README."
|
| 26 |
+
}
|
flops_efficiency.png
ADDED
|
intelligence_per_param.png
ADDED
|
loss_datamix.png
ADDED
|
Git LFS Details
|
model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:31dbd650d5e639f75a51b29ffdd4f463019741921cb83c96acfa72ade82c829c
|
| 3 |
+
size 45878253
|
tokenizer.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
v6_v9_metrics.png
ADDED
|