j0no12 commited on
Commit
7aa9bc4
·
verified ·
1 Parent(s): a171460

Release Sol Milkshake 3M Base

Browse files

Private initial release of the validated 900M recovery checkpoint with standalone MLX runtime, tokenizer, provenance, and raw evaluations.

LICENSE ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Creative Commons Attribution 4.0 International
2
+
3
+ Copyright (c) 2026 Sol Labs
4
+
5
+ Licensed under the Creative Commons Attribution 4.0 International License.
6
+ You may obtain a copy of the License at:
7
+
8
+ https://creativecommons.org/licenses/by/4.0/legalcode
9
+
10
+ Unless required by applicable law or agreed to in writing, material distributed
11
+ under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
12
+ CONDITIONS OF ANY KIND, either express or implied.
README.md ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: cc-by-4.0
3
+ language:
4
+ - en
5
+ pipeline_tag: text-generation
6
+ tags:
7
+ - causal-lm
8
+ - decoder-only
9
+ - small-language-model
10
+ - recurrent-depth
11
+ - mlx
12
+ - ngpt
13
+ - research
14
+ ---
15
+
16
+ ![Sol Milkshake](./sol-milkshake-banner.svg)
17
+
18
+ # Sol Milkshake 3M Base
19
+
20
+ Sol Milkshake is an exact **2,990,000-parameter** recurrent, hyperspherical decoder-only language model trained from scratch and continued through **2,526,565,888 total token exposures**.
21
+
22
+ It combines recurrent block reuse, grouped-query attention, parameter-free XSA value subtraction, value residuals, tensorized 2–5-gram memory, completed-chunk rolling memory, adaptive recurrent routing, and nGPT-style hyperspherical optimization.
23
+
24
+ **This is a base model, not an instruction-tuned assistant.**
25
+
26
+ ## Model summary
27
+
28
+ | Property | Value |
29
+ | --- | --- |
30
+ | Parameters | **2,990,000** |
31
+ | Total token exposures | **2,526,565,888** |
32
+ | Architecture | Decoder-only recurrent causal language model |
33
+ | Physical / effective blocks | 5 / 11 |
34
+ | Residual width | 192 |
35
+ | Vocabulary | 2,048-token byte-level BPE |
36
+ | Maximum context | 2,048 tokens |
37
+ | Weight format | MLX NPZ |
38
+ | License | CC BY 4.0 |
39
+
40
+ ## Quick start
41
+
42
+ Install the runtime dependencies:
43
+
44
+ ```bash
45
+ pip install "mlx>=0.29" "tokenizers>=0.22" huggingface_hub
46
+ ```
47
+
48
+ Load the model and generate text on Apple Silicon:
49
+
50
+ ```python
51
+ from huggingface_hub import snapshot_download
52
+ import sys
53
+
54
+ model_dir = snapshot_download("solintellegence/Sol-Milkshake-3M-Base")
55
+ sys.path.insert(0, model_dir)
56
+
57
+ from modeling_sol_milkshake import load_model, generate
58
+
59
+ model, tokenizer = load_model(model_dir)
60
+
61
+ text = generate(
62
+ model,
63
+ tokenizer,
64
+ "The future of efficient language models is",
65
+ max_new_tokens=64,
66
+ )
67
+
68
+ print(text)
69
+ ```
70
+
71
+ The repository includes the complete standalone MLX implementation and does not require the original training repository.
72
+
73
+ ## Results
74
+
75
+ | Benchmark | Examples | Score |
76
+ | --- | ---: | ---: |
77
+ | HellaSwag | 10,042 | 25.02% |
78
+ | ARC-Easy | 2,376 | 29.76% |
79
+ | ARC-Challenge | 1,172 | 22.70% |
80
+ | PIQA | 1,838 | 52.50% |
81
+ | ArithMark-3 | 1,000 | 30.60% |
82
+ | Intelligence Index | — | **3.158** |
83
+
84
+ The four language-model tasks were evaluated zero-shot on their complete evaluation splits with `lm-eval` 0.4.12 using normalized accuracy.
85
+
86
+ ArithMark-3 used its independent-tokenization normalized-accuracy protocol.
87
+
88
+ Raw evaluation outputs are included under `evals/`.
89
+
90
+ These measurements are not independently verified, and no leaderboard position is claimed. The release checkpoint was selected from recovery checkpoints using this evaluation suite, so the results should not be treated as a pristine held-out estimate.
91
+
92
+ ## Architecture
93
+
94
+ | Component | Configuration |
95
+ | --- | --- |
96
+ | Stored parameters | **2,990,000** |
97
+ | Physical / effective blocks | 5 / 11 |
98
+ | Recurrent layout | 1 prelude + 3 middle blocks × 3 passes + 1 coda |
99
+ | Residual width | 192 |
100
+ | Attention | 6 query heads, 2 key/value heads, head dimension 32 |
101
+ | Attention modification | XSA value subtraction after causal attention |
102
+ | Attention normalization | Unit-normalized Q/K with RoPE |
103
+ | Recurrent routing | Full first pass, then 75% and 50% token capacity |
104
+ | MLP | Gated SiLU MLP, width 512 |
105
+ | Vocabulary | 2,048-token byte-level BPE |
106
+ | Learned memory | Rank-26 tensorized 2–5-gram memory |
107
+ | Rolling memory | 32-token chunks, 32 slots, width 64 |
108
+ | Embeddings | Tied input/output table |
109
+ | Maximum context | 2,048 tokens |
110
+
111
+ ## Training data
112
+
113
+ Initial pretraining used a staged English curriculum built from:
114
+
115
+ - FineWeb-Edu
116
+ - FinePDFs-Edu
117
+ - English UltraFineWeb multi-domain and question-answer subsets
118
+ - Cosmopedia
119
+ - FineMath
120
+
121
+ Recovery training used a **70% / 15% / 15%** mixture of the original frozen curriculum, Cosmopedia-v2 English, and FinePhrase.
122
+
123
+ The tokenizer and prepared training streams were frozen before training. Existing quality filtering, deduplication, and decontamination rules were retained.
124
+
125
+ ## Intended use
126
+
127
+ Sol Milkshake is intended for research on highly parameter-efficient language modeling, including:
128
+
129
+ - extremely small language models
130
+ - recurrent-depth architectures
131
+ - nGPT-style hyperspherical optimization
132
+ - compact learned and rolling memory
133
+ - grouped-query attention at small scale
134
+ - Apple Silicon and MLX inference research
135
+
136
+ Because this is a base model, users should expect raw next-token completion behavior rather than instruction-following or assistant-style responses.
137
+
138
+ ## Limitations
139
+
140
+ At approximately 3M parameters, Sol Milkshake has severe capability limitations.
141
+
142
+ Outputs may be:
143
+
144
+ - inconsistent
145
+ - factually incorrect
146
+ - repetitive
147
+ - incoherent
148
+ - unsafe
149
+
150
+ The model is not instruction tuned and should not be treated as a conversational assistant.
151
+
152
+ Do not use it for high-stakes decisions.
153
+
154
+ Users are responsible for evaluating outputs and determining whether the model is suitable for their application.
155
+
156
+ ## Repository files
157
+
158
+ | File | Purpose |
159
+ | --- | --- |
160
+ | `model.npz` | MLX model weights |
161
+ | `modeling_sol_milkshake.py` | Standalone model, loader, and generator |
162
+ | `sol_config.py` | Frozen architecture configuration |
163
+ | `config.json` | Model metadata |
164
+ | `tokenizer.json` | Frozen tokenizer |
165
+ | `tokenizer_config.json` | Tokenizer metadata |
166
+ | `training_state.json` | Training and provenance summary |
167
+ | `evals/` | Raw benchmark results |
168
+
169
+ ## Reproducibility and evaluation notes
170
+
171
+ The repository includes raw evaluation outputs under `evals/`.
172
+
173
+ Reported benchmark values should be interpreted as reference measurements for this release. They have not been independently verified, and checkpoint selection used the same suite.
174
+
175
+ No leaderboard position is claimed.
176
+
177
+ ## License
178
+
179
+ Sol Milkshake 3M Base is released under the **CC BY 4.0** license.
180
+
181
+ Dataset licenses and terms remain with their respective owners.
__pycache__/modeling_sol_milkshake.cpython-314.pyc ADDED
Binary file (30.4 kB). View file
 
__pycache__/sol_config.cpython-314.pyc ADDED
Binary file (2.3 kB). View file
 
config.json ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": ["SolMilkshake"],
3
+ "model_type": "sol_milkshake",
4
+ "model_name": "Sol Milkshake 3M Base",
5
+ "parameter_count": 2990000,
6
+ "vocab_size": 2048,
7
+ "hidden_size": 192,
8
+ "max_position_embeddings": 2048,
9
+ "num_hidden_layers": 5,
10
+ "num_attention_heads": 6,
11
+ "num_key_value_heads": 2,
12
+ "head_dim": 32,
13
+ "intermediate_size": 512,
14
+ "tie_word_embeddings": true,
15
+ "framework": "mlx"
16
+ }
evals/arithmark_3.json ADDED
The diff for this file is too large to render. See raw diff
 
evals/open_slm_lm_eval_0.4.12.json ADDED
@@ -0,0 +1,334 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "results": {
3
+ "hellaswag": {
4
+ "name": "hellaswag",
5
+ "alias": "hellaswag",
6
+ "sample_len": 10042,
7
+ "acc,none": 0.2577175861382195,
8
+ "acc_stderr,none": 0.004364838000336011,
9
+ "acc_norm,none": 0.2502489543915555,
10
+ "acc_norm_stderr,none": 0.004322710911026276
11
+ },
12
+ "arc_easy": {
13
+ "name": "arc_easy",
14
+ "alias": "arc_easy",
15
+ "sample_len": 2376,
16
+ "acc,none": 0.2857744107744108,
17
+ "acc_stderr,none": 0.009270380606981266,
18
+ "acc_norm,none": 0.2975589225589226,
19
+ "acc_norm_stderr,none": 0.009381226721815406
20
+ },
21
+ "arc_challenge": {
22
+ "name": "arc_challenge",
23
+ "alias": "arc_challenge",
24
+ "sample_len": 1172,
25
+ "acc,none": 0.17320819112627986,
26
+ "acc_stderr,none": 0.011058694183280343,
27
+ "acc_norm,none": 0.22696245733788395,
28
+ "acc_norm_stderr,none": 0.012240491536132965
29
+ },
30
+ "piqa": {
31
+ "name": "piqa",
32
+ "alias": "piqa",
33
+ "sample_len": 1838,
34
+ "acc,none": 0.5315560391730142,
35
+ "acc_stderr,none": 0.011642567710878958,
36
+ "acc_norm,none": 0.5250272034820457,
37
+ "acc_norm_stderr,none": 0.011651200967623562
38
+ }
39
+ },
40
+ "group_subtasks": {},
41
+ "configs": {
42
+ "arc_challenge": {
43
+ "task": "arc_challenge",
44
+ "dataset_path": "allenai/ai2_arc",
45
+ "dataset_name": "ARC-Challenge",
46
+ "training_split": "train",
47
+ "validation_split": "validation",
48
+ "test_split": "test",
49
+ "doc_to_text": "Question: {{question}}\nAnswer:",
50
+ "doc_to_target": "{{choices.label.index(answerKey)}}",
51
+ "unsafe_code": false,
52
+ "doc_to_choice": "{{choices.text}}",
53
+ "description": "",
54
+ "target_delimiter": " ",
55
+ "fewshot_delimiter": "\n\n",
56
+ "fewshot_config": {
57
+ "sampler": "default",
58
+ "split": null,
59
+ "process_docs": null,
60
+ "fewshot_indices": null,
61
+ "samples": null,
62
+ "doc_to_text": "Question: {{question}}\nAnswer:",
63
+ "doc_to_choice": "{{choices.text}}",
64
+ "doc_to_target": "{{choices.label.index(answerKey)}}",
65
+ "gen_prefix": null,
66
+ "fewshot_delimiter": "\n\n",
67
+ "target_delimiter": " "
68
+ },
69
+ "num_fewshot": 0,
70
+ "metric_list": [
71
+ {
72
+ "metric": "acc",
73
+ "aggregation": "mean",
74
+ "higher_is_better": true
75
+ },
76
+ {
77
+ "metric": "acc_norm",
78
+ "aggregation": "mean",
79
+ "higher_is_better": true
80
+ }
81
+ ],
82
+ "output_type": "multiple_choice",
83
+ "repeats": 1,
84
+ "should_decontaminate": true,
85
+ "doc_to_decontamination_query": "Question: {{question}}\nAnswer:",
86
+ "metadata": {
87
+ "version": 1.0,
88
+ "config_source": "/Users/jonah/Documents/ChatGPT/Training/.venv/lib/python3.14/site-packages/lm_eval/tasks/arc/arc_challenge.yaml"
89
+ }
90
+ },
91
+ "arc_easy": {
92
+ "task": "arc_easy",
93
+ "dataset_path": "allenai/ai2_arc",
94
+ "dataset_name": "ARC-Easy",
95
+ "training_split": "train",
96
+ "validation_split": "validation",
97
+ "test_split": "test",
98
+ "doc_to_text": "Question: {{question}}\nAnswer:",
99
+ "doc_to_target": "{{choices.label.index(answerKey)}}",
100
+ "unsafe_code": false,
101
+ "doc_to_choice": "{{choices.text}}",
102
+ "description": "",
103
+ "target_delimiter": " ",
104
+ "fewshot_delimiter": "\n\n",
105
+ "fewshot_config": {
106
+ "sampler": "default",
107
+ "split": null,
108
+ "process_docs": null,
109
+ "fewshot_indices": null,
110
+ "samples": null,
111
+ "doc_to_text": "Question: {{question}}\nAnswer:",
112
+ "doc_to_choice": "{{choices.text}}",
113
+ "doc_to_target": "{{choices.label.index(answerKey)}}",
114
+ "gen_prefix": null,
115
+ "fewshot_delimiter": "\n\n",
116
+ "target_delimiter": " "
117
+ },
118
+ "num_fewshot": 0,
119
+ "metric_list": [
120
+ {
121
+ "metric": "acc",
122
+ "aggregation": "mean",
123
+ "higher_is_better": true
124
+ },
125
+ {
126
+ "metric": "acc_norm",
127
+ "aggregation": "mean",
128
+ "higher_is_better": true
129
+ }
130
+ ],
131
+ "output_type": "multiple_choice",
132
+ "repeats": 1,
133
+ "should_decontaminate": true,
134
+ "doc_to_decontamination_query": "Question: {{question}}\nAnswer:",
135
+ "metadata": {
136
+ "version": 1.0,
137
+ "config_source": "/Users/jonah/Documents/ChatGPT/Training/.venv/lib/python3.14/site-packages/lm_eval/tasks/arc/arc_easy.yaml"
138
+ }
139
+ },
140
+ "hellaswag": {
141
+ "task": "hellaswag",
142
+ "dataset_path": "Rowan/hellaswag",
143
+ "training_split": "train",
144
+ "validation_split": "validation",
145
+ "process_docs": "def process_docs(dataset: datasets.Dataset) -> datasets.Dataset:\n def _process_doc(doc):\n ctx = doc[\"ctx_a\"] + \" \" + doc[\"ctx_b\"].capitalize()\n out_doc = {\n \"query\": preprocess(doc[\"activity_label\"] + \": \" + ctx),\n \"choices\": [preprocess(ending) for ending in doc[\"endings\"]],\n \"gold\": int(doc[\"label\"]),\n }\n return out_doc\n\n return dataset.map(_process_doc)\n",
146
+ "doc_to_text": "{{query}}",
147
+ "doc_to_target": "{{label}}",
148
+ "unsafe_code": false,
149
+ "doc_to_choice": "choices",
150
+ "description": "",
151
+ "target_delimiter": " ",
152
+ "fewshot_delimiter": "\n\n",
153
+ "fewshot_config": {
154
+ "sampler": "default",
155
+ "split": null,
156
+ "process_docs": "<function process_docs at 0x136493740>",
157
+ "fewshot_indices": null,
158
+ "samples": null,
159
+ "doc_to_text": "{{query}}",
160
+ "doc_to_choice": "choices",
161
+ "doc_to_target": "{{label}}",
162
+ "gen_prefix": null,
163
+ "fewshot_delimiter": "\n\n",
164
+ "target_delimiter": " "
165
+ },
166
+ "num_fewshot": 0,
167
+ "metric_list": [
168
+ {
169
+ "metric": "acc",
170
+ "aggregation": "mean",
171
+ "higher_is_better": true
172
+ },
173
+ {
174
+ "metric": "acc_norm",
175
+ "aggregation": "mean",
176
+ "higher_is_better": true
177
+ }
178
+ ],
179
+ "output_type": "multiple_choice",
180
+ "repeats": 1,
181
+ "should_decontaminate": false,
182
+ "metadata": {
183
+ "version": 1.0,
184
+ "config_source": "/Users/jonah/Documents/ChatGPT/Training/.venv/lib/python3.14/site-packages/lm_eval/tasks/hellaswag/hellaswag.yaml"
185
+ }
186
+ },
187
+ "piqa": {
188
+ "task": "piqa",
189
+ "dataset_path": "baber/piqa",
190
+ "training_split": "train",
191
+ "validation_split": "validation",
192
+ "doc_to_text": "Question: {{goal}}\nAnswer:",
193
+ "doc_to_target": "label",
194
+ "unsafe_code": false,
195
+ "doc_to_choice": "{{[sol1, sol2]}}",
196
+ "description": "",
197
+ "target_delimiter": " ",
198
+ "fewshot_delimiter": "\n\n",
199
+ "fewshot_config": {
200
+ "sampler": "default",
201
+ "split": null,
202
+ "process_docs": null,
203
+ "fewshot_indices": null,
204
+ "samples": null,
205
+ "doc_to_text": "Question: {{goal}}\nAnswer:",
206
+ "doc_to_choice": "{{[sol1, sol2]}}",
207
+ "doc_to_target": "label",
208
+ "gen_prefix": null,
209
+ "fewshot_delimiter": "\n\n",
210
+ "target_delimiter": " "
211
+ },
212
+ "num_fewshot": 0,
213
+ "metric_list": [
214
+ {
215
+ "metric": "acc",
216
+ "aggregation": "mean",
217
+ "higher_is_better": true
218
+ },
219
+ {
220
+ "metric": "acc_norm",
221
+ "aggregation": "mean",
222
+ "higher_is_better": true
223
+ }
224
+ ],
225
+ "output_type": "multiple_choice",
226
+ "repeats": 1,
227
+ "should_decontaminate": true,
228
+ "doc_to_decontamination_query": "goal",
229
+ "metadata": {
230
+ "version": 1.0,
231
+ "config_source": "/Users/jonah/Documents/ChatGPT/Training/.venv/lib/python3.14/site-packages/lm_eval/tasks/piqa/piqa.yaml"
232
+ }
233
+ }
234
+ },
235
+ "versions": {
236
+ "arc_challenge": 1.0,
237
+ "arc_easy": 1.0,
238
+ "hellaswag": 1.0,
239
+ "piqa": 1.0
240
+ },
241
+ "n-shot": {
242
+ "arc_challenge": 0,
243
+ "arc_easy": 0,
244
+ "hellaswag": 0,
245
+ "piqa": 0
246
+ },
247
+ "higher_is_better": {
248
+ "arc_challenge": {
249
+ "acc": true,
250
+ "acc_norm": true
251
+ },
252
+ "arc_easy": {
253
+ "acc": true,
254
+ "acc_norm": true
255
+ },
256
+ "hellaswag": {
257
+ "acc": true,
258
+ "acc_norm": true
259
+ },
260
+ "piqa": {
261
+ "acc": true,
262
+ "acc_norm": true
263
+ }
264
+ },
265
+ "n-samples": {
266
+ "hellaswag": {
267
+ "original": 10042,
268
+ "effective": 10042
269
+ },
270
+ "arc_easy": {
271
+ "original": 2376,
272
+ "effective": 2376
273
+ },
274
+ "arc_challenge": {
275
+ "original": 1172,
276
+ "effective": 1172
277
+ },
278
+ "piqa": {
279
+ "original": 1838,
280
+ "effective": 1838
281
+ }
282
+ },
283
+ "config": {
284
+ "model": "MLXCheckpointLM",
285
+ "model_args": null,
286
+ "batch_size": 2,
287
+ "batch_sizes": [],
288
+ "device": null,
289
+ "use_cache": null,
290
+ "limit": null,
291
+ "bootstrap_iters": 100000,
292
+ "gen_kwargs": null,
293
+ "random_seed": 0,
294
+ "numpy_seed": 1234,
295
+ "torch_seed": 1234,
296
+ "fewshot_seed": 1234
297
+ },
298
+ "git_hash": "6bdcd14",
299
+ "date": 1789940047.395892,
300
+ "pretty_env_info": "PyTorch version: 2.13.0\nIs debug build: False\nCUDA used to build PyTorch: None\nROCM used to build PyTorch: N/A\n\nOS: macOS 27.0 (arm64)\nGCC version: Could not collect\nClang version: 21.0.0 (clang-2100.1.1.101)\nCMake version: version 4.3.3\nLibc version: N/A\n\nPython version: 3.14.7 (main, Aug 5 2026, 10:29:49) [Clang 21.0.0 (clang-2100.1.1.101)] (64-bit runtime)\nPython platform: macOS-27.0-arm64-arm-64bit-Mach-O\nIs CUDA available: False\nCUDA runtime version: No CUDA\nCUDA_MODULE_LOADING set to: N/A\nGPU models and configuration: No CUDA\nNvidia driver version: No CUDA\ncuDNN version: No CUDA\nIs XPU available: False\nHIP runtime version: N/A\nMIOpen runtime version: N/A\nIs XNNPACK available: False\nCaching allocator config: N/A\n\nCPU:\nApple M5 Max\n\nVersions of relevant libraries:\n[pip3] numpy==2.5.2\n[pip3] torch==2.13.0\n[conda] Could not collect",
301
+ "transformers_version": "5.16.1",
302
+ "lm_eval_version": "0.4.12",
303
+ "upper_git_hash": null,
304
+ "tokenizer_pad_token": [
305
+ null,
306
+ "None"
307
+ ],
308
+ "tokenizer_eos_token": [
309
+ "<|eos|>",
310
+ "1"
311
+ ],
312
+ "tokenizer_bos_token": [
313
+ null,
314
+ "None"
315
+ ],
316
+ "eot_token_id": 1,
317
+ "max_length": 2048,
318
+ "xsa_evaluation": {
319
+ "checkpoint": "/Users/jonah/Documents/ChatGPT/Training/runs/sol_milkshake_recovery_500m_v1/checkpoint_000900005888",
320
+ "checkpoint_tokens_seen": null,
321
+ "lm_eval_version": "0.4.12",
322
+ "tasks": [
323
+ "hellaswag",
324
+ "arc_easy",
325
+ "arc_challenge",
326
+ "piqa"
327
+ ],
328
+ "num_fewshot": 0,
329
+ "metric": "acc_norm",
330
+ "max_length": 2048,
331
+ "batch_size": 2,
332
+ "elapsed_seconds": 174.03599381446838
333
+ }
334
+ }
model.npz ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b8bd1efee28989bd49c85c70d72824c91c3872b871a34f48ccad21903b8ef36a
3
+ size 11980144
modeling_sol_milkshake.py ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Exact 2,990,000-parameter Sol Milkshake model implemented in MLX."""
2
+ from __future__ import annotations
3
+ import math
4
+ from collections import OrderedDict
5
+ import mlx.core as mx
6
+ import mlx.nn as nn
7
+ from mlx.utils import tree_flatten
8
+ from sol_config import DEPLOYED_PARAMS, SolConfig
9
+
10
+ def unit_norm(x):
11
+ xf=x.astype(mx.float32)
12
+ return (xf*mx.rsqrt(mx.maximum(mx.sum(xf*xf,axis=-1,keepdims=True),mx.array(1e-12)))).astype(x.dtype)
13
+
14
+ def _count(module): return sum(int(x.size) for _,x in tree_flatten(module.trainable_parameters()))
15
+
16
+ def _rope(q,k,theta):
17
+ n,d=q.shape[-2:]; inv=mx.exp(mx.arange(0,d,2,dtype=mx.float32)*(-math.log(theta)/d))
18
+ ang=mx.arange(n,dtype=mx.float32)[:,None]*inv[None,:]; cs,sn=mx.cos(ang)[None,None],mx.sin(ang)[None,None]
19
+ def rot(x):
20
+ xf=x.astype(mx.float32); a,b=xf[...,::2],xf[...,1::2]
21
+ return mx.stack((a*cs-b*sn,a*sn+b*cs),axis=-1).reshape(x.shape).astype(x.dtype)
22
+ return rot(q),rot(k)
23
+
24
+ class TiedTokens(nn.Module):
25
+ def __init__(self,c):
26
+ super().__init__(); self.weight=unit_norm(mx.random.normal((c.vocab_size,c.d_model)))
27
+ def encode(self,ids): return self.weight[ids]
28
+ def decode(self,x,scale): return x.astype(mx.float32)@unit_norm(self.weight).astype(mx.float32).T*mx.exp(scale)
29
+
30
+ class TNGram(nn.Module):
31
+ """Causal hashed CP retrieval for orders 2--5 (exactly 573,460 parameters)."""
32
+ def __init__(self,c):
33
+ super().__init__(); r=c.tn_rank; self.token_factor=mx.random.normal((c.vocab_size,r))*.02
34
+ self.hash_tables=mx.random.normal((4,4950,r))*.02; self.order_factors=mx.ones((4,r))
35
+ self.rank_to_hidden=mx.random.normal((r,c.d_model))*.02; self.output_bias=mx.zeros((c.d_model,))
36
+ self.gates=mx.full((4,),-2.0); self.order_mix=mx.eye(4,r); self.context_controls=mx.zeros((16,))
37
+ def __call__(self,ids):
38
+ b,t=ids.shape; out=mx.broadcast_to(self.output_bias,(b,t,self.output_bias.shape[0]))
39
+ for oi,order in enumerate(range(2,6)):
40
+ h=mx.zeros_like(ids); prod=mx.ones((b,t,self.token_factor.shape[1]),dtype=mx.float32); valid=mx.arange(t)>=order-1
41
+ for lag in range(1,order):
42
+ shifted=mx.zeros((b,t),dtype=ids.dtype) if lag>=t else mx.concatenate((mx.zeros((b,lag),dtype=ids.dtype),ids[:,:-lag]),axis=1)
43
+ h=(h*(131+oi*6)+shifted)%4950; prod=prod*self.token_factor[shifted].astype(mx.float32)
44
+ rank=prod*self.hash_tables[oi,h]*self.order_factors[oi]
45
+ rank=rank+self.order_mix[oi][None,None,:]*mx.mean(mx.tanh(self.context_controls[oi*4:(oi+1)*4]))
46
+ out=out+(rank@self.rank_to_hidden)*mx.sigmoid(self.gates[oi])*valid[None,:,None]
47
+ return out
48
+
49
+ class ShakeMemory(nn.Module):
50
+ """Completed-chunk rolling memory (exactly 51,520 parameters)."""
51
+ def __init__(self,c):
52
+ super().__init__(); w=c.memory_width; self.chunk_size=c.chunk_size; self.slots=c.memory_slots
53
+ self.summary_router=nn.Linear(c.d_model,1,bias=False); self.compress=nn.Linear(c.d_model,w,bias=False)
54
+ self.query=nn.Linear(c.d_model,w,bias=False); self.key=nn.Linear(w,w,bias=False)
55
+ self.value=nn.Linear(w,w,bias=False); self.output=nn.Linear(w,c.d_model,bias=False)
56
+ self.slot_positions=mx.random.normal((c.memory_slots,c.d_model))*.01
57
+ self.decay=mx.zeros((w,)); self.update_gate=mx.zeros((w,))
58
+ def __call__(self,x):
59
+ b,t,d=x.shape; chunks=[]; reads=[]
60
+ for start in range(0,t,self.chunk_size):
61
+ end=min(t,start+self.chunk_size); length=end-start
62
+ if chunks:
63
+ mem=mx.stack(chunks[-self.slots:],axis=1); count=mem.shape[1]
64
+ mem=mem+self.compress(self.slot_positions[:count])[None]*mx.sigmoid(self.decay)[None,None]
65
+ a=mx.softmax((self.query(x[:,start:end]).astype(mx.float32)@self.key(mem).astype(mx.float32).transpose(0,2,1))/math.sqrt(mem.shape[-1]),axis=-1)
66
+ reads.append(self.output((a@self.value(mem).astype(mx.float32)).astype(x.dtype)))
67
+ else: reads.append(mx.zeros((b,length,d),dtype=x.dtype))
68
+ if length==self.chunk_size:
69
+ weights=mx.softmax(self.summary_router(x[:,start:end]).astype(mx.float32).squeeze(-1),axis=-1)
70
+ summary=mx.sum(weights[:,:,None]*x[:,start:end].astype(mx.float32),axis=1)
71
+ chunks.append(self.compress(summary.astype(x.dtype))*mx.sigmoid(self.update_gate))
72
+ return mx.concatenate(reads,axis=1)
73
+
74
+ class Attention(nn.Module):
75
+ def __init__(self,c):
76
+ super().__init__(); d=c.d_model; self.q=nn.Linear(d,d,bias=False); self.k=nn.Linear(d,c.n_kv_heads*c.head_dim,bias=False)
77
+ self.v=nn.Linear(d,c.n_kv_heads*c.head_dim,bias=False); self.o=nn.Linear(d,d,bias=False)
78
+ self.hq=c.n_q_heads; self.hk=c.n_kv_heads; self.hd=c.head_dim; self.theta=c.rope_theta
79
+ self.value_current=mx.zeros((1,)); self.value_prior=mx.zeros((1,))
80
+ def __call__(self,x,prior=None):
81
+ b,t,_=x.shape; q=unit_norm(self.q(x).reshape(b,t,self.hq,self.hd)).transpose(0,2,1,3)
82
+ k=unit_norm(self.k(x).reshape(b,t,self.hk,self.hd)).transpose(0,2,1,3); v=self.v(x).reshape(b,t,self.hk,self.hd).transpose(0,2,1,3)
83
+ if prior is not None: v=mx.sigmoid(self.value_current)*v+mx.sigmoid(self.value_prior)*prior
84
+ q,k=_rope(q,k,self.theta); k=mx.repeat(k,self.hq//self.hk,axis=1); vr=mx.repeat(v,self.hq//self.hk,axis=1)
85
+ scores=(q.astype(mx.float32)@k.astype(mx.float32).transpose(0,1,3,2))/math.sqrt(self.hd)
86
+ pos=mx.arange(t); mask=mx.where(pos[:,None]>=pos[None,:],0.0,-1e9)
87
+ attended=(mx.softmax(scores+mask[None,None],axis=-1)@vr.astype(mx.float32)).astype(x.dtype)
88
+ vh=unit_norm(vr); attended=attended-mx.sum(attended.astype(mx.float32)*vh.astype(mx.float32),axis=-1,keepdims=True).astype(x.dtype)*vh
89
+ return self.o(attended.transpose(0,2,1,3).reshape(b,t,-1)),v
90
+
91
+ class Block(nn.Module):
92
+ def __init__(self,c):
93
+ super().__init__(); self.attention=Attention(c); self.gate=nn.Linear(c.d_model,c.ffn_hidden,bias=False)
94
+ self.up=nn.Linear(c.d_model,c.ffn_hidden,bias=False); self.down=nn.Linear(c.ffn_hidden,c.d_model,bias=False)
95
+ self.attn_step=mx.zeros((c.d_model,)); self.ffn_step=mx.zeros((c.d_model,))
96
+ def __call__(self,x,prior=None):
97
+ a,v=self.attention(unit_norm(x),prior); x=unit_norm(x+mx.sigmoid(self.attn_step)*(unit_norm(a)-x))
98
+ f=self.down(nn.silu(self.gate(x))*self.up(x)); return unit_norm(x+mx.sigmoid(self.ffn_step)*(unit_norm(f)-x)),v
99
+
100
+ class SolMilkshake(nn.Module):
101
+ def __init__(self,c=None):
102
+ super().__init__(); self.config=c or SolConfig(); self.tokens=TiedTokens(self.config); self.tn_gram=TNGram(self.config)
103
+ self.shake_memory=ShakeMemory(self.config); self.blocks=[Block(self.config) for _ in range(5)]
104
+ self.pass_embeddings=mx.zeros((3,self.config.d_model)); self.mod_routers=[nn.Linear(self.config.d_model,1) for _ in range(6)]
105
+ self.logit_scale=mx.zeros((self.config.vocab_size,)); self.misc_controls=mx.zeros((12,)); self._verify_ledger()
106
+ def _select(self,x,old,router,capacity,lam):
107
+ delta=1-mx.sum(unit_norm(x).astype(mx.float32)*unit_norm(old).astype(mx.float32),axis=-1)
108
+ score=router(x).squeeze(-1)+lam*delta
109
+ positions=mx.arange(x.shape[1])
110
+ mask=(positions%4!=3) if capacity==.75 else (positions%2==0)
111
+ return mx.broadcast_to(mask[None,:],score.shape),mx.sigmoid(score)
112
+ def __call__(self,ids):
113
+ x=self.tokens.encode(ids); x=unit_norm(x+mx.sigmoid(self.misc_controls[0])*self.tn_gram(ids))
114
+ x=unit_norm(x+mx.sigmoid(self.misc_controls[1])*self.shake_memory(x)); x,v=self.blocks[0](x); ri=0
115
+ for p in range(3):
116
+ old=x; x=unit_norm(x+self.pass_embeddings[p]); prior=None
117
+ for bi in range(1,4):
118
+ proposal,prior=self.blocks[bi](x,prior)
119
+ if p==0: x=proposal
120
+ else:
121
+ mask,route_gate=self._select(x,old,self.mod_routers[ri],.75 if p==1 else .5,self.misc_controls[2+ri]); ri+=1
122
+ routed=unit_norm(x+route_gate[:,:,None]*(proposal-x))
123
+ x=mx.where(mask[:,:,None],routed,x)
124
+ x,_=self.blocks[4](x,v); return self.tokens.decode(x,self.logit_scale)
125
+ def parameter_ledger(self):
126
+ ledger=OrderedDict(token_embedding_tied_head=int(self.tokens.weight.size),attention_total=sum(_count(b.attention)-2 for b in self.blocks),
127
+ swiglu_total=sum(_count(b.gate)+_count(b.up)+_count(b.down) for b in self.blocks),tn_gram=_count(self.tn_gram),shake_memory=_count(self.shake_memory),
128
+ ngpt_steps=sum(int(b.attn_step.size+b.ffn_step.size) for b in self.blocks),pass_embeddings=int(self.pass_embeddings.size),
129
+ mod_routers=sum(_count(r) for r in self.mod_routers),logit_scale=int(self.logit_scale.size),value_residual=10,
130
+ misc_controls=int(self.misc_controls.size),xsa=0); ledger['deployed_total']=sum(ledger.values()); return dict(ledger)
131
+ def deployed_parameter_count(self): return _count(self)
132
+ def _verify_ledger(self):
133
+ expected={"token_embedding_tied_head":393216,"attention_total":491520,"swiglu_total":1474560,"tn_gram":573460,"shake_memory":51520,"ngpt_steps":1920,"pass_embeddings":576,"mod_routers":1158,"logit_scale":2048,"value_residual":10,"misc_controls":12,"xsa":0,"deployed_total":DEPLOYED_PARAMS}
134
+ if self.parameter_ledger()!=expected or self.deployed_parameter_count()!=DEPLOYED_PARAMS: raise RuntimeError(f"parameter ledger mismatch: {self.parameter_ledger()}, actual={self.deployed_parameter_count()}")
135
+
136
+ def future_token_causality_error(model,sequence_length=35):
137
+ ids=mx.arange(sequence_length,dtype=mx.int32)[None]%model.config.vocab_size; boundary=sequence_length//2
138
+ changed=mx.concatenate((ids[:,:boundary],(ids[:,boundary:]+17)%model.config.vocab_size),axis=1)
139
+ a,b=model(ids),model(changed); mx.eval(a,b); return float(mx.max(mx.abs(a[:,:boundary]-b[:,:boundary])))
140
+
141
+ def load_model(model_dir):
142
+ """Load the standalone MLX model and frozen tokenizer from a snapshot."""
143
+ from pathlib import Path
144
+ from tokenizers import Tokenizer
145
+
146
+ model_dir = Path(model_dir)
147
+ model = SolMilkshake(SolConfig())
148
+ model.load_weights(str(model_dir / "model.npz"))
149
+ mx.eval(model.parameters())
150
+ tokenizer = Tokenizer.from_file(str(model_dir / "tokenizer.json"))
151
+ return model, tokenizer
152
+
153
+ def generate(model, tokenizer, prompt, max_new_tokens=64, temperature=0.0, seed=0):
154
+ """Generate a single continuation with greedy or temperature sampling."""
155
+ mx.random.seed(seed)
156
+ tokens = tokenizer.encode(prompt).ids
157
+ if not tokens:
158
+ tokens = [0]
159
+ for _ in range(max_new_tokens):
160
+ context = tokens[-model.config.max_context:]
161
+ logits = model(mx.array([context], dtype=mx.int32))[0, -1]
162
+ if temperature and temperature > 0:
163
+ next_token = int(mx.random.categorical(logits / temperature).item())
164
+ else:
165
+ next_token = int(mx.argmax(logits).item())
166
+ tokens.append(next_token)
167
+ if next_token == 1:
168
+ break
169
+ return tokenizer.decode(tokens)
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ mlx>=0.29
2
+ tokenizers>=0.22
3
+ numpy>=2.0
4
+ huggingface_hub>=1.0
sol-milkshake-banner.svg ADDED
sol_config.py ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import asdict, dataclass
4
+
5
+ MODEL_NAME = "Sol Milkshake"
6
+ ORGANIZATION = "Sol Labs"
7
+ DEPLOYED_PARAMS = 2_990_000
8
+ VOCAB_SIZE = 2_048
9
+ D_MODEL = 192
10
+ MAX_CONTEXT = 2_048
11
+ N_PHYSICAL_BLOCKS = 5
12
+ N_Q_HEADS = 6
13
+ N_KV_HEADS = 2
14
+ HEAD_DIM = 32
15
+ FFN_HIDDEN = 512
16
+ TN_RANK = 26
17
+ TIED_EMBEDDING_HEAD = True
18
+ XSA_PARAMETER_COUNT = 0
19
+ TOKENS_PER_PARAMETER = 544
20
+ GLOBAL_TOKENS = 1_626_560_000
21
+ GLOBAL_BATCH = 32_768
22
+ COMPLETE_UPDATES = 49_638
23
+ FINAL_PARTIAL_TOKENS = 22_016
24
+
25
+ assert DEPLOYED_PARAMS * TOKENS_PER_PARAMETER == GLOBAL_TOKENS
26
+ assert COMPLETE_UPDATES * GLOBAL_BATCH + FINAL_PARTIAL_TOKENS == GLOBAL_TOKENS
27
+
28
+
29
+ @dataclass(frozen=True)
30
+ class SolConfig:
31
+ model_name: str = MODEL_NAME
32
+ vocab_size: int = VOCAB_SIZE
33
+ d_model: int = D_MODEL
34
+ max_context: int = MAX_CONTEXT
35
+ n_blocks: int = N_PHYSICAL_BLOCKS
36
+ n_q_heads: int = N_Q_HEADS
37
+ n_kv_heads: int = N_KV_HEADS
38
+ head_dim: int = HEAD_DIM
39
+ ffn_hidden: int = FFN_HIDDEN
40
+ tn_rank: int = TN_RANK
41
+ rope_theta: float = 20_000.0
42
+ memory_width: int = 64
43
+ memory_slots: int = 32
44
+ chunk_size: int = 32
45
+ passes: int = 3
46
+ dropout: float = 0.0
47
+
48
+ def as_dict(self) -> dict:
49
+ return asdict(self)
tokenizer.json ADDED
The diff for this file is too large to render. See raw diff
 
tokenizer_config.json ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "backend": "tokenizers",
3
+ "clean_up_tokenization_spaces": false,
4
+ "eos_token": "<|eos|>",
5
+ "model_max_length": 1000000000000000019884624838656,
6
+ "tokenizer_class": "TokenizersBackend",
7
+ "unk_token": "<|unk|>"
8
+ }
training_state.json ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "release_checkpoint": "checkpoint_000900005888",
3
+ "base_pretraining_tokens": 1626560000,
4
+ "recovery_tokens": 900005888,
5
+ "total_token_exposures": 2526565888,
6
+ "recovery_peak_learning_rate": 0.000005,
7
+ "recovery_terminal_learning_rate": 0.0000005,
8
+ "recovery_mixture": {
9
+ "original_curriculum": 0.70,
10
+ "cosmopedia_v2_english": 0.15,
11
+ "finephrase": 0.15
12
+ },
13
+ "recovery_context": {
14
+ "initial_phase": "75% 512 / 25% 1024",
15
+ "final_400982016_tokens": "512 only"
16
+ },
17
+ "optimizer": "nGPT-aware AdamW",
18
+ "seed": 20260920,
19
+ "weight_format": "MLX NPZ",
20
+ "selection_note": "Selected from recovery checkpoints using the reported evaluation suite."
21
+ }