StentorLabs commited on
Commit
73c72a2
Β·
verified Β·
1 Parent(s): 2367898

Update README.md

Browse files
Files changed (1) hide show
  1. README.md +1166 -21
README.md CHANGED
@@ -1,28 +1,1173 @@
1
- # Stentor-12M
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
- This checkpoint was produced by the Stentor-12M training pipeline.
4
 
5
- ## Metadata
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
 
7
  ```json
8
  {
9
- "hidden_size": 256,
10
- "intermediate_size": 768,
11
- "learning_rate": 0.0008,
12
- "max_position_embeddings": 1024,
13
- "mixed_precision": "fp16",
14
- "model_arch": "llama",
15
- "num_attention_heads": 4,
16
- "num_hidden_layers": 12,
17
- "num_key_value_heads": 4,
18
- "optimizer": "adamw",
19
- "pad_vocab_to_multiple": 128,
20
- "rope_theta": null,
21
- "scheduler": "cosine",
22
- "stable_ratio": 0.8,
23
- "torch_compile": false,
24
- "vocab_size": 8064,
25
- "warmup_ratio": 0.05,
26
- "weight_decay": 0.01
27
  }
28
  ```
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ language:
3
+ - en
4
+ license: apache-2.0
5
+ library_name: transformers
6
+ tags:
7
+ - text-generation
8
+ - llama
9
+ - small-language-model
10
+ - efficient
11
+ - edge-deployment
12
+ - speculative-decoding
13
+ - tiny-model
14
+ - 12m-parameters
15
+ - kaggle-trained
16
+ - educational
17
+ - research
18
+ - low-resource
19
+ - cpu-inference
20
+ - mobile-deployment
21
+ - stentor2
22
+ - tokenmonster
23
+ pipeline_tag: text-generation
24
+ datasets:
25
+ - StentorLabs/StenCore
26
+ - epfml/FineWeb-HQ
27
+ widget:
28
+ - text: "Once upon a time"
29
+ example_title: "Story Generation"
30
+ - text: "Explain neural networks in simple terms."
31
+ example_title: "Toy Explanation (Often Wrong)"
32
+ - text: "def fibonacci(n):"
33
+ example_title: "Code Continuation"
34
+ - text: "The laws of thermodynamics describe"
35
+ example_title: "Science Continuation"
36
+ model_card_authors:
37
+ - StentorLabs
38
+ model-index:
39
+ - name: Stentor2-12M
40
+ results:
41
+ - task:
42
+ type: text-generation
43
+ dataset:
44
+ name: FineWeb-HQ / StenCore (validation split)
45
+ type: epfml/FineWeb-HQ
46
+ metrics:
47
+ - name: Final Validation Loss
48
+ type: loss
49
+ value: 3.2565
50
+ - name: Final Perplexity
51
+ type: perplexity
52
+ value: 25.96
53
+ - name: Best Checkpoint Validation Loss
54
+ type: loss
55
+ value: 3.2814
56
+ - name: BLiMP Overall Accuracy (67 tasks)
57
+ type: accuracy
58
+ value: 68.95
59
+ ---
60
 
61
+ # Stentor2-12M
62
 
63
+ ![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)
64
+ ![Model Size](https://img.shields.io/badge/parameters-12.3M-green.svg)
65
+ ![Training Time](https://img.shields.io/badge/training-2.13h-orange.svg)
66
+ ![Hardware](https://img.shields.io/badge/hardware-2x%20T4-red.svg)
67
+ ![Context Length](https://img.shields.io/badge/context-1024%20tokens-purple.svg)
68
+ ![Vocab Size](https://img.shields.io/badge/vocab-8064%20tokens-blue.svg)
69
+ [![Hugging Face](https://img.shields.io/badge/πŸ€—-Hugging%20Face-yellow.svg)](https://huggingface.co/StentorLabs)
70
+ ![BLiMP](https://img.shields.io/badge/BLiMP-68.95%25-brightgreen.svg)
71
+
72
+ > πŸ”¬ **Research Artifact β€” Not a Production Model.** This is a base language model released for research, experimentation, and community use. It is not suitable for deployment in any user-facing application without significant additional work. See [Intended Uses](#use-cases--intended-uses) for details.
73
+
74
+ ---
75
+
76
+ ## Table of Contents
77
+
78
+ 1. [What Is This?](#what-is-this)
79
+ 2. [The Core Design Insight: Vocabulary Efficiency](#the-core-design-insight-vocabulary-efficiency)
80
+ 3. [Why Full MHA Instead of GQA](#why-full-mha-instead-of-gqa)
81
+ 4. [Head-to-Head: Stentor v1 β†’ Stentor2-Preview β†’ Stentor2-12M](#head-to-head)
82
+ 5. [Quick Start](#quick-start)
83
+ 6. [Important Limitations](#important-limitations)
84
+ 7. [Model Architecture β€” Full Specification](#model-architecture--full-specification)
85
+ 8. [The Tokenizer: TokenMonster](#the-tokenizer-tokenmonster)
86
+ 9. [Training Infrastructure](#training-infrastructure)
87
+ 10. [Training Hyperparameters β€” Complete Reference](#training-hyperparameters--complete-reference)
88
+ 11. [The T4 Mixed-Precision Recipe β€” Deep Dive](#the-t4-mixed-precision-recipe--deep-dive)
89
+ 12. [Data Pipeline](#data-pipeline)
90
+ 13. [Weight Initialization](#weight-initialization)
91
+ 14. [Evaluation & Results](#evaluation--results)
92
+ 15. [BLiMP Benchmark](#blimp-benchmark)
93
+ 16. [Training Dynamics](#training-dynamics)
94
+ 17. [Use Cases & Intended Uses](#use-cases--intended-uses)
95
+ 18. [Out-of-Scope Uses](#out-of-scope-uses)
96
+ 19. [Ethical Considerations & Societal Impact](#ethical-considerations--societal-impact)
97
+ 20. [Inference Guide](#inference-guide)
98
+ 21. [Real Model Responses](#real-model-responses)
99
+ 22. [Quantization](#quantization)
100
+ 23. [Format Conversion](#format-conversion)
101
+ 24. [Speculative Decoding](#speculative-decoding)
102
+ 25. [Bias, Risks & Limitations](#bias-risks--limitations)
103
+ 26. [Related Work](#related-work)
104
+ 27. [Environmental Impact](#environmental-impact)
105
+ 28. [Citation](#citation)
106
+
107
+ ---
108
+
109
+ ## What Is This?
110
+
111
+ Stentor2-12M is the official release of the **Stentor2** model family β€” a ground-up redesign of the original Stentor v1 line. At ~12.3M parameters, it is a compact base language model built entirely from scratch on free-tier Kaggle compute using two NVIDIA Tesla T4 GPUs in a true dual-GPU distributed training configuration.
112
+
113
+ Like all Stentor models, this is a **base next-token predictor**, not a chat assistant. It will not reliably follow instructions, has no safety tuning, and is best used for research, prototyping, speculative decoding, and edge-deployment experimentation. The value of this model is not its conversational capability β€” it is what it represents architecturally: dramatically improved efficiency over both Stentor v1 and the Stentor2-Preview, achieved by fixing the root vocabulary imbalance of v1 and removing architectural overhead that actively harmed training at this scale.
114
+
115
+ ---
116
+
117
+ ## The Core Design Insight: Vocabulary Efficiency
118
+
119
+ The most consequential change in Stentor2 is the replacement of the standard Llama/Mistral 32,768-token vocabulary with a purpose-built **8,000-token English vocabulary** from the TokenMonster project (`english-8000-strict-nocapcode-v1`, padded to 8,064 for hardware alignment).
120
+
121
+ This is not a minor tweak β€” it is the entire architectural story of Stentor2.
122
+
123
+ ### Why Vocabulary Size Matters So Much at This Scale
124
+
125
+ In a transformer language model, the embedding table has shape `[vocab_size Γ— hidden_size]`. When you tie word embeddings (share the embedding and output projection weights, which Stentor does), this table appears once in the parameter count. At 12M total parameters, the fraction consumed by this table dictates how much "brain" is left over for the actual transformer layers.
126
+
127
+ **Stentor-12M (v1)** used a 32,768-token vocabulary. At a hidden size of 192:
128
+
129
+ ```
130
+ embedding_params = 32,768 Γ— 192 = 6,291,456
131
+ total_params = 12,047,040
132
+ embedding_share = 52.2%
133
+ ```
134
+
135
+ Over half of the model was a lookup table. The transformer stack β€” the part that actually *learns language patterns* β€” had fewer than 6 million parameters to work with. It was more dictionary than reasoner.
136
+
137
+ **Stentor2-12M** uses an 8,064-token vocabulary. At a hidden size of 256:
138
+
139
+ ```
140
+ embedding_params = 8,064 Γ— 256 = 2,064,384
141
+ total_params = 12,294,400
142
+ embedding_share = 16.8%
143
+ ```
144
+
145
+ By shrinking the vocabulary, the embedding table was cut from 6.3M to 2.1M parameters β€” freeing up ~4.2M parameters that were redistributed into transformer depth (12 layers vs 9) and width (hidden size 256 vs 192), where they contribute directly to language modeling quality.
146
+
147
+ The combined result of vocabulary efficiency, full MHA (see next section), a richer dataset pipeline, and true dual-GPU training is a **~70.8% reduction in perplexity** versus Stentor v1 (89.01 β†’ 25.96).
148
+
149
+ ---
150
+
151
+ ## Why Full MHA Instead of GQA
152
+
153
+ Stentor2-12M uses standard **full multi-head attention** (MHA) where the number of key/value heads equals the number of query heads (both = 4). The earlier Stentor2-Preview experimented with Grouped Query Attention (GQA), but it was removed for this release. This decision deserves a detailed explanation because it runs counter to the trend in larger models.
154
+
155
+ ### The GQA Problem at Small Scale
156
+
157
+ Grouped Query Attention was designed to reduce the KV cache memory footprint in billion-parameter models where KV cache size becomes a serious inference bottleneck. At 12M parameters with a 1,024-token context, this problem simply does not exist. The KV cache per sequence per layer is:
158
+
159
+ ```
160
+ 2 (K and V) Γ— 4 heads Γ— 64 head_dim Γ— 2 bytes (FP16) = 1,024 bytes per layer
161
+ Γ— 12 layers = 12,288 bytes β‰ˆ 12 KB per sequence
162
+ ```
163
+
164
+ GQA saves nothing meaningful here. But it costs something real during training.
165
+
166
+ ### How GQA Introduces Optimizer Noise
167
+
168
+ With only 4 attention heads total, the valid GQA configurations are 1, 2, or 4 KV heads. Using fewer than 4 (say, 2) means each KV projection must simultaneously serve 2 query heads.
169
+
170
+ During backpropagation, the gradients from those 2 query heads are **accumulated into a single KV projection update**. The optimizer (AdamW) then computes a single step direction for a gradient that is actually the sum of two independent signals β€” signals that may be pointing in meaningfully different directions depending on what each query head has learned to attend to. At scale, these signals tend to be well-correlated (which is part of why GQA works in large models). At 4 heads in a 12M parameter model, that correlation assumption breaks down. The optimizer is forced to find a compromise update that fully satisfies neither head, which introduces noise into the gradient estimates, destabilizes the loss curve, and slows convergence.
171
+
172
+ This effect was directly observed in preliminary training runs for the Stentor2 family. GQA produced a visibly noisier training loss curve and slower convergence toward low perplexity β€” the exact opposite of its intended benefit.
173
+
174
+ ### The Throughput Cost
175
+
176
+ Beyond gradient noise, GQA at 4 heads adds computational overhead with no offsetting gain. The head-sharing logic creates synchronization cost in the attention kernel that measurably reduced tokens-per-second throughput on T4 GPUs compared to clean full MHA. Every efficiency argument for GQA β€” memory, throughput, inference speed β€” inverts at this parameter scale.
177
+
178
+ **The conclusion is straightforward:** GQA is a tool designed for large models with large KV caches. Applying it to a 12M parameter model with 4 heads actively harms training without providing any of its intended benefits. Full MHA is the correct choice here.
179
+
180
+ ---
181
+
182
+ ## Head-to-Head
183
+
184
+ <a name="head-to-head"></a>
185
+
186
+ | Property | Stentor-12M (v1) | Stentor2-12M-Preview | **Stentor2-12M** |
187
+ |---|---|---|---|
188
+ | **Vocabulary** | 32,768 (Mistral BPE) | 8,064 (TokenMonster) | **8,064 (TokenMonster)** |
189
+ | **Hidden Size** | 192 | 256 | **256** |
190
+ | **Intermediate Size** | 576 | 768 | **768** |
191
+ | **Num Layers** | 9 | 12 | **12** |
192
+ | **Attention Heads** | 3 | 4 | **4** |
193
+ | **KV Heads (GQA)** | 3 (full MHA) | 4 (full MHA) | **4 (full MHA)** |
194
+ | **Head Dimension** | 64 | 64 | **64** |
195
+ | **Context Length** | 512 tokens | 1,024 tokens | **1,024 tokens** |
196
+ | **Total Parameters** | 12,047,040 | 12,294,400 | **12,294,400** |
197
+ | **Embedding Share** | 52.2% | 16.8% | **16.8%** |
198
+ | **Non-Embedding Params** | ~5.76M | ~10.23M | **~10.23M** |
199
+ | **Token Budget** | 200M | 240M | **480M (2 epochs)** |
200
+ | **Training Processes** | 1 | 1 (2 GPUs available) | **2 (true distributed)** |
201
+ | **Training Time** | ~1.3h | ~4.4h | **~2.13h** |
202
+ | **Final Perplexity** | 89.01 | ~50.07 | **25.96** |
203
+ | **Perplexity vs v1** | β€” | ↓43.8% | **↓70.8%** |
204
+ | **Dataset** | FineWeb-Edu + Cosmopedia | FineWeb-Edu | **StenCore + FineWeb-HQ** |
205
+ | **Architecture** | LlamaForCausalLM | LlamaForCausalLM | **LlamaForCausalLM** |
206
+ | **Training Precision** | fp16 | fp16 | **fp16** |
207
+ | **AutoModel compatible** | βœ… | ❌ (custom loader) | **βœ…** |
208
+
209
+ ---
210
+
211
+ ## πŸš€ Quick Start
212
+
213
+ ### 1. Install Dependencies
214
+
215
+ ```bash
216
+ pip install transformers torch safetensors tokenmonster
217
+ ```
218
+
219
+ ### 2. Load the Model
220
+
221
+ Stentor2-12M loads cleanly with the standard HuggingFace API β€” no custom loader required.
222
+
223
+ ```python
224
+ from transformers import AutoModelForCausalLM, AutoTokenizer
225
+ import torch
226
+
227
+ model = AutoModelForCausalLM.from_pretrained("StentorLabs/Stentor2-12M")
228
+ tokenizer = AutoTokenizer.from_pretrained("StentorLabs/Stentor2-12M", trust_remote_code=True)
229
+ ```
230
+
231
+ **GPU (FP16) β€” recommended if you have a CUDA GPU:**
232
+
233
+ ```python
234
+ model = AutoModelForCausalLM.from_pretrained(
235
+ "StentorLabs/Stentor2-12M",
236
+ torch_dtype=torch.float16,
237
+ device_map="auto"
238
+ )
239
+ ```
240
+
241
+ ### 3. Generate Text
242
+
243
+ This is a **base model** β€” it continues text rather than answering questions. Give it the beginning of a sentence and it will complete it.
244
+
245
+ ```python
246
+ input_ids = torch.tensor([tokenizer.encode("The history of computing")], dtype=torch.long).to(next(model.parameters()).device)
247
+ attention_mask = torch.ones_like(input_ids)
248
+
249
+ with torch.inference_mode():
250
+ output = model.generate(
251
+ input_ids,
252
+ attention_mask=attention_mask,
253
+ max_new_tokens=80,
254
+ do_sample=True,
255
+ temperature=1.1,
256
+ top_p=0.55,
257
+ repetition_penalty=1.15,
258
+ pad_token_id=tokenizer.pad_token_id,
259
+ )
260
+
261
+ print(tokenizer.decode(output[0].tolist()))
262
+ ```
263
+
264
+ > **Why `attention_mask`?** The model's pad token and EOS token share the same ID. Without an explicit attention mask, HuggingFace throws a warning because it cannot tell which tokens are real vs padding. Passing `torch.ones_like(input_ids)` tells the model that every token in the input is real β€” which is always true for single-sequence inference.
265
+
266
+ ### 4. Recommended Generation Settings
267
+
268
+ | Parameter | Recommended Range | Notes |
269
+ |---|---|---|
270
+ | `temperature` | 0.65 – 1.2 | Lower = more focused, higher = more creative |
271
+ | `top_p` | 0.5 – 0.8 | Nucleus sampling cutoff |
272
+ | `max_new_tokens` | 10 – 60 | Keep outputs short for best coherence |
273
+ | `repetition_penalty` | 1.1 – 1.2 | Helps prevent looping |
274
+
275
+ > ⚠️ **Keep `max_new_tokens` low.** This is a 12M parameter base model β€” coherence degrades beyond ~60 tokens. Short completions are significantly more on-topic than long ones.
276
+
277
+ ---
278
+
279
+ ## ⚠️ Important Limitations
280
+
281
+ - **Not Instruction-Tuned:** This is a base model. It will often ignore prompts, continue in unexpected directions, or respond off-topic.
282
+ - **No Safety Tuning:** No RLHF, no constitutional AI, no content filtering. Use with appropriate caution.
283
+ - **Limited World Knowledge:** ~12M parameters cannot store meaningful world knowledge. Do not treat outputs as factual.
284
+ - **Context Window:** Hard limit of 1,024 tokens. Longer contexts are untested and likely to degrade.
285
+ - **English Only:** The TokenMonster `english-8000-strict-nocapcode-v1` vocabulary is English-specific. Non-English text will tokenize very poorly.
286
+ - **Custom Tokenizer:** This model uses a TokenMonster adapter, not a standard HuggingFace fast tokenizer. The `tokenmonster` package must be installed. `trust_remote_code=True` is required when loading the tokenizer.
287
+ - **`skip_special_tokens` Not Supported:** The TokenMonster tokenizer does **not** support the `skip_special_tokens` argument in its decode method. Always decode without it and strip special tokens manually if needed.
288
+ - **Shared Tensor Warning:** When saving or loading, you may see: `Removed shared tensor {'lm_head.weight'} while saving`. This is expected behavior from tied word embeddings and is safe to ignore.
289
+
290
+ ---
291
+
292
+ ## Model Architecture β€” Full Specification
293
+
294
+ Stentor2-12M is a `LlamaForCausalLM` model. All architecture values below were derived directly from the training script and validated against the logged parameter counts.
295
+
296
+ ### Core Configuration
297
+
298
+ | Component | Value | Derivation |
299
+ |---|---|---|
300
+ | **Architecture** | `LlamaForCausalLM` | Hard-coded in training script |
301
+ | **Hidden Size** | 256 | Inferred: embedding_params (2,064,384) Γ· vocab_size (8,064) = 256 βœ“ |
302
+ | **Intermediate Size (FFN)** | 768 | Hidden Γ— 3 (verified via total param count) |
303
+ | **Num Hidden Layers** | 12 | Verified via total param count formula |
304
+ | **Num Attention Heads** | 4 | Hidden Γ· head_dim = 256 Γ· 64 = 4 |
305
+ | **Num Key/Value Heads** | 4 | Full MHA β€” no GQA (see [above](#why-full-mha-instead-of-gqa)) |
306
+ | **Head Dimension** | 64 | Enforced by training script hardware constraint |
307
+ | **Vocab Size** | 8,064 | TokenMonster 8K base + 62 padding tokens (padded to multiple of 128) |
308
+ | **Max Position Embeddings** | 1,024 | `block_size` in training configuration |
309
+ | **Hidden Activation** | SiLU | LlamaForCausalLM default |
310
+ | **Positional Encoding** | RoPE | `rope_theta = 10,000.0` |
311
+ | **RMS Norm Epsilon** | 1e-5 | Training script default |
312
+ | **Tie Word Embeddings** | True | Shared embedding / LM head weights |
313
+ | **Attention Implementation** | SDPA | PyTorch Scaled Dot Product Attention |
314
+ | **Attention Pattern** | Full causal | No sliding window, no sparse patterns |
315
+
316
+ ### Parameter Count Breakdown
317
+
318
+ The total parameter count can be reproduced exactly using the following formula from the training script:
319
+
320
+ ```python
321
+ def estimate_llama_params(vocab_size, hidden_size, intermediate_size,
322
+ num_hidden_layers, num_attention_heads, num_key_value_heads):
323
+ kv_dim = int(hidden_size * num_key_value_heads / num_attention_heads)
324
+ attn = 2 * hidden_size * hidden_size + 2 * hidden_size * kv_dim
325
+ mlp = 3 * hidden_size * intermediate_size
326
+ norm = 2 * hidden_size
327
+ total = vocab_size * hidden_size + num_hidden_layers * (attn + mlp + norm) + hidden_size
328
+ return total
329
+ ```
330
+
331
+ Plugging in Stentor2-12M values:
332
+
333
+ ```
334
+ kv_dim = 256 * 4 / 4 = 256
335
+ attn = 2Γ—256Γ—256 + 2Γ—256Γ—256 = 262,144
336
+ mlp = 3Γ—256Γ—768 = 589,824
337
+ norm = 2Γ—256 = 512
338
+ per_layer = 852,480
339
+
340
+ embedding = 8,064 Γ— 256 = 2,064,384
341
+ layers = 12 Γ— 852,480 = 10,229,760
342
+ final_norm = 256
343
+
344
+ total = 2,064,384 + 10,229,760 + 256 = 12,294,400 βœ“
345
+ ```
346
+
347
+ | Component | Parameters | % of Total |
348
+ |---|---|---|
349
+ | Embedding Table (tied with LM Head) | 2,064,384 | 16.8% |
350
+ | Transformer Layers Γ— 12 | 10,229,760 | 83.2% |
351
+ | β€” Attention (per layer Γ— 12) | 3,145,728 | 25.6% |
352
+ | β€” FFN/MLP (per layer Γ— 12) | 7,077,888 | 57.5% |
353
+ | β€” Layer Norms (per layer Γ— 12) | 6,144 | 0.05% |
354
+ | Final RMS Norm | 256 | 0.002% |
355
+ | **Total** | **12,294,400** | **100%** |
356
+
357
+ ---
358
+
359
+ ## The Tokenizer: TokenMonster
360
+
361
+ Stentor2-12M uses a custom tokenizer adapter wrapping the **TokenMonster** `english-8000-strict-nocapcode-v1` vocabulary rather than a standard BPE tokenizer from the HuggingFace ecosystem.
362
+
363
+ ### What Is TokenMonster?
364
+
365
+ TokenMonster ([alasdairforsythe/tokenmonster](https://huggingface.co/alasdairforsythe/tokenmonster)) is an alternative tokenization approach optimized for compact English vocabulary sizes. The `english-8000-strict-nocapcode-v1` vocabulary is a purpose-built ~8,000-token English vocabulary designed for efficiency at small model scales.
366
+
367
+ ### ⚠️ `skip_special_tokens` Is Not Supported
368
+
369
+ The TokenMonster tokenizer **does not support the `skip_special_tokens` argument** in its decode method. Always decode without it:
370
+
371
+ ```python
372
+ # βœ… Correct
373
+ text = tokenizer.decode(output_ids)
374
+
375
+ # ❌ This will raise an error
376
+ text = tokenizer.decode(output_ids, skip_special_tokens=True)
377
+ ```
378
+
379
+ If you are using HuggingFace's `TextIteratorStreamer` or any wrapper that internally passes `skip_special_tokens=True`, you will need to patch or replace that wrapper.
380
+
381
+ ### Tokenizer Efficiency vs. v1
382
+
383
+ You may notice this tokenizer produces **more tokens per word** compared to Stentor v1. This is expected and by design. The v1 models used a 32,768-token Mistral BPE vocabulary, which encodes common English words as single tokens very efficiently. Stentor2 uses an 8,064-token TokenMonster vocabulary β€” smaller vocabulary means more tokens per word on average. This is the direct tradeoff for freeing up ~4.2M parameters for the transformer layers. The ~70.8% perplexity improvement shows the tradeoff was worth it.
384
+
385
+ ### Vocabulary Construction
386
+
387
+ 1. Base vocabulary loaded from `alasdairforsythe/tokenmonster` β†’ `vocabs/english-8000-strict-nocapcode-v1.vocab`
388
+ 2. Special tokens added: `</s>` (EOS), `<s>` (BOS), `<pad>` (set equal to EOS)
389
+ 3. A default chat template is injected for structural compatibility
390
+ 4. Vocabulary padded to the nearest multiple of 128 using dummy tokens `<|extra_0|>` through `<|extra_61|>`, resulting in a final vocabulary size of **8,064 tokens**
391
+
392
+ ### Tokenizer Configuration
393
 
394
  ```json
395
  {
396
+ "tokenizer_type": "tokenmonster",
397
+ "vocab_file": "tokenmonster.vocab",
398
+ "model_max_length": 1024,
399
+ "eos_token": "</s>",
400
+ "bos_token": "<s>",
401
+ "pad_token": "</s>",
402
+ "vocab_size": 8064
 
 
 
 
 
 
 
 
 
 
 
403
  }
404
  ```
405
+
406
+ ### Chat Template
407
+
408
+ A simple chat template is injected for structural compatibility with chat formatting tools. The base model is **not trained to follow it**:
409
+
410
+ ```jinja
411
+ {% for message in messages %}
412
+ <|{{ message['role'] }}|>
413
+ {{ message['content'] }}
414
+ {% endfor %}
415
+ {% if add_generation_prompt %}<|assistant|>
416
+ {% endif %}
417
+ ```
418
+
419
+ ### Loading the Tokenizer
420
+
421
+ ```bash
422
+ pip install tokenmonster
423
+ ```
424
+
425
+ ```python
426
+ from transformers import AutoTokenizer
427
+ tokenizer = AutoTokenizer.from_pretrained("StentorLabs/Stentor2-12M", trust_remote_code=True)
428
+ ```
429
+
430
+ ---
431
+
432
+ ## Training Infrastructure
433
+
434
+ ### Hardware
435
+
436
+ | Component | Specification |
437
+ |---|---|
438
+ | GPU Count | 2Γ— NVIDIA Tesla T4 |
439
+ | VRAM per GPU | 15.64 GB |
440
+ | Total VRAM | ~31.3 GB |
441
+ | Training Processes | 2 (true data-parallel distributed training) |
442
+ | Platform | Kaggle Notebooks (free tier) |
443
+ | Accelerator Library | HuggingFace Accelerate |
444
+ | Communication Backend | NCCL |
445
+
446
+ > **Note on Dual-GPU Setup:** Unlike Stentor2-12M-Preview (which had 2 GPUs available but ran as a single process), Stentor2-12M ran as a true 2-process distributed job with `world_size=2`. Both GPUs contributed to training, which is why total_batch_size=128 with per_device_batch_size=32 and gradient_accumulation=2 (32 Γ— 2 processes Γ— 2 = 128).
447
+
448
+ ### Software Stack
449
+
450
+ | Package | Role |
451
+ |---|---|
452
+ | PyTorch | Core tensor operations and autograd |
453
+ | HuggingFace Transformers | Model architecture (LlamaForCausalLM) |
454
+ | HuggingFace Accelerate | Distributed training loop and device management |
455
+ | HuggingFace Datasets | Streaming data loading |
456
+ | bitsandbytes | Quantization primitives |
457
+ | tokenmonster | Custom vocabulary |
458
+ | safetensors | Model serialization |
459
+
460
+ ---
461
+
462
+ ## Training Hyperparameters β€” Complete Reference
463
+
464
+ ### Core Training Parameters
465
+
466
+ | Hyperparameter | Value | Notes |
467
+ |---|---|---|
468
+ | `learning_rate` | 8e-4 | AdamW LR |
469
+ | `weight_decay` | 0.01 | Applied to non-embedding, non-norm, non-bias params |
470
+ | `max_grad_norm` | 1.0 | Gradient clipping threshold |
471
+ | `optimizer` | AdamW | With `betas=(0.9, 0.95)`, `eps=1e-8` |
472
+ | `scheduler` | Cosine | Cosine decay with linear warmup |
473
+ | `warmup_steps` | 477 | ~5% of max_train_steps |
474
+ | `stable_steps` | 7,641 | ~80% of max_train_steps |
475
+ | `source_token_budget` | 240,000,000 | Raw source tokens per epoch |
476
+ | `token_budget` | 480,000,000 | Total tokens across all epochs (2 epochs Γ— 240M) |
477
+ | `max_train_steps` | 9,552 | Theoretical max; training stopped early at token budget |
478
+ | `actual_steps_trained` | ~3,600 | Token budget reached at step ~3,600 in epoch 1 |
479
+ | `num_train_epochs` | 2 | |
480
+ | `seed` | 42 | Reproducibility seed |
481
+ | `mixed_precision` | fp16 | All activations/gradients in FP16 |
482
+
483
+ ### Batch & Sequence Parameters
484
+
485
+ | Hyperparameter | Value | Notes |
486
+ |---|---|---|
487
+ | `per_device_train_batch_size` | 32 | Per GPU per gradient accumulation step |
488
+ | `per_device_eval_batch_size` | 16 | Evaluation batch size |
489
+ | `gradient_accumulation_steps` | 2 | Optimizer step every 2 forward passes |
490
+ | `num_processes` | 2 | True distributed, 2 GPUs |
491
+ | `total_batch_size` | 128 | `32 Γ— 2 processes Γ— 2 grad_accum` |
492
+ | `block_size` | 1,024 | Sequence length; training packed to this size |
493
+ | `tokens_per_optimizer_step` | 131,072 | `total_batch_size Γ— block_size` |
494
+
495
+ ### Evaluation & Checkpointing
496
+
497
+ | Hyperparameter | Value |
498
+ |---|---|
499
+ | `eval_steps` | 900 |
500
+ | `best_eval_steps` | 900 |
501
+ | `best_eval_start_step` | 1,500 |
502
+ | `save_every_minutes` | 30 |
503
+ | `save_total_limit` | 2 |
504
+ | `logging_steps` | 300 |
505
+ | `max_eval_samples` | 5,000 |
506
+
507
+ ### Precision Stability Configuration
508
+
509
+ | Setting | Value |
510
+ |---|---|
511
+ | `fp32_norms` | True |
512
+ | `fp32_norm_modules` | 25 |
513
+ | `fp32_softmax_modules` | 0 (SDPA-compatible path) |
514
+ | `fp32_critical_layers` | 2 (first 1 + last 1) |
515
+ | `torch_compile` | False |
516
+ | `gradient_checkpointing` | False |
517
+
518
+ ### AdamW Optimizer β€” Detailed
519
+
520
+ The optimizer uses a decoupled parameter group strategy:
521
+
522
+ - **Decay group:** All `nn.Linear` weight matrices (excludes bias, norm weights, embedding)
523
+ - `weight_decay = 0.01`
524
+ - **No-decay group:** Bias terms, normalization parameters, embedding parameters
525
+ - `weight_decay = 0.0`
526
+ - **Betas:** `(0.9, 0.95)` β€” the 0.95 Ξ²β‚‚ is a modern LLM default (vs the 0.999 PyTorch default)
527
+ - **Epsilon:** `1e-8`
528
+ - **Fused kernel:** Enabled when CUDA is available (`fused=True`)
529
+
530
+ ### Learning Rate Schedule
531
+
532
+ ```
533
+ Phase 1 β€” Warmup (steps 0–477):
534
+ LR ramps linearly from 0 β†’ 8e-4
535
+
536
+ Phase 2 β€” Cosine Decay (steps 477–9,552):
537
+ LR follows cosine curve from 8e-4 β†’ 0
538
+ (Training stopped early at ~3,600 steps due to token budget)
539
+ ```
540
+
541
+ ---
542
+
543
+ ## The T4 Mixed-Precision Recipe β€” Deep Dive
544
+
545
+ The most technically interesting aspect of Stentor2's training pipeline is its custom **T4 Mixed-Precision Recipe** β€” a bespoke approach to stable FP16 training on NVIDIA Tesla T4 GPUs, which lack BF16 support and have known numerical instability issues with FP16 on certain operations.
546
+
547
+ This recipe involves three distinct techniques applied simultaneously:
548
+
549
+ ### 1. FP32 Critical Layers (2 layers: first 1 + last 1)
550
+
551
+ The first and last transformer layers run entirely in FP32:
552
+
553
+ - Their weights are cast to `.float()` at setup time
554
+ - Their `forward()` method is monkey-patched to cast all inputs to FP32 before the call and cast outputs back to the original dtype afterward
555
+ - `torch.amp.autocast("cuda", enabled=False)` context prevents autocast from re-downcasting inside the layer
556
+
557
+ **Rationale:** The first layer is responsible for embedding projection and initial feature extraction; instability here corrupts the entire forward pass. The last layer handles final token prediction; numerical errors here directly impact loss. Running these in FP32 provides a stability floor at minimal compute cost.
558
+
559
+ ### 2. FP32 Normalization Layers (25 modules)
560
+
561
+ All RMSNorm and LayerNorm modules are monkey-patched to run their computation in FP32 regardless of input dtype:
562
+
563
+ ```python
564
+ def _fp32_norm_forward(hidden_states, *args, **kwargs):
565
+ input_dtype = hidden_states.dtype
566
+ output = original_forward(hidden_states.float().contiguous(), *args, **kwargs)
567
+ return output.clone().to(input_dtype)
568
+ ```
569
+
570
+ The `.clone()` call prevents returning graph-managed buffers that could be overwritten across replay steps. The `.contiguous()` call prevents strided-tensor issues in FP32 norm operations.
571
+
572
+ **Why 25 modules:** 12 transformer layers Γ— 2 norms each (input norm + post-attention norm) + 1 final norm = 25 total.
573
+
574
+ **This is why `torch_compile` is disabled.** The FP32 norm wrappers are incompatible with CUDAGraph replay, which `torch.compile` uses under `reduce-overhead` mode.
575
+
576
+ ### 3. SDPA-Compatible Path (No FP32 Softmax Wrapper)
577
+
578
+ Unlike earlier experiments, Stentor2-12M uses the SDPA (Scaled Dot Product Attention) path from PyTorch, which handles numerical stability in the attention kernel natively. The FP32 softmax monkey-patch used in some experimental runs was intentionally skipped here because SDPA and FlashAttention kernels require FP16/BF16 inputs for their fast paths. Wrapping the softmax in FP32 would disable these kernels and cause a significant throughput regression. SDPA's internal numerics are already stable enough for 1,024-token sequences on T4 hardware.
579
+
580
+ ### T4 Recipe Summary Table
581
+
582
+ | Technique | Count | Scope |
583
+ |---|---|---|
584
+ | FP32 critical layers | 2 | First and last transformer layers |
585
+ | FP32 norm modules | 25 | All RMSNorm / LayerNorm |
586
+ | FP32 softmax modules | 0 | Skipped β€” SDPA handles this natively |
587
+
588
+ ---
589
+
590
+ ## Data Pipeline
591
+
592
+ ### Dataset
593
+
594
+ Stentor2-12M was trained on a **cascaded StenCore β†’ FineWeb-HQ pipeline**, a two-stage data mixture combining StentorLabs' curated source corpus with high-quality filtered web text. This represents an upgrade from the FineWeb-Edu-only data used in Stentor2-12M-Preview.
595
+
596
+ **Total tokens processed:** 480,116,736 (2 epochs Γ— 240M source tokens per epoch)
597
+
598
+ ### Cascaded Data Pipeline
599
+
600
+ The training pipeline loads data in priority order:
601
+
602
+ 1. **StenCore** ([StentorLabs/StenCore](https://huggingface.co/datasets/StentorLabs/StenCore)) β€” StentorLabs' curated training corpus, loaded first up to the source token budget
603
+ 2. **FineWeb-HQ** ([epfml/FineWeb-HQ](https://huggingface.co/datasets/epfml/FineWeb-HQ)) β€” High-quality filtered web text, used to fill the remaining token budget after StenCore is exhausted. FineWeb-HQ documents are deduplicated against StenCore by content fingerprint to prevent overlap
604
+
605
+ The pipeline materializes the dataset on rank 0 and distributes it across both GPU processes via a shared disk cache, eliminating duplicated downloads in the distributed setting.
606
+
607
+ ### Dataset Scale
608
+
609
+ | Component | Value |
610
+ |---|---|
611
+ | Materialized raw rows | 1,137,414 |
612
+ | Source token budget per epoch | 240,000,000 |
613
+ | Total token budget | 480,000,000 |
614
+ | Actual tokens trained on | 480,116,736 |
615
+ | Source download budget | 3 GB |
616
+
617
+ ### Text Preprocessing
618
+
619
+ Each raw text sample undergoes the following cleaning pipeline before tokenization:
620
+
621
+ ```python
622
+ def clean_text(text: str) -> str:
623
+ text = unicodedata.normalize("NFKC", text) # normalize unicode
624
+ lines = [line.strip() for line in text.splitlines() if line.strip()]
625
+ text = " ".join(lines) # collapse newlines
626
+ text = " ".join(text.split()) # normalize whitespace
627
+ return text
628
+ ```
629
+
630
+ **NFKC normalization** maps visually equivalent Unicode characters to a single canonical form (e.g., full-width `οΌ‘` β†’ `A`, ligature `fi` β†’ `fi`). This is the standard approach used in T5, BERT, and most modern LLM preprocessing pipelines.
631
+
632
+ ### Tokenization
633
+
634
+ - `add_special_tokens=False` during tokenization
635
+ - EOS token (`</s>`) appended to every sample
636
+ - Attention mask generated (all 1s for real tokens)
637
+
638
+ ### Sequence Packing
639
+
640
+ After tokenization, samples are **packed** into fixed 1,024-token blocks:
641
+
642
+ ```
643
+ Sample 1: [tok, tok, ..., </s>] (e.g., 347 tokens)
644
+ Sample 2: [tok, tok, ..., </s>] (e.g., 891 tokens)
645
+ ↓
646
+ Block 1: [<sample1...>, <first 677 tokens of sample2>] (1024 tokens)
647
+ Block 2: [<remaining 214 tokens of sample2>, <sample3...>]
648
+ ```
649
+
650
+ Packing eliminates all padding waste and ensures every training token is a real content token. Labels for packed sequences are identical to `input_ids` β€” there is no special boundary masking between packed samples, which is standard practice.
651
+
652
+ ### Data Collation
653
+
654
+ - `input_ids`: padded with `pad_token_id`
655
+ - `labels`: padded with `-100` (ignored in loss computation)
656
+ - `attention_mask`: padded with `0`
657
+ - All batches padded to nearest multiple of 8 for hardware alignment
658
+
659
+ ---
660
+
661
+ ## Weight Initialization
662
+
663
+ All parameters are initialized using a **truncated normal distribution** with `std=0.02` β€” the same initialization used in GPT-2 and most modern LLMs:
664
+
665
+ ```python
666
+ def initialize_weights(model, std=0.02):
667
+ for module in model.modules():
668
+ if isinstance(module, (nn.Linear, nn.Embedding)):
669
+ module.weight.data.normal_(mean=0.0, std=std)
670
+ if module.bias is not None:
671
+ module.bias.data.zero_()
672
+ elif "rmsnorm" in type(module).__name__.lower():
673
+ if module.weight is not None:
674
+ module.weight.data.fill_(1.0) # scale initialized to 1
675
+ if module.bias is not None:
676
+ module.bias.data.zero_()
677
+ ```
678
+
679
+ - Linear layers: normal(0, 0.02)
680
+ - Embeddings: normal(0, 0.02)
681
+ - RMSNorm scale weights: initialized to 1.0 (identity transform at start)
682
+ - All biases: zero
683
+
684
+ ---
685
+
686
+ ## Evaluation & Results
687
+
688
+ ### Metrics
689
+
690
+ - **Validation Loss:** Cross-entropy loss over a held-out validation prefix drawn from the tokenized train set (5,000 samples)
691
+ - **Perplexity (PPL):** `exp(loss)` β€” lower means the model is less "surprised" by unseen text
692
+
693
+ ### Training Loss Progression
694
+
695
+ | Step | Loss (train batch) |
696
+ |---|---|
697
+ | 300 | 4.9760 |
698
+ | 600 | 4.1367 |
699
+ | 900 | 3.4398 |
700
+ | 1200 | 3.6314 |
701
+ | 1500 | 3.3766 |
702
+ | 1800 | 3.3155 |
703
+ | 2100 | 3.2452 |
704
+ | 2400 | 3.3610 |
705
+ | 2700 | 3.2639 |
706
+ | 3000 | 3.3475 |
707
+ | 3300 | 3.0684 |
708
+ | 3600 | 2.9352 |
709
+
710
+ ### Evaluation Results
711
+
712
+ | Checkpoint | Step | Eval Loss | Perplexity |
713
+ |---|---|---|---|
714
+ | End of Epoch 0 | ~2,400 | 3.3776 | 29.30 |
715
+ | Best Checkpoint | 3,300 | 3.2814 | ~26.62 |
716
+ | **Final Model** | **~3,600** | **3.2565** | **25.96** |
717
+
718
+ ### Comparison to Previous Stentor Models
719
+
720
+ | Model | Final Eval Loss | Final Perplexity | vs v1 |
721
+ |---|---|---|---|
722
+ | Stentor-12M (v1) | 4.4887 | 89.01 | β€” |
723
+ | Stentor2-12M-Preview | ~3.91 | ~50.07 | ↓43.8% |
724
+ | **Stentor2-12M** | **3.2565** | **25.96** | **↓70.8%** |
725
+
726
+ > **Comparison note:** These three models were trained on different datasets and with different configurations. The comparison is meaningful directionally but is not a rigorously controlled ablation. Stentor v1 used FineWeb-Edu + Cosmopedia; Stentor2-Preview used FineWeb-Edu only; Stentor2-12M used StenCore + FineWeb-HQ.
727
+
728
+ ---
729
+
730
+ ## BLiMP Benchmark
731
+
732
+ BLiMP (Benchmark of Linguistic Minimal Pairs) evaluates a language model's grammatical knowledge by presenting pairs of sentences β€” one grammatical and one ungrammatical β€” and measuring how often the model assigns higher probability to the grammatical sentence. Random chance is 50%.
733
+
734
+ **Overall BLiMP Average (67 tasks): 68.95%**
735
+ **Random Baseline: 50.00%**
736
+ **Above Chance: +18.95 percentage points**
737
+
738
+ ### Full BLiMP Results
739
+
740
+ | Task | Accuracy |
741
+ |---|---|
742
+ | **Overall Average** | **68.95%** |
743
+ | blimp_adjunct_island | 72.00% |
744
+ | blimp_anaphor_gender_agreement | 65.70% |
745
+ | blimp_anaphor_number_agreement | 95.70% |
746
+ | blimp_animate_subject_passive | 68.70% |
747
+ | blimp_animate_subject_trans | 82.80% |
748
+ | blimp_causative | 60.80% |
749
+ | blimp_complex_NP_island | 41.00% |
750
+ | blimp_coordinate_structure_constraint_complex_left_branch | 40.20% |
751
+ | blimp_coordinate_structure_constraint_object_extraction | 75.00% |
752
+ | blimp_determiner_noun_agreement_1 | 97.40% |
753
+ | blimp_determiner_noun_agreement_2 | 92.50% |
754
+ | blimp_determiner_noun_agreement_irregular_1 | 75.50% |
755
+ | blimp_determiner_noun_agreement_irregular_2 | 90.00% |
756
+ | blimp_determiner_noun_agreement_with_adj_2 | 85.40% |
757
+ | blimp_determiner_noun_agreement_with_adj_irregular_1 | 78.60% |
758
+ | blimp_determiner_noun_agreement_with_adj_irregular_2 | 81.40% |
759
+ | blimp_determiner_noun_agreement_with_adjective_1 | 89.60% |
760
+ | blimp_distractor_agreement_relational_noun | 70.90% |
761
+ | blimp_distractor_agreement_relative_clause | 42.50% |
762
+ | blimp_drop_argument | 70.20% |
763
+ | blimp_ellipsis_n_bar_1 | 67.30% |
764
+ | blimp_ellipsis_n_bar_2 | 79.70% |
765
+ | blimp_existential_there_object_raising | 79.70% |
766
+ | blimp_existential_there_quantifiers_1 | 95.80% |
767
+ | blimp_existential_there_quantifiers_2 | 29.60% |
768
+ | blimp_existential_there_subject_raising | 72.80% |
769
+ | blimp_expletive_it_object_raising | 73.30% |
770
+ | blimp_inchoative | 49.50% |
771
+ | blimp_intransitive | 60.60% |
772
+ | blimp_irregular_past_participle_adjectives | 99.70% |
773
+ | blimp_irregular_past_participle_verbs | 78.50% |
774
+ | blimp_irregular_plural_subject_verb_agreement_1 | 77.40% |
775
+ | blimp_irregular_plural_subject_verb_agreement_2 | 87.40% |
776
+ | blimp_left_branch_island_echo_question | 40.00% |
777
+ | blimp_left_branch_island_simple_question | 38.80% |
778
+ | blimp_matrix_question_npi_licensor_present | 16.30% |
779
+ | blimp_npi_present_1 | 65.50% |
780
+ | blimp_npi_present_2 | 63.80% |
781
+ | blimp_only_npi_licensor_present | 52.90% |
782
+ | blimp_only_npi_scope | 41.60% |
783
+ | blimp_passive_1 | 86.10% |
784
+ | blimp_passive_2 | 85.80% |
785
+ | blimp_principle_A_c_command | 65.60% |
786
+ | blimp_principle_A_case_1 | 100.00% |
787
+ | blimp_principle_A_case_2 | 85.90% |
788
+ | blimp_principle_A_domain_1 | 88.90% |
789
+ | blimp_principle_A_domain_2 | 64.10% |
790
+ | blimp_principle_A_domain_3 | 50.50% |
791
+ | blimp_principle_A_reconstruction | 40.20% |
792
+ | blimp_regular_plural_subject_verb_agreement_1 | 87.30% |
793
+ | blimp_regular_plural_subject_verb_agreement_2 | 79.70% |
794
+ | blimp_sentential_negation_npi_licensor_present | 99.20% |
795
+ | blimp_sentential_negation_npi_scope | 46.40% |
796
+ | blimp_sentential_subject_island | 39.30% |
797
+ | blimp_superlative_quantifiers_1 | 48.00% |
798
+ | blimp_superlative_quantifiers_2 | 75.60% |
799
+ | blimp_tough_vs_raising_1 | 34.00% |
800
+ | blimp_tough_vs_raising_2 | 83.10% |
801
+ | blimp_transitive | 76.80% |
802
+ | blimp_wh_island | 63.40% |
803
+ | blimp_wh_questions_object_gap | 59.10% |
804
+ | blimp_wh_questions_subject_gap | 87.60% |
805
+ | blimp_wh_questions_subject_gap_long_distance | 92.40% |
806
+ | blimp_wh_vs_that_no_gap | 92.10% |
807
+ | blimp_wh_vs_that_no_gap_long_distance | 97.50% |
808
+ | blimp_wh_vs_that_with_gap | 33.80% |
809
+ | blimp_wh_vs_that_with_gap_long_distance | 11.00% |
810
+
811
+ ### BLiMP Interpretation
812
+
813
+ **Strong performance (>85%):** Determiner-noun agreement, passive constructions, anaphor number agreement, irregular past participles, existential quantifiers, sentential negation licensing, wh-movement without gaps, Principle A case conditions. These tasks test local agreement and well-practiced syntactic patterns β€” areas where token-level statistics are strong predictors.
814
+
815
+ **Moderate performance (60–85%):** Most agreement tasks, animate subject constructions, ellipsis, raising constructions, WH questions with subject gaps. The model has learned these patterns but applies them inconsistently across surface variations.
816
+
817
+ **Below-chance or near-chance performance (<50%):** NPI licensing (particularly `matrix_question_npi_licensor_present` at 16.3%, `wh_vs_that_with_gap_long_distance` at 11%), island constraints, some scope tasks. These require tracking long-distance syntactic dependencies or operator scope β€” phenomena that demand more representational capacity than 12M parameters can reliably support at 1,024-token context.
818
+
819
+ **Key takeaway:** At 68.95% vs 50% random, this model demonstrates genuine grammatical knowledge well above chance at only 12.3M parameters. The profile of strengths (local agreement) and weaknesses (long-distance dependencies, operator scope) is consistent with what scaling laws predict for a model of this size.
820
+
821
+ ---
822
+
823
+ ## Training Dynamics
824
+
825
+ The training run proceeded for 2 epochs, stopping when the 480M token budget was reached at ~step 3,600 of a theoretical 9,552-step schedule.
826
+
827
+ **Epoch 0 (steps 0–~2,400, 3,497 seconds):** Loss dropped rapidly from ~8+ initially, stabilizing around 3.2–3.6 by step 2,100. The first best checkpoint was recorded at step 1,500 (eval loss 3.5410), with a better checkpoint at step 2,400 (eval loss 3.3743) marking the end of epoch 0.
828
+
829
+ **Epoch 1 (steps ~2,400–3,600, 1,906 seconds):** Training continued with a fresh data pass. The best checkpoint was achieved at step 3,300 (eval loss 3.2814). Notably, the final epoch evaluation after the token budget was reached produced 3.2565 (ppl 25.96) β€” slightly better than the best saved checkpoint eval, consistent with the cosine schedule still having meaningful learning rate at that point.
830
+
831
+ **Throughput:** ~91,500–92,000 tokens/second average across both GPUs throughout training β€” a substantial improvement from the single-process ~91,600 tps of the Preview, now representing genuine dual-GPU utilization.
832
+
833
+ ---
834
+
835
+ ## Use Cases & Intended Uses
836
+
837
+ > πŸ”¬ **Reminder:** This is a **research artifact**. It is a base language model with no safety tuning, no instruction following, and no factual grounding.
838
+
839
+ ### Intended Uses
840
+
841
+ | Use Case | Suitability | Notes |
842
+ |---|---|---|
843
+ | Studying transformer training dynamics | βœ… High | Small enough to train/fine-tune on free compute |
844
+ | Tokenization efficiency research | βœ… High | 8K vs 32K vocab tradeoff is directly observable |
845
+ | GQA vs MHA ablation research | βœ… High | Contrast directly with Stentor2-Preview |
846
+ | Speculative decoding experiments | βœ… High | Fast enough to serve as a draft model |
847
+ | Benchmarking CPU/edge inference latency | βœ… High | ~25 MB in FP16, runs on any hardware |
848
+ | Testing quantization/conversion pipelines | βœ… High | GGUF, ONNX, INT8 pipeline validation |
849
+ | Teaching material for LLM courses | βœ… High | Architecture is simple enough to trace by hand |
850
+ | LoRA / QLoRA fine-tuning experiments | βœ… Moderate | Base model only; start from scratch for any task |
851
+ | Text continuation / creative prompting | βœ… Moderate | Works best on short completions ≀60 tokens |
852
+ | Domain-specific fine-tuning research | βœ… Moderate | Small enough to iterate rapidly |
853
+ | Factual Q&A | ❌ Not suitable | Model has no reliable world knowledge |
854
+ | Production deployment | ❌ Not suitable | No safety tuning; base model only |
855
+ | Non-English text | ❌ Not suitable | TokenMonster vocab is English-only |
856
+ | Long coherent outputs (>60 tokens) | ❌ Not suitable | Coherence degrades quickly at this scale |
857
+
858
+ ---
859
+
860
+ ## Out-of-Scope Uses
861
+
862
+ - **User-facing applications of any kind** β€” This model has no safety filtering, no alignment, and no factual reliability.
863
+ - **Medical, legal, or financial advice** β€” 12M parameters cannot store or reason over specialized knowledge reliably.
864
+ - **Generating content about real people** β€” The model has no awareness of real people. Outputs mentioning real people are likely fabricated.
865
+ - **Automated content pipelines** β€” Do not use this model to generate content at scale without human review.
866
+ - **Non-English use** β€” The 8,064-token TokenMonster vocabulary is built exclusively for English.
867
+ - **Instruction following** β€” This is a base model. It does not reliably follow instructions, answer questions, or complete structured tasks.
868
+
869
+ ---
870
+
871
+ ## Ethical Considerations & Societal Impact
872
+
873
+ ### Inherited Data Biases
874
+
875
+ Stentor2-12M was trained on StenCore and FineWeb-HQ. Despite quality filtering, this data inherits biases present in English-language web text:
876
+
877
+ - **Western-centric perspective** β€” Educational content on the web skews heavily toward Western viewpoints and examples
878
+ - **English monolingualism** β€” The training data and vocabulary are both English-only
879
+ - **Demographic underrepresentation** β€” Groups underrepresented in English-language web text will be underrepresented in outputs
880
+ - **Temporal cutoff** β€” The model has no knowledge of events after the data collection cutoff
881
+
882
+ ### No Safety Tuning
883
+
884
+ This model has received **no safety training of any kind**. It is a raw base model that predicts the next token based on statistical patterns. It should not be used in any context where harmful outputs would cause real-world harm.
885
+
886
+ ### Positive Societal Aspects
887
+
888
+ - **Democratizing AI research** β€” Trained entirely on free-tier Kaggle compute, this model demonstrates that meaningful LLM research does not require significant financial resources.
889
+ - **Transparency** β€” Full training hyperparameters, architecture details, training script, and benchmark results are published. This is a contribution to reproducible ML research.
890
+ - **Minimal environmental footprint** β€” ~2.13 hours of dual-GPU compute. Estimated carbon footprint well under 0.5 kg COβ‚‚e.
891
+
892
+ ---
893
+
894
+ ## Inference Guide
895
+
896
+ ### Basic Generation
897
+
898
+ ```python
899
+ from transformers import AutoModelForCausalLM, AutoTokenizer
900
+ import torch
901
+
902
+ model = AutoModelForCausalLM.from_pretrained("StentorLabs/Stentor2-12M", torch_dtype=torch.float16, device_map="auto")
903
+ tokenizer = AutoTokenizer.from_pretrained("StentorLabs/Stentor2-12M", trust_remote_code=True)
904
+
905
+ device = next(model.parameters()).device
906
+
907
+ def generate(prompt, max_new_tokens=50, temperature=0.9, top_p=0.65):
908
+ input_ids = torch.tensor([tokenizer.encode(prompt)], dtype=torch.long).to(device)
909
+ attention_mask = torch.ones_like(input_ids)
910
+ with torch.inference_mode():
911
+ output = model.generate(
912
+ input_ids,
913
+ attention_mask=attention_mask,
914
+ max_new_tokens=max_new_tokens,
915
+ do_sample=True,
916
+ temperature=temperature,
917
+ top_p=top_p,
918
+ repetition_penalty=1.15,
919
+ pad_token_id=tokenizer.pad_token_id,
920
+ )
921
+ new_ids = output[0][input_ids.shape[1]:].tolist()
922
+ return tokenizer.decode(new_ids).strip()
923
+
924
+ print(generate("The history of computing began"))
925
+ ```
926
+
927
+ ### CPU (FP32)
928
+
929
+ ```python
930
+ model = AutoModelForCausalLM.from_pretrained("StentorLabs/Stentor2-12M", torch_dtype=torch.float32)
931
+ model = model.to("cpu")
932
+ ```
933
+
934
+ ### GPU (FP16)
935
+
936
+ ```python
937
+ model = AutoModelForCausalLM.from_pretrained("StentorLabs/Stentor2-12M", torch_dtype=torch.float16, device_map="auto")
938
+ ```
939
+
940
+ ### From a Local Checkpoint
941
+
942
+ ```python
943
+ model = AutoModelForCausalLM.from_pretrained("./path/to/local/checkpoint")
944
+ tokenizer = AutoTokenizer.from_pretrained("./path/to/local/checkpoint", trust_remote_code=True)
945
+ ```
946
+
947
+ ---
948
+
949
+ ## Real Model Responses
950
+
951
+ *[Placeholder β€” generation examples coming soon]*
952
+
953
+ ---
954
+
955
+ ## Quantization
956
+
957
+ ### FP16 β€” Recommended First Step
958
+
959
+ ```python
960
+ model = AutoModelForCausalLM.from_pretrained("StentorLabs/Stentor2-12M", torch_dtype=torch.float16)
961
+ model = model.to("cuda")
962
+ ```
963
+
964
+ ### Dynamic INT8 Quantization (CPU, PyTorch native)
965
+
966
+ ```python
967
+ import torch
968
+ from transformers import AutoModelForCausalLM
969
+
970
+ model = AutoModelForCausalLM.from_pretrained("StentorLabs/Stentor2-12M", torch_dtype=torch.float32)
971
+ model = model.to("cpu").eval()
972
+
973
+ model_int8 = torch.quantization.quantize_dynamic(
974
+ model,
975
+ {torch.nn.Linear},
976
+ dtype=torch.qint8,
977
+ )
978
+ # Approximate memory: ~12 MB β€” 75% reduction from FP32
979
+ ```
980
+
981
+ ### bitsandbytes INT8 (GPU)
982
+
983
+ ```python
984
+ from transformers import AutoModelForCausalLM, BitsAndBytesConfig
985
+ import torch
986
+
987
+ bnb_config = BitsAndBytesConfig(load_in_8bit=True)
988
+ model = AutoModelForCausalLM.from_pretrained(
989
+ "StentorLabs/Stentor2-12M",
990
+ quantization_config=bnb_config,
991
+ device_map="auto"
992
+ )
993
+ ```
994
+
995
+ ---
996
+
997
+ ## Format Conversion
998
+
999
+ ### Convert to GGUF (for llama.cpp)
1000
+
1001
+ ```bash
1002
+ git clone https://github.com/ggerganov/llama.cpp
1003
+ cd llama.cpp
1004
+ pip install -r requirements.txt
1005
+
1006
+ huggingface-cli download StentorLabs/Stentor2-12M --local-dir stentor2-12m
1007
+
1008
+ python convert_hf_to_gguf.py stentor2-12m/ \
1009
+ --outfile stentor2-12m.gguf \
1010
+ --outtype f16
1011
+
1012
+ ./llama-quantize stentor2-12m.gguf stentor2-12m-q4_0.gguf q4_0
1013
+ ./llama-cli -m stentor2-12m-q4_0.gguf -p "The science of" -n 50
1014
+ ```
1015
+
1016
+ > **Note on GGUF + TokenMonster:** The custom TokenMonster tokenizer may require manual vocabulary mapping when using llama.cpp. The standard `convert_hf_to_gguf.py` script expects a HuggingFace tokenizer format and may need adjustment for the TokenMonster vocabulary.
1017
+
1018
+ ### Convert to ONNX
1019
+
1020
+ ```bash
1021
+ pip install optimum[exporters]
1022
+
1023
+ optimum-cli export onnx \
1024
+ --model StentorLabs/Stentor2-12M \
1025
+ --task text-generation-with-past \
1026
+ stentor2-12m-onnx/
1027
+ ```
1028
+
1029
+ ---
1030
+
1031
+ ## Speculative Decoding
1032
+
1033
+ Stentor2-12M can serve as a fast **draft model** to accelerate inference from larger Llama-family target models.
1034
+
1035
+ ```python
1036
+ from transformers import AutoModelForCausalLM, AutoTokenizer
1037
+ import torch
1038
+
1039
+ draft_model = AutoModelForCausalLM.from_pretrained(
1040
+ "StentorLabs/Stentor2-12M",
1041
+ torch_dtype=torch.float16,
1042
+ device_map="auto"
1043
+ )
1044
+
1045
+ target_model = AutoModelForCausalLM.from_pretrained(
1046
+ "meta-llama/Llama-3.2-1B",
1047
+ torch_dtype=torch.float16,
1048
+ device_map="auto"
1049
+ )
1050
+ target_tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-3.2-1B")
1051
+
1052
+ prompt = "Explain the concept of recursion"
1053
+ inputs = target_tokenizer(prompt, return_tensors="pt")
1054
+
1055
+ outputs = target_model.generate(
1056
+ **inputs,
1057
+ assistant_model=draft_model,
1058
+ do_sample=True,
1059
+ max_new_tokens=100
1060
+ )
1061
+ print(target_tokenizer.decode(outputs[0], skip_special_tokens=True))
1062
+ ```
1063
+
1064
+ **Important caveat:** Stentor2 uses a different vocabulary (8,064-token TokenMonster) than standard Llama models (32,000-token BPE). This vocabulary mismatch means the target model's token acceptance rate may be lower than it would be with a vocabulary-compatible draft model. Speedups depend heavily on the similarity of the generated distribution between draft and target.
1065
+
1066
+ ---
1067
+
1068
+ ## Bias, Risks & Limitations
1069
+
1070
+ - **Prompt Relevance:** Outputs are frequently off-topic for complex prompts. The model is pattern-completing, not comprehending.
1071
+ - **Factual Accuracy:** All factual claims should be treated as unreliable. 12M parameters cannot store meaningful world knowledge.
1072
+ - **Context Boundary:** Hard limit of 1,024 tokens.
1073
+ - **Short Output Window for Coherence:** Even within the context limit, coherence degrades beyond ~60 tokens. `max_new_tokens` at 60 or below is strongly recommended.
1074
+ - **English Bias:** TokenMonster English vocabulary β€” other languages will produce poor output.
1075
+ - **Training Data Bias:** Inherits biases present in English-language web data.
1076
+ - **Hallucination:** Like all LLMs, this model may confidently produce plausible-sounding but entirely fabricated content.
1077
+ - **No Alignment:** No RLHF, no DPO, no constitutional training.
1078
+ - **Tokenizer Efficiency:** The 8K TokenMonster vocabulary produces more tokens per word than standard 32K BPE vocabularies. This is expected and is the direct architectural tradeoff for the perplexity improvement.
1079
+ - **Shared Tensor Warning:** `Removed shared tensor {'lm_head.weight'} while saving` is expected from tied word embeddings. Safe to ignore.
1080
+
1081
+ ---
1082
+
1083
+ ## Related Work
1084
+
1085
+ ### Comparable Sub-50M Models
1086
+
1087
+ | Model | Parameters | Perplexity | Vocab | Notes |
1088
+ |---|---|---|---|---|
1089
+ | **Stentor2-12M** (this model) | 12.3M | 25.96 | 8,064 | Full MHA, StenCore+FineWeb-HQ, dual-GPU |
1090
+ | Stentor2-12M-Preview | 12.3M | ~50.07 | 8,064 | Single-process, FineWeb-Edu only |
1091
+ | Stentor-12M (v1) | 12.0M | 89.01 | 32,768 | Mistral BPE, FineWeb-Edu+Cosmopedia |
1092
+ | Stentor-30M (v1) | 30.4M | 33.02 | 32,768 | Larger v1 model |
1093
+ | Pythia-14M | 14M | varies (Pile) | 50,254 | EleutherAI; well-studied scaling baseline |
1094
+ | TinyStories-33M | ~33M | varies | ~50K | Synthetic story data; Eldan & Li, 2023 |
1095
+
1096
+ > **Comparison caveats:** Perplexity numbers are not directly comparable across models β€” different validation sets, vocabularies, tokenizers, and training data all affect the number. The table is a rough orientation, not a rigorous benchmark.
1097
+
1098
+ ### Related Research Papers
1099
+
1100
+ | Paper | Relevance |
1101
+ |---|---|
1102
+ | [TinyStories](https://arxiv.org/abs/2305.07759) β€” Eldan & Li, 2023 | Demonstrates meaningful language generation from sub-33M models |
1103
+ | [Pythia](https://arxiv.org/abs/2304.01373) β€” Biderman et al., 2023 | Systematic study of small model scaling |
1104
+ | [Scaling Laws](https://arxiv.org/abs/2001.08361) β€” Kaplan et al., 2020 | Foundational work on compute-optimal training |
1105
+ | [Chinchilla](https://arxiv.org/abs/2203.15556) β€” Hoffmann et al., 2022 | Revised scaling laws; informs token budget decisions |
1106
+ | [GQA](https://arxiv.org/abs/2305.13245) β€” Ainslie et al., 2023 | Grouped Query Attention β€” and why it doesn't help here |
1107
+ | [RoPE](https://arxiv.org/abs/2104.09864) β€” Su et al., 2021 | Positional encoding used in this model |
1108
+ | [Speculative Decoding](https://arxiv.org/abs/2211.17192) β€” Leviathan et al., 2023 | Primary deployment use case for this model |
1109
+ | [Model Cards](https://arxiv.org/abs/1810.03993) β€” Mitchell et al., 2018 | Methodology underlying this model card |
1110
+ | [T5](https://arxiv.org/abs/1910.10683) β€” Raffel et al., 2020 | Source of NFKC text normalization approach |
1111
+
1112
+ ### StentorLabs Models
1113
+
1114
+ - [Stentor-30M](https://huggingface.co/StentorLabs/Stentor-30M) β€” Larger v1 base model
1115
+ - [Stentor-12M](https://huggingface.co/StentorLabs/Stentor-12M) β€” v1 baseline this model improves upon
1116
+ - [Stentor-30M-Instruct](https://huggingface.co/StentorLabs/Stentor-30M-Instruct) β€” Instruction-tuned v1
1117
+ - [Stentor-12M-Instruct](https://huggingface.co/StentorLabs/Stentor-12M-Instruct) β€” Instruction-tuned v1
1118
+ - [Stentor2-12M-Preview](https://huggingface.co/StentorLabs/Stentor2-12M-Preview) β€” Preview checkpoint preceding this release
1119
+ - [StentorLabs Collection](https://huggingface.co/StentorLabs) β€” All models from StentorLabs
1120
+
1121
+ ### Referenced Tools & Datasets
1122
+
1123
+ - [StenCore](https://huggingface.co/datasets/StentorLabs/StenCore) β€” Primary training data
1124
+ - [FineWeb-HQ](https://huggingface.co/datasets/epfml/FineWeb-HQ) β€” Secondary training data
1125
+ - [TokenMonster](https://huggingface.co/alasdairforsythe/tokenmonster) β€” Tokenizer vocabulary
1126
+ - [HuggingFace Accelerate](https://github.com/huggingface/accelerate) β€” Training framework
1127
+ - [bitsandbytes](https://github.com/TimDettmers/bitsandbytes) β€” Quantization library
1128
+
1129
+ ---
1130
+
1131
+ ## Environmental Impact
1132
+
1133
+ | Factor | Value |
1134
+ |---|---|
1135
+ | Hardware | 2Γ— NVIDIA Tesla T4 (both active) |
1136
+ | Active Training Duration | ~2.13 hours |
1137
+ | Cloud Provider | Kaggle (free tier) |
1138
+ | Compute Region | Western USA |
1139
+ | Estimated Carbon | Minimal (< 0.5 kg COβ‚‚e estimated) |
1140
+
1141
+ Training on free-tier cloud compute demonstrates that meaningful SLM research is accessible to independent researchers and students without significant hardware investment or carbon cost.
1142
+
1143
+ ---
1144
+
1145
+ ## Citation
1146
+
1147
+ ```bibtex
1148
+ @misc{izumoto2026stentor2_12m,
1149
+ title = {Stentor2-12M},
1150
+ author = {Kai Izumoto},
1151
+ year = {2026},
1152
+ publisher = {StentorLabs},
1153
+ howpublished = {\url{https://huggingface.co/StentorLabs/Stentor2-12M}},
1154
+ note = {Official release of the Stentor2 model family.
1155
+ 12.3M parameter LlamaForCausalLM base model trained on
1156
+ StenCore and FineWeb-HQ with a TokenMonster 8K vocabulary.
1157
+ Dual-GPU distributed training on 2x NVIDIA T4.
1158
+ Apache 2.0 license.}
1159
+ }
1160
+ ```
1161
+
1162
+ ---
1163
+
1164
+ ## Model Card Contact
1165
+
1166
+ Questions, benchmarks, or feedback: [StentorLabs@gmail.com](mailto:StentorLabs@gmail.com) or open a [discussion](https://huggingface.co/StentorLabs/Stentor2-12M/discussions).
1167
+
1168
+ ---
1169
+
1170
+ <p align="center">
1171
+ Made with ❀️ by <a href="https://huggingface.co/StentorLabs">StentorLabs</a><br>
1172
+ <i>Democratizing AI through accessible, efficient models</i>
1173
+ </p>