Text Generation
MLX
English
sol_milkshake
causal-lm
decoder-only
small-language-model
recurrent-depth
ngpt
research
Instructions to use solintellegence/Sol-Milkshake with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- MLX
How to use solintellegence/Sol-Milkshake with MLX:
# Make sure mlx-lm is installed # pip install --upgrade mlx-lm # if on a CUDA device, also pip install mlx[cuda] # Generate text with mlx-lm from mlx_lm import load, generate model, tokenizer = load("solintellegence/Sol-Milkshake") prompt = "Once upon a time in" text = generate(model, tokenizer, prompt=prompt, verbose=True) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- LM Studio
- MLX LM
How to use solintellegence/Sol-Milkshake with MLX LM:
Generate or start a chat session
# Install MLX LM uv tool install mlx-lm # Generate some text mlx_lm.generate --model "solintellegence/Sol-Milkshake" --prompt "Once upon a time"
- Atomic Chat
Release Sol Milkshake 3M Base
Browse filesPrivate initial release of the validated 900M recovery checkpoint with standalone MLX runtime, tokenizer, provenance, and raw evaluations.
- LICENSE +12 -0
- README.md +181 -0
- __pycache__/modeling_sol_milkshake.cpython-314.pyc +0 -0
- __pycache__/sol_config.cpython-314.pyc +0 -0
- config.json +16 -0
- evals/arithmark_3.json +0 -0
- evals/open_slm_lm_eval_0.4.12.json +334 -0
- model.npz +3 -0
- modeling_sol_milkshake.py +169 -0
- requirements.txt +4 -0
- sol-milkshake-banner.svg +9 -0
- sol_config.py +49 -0
- tokenizer.json +0 -0
- tokenizer_config.json +8 -0
- training_state.json +21 -0
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 |
+

|
| 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 |
+
}
|