mini-beatrix-3: the stage arms (arms off and arms on in one package)
Browse files- README.md +176 -38
- arms.py +598 -0
- arms/index.json +854 -0
- arms/solo/s1_perspective.safetensors +3 -0
- arms/solo/s2_concept.safetensors +3 -0
- arms/solo/s3_rules.safetensors +3 -0
- arms/solo/s4_arith.safetensors +3 -0
- arms/stages-1-4/s1_perspective.safetensors +3 -0
- arms/stages-1-4/s2_concept.safetensors +3 -0
- arms/stages-1-4/s3_rules.safetensors +3 -0
- arms/stages-1-4/s4_arith.safetensors +3 -0
- config.json +8 -1
- modeling_minibeatrix.py +203 -12
README.md
CHANGED
|
@@ -7,20 +7,52 @@ tags:
|
|
| 7 |
- linear-attention
|
| 8 |
- splat-attention
|
| 9 |
- aleph
|
|
|
|
| 10 |
- custom_code
|
| 11 |
---
|
| 12 |
|
| 13 |
# mini-beatrix-3
|
| 14 |
|
| 15 |
-
**The 32-block rung of the mini-beatrix ladder.** 376M
|
| 16 |
-
byte-level (vocab 256), with a multi-constellation
|
| 17 |
-
(signed-address linear attention over learned codebook
|
| 18 |
-
anchored expert bank in **every one of its 32 blocks**.
|
| 19 |
-
softmax-over-positions attention anywhere. Trained on 64.4B bytes (245,674
|
| 20 |
steps, about 295 hours on two RTX 5090s) through a staged curriculum,
|
| 21 |
completed 2026-10-05.
|
| 22 |
|
| 23 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
|
| 25 |
## Architecture
|
| 26 |
|
|
@@ -43,6 +75,121 @@ This repository is the bare model: no adapter arms are attached or included.
|
|
| 43 |
the end (+6.13) is nearly twice every earlier one and is being repeated
|
| 44 |
before it is relied on.
|
| 45 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
## The special-token control plane
|
| 47 |
|
| 48 |
Thirteen ids that valid UTF-8 can never produce carry structure and are
|
|
@@ -59,44 +206,33 @@ Thirteen ids that valid UTF-8 can never produce carry structure and are
|
|
| 59 |
| 0xFA 0xF9 0xF8 0xF6 0xC0 0xC1 | THINK DATA SEP CUE RES | reserved/instrument |
|
| 60 |
|
| 61 |
Chat format: `[SYS] text [END] [USER] text [END] [MODEL] text [END]`. The
|
| 62 |
-
frame is unforgeable (encoded text cannot contain a special).
|
|
|
|
|
|
|
| 63 |
|
| 64 |
-
##
|
| 65 |
|
| 66 |
```python
|
| 67 |
-
import torch
|
| 68 |
-
from transformers import AutoModelForCausalLM
|
| 69 |
-
|
| 70 |
-
model = AutoModelForCausalLM.from_pretrained(
|
| 71 |
-
"AbstractPhil/mini-beatrix-3", trust_remote_code=True)
|
| 72 |
-
|
| 73 |
prompt = "The history of astronomy begins"
|
| 74 |
ids = torch.tensor([list(prompt.encode("utf-8"))])
|
| 75 |
-
out =
|
| 76 |
print(bytes(int(i) for i in out[0]).decode("utf-8", errors="replace"))
|
| 77 |
```
|
| 78 |
|
| 79 |
-
Chat-framed prompting (the frame was taught in the final 4.0B bytes, so
|
| 80 |
-
treat it as a young capability):
|
| 81 |
-
|
| 82 |
-
```python
|
| 83 |
-
SYS, USER, MODEL, END = 0xFB, 0xFE, 0xFD, 0xFC
|
| 84 |
-
turns = ([SYS] + list(b"You are Beatrix, a small byte-level language model.")
|
| 85 |
-
+ [END] + [USER] + list(b"Who are you?") + [END] + [MODEL])
|
| 86 |
-
out = model.generate(torch.tensor([turns]), max_new_tokens=100,
|
| 87 |
-
do_sample=True, top_p=0.95)
|
| 88 |
-
```
|
| 89 |
-
|
| 90 |
## Weight files
|
| 91 |
|
| 92 |
| file | precision | what it is |
|
| 93 |
|------|-----------|------------|
|
| 94 |
-
| `model.safetensors` | bf16 | the final weights (step 245,674); the default |
|
| 95 |
| `model.fp32.safetensors` | fp32 | the trainer's master weights at the same step; load with `variant="fp32"` |
|
|
|
|
|
|
|
|
|
|
| 96 |
|
| 97 |
Training ran under bf16 autocast over fp32 master weights. The bf16 file is
|
| 98 |
those masters rounded to bf16, tensor for tensor. Each file's header names
|
| 99 |
-
its precision and step.
|
|
|
|
| 100 |
|
| 101 |
## Training
|
| 102 |
|
|
@@ -117,20 +253,22 @@ Final validation: **0.9861 bpb** on the fineweb-edu holdout. The web
|
|
| 117 |
pretraining phase closed at 0.9838; the curriculum's stage diets moved the
|
| 118 |
web holdout as high as 1.15, and the anneal brought it back.
|
| 119 |
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
were trained with (through step 148,000), not to these final weights. This
|
| 127 |
-
repository carries no arms.
|
| 128 |
|
| 129 |
## Lineage
|
| 130 |
|
| 131 |
Code: [AbstractEyes/alephllm](https://github.com/AbstractEyes/alephllm)
|
| 132 |
-
(this repo vendors the model files; alephllm 0.10.5)
|
|
|
|
|
|
|
| 133 |
Siblings: [mini-beatrix-2s](https://huggingface.co/AbstractPhil/mini-beatrix-2s)
|
| 134 |
-
(237M, 20 blocks, the previous rung)
|
|
|
|
|
|
|
| 135 |
[mini-beatrix-1](https://huggingface.co/AbstractPhil/mini-beatrix-1)
|
| 136 |
(112M, the 3-hub hybrid).
|
|
|
|
| 7 |
- linear-attention
|
| 8 |
- splat-attention
|
| 9 |
- aleph
|
| 10 |
+
- adapters
|
| 11 |
- custom_code
|
| 12 |
---
|
| 13 |
|
| 14 |
# mini-beatrix-3
|
| 15 |
|
| 16 |
+
**The 32-block rung of the mini-beatrix ladder, with its stage arms.** 376M
|
| 17 |
+
parameters, byte-level (vocab 256), with a multi-constellation
|
| 18 |
+
**CausalSplatHUB** (signed-address linear attention over learned codebook
|
| 19 |
+
blackboards) and an anchored expert bank in **every one of its 32 blocks**.
|
| 20 |
+
No softmax-over-positions attention anywhere. Trained on 64.4B bytes (245,674
|
| 21 |
steps, about 295 hours on two RTX 5090s) through a staged curriculum,
|
| 22 |
completed 2026-10-05.
|
| 23 |
|
| 24 |
+
The package ships **both states in one repository**: the bare model (arms
|
| 25 |
+
off) and the model with its **stage arms** mounted (arms on). An arm is a
|
| 26 |
+
small detachable adapter trained on this exact frozen core. The arms are a
|
| 27 |
+
**group**: they were trained switched on together, and they are mounted
|
| 28 |
+
together. Each stage also has a **solo arm**, trained alone, for use one at
|
| 29 |
+
a time. Arms are guests on the core, never a change to it: the core's
|
| 30 |
+
weights are the same files either way, and detaching restores the bare
|
| 31 |
+
model bit for bit.
|
| 32 |
+
|
| 33 |
+
```python
|
| 34 |
+
import torch
|
| 35 |
+
from transformers import AutoModelForCausalLM
|
| 36 |
+
|
| 37 |
+
m = AutoModelForCausalLM.from_pretrained(
|
| 38 |
+
"AbstractPhil/mini-beatrix-3", trust_remote_code=True).eval()
|
| 39 |
+
|
| 40 |
+
m.say("Who are you?") # arms off: the bare core, its own chat frame
|
| 41 |
+
|
| 42 |
+
m.mount_arm("stages-1-4") # arms on: the stage arms, all on together
|
| 43 |
+
m.detach_arm() # the bare core again, verified bit-exact
|
| 44 |
+
```
|
| 45 |
+
|
| 46 |
+
Arms on from the first call:
|
| 47 |
+
|
| 48 |
+
```python
|
| 49 |
+
m = AutoModelForCausalLM.from_pretrained(
|
| 50 |
+
"AbstractPhil/mini-beatrix-3", trust_remote_code=True,
|
| 51 |
+
default_arm="stages-1-4").eval()
|
| 52 |
+
```
|
| 53 |
+
|
| 54 |
+
The model reads **raw UTF-8 bytes**: `input_ids` are byte values 0–255.
|
| 55 |
+
There is no tokenizer to download.
|
| 56 |
|
| 57 |
## Architecture
|
| 58 |
|
|
|
|
| 75 |
the end (+6.13) is nearly twice every earlier one and is being repeated
|
| 76 |
before it is relied on.
|
| 77 |
|
| 78 |
+
## The stage arms
|
| 79 |
+
|
| 80 |
+
The curriculum taught the model nine kinds of text, one stage after another,
|
| 81 |
+
and the finished model moved on from each stage's text as the later stages
|
| 82 |
+
came. The stage arms bring that text back without touching the core. They
|
| 83 |
+
were fitted on the finished, frozen core **as a group**: the arms of stages 1 and 2 were trained first and then held fixed, and the arms of stages 3 and 4 were attached over them, one after the other, and trained in their presence, so the group works with every arm on at once.
|
| 84 |
+
`m.arms()` returns the tables below as data, with each row's recipe and full
|
| 85 |
+
measurements.
|
| 86 |
+
|
| 87 |
+
**The group `stages-1-4`** (4 arms, 54.8M parameters; two seeds):
|
| 88 |
+
|
| 89 |
+
| arm | its stage's text | stage text, arms off → the group on (bpb) | this arm alone (bpb) | stage items in the stage's own form, off → group on | stage items in a new form, off → group on |
|
| 90 |
+
|---|---|---|---|---|---|
|
| 91 |
+
| `stages-1-4/s1_perspective` | one small event retold from each side: seen from a person, said to them, told about them | 0.554 → 0.048 | 0.052 | 55% → 96% | 46% → 48% |
|
| 92 |
+
| `stages-1-4/s2_concept` | kinds, properties and differences: what a thing is a kind of, what its kind can do, how two things differ | 0.721 → 0.112 | 0.350 | 30% → 98% | 68% → 78% |
|
| 93 |
+
| `stages-1-4/s3_rules` | if-then rules over made-up words, followed step by step to what follows | 0.930 → 0.224 | 0.824 | 16% → 90% | 7% → 30% |
|
| 94 |
+
| `stages-1-4/s4_arith` | small arithmetic worked out in text | 1.234 → 0.338 | 1.127 | 34% → 61% | 24% → 24% |
|
| 95 |
+
|
| 96 |
+
With the group on, held-out web text moves by +0.0017 bpb (the limit set for it was +0.012), and the nine-suite probe mean reads 0.411 with arms off and 0.419 with the group on.
|
| 97 |
+
|
| 98 |
+
The numbers are those of the packaged weights. A second run of the same recipe from other seeds read the stage texts with its group on at 0.048, 0.121, 0.222, 0.338 bpb and moved web text by +0.0011.
|
| 99 |
+
|
| 100 |
+
Each member mounted alone, and the whole group, on every stage's held-out text (bpb):
|
| 101 |
+
|
| 102 |
+
| mounted | stage 1 text | stage 2 text | stage 3 text | stage 4 text | web text |
|
| 103 |
+
|---|---|---|---|---|---|
|
| 104 |
+
| nothing (arms off) | 0.554 | 0.721 | 0.930 | 1.234 | 0.951 |
|
| 105 |
+
| only `stages-1-4/s1_perspective` | 0.053 | 0.239 | 0.509 | 0.723 | 0.952 |
|
| 106 |
+
| only `stages-1-4/s2_concept` | 0.527 | 0.350 | 0.619 | 0.880 | 0.952 |
|
| 107 |
+
| only `stages-1-4/s3_rules` | 0.554 | 0.724 | 0.824 | 1.234 | 0.951 |
|
| 108 |
+
| only `stages-1-4/s4_arith` | 0.553 | 0.721 | 0.928 | 1.127 | 0.951 |
|
| 109 |
+
| the whole group `stages-1-4` | 0.048 | 0.112 | 0.224 | 0.338 | 0.952 |
|
| 110 |
+
|
| 111 |
+
**How to read the tables.** *Stage text* is the loss, in bits per byte, on
|
| 112 |
+
held-out text of the arm's own stage, with arms off and with the whole group
|
| 113 |
+
on. *This arm alone* is the same loss with only that one arm mounted. *Stage
|
| 114 |
+
items* are short test questions from that stage, scored as the share answered
|
| 115 |
+
correctly, first in the form the stage text uses and then in a form it does
|
| 116 |
+
not. The *web text* figure is the change on held-out ordinary web text
|
| 117 |
+
(fineweb-edu) with the group on: the arms were trained to leave it alone.
|
| 118 |
+
|
| 119 |
+
**The group is the unit.** The group does not divide its work one stage to an arm. The first arm is a generalist: mounted alone it reads every stage's text well below the bare model, and the second holds a part of stages 2 to 4 (second table). The arms of stages 3 and 4 were trained on top of that fixed pair and add what the pair leaves: with only the first two arms on, stage 3 text reads 0.32 bpb and stage 4 text 0.48, and with the whole group they read 0.22 and 0.34. Mounted alone, the last two arms do little. So mount the group as a whole: that is the state it was trained and measured in. For one stage by itself, use its solo arm below. The first two arms also had more training than the last two (3,200 and 2,400 steps against 800), and no arm here is trained to its limit: at 800 steps a solo arm's stage text is still falling.
|
| 120 |
+
|
| 121 |
+
**What the arms do, plainly.** The group recovers each stage's own kind of
|
| 122 |
+
text. The gain is in each stage's own forms. Whether it carries over to new
|
| 123 |
+
forms is shown in the first table's last column, where it was measured.
|
| 124 |
+
|
| 125 |
+
### Arms off and arms on
|
| 126 |
+
|
| 127 |
+
```python
|
| 128 |
+
m.arm # None: arms off
|
| 129 |
+
h = m.mount_arm("stages-1-4") # arms on: the whole group
|
| 130 |
+
with h.all_off(): # every member masked: the bare core's logits
|
| 131 |
+
m.say("Hello there.")
|
| 132 |
+
m.detach_arm(verify=True) # raises if the restored core is not bit-exact
|
| 133 |
+
m.mount_arm("stages-1-4/s1_perspective") # one member alone (the "alone" readings)
|
| 134 |
+
```
|
| 135 |
+
|
| 136 |
+
The group mounts its members in the order they were trained, always on, one
|
| 137 |
+
after another at every block, with no mixer between them. `default_arm` in
|
| 138 |
+
`from_pretrained` (or in `config.json`) mounts the group as the model loads.
|
| 139 |
+
`save_pretrained` refuses while an arm is mounted, so an arm can never be
|
| 140 |
+
written into the core's weight file.
|
| 141 |
+
|
| 142 |
+
### Solo arms
|
| 143 |
+
|
| 144 |
+
Each stage also has an arm that was trained **alone** on the same frozen
|
| 145 |
+
core, for when one stage's text is all that matters. A solo arm holds its
|
| 146 |
+
whole stage by itself.
|
| 147 |
+
|
| 148 |
+
| arm | its stage's text | stage text, arm off → on (bpb) | web text (bpb) | stage items in the stage's own form | stage items in a new form | seeds |
|
| 149 |
+
|---|---|---|---|---|---|---|
|
| 150 |
+
| `solo/s1_perspective` | one small event retold from each side: seen from a person, said to them, told about them | 0.554 → 0.081 (a second seed: 0.080) | +0.0002 | 55% → 75% | 46% → 41% | two |
|
| 151 |
+
| `solo/s2_concept` | kinds, properties and differences: what a thing is a kind of, what its kind can do, how two things differ | 0.721 → 0.120 (a second seed: 0.120) | +0.0003 | 30% → 84% | 68% → 73% | two |
|
| 152 |
+
| `solo/s3_rules` | if-then rules over made-up words, followed step by step to what follows | 0.930 → 0.372 (a second seed: 0.374) | +0.0009 | 16% → 60% | 7% → 12% | two |
|
| 153 |
+
| `solo/s4_arith` | small arithmetic worked out in text | 1.234 → 0.389 (a second seed: 0.388) | +0.0007 | 34% → 49% | 24% → 24% | two |
|
| 154 |
+
|
| 155 |
+
```python
|
| 156 |
+
m.mount_arm("solo/s1_perspective") # one solo arm; mounting another swaps it
|
| 157 |
+
```
|
| 158 |
+
|
| 159 |
+
Solo arms are for use **one at a time**. Each was trained to leave ordinary
|
| 160 |
+
web text alone, but nothing taught it to stay out of the other stages' text.
|
| 161 |
+
When the four solo arms of stages 1 to 4 were switched on together, three of
|
| 162 |
+
the four stage texts read worse than with no arm at all. To have several
|
| 163 |
+
stages on at once, mount the group: its arms were trained together for
|
| 164 |
+
exactly that.
|
| 165 |
+
|
| 166 |
+
### What an arm is
|
| 167 |
+
|
| 168 |
+
Each arm places one module after every decoder block. At a site it projects
|
| 169 |
+
the block's output into 16 slots of dimension 8, reads them through a 16-atom
|
| 170 |
+
**aleph address** (a closed-form signed read, never a selector), and adds a
|
| 171 |
+
gated patch back into the residual stream:
|
| 172 |
+
|
| 173 |
+
slots = proj(x).view(B, n, 16, 8)
|
| 174 |
+
m_hat = sum_k sinh(u_k) A_k / sum_k cosh(u_k), u = (x_hat . A) / tau
|
| 175 |
+
x = x + sigmoid(gate) * consume(m_hat)
|
| 176 |
+
|
| 177 |
+
13.7M parameters per arm over the 32 blocks. Every arm was trained with a
|
| 178 |
+
quiet term: beside each chunk of its own stage's text it saw a chunk of text
|
| 179 |
+
that is not its own and was penalised for changing the model's predictions
|
| 180 |
+
there. For a group arm that text was ordinary web text and a partner arm's
|
| 181 |
+
stage text, which is why the group can be left on and why no member harms a
|
| 182 |
+
partner's stage. For a solo arm it was web text only.
|
| 183 |
+
|
| 184 |
+
### Detach is exact
|
| 185 |
+
|
| 186 |
+
Mounting swaps the model's block list for wrapped blocks and keeps the
|
| 187 |
+
originals. `detach_arm()` puts the originals back and re-runs a fixed probe:
|
| 188 |
+
the restored logits must equal the pre-mount fingerprint bit for bit, or it
|
| 189 |
+
raises. Before release, every packaged row was checked to produce logits
|
| 190 |
+
identical to the adapter library the arms were trained with
|
| 191 |
+
([amoe-lora](https://github.com/AbstractEyes/amoe-lora)).
|
| 192 |
+
|
| 193 |
## The special-token control plane
|
| 194 |
|
| 195 |
Thirteen ids that valid UTF-8 can never produce carry structure and are
|
|
|
|
| 206 |
| 0xFA 0xF9 0xF8 0xF6 0xC0 0xC1 | THINK DATA SEP CUE RES | reserved/instrument |
|
| 207 |
|
| 208 |
Chat format: `[SYS] text [END] [USER] text [END] [MODEL] text [END]`. The
|
| 209 |
+
frame is unforgeable (encoded text cannot contain a special). `m.say()`
|
| 210 |
+
renders it for you; the chat frame was taught in the final 4.0B bytes, so
|
| 211 |
+
treat it as a young capability.
|
| 212 |
|
| 213 |
+
## Plain generation
|
| 214 |
|
| 215 |
```python
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 216 |
prompt = "The history of astronomy begins"
|
| 217 |
ids = torch.tensor([list(prompt.encode("utf-8"))])
|
| 218 |
+
out = m.generate(ids, max_new_tokens=200, do_sample=True, top_p=0.95)
|
| 219 |
print(bytes(int(i) for i in out[0]).decode("utf-8", errors="replace"))
|
| 220 |
```
|
| 221 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 222 |
## Weight files
|
| 223 |
|
| 224 |
| file | precision | what it is |
|
| 225 |
|------|-----------|------------|
|
| 226 |
+
| `model.safetensors` | bf16 | the final weights (step 245,674); the default, and the core every arm was trained on |
|
| 227 |
| `model.fp32.safetensors` | fp32 | the trainer's master weights at the same step; load with `variant="fp32"` |
|
| 228 |
+
| `arms/<group>/<arm>.safetensors` | fp32 | one file per arm of a group, as trained |
|
| 229 |
+
| `arms/solo/<arm>.safetensors` | fp32 | one file per solo arm, as trained |
|
| 230 |
+
| `arms/index.json` | | the arm table as data |
|
| 231 |
|
| 232 |
Training ran under bf16 autocast over fp32 master weights. The bf16 file is
|
| 233 |
those masters rounded to bf16, tensor for tensor. Each file's header names
|
| 234 |
+
its precision and step. The arms were fitted on the bf16 file, so that is
|
| 235 |
+
the core their measurements belong to.
|
| 236 |
|
| 237 |
## Training
|
| 238 |
|
|
|
|
| 253 |
pretraining phase closed at 0.9838; the curriculum's stage diets moved the
|
| 254 |
web holdout as high as 1.15, and the anneal brought it back.
|
| 255 |
|
| 256 |
+
**The arms.** During curriculum stages 1–4 an arm was trained beside the
|
| 257 |
+
trunk for each stage; those arms stayed nearly empty, because the trunk took
|
| 258 |
+
each stage's text in before its arm could, and they were detached at step
|
| 259 |
+
148,000 (their files are in the training repo under `mini-beatrix-3/arms/`,
|
| 260 |
+
bound to those earlier trunk states). The arms in this repository were fitted
|
| 261 |
+
afterwards on the finished, frozen core: as a group in two steps (first the arms of stages 1 and 2, in a run that added the four stages' text one stage at a time with every attached arm training under one pure Adam, 800 steps per stage; then, with those two held fixed and on, a new arm for stage 3 and after it a new arm for stage 4, 800 steps each), every arm kept quiet on ordinary web text and on its partners' stage text; and each alone, 800 steps, kept quiet on web text.
|
|
|
|
|
|
|
| 262 |
|
| 263 |
## Lineage
|
| 264 |
|
| 265 |
Code: [AbstractEyes/alephllm](https://github.com/AbstractEyes/alephllm)
|
| 266 |
+
(this repo vendors the model files; alephllm 0.10.5) and
|
| 267 |
+
[AbstractEyes/amoe-lora](https://github.com/AbstractEyes/amoe-lora) (the arm
|
| 268 |
+
library; `arms.py` here is a self-contained re-expression of its runtime).
|
| 269 |
Siblings: [mini-beatrix-2s](https://huggingface.co/AbstractPhil/mini-beatrix-2s)
|
| 270 |
+
(237M, 20 blocks, the previous rung),
|
| 271 |
+
[mini-beatrix-2.5s](https://huggingface.co/AbstractPhil/mini-beatrix-2.5s)
|
| 272 |
+
(the 2s core with its arms) and
|
| 273 |
[mini-beatrix-1](https://huggingface.co/AbstractPhil/mini-beatrix-1)
|
| 274 |
(112M, the 3-hub hybrid).
|
arms.py
ADDED
|
@@ -0,0 +1,598 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""arms.py — the detachable arm runtime for mini-beatrix-3.
|
| 2 |
+
|
| 3 |
+
An *arm* is a small aleph-addressed patch head placed after every decoder
|
| 4 |
+
block of one exact frozen core. It is not a low-rank matrix: each site
|
| 5 |
+
projects the block output into `n_slots` slots of dimension D, reads them
|
| 6 |
+
through a K-atom aleph address (a closed-form signed read, never a
|
| 7 |
+
selector), and adds a gated patch back into the residual stream:
|
| 8 |
+
|
| 9 |
+
slots = proj(x).view(B, n, n_slots, D)
|
| 10 |
+
m_hat = sum_k sinh(u_k) A_k / sum_k cosh(u_k), u = (x_hat . A) / tau
|
| 11 |
+
x = x + sigmoid(gate) * consume(m_hat)
|
| 12 |
+
|
| 13 |
+
`mount()` makes the live core carry exactly one recipe and returns a
|
| 14 |
+
handle; `handle.detach(verify=True)` restores the untouched blocks and
|
| 15 |
+
asserts the restored logits are BIT-EXACT with a pre-attach fingerprint.
|
| 16 |
+
That exactness is the point of the design: an arm is a guest, never a
|
| 17 |
+
migration of the core.
|
| 18 |
+
|
| 19 |
+
Recipes carried by this package:
|
| 20 |
+
anchor one arm, always on
|
| 21 |
+
stack several anchor arms applied always-on, nested inner to outer,
|
| 22 |
+
with no mixer between them (the ungated composition)
|
| 23 |
+
routed a dense signed dispatch over member arms, optionally with a
|
| 24 |
+
detached bounded per-member gain read from a causal usage EMA
|
| 25 |
+
|
| 26 |
+
THE DAMPING LAW (do not "fix" it): the dispatch denominator sums cosh
|
| 27 |
+
over ALL members, including masked ones, and masking a member removes
|
| 28 |
+
its delta WITHOUT renormalizing. Masked is not solo.
|
| 29 |
+
|
| 30 |
+
This file is self-contained (torch + safetensors). It is a vendored,
|
| 31 |
+
verified re-expression of the amoe-lora runtime the arms were trained
|
| 32 |
+
with (github.com/AbstractEyes/amoe-lora): the packaged arms were checked
|
| 33 |
+
to produce bit-identical logits through both paths before release.
|
| 34 |
+
"""
|
| 35 |
+
from __future__ import annotations
|
| 36 |
+
|
| 37 |
+
import contextlib
|
| 38 |
+
import json
|
| 39 |
+
import os
|
| 40 |
+
from dataclasses import dataclass
|
| 41 |
+
|
| 42 |
+
import torch
|
| 43 |
+
import torch.nn as nn
|
| 44 |
+
import torch.nn.functional as F
|
| 45 |
+
|
| 46 |
+
ARM_TYPES = ("anchor", "stack", "routed")
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
# --------------------------------------------------------------- geometry
|
| 50 |
+
@dataclass
|
| 51 |
+
class AdapterSpec:
|
| 52 |
+
n_slots: int = 16
|
| 53 |
+
K: int = 64
|
| 54 |
+
D: int = 4
|
| 55 |
+
tau: float = 0.1
|
| 56 |
+
hidden: int = 178
|
| 57 |
+
gate_init: float = -3.0
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
class AlephAddress(nn.Module):
|
| 61 |
+
"""m_hat(x) = sum_k sinh(u_k) A_k / sum_k cosh(u_k), max-|u| stabilized.
|
| 62 |
+
|
| 63 |
+
`home` snapshots the codebook at init and is required in every arm
|
| 64 |
+
state (the drift gauge)."""
|
| 65 |
+
|
| 66 |
+
def __init__(self, K: int = 64, D: int = 4, tau: float = 0.1):
|
| 67 |
+
super().__init__()
|
| 68 |
+
self.K, self.D, self.tau = K, D, tau
|
| 69 |
+
self.codebook = nn.Parameter(F.normalize(torch.randn(K, D), dim=-1))
|
| 70 |
+
self.register_buffer("home", self.codebook.detach().clone())
|
| 71 |
+
|
| 72 |
+
def m_hat(self, x: torch.Tensor) -> torch.Tensor:
|
| 73 |
+
A = F.normalize(self.codebook, dim=-1)
|
| 74 |
+
u = (F.normalize(x, dim=-1) @ A.transpose(-1, -2)) / self.tau
|
| 75 |
+
m = u.abs().amax(dim=-1, keepdim=True)
|
| 76 |
+
ep, en = torch.exp(u - m), torch.exp(-u - m)
|
| 77 |
+
return ((ep - en) @ A) / (ep + en).sum(dim=-1, keepdim=True)
|
| 78 |
+
|
| 79 |
+
@torch.no_grad()
|
| 80 |
+
def drift(self) -> float:
|
| 81 |
+
"""Mean angular drift (radians) of the codebook from `home`."""
|
| 82 |
+
A = F.normalize(self.codebook, dim=-1)
|
| 83 |
+
H = F.normalize(self.home, dim=-1)
|
| 84 |
+
return float(torch.arccos((A * H).sum(-1).clamp(-1.0, 1.0)).mean())
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
class SquaredReLU(nn.Module):
|
| 88 |
+
def forward(self, x):
|
| 89 |
+
return F.relu(x) ** 2
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
class RelayPatchwork(nn.Module):
|
| 93 |
+
"""One arm site: projection -> aleph address -> squared-ReLU patch head,
|
| 94 |
+
added through a sigmoid gate."""
|
| 95 |
+
|
| 96 |
+
def __init__(self, d: int, spec: AdapterSpec | None = None):
|
| 97 |
+
super().__init__()
|
| 98 |
+
s = spec or AdapterSpec()
|
| 99 |
+
self.spec = s
|
| 100 |
+
self.n_slots = s.n_slots
|
| 101 |
+
self.proj = nn.Linear(d, s.n_slots * s.D, bias=False)
|
| 102 |
+
self.addr = AlephAddress(s.K, s.D, s.tau)
|
| 103 |
+
self.consume = nn.Sequential(
|
| 104 |
+
nn.Linear(s.n_slots * s.D, s.hidden), SquaredReLU(),
|
| 105 |
+
nn.LayerNorm(s.hidden), nn.Linear(s.hidden, d))
|
| 106 |
+
self.gate = nn.Parameter(torch.tensor(float(s.gate_init)))
|
| 107 |
+
|
| 108 |
+
def forward(self, x):
|
| 109 |
+
B, n, _ = x.shape
|
| 110 |
+
slots = self.proj(x).view(B, n, self.n_slots, self.spec.D)
|
| 111 |
+
feats = self.addr.m_hat(slots).reshape(B, n, -1)
|
| 112 |
+
return x + torch.sigmoid(self.gate) * self.consume(feats)
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
def spec_from_state(state: dict, prefix: str | None = None) -> AdapterSpec:
|
| 116 |
+
"""Arm geometry read from the arm's own tensors (the library holds more
|
| 117 |
+
than one shape, and modules are built before the state loads)."""
|
| 118 |
+
if prefix is None:
|
| 119 |
+
blocks = sorted(int(k.split(".")[0]) for k in state
|
| 120 |
+
if k.endswith(".addr.codebook") and k.split(".")[0].isdigit())
|
| 121 |
+
prefix = f"{blocks[0]}." if blocks else "0."
|
| 122 |
+
cb = state.get(f"{prefix}addr.codebook")
|
| 123 |
+
proj = state.get(f"{prefix}proj.weight")
|
| 124 |
+
cons = state.get(f"{prefix}consume.0.weight")
|
| 125 |
+
if cb is None or proj is None or cons is None:
|
| 126 |
+
return AdapterSpec()
|
| 127 |
+
D = int(cb.shape[1])
|
| 128 |
+
return AdapterSpec(n_slots=int(proj.shape[0] // D), K=int(cb.shape[0]),
|
| 129 |
+
D=D, hidden=int(cons.shape[0]))
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
# --------------------------------------------------------------- wrappers
|
| 133 |
+
class BlockWithAdapter(nn.Module):
|
| 134 |
+
"""Wraps one decoder block. `enabled=False` returns the block output
|
| 135 |
+
untouched — the single-arm half of the toggle law."""
|
| 136 |
+
|
| 137 |
+
def __init__(self, block: nn.Module, adapter: RelayPatchwork):
|
| 138 |
+
super().__init__()
|
| 139 |
+
self.block = block
|
| 140 |
+
self.adapter = adapter
|
| 141 |
+
self.enabled = True
|
| 142 |
+
|
| 143 |
+
def forward(self, *args, **kwargs):
|
| 144 |
+
out = self.block(*args, **kwargs)
|
| 145 |
+
if not self.enabled:
|
| 146 |
+
return out
|
| 147 |
+
if isinstance(out, tuple):
|
| 148 |
+
return (self.adapter(out[0]),) + out[1:]
|
| 149 |
+
return self.adapter(out)
|
| 150 |
+
|
| 151 |
+
def prefill(self, *args, **kwargs):
|
| 152 |
+
out, cache = self.block.prefill(*args, **kwargs)
|
| 153 |
+
return (self.adapter(out) if self.enabled else out), cache
|
| 154 |
+
|
| 155 |
+
def step(self, *args, **kwargs):
|
| 156 |
+
out = self.block.step(*args, **kwargs)
|
| 157 |
+
if not self.enabled:
|
| 158 |
+
return out
|
| 159 |
+
if isinstance(out, tuple):
|
| 160 |
+
return (self.adapter(out[0]),) + out[1:]
|
| 161 |
+
return self.adapter(out)
|
| 162 |
+
|
| 163 |
+
|
| 164 |
+
class AnchorDispatch(nn.Module):
|
| 165 |
+
"""Dense signed sinh/cosh dispatch over frozen member arms.
|
| 166 |
+
|
| 167 |
+
out = x + sum_a (sinh u_a / sum_all cosh u) (arm_a(x) - x)
|
| 168 |
+
|
| 169 |
+
No top-k, no softmax over members, no load balancing. The denominator
|
| 170 |
+
sums over every member, masked or not (the damping law)."""
|
| 171 |
+
|
| 172 |
+
def __init__(self, anchors: nn.ModuleList, d: int, emb: int = 64,
|
| 173 |
+
tau: float = 0.1):
|
| 174 |
+
super().__init__()
|
| 175 |
+
self.anchors = anchors
|
| 176 |
+
self.tau = tau
|
| 177 |
+
A = len(anchors)
|
| 178 |
+
self.register_buffer("key_proj", torch.zeros(emb, d))
|
| 179 |
+
self.dispatch = nn.Parameter(torch.zeros(A, emb))
|
| 180 |
+
self.enabled = [True] * A
|
| 181 |
+
|
| 182 |
+
def forward(self, x):
|
| 183 |
+
emb = F.normalize(x @ self.key_proj.T, dim=-1)
|
| 184 |
+
C = F.normalize(self.dispatch, dim=-1)
|
| 185 |
+
u = (emb @ C.T) / self.tau
|
| 186 |
+
m = u.abs().amax(dim=-1, keepdim=True)
|
| 187 |
+
ep, en = torch.exp(u - m), torch.exp(-u - m)
|
| 188 |
+
w = ep - en
|
| 189 |
+
z = (ep + en).sum(dim=-1, keepdim=True) # ALL members (law)
|
| 190 |
+
out = x
|
| 191 |
+
for k, a in enumerate(self.anchors):
|
| 192 |
+
if self.enabled[k]:
|
| 193 |
+
out = out + (w[..., k:k + 1] / z) * (a(x) - x)
|
| 194 |
+
return out
|
| 195 |
+
|
| 196 |
+
|
| 197 |
+
class GainDispatch(AnchorDispatch):
|
| 198 |
+
"""AnchorDispatch with a detached bounded per-member gain on the
|
| 199 |
+
numerator (the denominator is untouched, the signs are untouched):
|
| 200 |
+
|
| 201 |
+
s_a(t) = cosh u_a / sum_all cosh u (the member's share)
|
| 202 |
+
ubar_a = causal EMA of s_a over bytes, rate rho, start 1/A
|
| 203 |
+
alpha_a = clamp(1 + gamma (A ubar_a - 1), a_min, a_max)
|
| 204 |
+
out = x + sum_a alpha_a (sinh u_a / sum_all cosh u)(arm_a(x) - x)
|
| 205 |
+
|
| 206 |
+
mode 'growth' lets the mean gain rise toward the cap; 'conserved'
|
| 207 |
+
renormalizes alpha to mean 1 over members at every byte. `coherent`
|
| 208 |
+
reads alpha once at the first site and shares it ('alpha'), and with
|
| 209 |
+
'full' shares the first site's signed routing weight too."""
|
| 210 |
+
|
| 211 |
+
def configure(self, *, mode="growth", rho=1 / 16, gamma=1.5, a_min=0.5,
|
| 212 |
+
a_max=2.0, coherent=None, role="ref", holder=None):
|
| 213 |
+
self.mode, self.rho, self.gamma = mode, float(rho), float(gamma)
|
| 214 |
+
self.a_min, self.a_max = float(a_min), float(a_max)
|
| 215 |
+
self.coherent, self.role = coherent, role
|
| 216 |
+
self.holder = {} if holder is None else holder
|
| 217 |
+
self._state, self._call = None, "forward"
|
| 218 |
+
return self
|
| 219 |
+
|
| 220 |
+
def reset_state(self):
|
| 221 |
+
self._state = None
|
| 222 |
+
|
| 223 |
+
def _alpha(self, ep, en, z):
|
| 224 |
+
with torch.no_grad():
|
| 225 |
+
s = ((ep + en) / z).float()
|
| 226 |
+
B, T, A = s.shape
|
| 227 |
+
st = (self._state if (self._call == "step" and self._state is not None
|
| 228 |
+
and self._state.shape[0] == B) else None)
|
| 229 |
+
if st is None:
|
| 230 |
+
st = torch.full((B, A), 1.0 / A, device=s.device, dtype=torch.float32)
|
| 231 |
+
if T == 1:
|
| 232 |
+
st = st + self.rho * (s[:, 0] - st)
|
| 233 |
+
ubar = st.unsqueeze(1)
|
| 234 |
+
elif T <= 4200:
|
| 235 |
+
q = 1.0 - self.rho
|
| 236 |
+
t = torch.arange(1, T + 1, device=s.device, dtype=torch.float64)
|
| 237 |
+
acc = torch.cumsum((q ** -t).view(1, T, 1) * s.double(), dim=1)
|
| 238 |
+
ubar = ((q ** t).view(1, T, 1)
|
| 239 |
+
* (st.double().unsqueeze(1) + self.rho * acc)).float()
|
| 240 |
+
st = ubar[:, -1]
|
| 241 |
+
else:
|
| 242 |
+
ubar = torch.empty_like(s)
|
| 243 |
+
for i in range(T):
|
| 244 |
+
st = st + self.rho * (s[:, i] - st)
|
| 245 |
+
ubar[:, i] = st
|
| 246 |
+
self._state = st.clone() if self._call in ("prefill", "step") else None
|
| 247 |
+
alpha = (1.0 + self.gamma * (A * ubar - 1.0)).clamp(self.a_min, self.a_max)
|
| 248 |
+
if self.mode == "conserved":
|
| 249 |
+
alpha = alpha * (A / alpha.sum(dim=-1, keepdim=True))
|
| 250 |
+
return alpha.to(z.dtype)
|
| 251 |
+
|
| 252 |
+
def forward(self, x):
|
| 253 |
+
shared_route = self.coherent == "full" and self.role != "ref"
|
| 254 |
+
if not shared_route:
|
| 255 |
+
emb = F.normalize(x @ self.key_proj.T, dim=-1)
|
| 256 |
+
C = F.normalize(self.dispatch, dim=-1)
|
| 257 |
+
u = (emb @ C.T) / self.tau
|
| 258 |
+
m = u.abs().amax(dim=-1, keepdim=True)
|
| 259 |
+
ep, en = torch.exp(u - m), torch.exp(-u - m)
|
| 260 |
+
z = (ep + en).sum(dim=-1, keepdim=True)
|
| 261 |
+
g = (ep - en) / z
|
| 262 |
+
if self.coherent and self.role != "ref":
|
| 263 |
+
alpha = self.holder["alpha"]
|
| 264 |
+
if shared_route:
|
| 265 |
+
g = self.holder["g"]
|
| 266 |
+
else:
|
| 267 |
+
alpha = self._alpha(ep, en, z)
|
| 268 |
+
if self.coherent:
|
| 269 |
+
self.holder["alpha"], self.holder["g"] = alpha, g
|
| 270 |
+
out = x
|
| 271 |
+
for k, a in enumerate(self.anchors):
|
| 272 |
+
if self.enabled[k]:
|
| 273 |
+
out = out + (g[..., k:k + 1] * alpha[..., k:k + 1]) * (a(x) - x)
|
| 274 |
+
return out
|
| 275 |
+
|
| 276 |
+
|
| 277 |
+
class BlockWithDispatch(nn.Module):
|
| 278 |
+
def __init__(self, block: nn.Module, disp: AnchorDispatch):
|
| 279 |
+
super().__init__()
|
| 280 |
+
self.block = block
|
| 281 |
+
self.disp = disp
|
| 282 |
+
|
| 283 |
+
def forward(self, *args, **kwargs):
|
| 284 |
+
out = self.block(*args, **kwargs)
|
| 285 |
+
if isinstance(out, tuple):
|
| 286 |
+
return (self.disp(out[0]),) + out[1:]
|
| 287 |
+
return self.disp(out)
|
| 288 |
+
|
| 289 |
+
def prefill(self, *args, **kwargs):
|
| 290 |
+
out, cache = self.block.prefill(*args, **kwargs)
|
| 291 |
+
self.disp._call = getattr(self.disp, "_call", "forward")
|
| 292 |
+
if isinstance(self.disp, GainDispatch):
|
| 293 |
+
self.disp._call = "prefill"
|
| 294 |
+
try:
|
| 295 |
+
return self.disp(out), cache
|
| 296 |
+
finally:
|
| 297 |
+
self.disp._call = "forward"
|
| 298 |
+
return self.disp(out), cache
|
| 299 |
+
|
| 300 |
+
def step(self, *args, **kwargs):
|
| 301 |
+
out = self.block.step(*args, **kwargs)
|
| 302 |
+
if isinstance(self.disp, GainDispatch):
|
| 303 |
+
self.disp._call = "step"
|
| 304 |
+
try:
|
| 305 |
+
return self.disp(out[0]) if isinstance(out, tuple) else self.disp(out)
|
| 306 |
+
finally:
|
| 307 |
+
self.disp._call = "forward"
|
| 308 |
+
if isinstance(out, tuple):
|
| 309 |
+
return (self.disp(out[0]),) + out[1:]
|
| 310 |
+
return self.disp(out)
|
| 311 |
+
|
| 312 |
+
|
| 313 |
+
# --------------------------------------------------------------- handles
|
| 314 |
+
def _probe(aleph) -> torch.Tensor:
|
| 315 |
+
"""Deterministic logits fingerprint on a tiny fixed input."""
|
| 316 |
+
dev = next(aleph.parameters()).device
|
| 317 |
+
ids = torch.arange(8, dtype=torch.long, device=dev).unsqueeze(0) % 7 + 1
|
| 318 |
+
was_training = aleph.training
|
| 319 |
+
aleph.eval()
|
| 320 |
+
with torch.no_grad():
|
| 321 |
+
out = aleph(ids)
|
| 322 |
+
if was_training:
|
| 323 |
+
aleph.train()
|
| 324 |
+
logits = out[0] if isinstance(out, (tuple, list)) else out.logits
|
| 325 |
+
return logits.detach().float().cpu()
|
| 326 |
+
|
| 327 |
+
|
| 328 |
+
class ArmHandle:
|
| 329 |
+
"""One mounted recipe. `detach(verify=True)` restores the core and
|
| 330 |
+
proves the restoration is bit-exact."""
|
| 331 |
+
|
| 332 |
+
def __init__(self, aleph, original, names, wrapped, fingerprint, card=None):
|
| 333 |
+
self.aleph = aleph
|
| 334 |
+
self._original = original
|
| 335 |
+
self.names = list(names)
|
| 336 |
+
self._blocks = wrapped
|
| 337 |
+
self._fingerprint = fingerprint
|
| 338 |
+
self.card = card or {}
|
| 339 |
+
|
| 340 |
+
# -- masking (the toggle law) --------------------------------------
|
| 341 |
+
def set_mask(self, mask: dict) -> None:
|
| 342 |
+
enabled = [bool(mask.get(n, True)) for n in self.names]
|
| 343 |
+
for b in self._blocks:
|
| 344 |
+
if isinstance(b, BlockWithDispatch):
|
| 345 |
+
b.disp.enabled = list(enabled)
|
| 346 |
+
else:
|
| 347 |
+
b.enabled = enabled[0]
|
| 348 |
+
|
| 349 |
+
def enable(self, *names):
|
| 350 |
+
self.set_mask({n: (n in names or not names) for n in self.names})
|
| 351 |
+
|
| 352 |
+
def disable(self, *names):
|
| 353 |
+
self.set_mask({n: n not in names for n in self.names})
|
| 354 |
+
|
| 355 |
+
def _snapshot(self):
|
| 356 |
+
return [list(b.disp.enabled) if isinstance(b, BlockWithDispatch) else b.enabled
|
| 357 |
+
for b in self._blocks]
|
| 358 |
+
|
| 359 |
+
def _restore(self, snap):
|
| 360 |
+
for b, s in zip(self._blocks, snap):
|
| 361 |
+
if isinstance(b, BlockWithDispatch):
|
| 362 |
+
b.disp.enabled = list(s)
|
| 363 |
+
else:
|
| 364 |
+
b.enabled = s
|
| 365 |
+
|
| 366 |
+
@contextlib.contextmanager
|
| 367 |
+
def only(self, *names):
|
| 368 |
+
prev = self._snapshot()
|
| 369 |
+
self.set_mask({n: n in names for n in self.names})
|
| 370 |
+
try:
|
| 371 |
+
yield self
|
| 372 |
+
finally:
|
| 373 |
+
self._restore(prev)
|
| 374 |
+
|
| 375 |
+
@contextlib.contextmanager
|
| 376 |
+
def all_off(self):
|
| 377 |
+
prev = self._snapshot()
|
| 378 |
+
self.set_mask({n: False for n in self.names})
|
| 379 |
+
try:
|
| 380 |
+
yield self
|
| 381 |
+
finally:
|
| 382 |
+
self._restore(prev)
|
| 383 |
+
|
| 384 |
+
# -- decode state ---------------------------------------------------
|
| 385 |
+
def reset_state(self):
|
| 386 |
+
"""Forget the carried gain state (a fresh context starts over)."""
|
| 387 |
+
for b in self._blocks:
|
| 388 |
+
if isinstance(b, BlockWithDispatch):
|
| 389 |
+
getattr(b.disp, "reset_state", lambda: None)()
|
| 390 |
+
|
| 391 |
+
# -- detach ---------------------------------------------------------
|
| 392 |
+
def detach(self, *, verify: bool = True):
|
| 393 |
+
self.aleph.blocks = self._original
|
| 394 |
+
if verify:
|
| 395 |
+
post = _probe(self.aleph)
|
| 396 |
+
if not torch.equal(post, self._fingerprint):
|
| 397 |
+
raise RuntimeError(
|
| 398 |
+
"detach verification FAILED: the restored core's logits "
|
| 399 |
+
"are not bit-exact with the pre-mount fingerprint")
|
| 400 |
+
return self.aleph
|
| 401 |
+
|
| 402 |
+
|
| 403 |
+
class StackHandle:
|
| 404 |
+
"""Nested always-on handles, detached outermost first."""
|
| 405 |
+
|
| 406 |
+
def __init__(self, handles, names=None, card=None):
|
| 407 |
+
self.handles = list(handles)
|
| 408 |
+
self.names = list(names or [h.names[0] for h in self.handles])
|
| 409 |
+
self.card = card or {}
|
| 410 |
+
|
| 411 |
+
def reset_state(self):
|
| 412 |
+
for h in self.handles:
|
| 413 |
+
h.reset_state()
|
| 414 |
+
|
| 415 |
+
def set_mask(self, mask: dict) -> None:
|
| 416 |
+
for h, n in zip(self.handles, self.names):
|
| 417 |
+
h.set_mask({h.names[0]: bool(mask.get(n, True))})
|
| 418 |
+
|
| 419 |
+
@contextlib.contextmanager
|
| 420 |
+
def all_off(self):
|
| 421 |
+
prev = [h._snapshot() for h in self.handles]
|
| 422 |
+
for h in self.handles:
|
| 423 |
+
h.set_mask({h.names[0]: False})
|
| 424 |
+
try:
|
| 425 |
+
yield self
|
| 426 |
+
finally:
|
| 427 |
+
for h, p in zip(self.handles, prev):
|
| 428 |
+
h._restore(p)
|
| 429 |
+
|
| 430 |
+
def detach(self, *, verify: bool = True):
|
| 431 |
+
for h in reversed(self.handles):
|
| 432 |
+
h.detach(verify=verify)
|
| 433 |
+
return self.handles[0].aleph if self.handles else None
|
| 434 |
+
|
| 435 |
+
|
| 436 |
+
# --------------------------------------------------------------- loading
|
| 437 |
+
def load_arm_file(path: str):
|
| 438 |
+
"""-> (tensors, meta). Reads the packaged safetensors arm; also accepts
|
| 439 |
+
an amoe .pt anchor/dispatch checkpoint for local experiments."""
|
| 440 |
+
if str(path).endswith(".safetensors"):
|
| 441 |
+
from safetensors.torch import load_file
|
| 442 |
+
from safetensors import safe_open
|
| 443 |
+
with safe_open(path, framework="pt") as f:
|
| 444 |
+
meta = dict(f.metadata() or {})
|
| 445 |
+
card = json.loads(meta.get("arm", "{}"))
|
| 446 |
+
return load_file(path), card
|
| 447 |
+
blob = torch.load(path, map_location="cpu", weights_only=True)
|
| 448 |
+
if "adapters" in blob:
|
| 449 |
+
return blob["adapters"], dict(blob.get("meta") or {})
|
| 450 |
+
if "dispatch" in blob: # flatten to "<block>.<key>"
|
| 451 |
+
flat = {f"{i}.{k}": v for i, st in enumerate(blob["dispatch"])
|
| 452 |
+
for k, v in st.items()}
|
| 453 |
+
return flat, dict(blob.get("meta") or {})
|
| 454 |
+
raise ValueError(f"unrecognized arm file: {path}")
|
| 455 |
+
|
| 456 |
+
|
| 457 |
+
def _per_block(state: dict, i: int) -> dict:
|
| 458 |
+
pre = f"{i}."
|
| 459 |
+
return {k[len(pre):]: v for k, v in state.items() if k.startswith(pre)}
|
| 460 |
+
|
| 461 |
+
|
| 462 |
+
def _target_blocks(aleph):
|
| 463 |
+
return list(aleph.blocks)
|
| 464 |
+
|
| 465 |
+
|
| 466 |
+
# --------------------------------------------------------------- mounting
|
| 467 |
+
def mount_anchor(aleph, state: dict, name: str = "arm", card=None) -> ArmHandle:
|
| 468 |
+
layers = _target_blocks(aleph)
|
| 469 |
+
d = int(aleph.cfg.d_model)
|
| 470 |
+
fingerprint = _probe(aleph)
|
| 471 |
+
spec = spec_from_state(state)
|
| 472 |
+
blocks, wrapped = [], []
|
| 473 |
+
for i, layer in enumerate(layers):
|
| 474 |
+
st = _per_block(state, i)
|
| 475 |
+
if not st:
|
| 476 |
+
blocks.append(layer)
|
| 477 |
+
continue
|
| 478 |
+
p = next(layer.parameters())
|
| 479 |
+
a = RelayPatchwork(d, spec)
|
| 480 |
+
a.load_state_dict(st, strict=True)
|
| 481 |
+
a.to(device=p.device, dtype=p.dtype)
|
| 482 |
+
w = BlockWithAdapter(layer, a)
|
| 483 |
+
blocks.append(w)
|
| 484 |
+
wrapped.append(w)
|
| 485 |
+
original = aleph.blocks
|
| 486 |
+
aleph.blocks = nn.ModuleList(blocks)
|
| 487 |
+
return ArmHandle(aleph, original, [name], wrapped, fingerprint, card)
|
| 488 |
+
|
| 489 |
+
|
| 490 |
+
def mount_routed(aleph, state: dict, names, gain=None, mode="growth",
|
| 491 |
+
coherent=None, card=None) -> ArmHandle:
|
| 492 |
+
layers = _target_blocks(aleph)
|
| 493 |
+
d = int(aleph.cfg.d_model)
|
| 494 |
+
fingerprint = _probe(aleph)
|
| 495 |
+
A = len(names)
|
| 496 |
+
member0 = {k[len("anchors.0."):]: v for k, v in _per_block(state, 0).items()
|
| 497 |
+
if k.startswith("anchors.0.")}
|
| 498 |
+
spec = spec_from_state(member0, prefix="")
|
| 499 |
+
holder, blocks, wrapped = {}, [], []
|
| 500 |
+
for i, layer in enumerate(layers):
|
| 501 |
+
st = _per_block(state, i)
|
| 502 |
+
p = next(layer.parameters())
|
| 503 |
+
members = nn.ModuleList([RelayPatchwork(d, spec) for _ in range(A)])
|
| 504 |
+
emb = int(st["key_proj"].shape[0])
|
| 505 |
+
dp = AnchorDispatch(members, d, emb=emb)
|
| 506 |
+
dp.load_state_dict(st, strict=True)
|
| 507 |
+
dp.to(device=p.device, dtype=p.dtype)
|
| 508 |
+
if gain:
|
| 509 |
+
dp.__class__ = GainDispatch
|
| 510 |
+
dp.configure(mode=mode, rho=gain["rho"], gamma=gain["gamma"],
|
| 511 |
+
a_min=gain["a_min"], a_max=gain["a_max"],
|
| 512 |
+
coherent=coherent, role="ref" if i == 0 else "follow",
|
| 513 |
+
holder=holder)
|
| 514 |
+
w = BlockWithDispatch(layer, dp)
|
| 515 |
+
blocks.append(w)
|
| 516 |
+
wrapped.append(w)
|
| 517 |
+
original = aleph.blocks
|
| 518 |
+
aleph.blocks = nn.ModuleList(blocks)
|
| 519 |
+
return ArmHandle(aleph, original, list(names), wrapped, fingerprint, card)
|
| 520 |
+
|
| 521 |
+
|
| 522 |
+
def mount(aleph, card: dict, files) -> "ArmHandle | StackHandle":
|
| 523 |
+
"""Attach one recipe. `card` is an arms/index.json row; `files` maps the
|
| 524 |
+
row's weight keys to local paths (a stack passes one file per member,
|
| 525 |
+
inner first)."""
|
| 526 |
+
kind = (card.get("mount") or {}).get("type", "anchor")
|
| 527 |
+
if kind == "anchor":
|
| 528 |
+
state, _ = load_arm_file(files[0])
|
| 529 |
+
return mount_anchor(aleph, state, card.get("name", "arm"), card)
|
| 530 |
+
if kind == "stack":
|
| 531 |
+
members = (card.get("mount") or {}).get("members") or []
|
| 532 |
+
handles = []
|
| 533 |
+
try:
|
| 534 |
+
for f, n in zip(files, members or [f"member{i}" for i in range(len(files))]):
|
| 535 |
+
state, _ = load_arm_file(f)
|
| 536 |
+
handles.append(mount_anchor(aleph, state, n, card))
|
| 537 |
+
except Exception:
|
| 538 |
+
StackHandle(handles).detach(verify=False) # never leave half a stack on
|
| 539 |
+
raise
|
| 540 |
+
return StackHandle(handles, members, card)
|
| 541 |
+
if kind == "routed":
|
| 542 |
+
state, _ = load_arm_file(files[0])
|
| 543 |
+
m = card.get("mount") or {}
|
| 544 |
+
return mount_routed(aleph, state, m.get("members") or [],
|
| 545 |
+
gain=m.get("gain"), mode=m.get("gain_mode", "growth"),
|
| 546 |
+
coherent=m.get("coherent"), card=card)
|
| 547 |
+
raise ValueError(f"unknown mount type {kind!r} (packaged types: {ARM_TYPES})")
|
| 548 |
+
|
| 549 |
+
|
| 550 |
+
# --------------------------------------------------------------- frames
|
| 551 |
+
CHAT_HEADER = ("A conversation with Beatrix, a small byte-level "
|
| 552 |
+
"language model still in pretraining.\n")
|
| 553 |
+
USER_TAG = "User: "
|
| 554 |
+
ASSISTANT_TAG = "Beatrix:"
|
| 555 |
+
TEXT_STOPS = ("\nUser:", "\nBeatrix:", "User:", "Beatrix:", "\n\n")
|
| 556 |
+
SPECIALS = {"DOC": 0xFF, "USER": 0xFE, "MODEL": 0xFD, "END": 0xFC, "SYS": 0xFB}
|
| 557 |
+
|
| 558 |
+
|
| 559 |
+
def render_text_frame(prompt: str, history=None) -> bytes:
|
| 560 |
+
"""A plain text chat frame: header + 'User: ...\\n' + 'Beatrix:' — the
|
| 561 |
+
reply follows the tag. The stage arms of this package were trained on
|
| 562 |
+
raw stage text, not in this frame; it is kept for arms that are."""
|
| 563 |
+
out = CHAT_HEADER
|
| 564 |
+
for turn in (history or []):
|
| 565 |
+
tag = USER_TAG if turn.get("role") == "user" else ASSISTANT_TAG + " "
|
| 566 |
+
out += f"{tag}{turn.get('content', '')}\n"
|
| 567 |
+
out += f"{USER_TAG}{prompt}\n{ASSISTANT_TAG}"
|
| 568 |
+
return out.encode("utf-8")
|
| 569 |
+
|
| 570 |
+
|
| 571 |
+
def render_specials_frame(prompt: str, system: str | None = None,
|
| 572 |
+
history=None) -> bytes:
|
| 573 |
+
"""The core's own annealed frame: [SYS] .. [END] [USER] .. [END] [MODEL].
|
| 574 |
+
Encoded text can never contain a special, so the frame is unforgeable."""
|
| 575 |
+
S = SPECIALS
|
| 576 |
+
ids = []
|
| 577 |
+
if system:
|
| 578 |
+
ids += [S["SYS"]] + list(system.encode("utf-8")) + [S["END"]]
|
| 579 |
+
for turn in (history or []):
|
| 580 |
+
tag = S["USER"] if turn.get("role") == "user" else S["MODEL"]
|
| 581 |
+
ids += [tag] + list(str(turn.get("content", "")).encode("utf-8")) + [S["END"]]
|
| 582 |
+
ids += [S["USER"]] + list(prompt.encode("utf-8")) + [S["END"], S["MODEL"]]
|
| 583 |
+
return bytes(ids)
|
| 584 |
+
|
| 585 |
+
|
| 586 |
+
def cut_text_reply(raw: bytes) -> bytes:
|
| 587 |
+
"""Cut a text-frame continuation at the first stop string."""
|
| 588 |
+
best = len(raw)
|
| 589 |
+
for s in TEXT_STOPS:
|
| 590 |
+
j = raw.find(s.encode("utf-8"))
|
| 591 |
+
if j != -1:
|
| 592 |
+
best = min(best, j)
|
| 593 |
+
return raw[:best]
|
| 594 |
+
|
| 595 |
+
|
| 596 |
+
def cut_specials_reply(raw: bytes) -> bytes:
|
| 597 |
+
j = raw.find(bytes([SPECIALS["END"]]))
|
| 598 |
+
return raw if j == -1 else raw[:j]
|
arms/index.json
ADDED
|
@@ -0,0 +1,854 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"craft": "mini-beatrix-3",
|
| 3 |
+
"core": {
|
| 4 |
+
"craft": "mini-beatrix-3",
|
| 5 |
+
"step": 245674,
|
| 6 |
+
"base_model_id": "alephllm/mini-beatrix-3@step245674",
|
| 7 |
+
"note": "every packaged arm was trained on this exact frozen core (the default bf16 weight file); arms are trunk-bound"
|
| 8 |
+
},
|
| 9 |
+
"generated": "2026-10-06 00:09 UTC",
|
| 10 |
+
"note": "the stage arms of curriculum stages 1 to 4, fitted on the finished, frozen core in two forms: as a group that is on together, and each alone; the arms trained beside the trunk during the run are on the training repo",
|
| 11 |
+
"arms": [
|
| 12 |
+
{
|
| 13 |
+
"id": "stages-1-4/s1_perspective",
|
| 14 |
+
"name": "stages-1-4/s1_perspective",
|
| 15 |
+
"group": "stages-1-4",
|
| 16 |
+
"base_model_id": "alephllm/mini-beatrix-3@step245674",
|
| 17 |
+
"step": 245674,
|
| 18 |
+
"params": 13692960,
|
| 19 |
+
"sites": 32,
|
| 20 |
+
"weights": [
|
| 21 |
+
"arms/stages-1-4/s1_perspective.safetensors"
|
| 22 |
+
],
|
| 23 |
+
"mount": {
|
| 24 |
+
"type": "anchor"
|
| 25 |
+
},
|
| 26 |
+
"precision": "fp32",
|
| 27 |
+
"template": {
|
| 28 |
+
"frame": "raw",
|
| 29 |
+
"template": "raw stage text, continued"
|
| 30 |
+
},
|
| 31 |
+
"decode": {
|
| 32 |
+
"temperature": 0.0,
|
| 33 |
+
"top_p": 1.0,
|
| 34 |
+
"max_new": 120,
|
| 35 |
+
"precision": "fp32"
|
| 36 |
+
},
|
| 37 |
+
"source": {
|
| 38 |
+
"file": "gRA_close4_s1_perspective.safetensors",
|
| 39 |
+
"sha256": "ac7dcb7c175c0b17260e2826b5a4a5e911c8d078d1bbc5602996757820c9b2ff"
|
| 40 |
+
},
|
| 41 |
+
"recipe": "trained as an always-on group on the frozen final core, in two steps. First the arms of stages 1 and 2: a run that attached one stage's arm at a time, each new arm over the earlier ones, every attached arm live and trainable under one pure Adam (lr 0.001), 800 steps per stage across the text of stages 1 to 4; the arms that run attached for stages 3 and 4 stayed nearly empty and were dropped. Then, with the arms of stages 1 and 2 held fixed and on, a new arm for stage 3 was attached and trained for 800 steps, and after it a new arm for stage 4 the same way. Each step gives a training arm 2 x 4096 bytes of its own stage's text with every attached arm on, and a quiet term at weight 2 on 2 rows that are not its text (one of web text, one of another member's stage text): the KL from the model with that arm switched off to the model with every arm on",
|
| 42 |
+
"title": "Stage 1: perspective",
|
| 43 |
+
"behavior": "one small event retold from each side: seen from a person, said to them, told about them",
|
| 44 |
+
"family": "stage",
|
| 45 |
+
"status": "a member of the group stages-1-4; trained always on with its partners, so alone is not its trained state",
|
| 46 |
+
"score": "alone: own stage text 0.554 -> 0.052 bpb; web text +0.0014 bpb",
|
| 47 |
+
"measured": {
|
| 48 |
+
"own_stage_bpb_arms_off": 0.5538,
|
| 49 |
+
"own_stage_bpb_group_on": 0.0482,
|
| 50 |
+
"own_stage_bpb_this_arm_alone": 0.0525,
|
| 51 |
+
"share_of_the_group_on_own_stage": -0.4767,
|
| 52 |
+
"share_of_the_group_on_own_stage_se": 0.0046,
|
| 53 |
+
"share_of_the_group_on_web_text": 0.00035,
|
| 54 |
+
"alone_on_web_text": 0.00143,
|
| 55 |
+
"share_of_the_group_on_partner_stages": {
|
| 56 |
+
"s2_concept": -0.2405,
|
| 57 |
+
"s3_rules": -0.1848,
|
| 58 |
+
"s4_arith": -0.1575
|
| 59 |
+
},
|
| 60 |
+
"alone_on_partner_stages": {
|
| 61 |
+
"s2_concept": -0.4817,
|
| 62 |
+
"s3_rules": -0.4209,
|
| 63 |
+
"s4_arith": -0.5113
|
| 64 |
+
},
|
| 65 |
+
"steps_trained": 3200,
|
| 66 |
+
"stage_items_own_form_accuracy": {
|
| 67 |
+
"arms_off": 0.55,
|
| 68 |
+
"group_on": 0.963,
|
| 69 |
+
"this_arm_alone": 0.838
|
| 70 |
+
},
|
| 71 |
+
"stage_items_new_form_accuracy": {
|
| 72 |
+
"arms_off": 0.463,
|
| 73 |
+
"group_on": 0.475,
|
| 74 |
+
"this_arm_alone": 0.463
|
| 75 |
+
},
|
| 76 |
+
"second_seed": {
|
| 77 |
+
"own_stage_bpb_arms_off": 0.5538,
|
| 78 |
+
"own_stage_bpb_group_on": 0.0479,
|
| 79 |
+
"own_stage_bpb_this_arm_alone": 0.0522,
|
| 80 |
+
"share_of_the_group_on_own_stage": -0.4733,
|
| 81 |
+
"share_of_the_group_on_own_stage_se": 0.0047,
|
| 82 |
+
"share_of_the_group_on_web_text": 4e-05,
|
| 83 |
+
"alone_on_web_text": 0.00134,
|
| 84 |
+
"share_of_the_group_on_partner_stages": {
|
| 85 |
+
"s2_concept": -0.238,
|
| 86 |
+
"s3_rules": -0.1951,
|
| 87 |
+
"s4_arith": -0.1591
|
| 88 |
+
},
|
| 89 |
+
"alone_on_partner_stages": {
|
| 90 |
+
"s2_concept": -0.4772,
|
| 91 |
+
"s3_rules": -0.4262,
|
| 92 |
+
"s4_arith": -0.4807
|
| 93 |
+
},
|
| 94 |
+
"steps_trained": 3200,
|
| 95 |
+
"stage_items_own_form_accuracy": {
|
| 96 |
+
"arms_off": 0.55,
|
| 97 |
+
"group_on": 1.0,
|
| 98 |
+
"this_arm_alone": 0.925
|
| 99 |
+
},
|
| 100 |
+
"stage_items_new_form_accuracy": {
|
| 101 |
+
"arms_off": 0.463,
|
| 102 |
+
"group_on": 0.463,
|
| 103 |
+
"this_arm_alone": 0.463
|
| 104 |
+
}
|
| 105 |
+
}
|
| 106 |
+
},
|
| 107 |
+
"examples": [
|
| 108 |
+
"Rui dropped the acorn near the box. Seen from Rui: I dropped the acorn near the box. Said to Rui: you dropped the acorn near the box. Told about Rui: Rui dropped the acorn near the box. Three ways of saying, one thing that happened."
|
| 109 |
+
]
|
| 110 |
+
},
|
| 111 |
+
{
|
| 112 |
+
"id": "stages-1-4/s2_concept",
|
| 113 |
+
"name": "stages-1-4/s2_concept",
|
| 114 |
+
"group": "stages-1-4",
|
| 115 |
+
"base_model_id": "alephllm/mini-beatrix-3@step245674",
|
| 116 |
+
"step": 245674,
|
| 117 |
+
"params": 13692960,
|
| 118 |
+
"sites": 32,
|
| 119 |
+
"weights": [
|
| 120 |
+
"arms/stages-1-4/s2_concept.safetensors"
|
| 121 |
+
],
|
| 122 |
+
"mount": {
|
| 123 |
+
"type": "anchor"
|
| 124 |
+
},
|
| 125 |
+
"precision": "fp32",
|
| 126 |
+
"template": {
|
| 127 |
+
"frame": "raw",
|
| 128 |
+
"template": "raw stage text, continued"
|
| 129 |
+
},
|
| 130 |
+
"decode": {
|
| 131 |
+
"temperature": 0.0,
|
| 132 |
+
"top_p": 1.0,
|
| 133 |
+
"max_new": 120,
|
| 134 |
+
"precision": "fp32"
|
| 135 |
+
},
|
| 136 |
+
"source": {
|
| 137 |
+
"file": "gRA_close4_s2_concept.safetensors",
|
| 138 |
+
"sha256": "15444e336608c82837c366adc813211d401777aff8ad22ae7b6956bf2c395331"
|
| 139 |
+
},
|
| 140 |
+
"recipe": "trained as an always-on group on the frozen final core, in two steps. First the arms of stages 1 and 2: a run that attached one stage's arm at a time, each new arm over the earlier ones, every attached arm live and trainable under one pure Adam (lr 0.001), 800 steps per stage across the text of stages 1 to 4; the arms that run attached for stages 3 and 4 stayed nearly empty and were dropped. Then, with the arms of stages 1 and 2 held fixed and on, a new arm for stage 3 was attached and trained for 800 steps, and after it a new arm for stage 4 the same way. Each step gives a training arm 2 x 4096 bytes of its own stage's text with every attached arm on, and a quiet term at weight 2 on 2 rows that are not its text (one of web text, one of another member's stage text): the KL from the model with that arm switched off to the model with every arm on",
|
| 141 |
+
"title": "Stage 2: concepts",
|
| 142 |
+
"behavior": "kinds, properties and differences: what a thing is a kind of, what its kind can do, how two things differ",
|
| 143 |
+
"family": "stage",
|
| 144 |
+
"status": "a member of the group stages-1-4; trained always on with its partners, so alone is not its trained state",
|
| 145 |
+
"score": "alone: own stage text 0.721 -> 0.350 bpb; web text +0.0010 bpb",
|
| 146 |
+
"measured": {
|
| 147 |
+
"own_stage_bpb_arms_off": 0.7212,
|
| 148 |
+
"own_stage_bpb_group_on": 0.1123,
|
| 149 |
+
"own_stage_bpb_this_arm_alone": 0.3503,
|
| 150 |
+
"share_of_the_group_on_own_stage": -0.1283,
|
| 151 |
+
"share_of_the_group_on_own_stage_se": 0.0014,
|
| 152 |
+
"share_of_the_group_on_web_text": -0.0001,
|
| 153 |
+
"alone_on_web_text": 0.00105,
|
| 154 |
+
"share_of_the_group_on_partner_stages": {
|
| 155 |
+
"s1_perspective": -0.0043,
|
| 156 |
+
"s3_rules": -0.1064,
|
| 157 |
+
"s4_arith": -0.1204
|
| 158 |
+
},
|
| 159 |
+
"alone_on_partner_stages": {
|
| 160 |
+
"s1_perspective": -0.0273,
|
| 161 |
+
"s3_rules": -0.3108,
|
| 162 |
+
"s4_arith": -0.3537
|
| 163 |
+
},
|
| 164 |
+
"steps_trained": 2400,
|
| 165 |
+
"stage_items_own_form_accuracy": {
|
| 166 |
+
"arms_off": 0.298,
|
| 167 |
+
"group_on": 0.976,
|
| 168 |
+
"this_arm_alone": 0.964
|
| 169 |
+
},
|
| 170 |
+
"stage_items_new_form_accuracy": {
|
| 171 |
+
"arms_off": 0.678,
|
| 172 |
+
"group_on": 0.78,
|
| 173 |
+
"this_arm_alone": 0.72
|
| 174 |
+
},
|
| 175 |
+
"second_seed": {
|
| 176 |
+
"own_stage_bpb_arms_off": 0.7212,
|
| 177 |
+
"own_stage_bpb_group_on": 0.1212,
|
| 178 |
+
"own_stage_bpb_this_arm_alone": 0.3603,
|
| 179 |
+
"share_of_the_group_on_own_stage": -0.1212,
|
| 180 |
+
"share_of_the_group_on_own_stage_se": 0.0014,
|
| 181 |
+
"share_of_the_group_on_web_text": -0.00055,
|
| 182 |
+
"alone_on_web_text": 0.00086,
|
| 183 |
+
"share_of_the_group_on_partner_stages": {
|
| 184 |
+
"s1_perspective": -0.0041,
|
| 185 |
+
"s3_rules": -0.1005,
|
| 186 |
+
"s4_arith": -0.1138
|
| 187 |
+
},
|
| 188 |
+
"alone_on_partner_stages": {
|
| 189 |
+
"s1_perspective": -0.03,
|
| 190 |
+
"s3_rules": -0.3138,
|
| 191 |
+
"s4_arith": -0.3577
|
| 192 |
+
},
|
| 193 |
+
"steps_trained": 2400,
|
| 194 |
+
"stage_items_own_form_accuracy": {
|
| 195 |
+
"arms_off": 0.298,
|
| 196 |
+
"group_on": 0.952,
|
| 197 |
+
"this_arm_alone": 0.905
|
| 198 |
+
},
|
| 199 |
+
"stage_items_new_form_accuracy": {
|
| 200 |
+
"arms_off": 0.678,
|
| 201 |
+
"group_on": 0.754,
|
| 202 |
+
"this_arm_alone": 0.703
|
| 203 |
+
}
|
| 204 |
+
}
|
| 205 |
+
},
|
| 206 |
+
"examples": [
|
| 207 |
+
"A whale is a kind of mammal. Most mammals can swim. A whale is not a beetle; they differ in kind."
|
| 208 |
+
]
|
| 209 |
+
},
|
| 210 |
+
{
|
| 211 |
+
"id": "stages-1-4/s3_rules",
|
| 212 |
+
"name": "stages-1-4/s3_rules",
|
| 213 |
+
"group": "stages-1-4",
|
| 214 |
+
"base_model_id": "alephllm/mini-beatrix-3@step245674",
|
| 215 |
+
"step": 245674,
|
| 216 |
+
"params": 13692960,
|
| 217 |
+
"sites": 32,
|
| 218 |
+
"weights": [
|
| 219 |
+
"arms/stages-1-4/s3_rules.safetensors"
|
| 220 |
+
],
|
| 221 |
+
"mount": {
|
| 222 |
+
"type": "anchor"
|
| 223 |
+
},
|
| 224 |
+
"precision": "fp32",
|
| 225 |
+
"template": {
|
| 226 |
+
"frame": "raw",
|
| 227 |
+
"template": "raw stage text, continued"
|
| 228 |
+
},
|
| 229 |
+
"decode": {
|
| 230 |
+
"temperature": 0.0,
|
| 231 |
+
"top_p": 1.0,
|
| 232 |
+
"max_new": 120,
|
| 233 |
+
"precision": "fp32"
|
| 234 |
+
},
|
| 235 |
+
"source": {
|
| 236 |
+
"file": "gRA_close4_s3_rules.safetensors",
|
| 237 |
+
"sha256": "ab53da93e6550cc450b29c93dd09ff94f12f8f9a60dafd167507bb4c5503166b"
|
| 238 |
+
},
|
| 239 |
+
"recipe": "trained as an always-on group on the frozen final core, in two steps. First the arms of stages 1 and 2: a run that attached one stage's arm at a time, each new arm over the earlier ones, every attached arm live and trainable under one pure Adam (lr 0.001), 800 steps per stage across the text of stages 1 to 4; the arms that run attached for stages 3 and 4 stayed nearly empty and were dropped. Then, with the arms of stages 1 and 2 held fixed and on, a new arm for stage 3 was attached and trained for 800 steps, and after it a new arm for stage 4 the same way. Each step gives a training arm 2 x 4096 bytes of its own stage's text with every attached arm on, and a quiet term at weight 2 on 2 rows that are not its text (one of web text, one of another member's stage text): the KL from the model with that arm switched off to the model with every arm on",
|
| 240 |
+
"title": "Stage 3: rule chains",
|
| 241 |
+
"behavior": "if-then rules over made-up words, followed step by step to what follows",
|
| 242 |
+
"family": "stage",
|
| 243 |
+
"status": "a member of the group stages-1-4; trained always on with its partners, so alone is not its trained state",
|
| 244 |
+
"score": "alone: own stage text 0.930 -> 0.824 bpb; web text +0.0005 bpb",
|
| 245 |
+
"measured": {
|
| 246 |
+
"own_stage_bpb_arms_off": 0.9296,
|
| 247 |
+
"own_stage_bpb_group_on": 0.2236,
|
| 248 |
+
"own_stage_bpb_this_arm_alone": 0.8236,
|
| 249 |
+
"share_of_the_group_on_own_stage": -0.0952,
|
| 250 |
+
"share_of_the_group_on_own_stage_se": 0.0009,
|
| 251 |
+
"share_of_the_group_on_web_text": 0.0004,
|
| 252 |
+
"alone_on_web_text": 0.00053,
|
| 253 |
+
"share_of_the_group_on_partner_stages": {
|
| 254 |
+
"s1_perspective": 0.0001,
|
| 255 |
+
"s2_concept": 0.0007,
|
| 256 |
+
"s4_arith": 0.0
|
| 257 |
+
},
|
| 258 |
+
"alone_on_partner_stages": {
|
| 259 |
+
"s1_perspective": -0.0003,
|
| 260 |
+
"s2_concept": 0.0031,
|
| 261 |
+
"s4_arith": 0.0001
|
| 262 |
+
},
|
| 263 |
+
"steps_trained": 800,
|
| 264 |
+
"stage_items_own_form_accuracy": {
|
| 265 |
+
"arms_off": 0.155,
|
| 266 |
+
"group_on": 0.905,
|
| 267 |
+
"this_arm_alone": 0.32
|
| 268 |
+
},
|
| 269 |
+
"stage_items_new_form_accuracy": {
|
| 270 |
+
"arms_off": 0.07,
|
| 271 |
+
"group_on": 0.3,
|
| 272 |
+
"this_arm_alone": 0.08
|
| 273 |
+
},
|
| 274 |
+
"second_seed": {
|
| 275 |
+
"own_stage_bpb_arms_off": 0.9296,
|
| 276 |
+
"own_stage_bpb_group_on": 0.2222,
|
| 277 |
+
"own_stage_bpb_this_arm_alone": 0.8702,
|
| 278 |
+
"share_of_the_group_on_own_stage": -0.0958,
|
| 279 |
+
"share_of_the_group_on_own_stage_se": 0.0013,
|
| 280 |
+
"share_of_the_group_on_web_text": 0.00038,
|
| 281 |
+
"alone_on_web_text": 0.00034,
|
| 282 |
+
"share_of_the_group_on_partner_stages": {
|
| 283 |
+
"s1_perspective": 0.0001,
|
| 284 |
+
"s2_concept": -0.0006,
|
| 285 |
+
"s4_arith": -0.0003
|
| 286 |
+
},
|
| 287 |
+
"alone_on_partner_stages": {
|
| 288 |
+
"s1_perspective": -0.0003,
|
| 289 |
+
"s2_concept": 0.0016,
|
| 290 |
+
"s4_arith": -0.0047
|
| 291 |
+
},
|
| 292 |
+
"steps_trained": 800,
|
| 293 |
+
"stage_items_own_form_accuracy": {
|
| 294 |
+
"arms_off": 0.155,
|
| 295 |
+
"group_on": 0.885,
|
| 296 |
+
"this_arm_alone": 0.335
|
| 297 |
+
},
|
| 298 |
+
"stage_items_new_form_accuracy": {
|
| 299 |
+
"arms_off": 0.07,
|
| 300 |
+
"group_on": 0.33,
|
| 301 |
+
"this_arm_alone": 0.1
|
| 302 |
+
}
|
| 303 |
+
}
|
| 304 |
+
},
|
| 305 |
+
"examples": [
|
| 306 |
+
"If someone is hoream, then they are hortil. If someone is hortil, then they are babig. If someone is babig, then they are banng. Vin is hoream. So Vin is hortil. So Vin is babig. So Vin is banng."
|
| 307 |
+
]
|
| 308 |
+
},
|
| 309 |
+
{
|
| 310 |
+
"id": "stages-1-4/s4_arith",
|
| 311 |
+
"name": "stages-1-4/s4_arith",
|
| 312 |
+
"group": "stages-1-4",
|
| 313 |
+
"base_model_id": "alephllm/mini-beatrix-3@step245674",
|
| 314 |
+
"step": 245674,
|
| 315 |
+
"params": 13692960,
|
| 316 |
+
"sites": 32,
|
| 317 |
+
"weights": [
|
| 318 |
+
"arms/stages-1-4/s4_arith.safetensors"
|
| 319 |
+
],
|
| 320 |
+
"mount": {
|
| 321 |
+
"type": "anchor"
|
| 322 |
+
},
|
| 323 |
+
"precision": "fp32",
|
| 324 |
+
"template": {
|
| 325 |
+
"frame": "raw",
|
| 326 |
+
"template": "raw stage text, continued"
|
| 327 |
+
},
|
| 328 |
+
"decode": {
|
| 329 |
+
"temperature": 0.0,
|
| 330 |
+
"top_p": 1.0,
|
| 331 |
+
"max_new": 120,
|
| 332 |
+
"precision": "fp32"
|
| 333 |
+
},
|
| 334 |
+
"source": {
|
| 335 |
+
"file": "gRA_close4_s4_arith.safetensors",
|
| 336 |
+
"sha256": "c721c34516322ee900f7c49a0de295512113ed49787448b760e52736df049abb"
|
| 337 |
+
},
|
| 338 |
+
"recipe": "trained as an always-on group on the frozen final core, in two steps. First the arms of stages 1 and 2: a run that attached one stage's arm at a time, each new arm over the earlier ones, every attached arm live and trainable under one pure Adam (lr 0.001), 800 steps per stage across the text of stages 1 to 4; the arms that run attached for stages 3 and 4 stayed nearly empty and were dropped. Then, with the arms of stages 1 and 2 held fixed and on, a new arm for stage 3 was attached and trained for 800 steps, and after it a new arm for stage 4 the same way. Each step gives a training arm 2 x 4096 bytes of its own stage's text with every attached arm on, and a quiet term at weight 2 on 2 rows that are not its text (one of web text, one of another member's stage text): the KL from the model with that arm switched off to the model with every arm on",
|
| 339 |
+
"title": "Stage 4: arithmetic",
|
| 340 |
+
"behavior": "small arithmetic worked out in text",
|
| 341 |
+
"family": "stage",
|
| 342 |
+
"status": "a member of the group stages-1-4; trained always on with its partners, so alone is not its trained state",
|
| 343 |
+
"score": "alone: own stage text 1.234 -> 1.127 bpb; web text +0.0001 bpb",
|
| 344 |
+
"measured": {
|
| 345 |
+
"own_stage_bpb_arms_off": 1.2339,
|
| 346 |
+
"own_stage_bpb_group_on": 0.3384,
|
| 347 |
+
"own_stage_bpb_this_arm_alone": 1.1267,
|
| 348 |
+
"share_of_the_group_on_own_stage": -0.1477,
|
| 349 |
+
"share_of_the_group_on_own_stage_se": 0.0022,
|
| 350 |
+
"share_of_the_group_on_web_text": 0.0001,
|
| 351 |
+
"alone_on_web_text": 0.00011,
|
| 352 |
+
"share_of_the_group_on_partner_stages": {
|
| 353 |
+
"s1_perspective": -0.0001,
|
| 354 |
+
"s2_concept": -0.0006,
|
| 355 |
+
"s3_rules": 0.0
|
| 356 |
+
},
|
| 357 |
+
"alone_on_partner_stages": {
|
| 358 |
+
"s1_perspective": -0.0009,
|
| 359 |
+
"s2_concept": -0.0004,
|
| 360 |
+
"s3_rules": -0.0016
|
| 361 |
+
},
|
| 362 |
+
"steps_trained": 800,
|
| 363 |
+
"stage_items_own_form_accuracy": {
|
| 364 |
+
"arms_off": 0.344,
|
| 365 |
+
"group_on": 0.613,
|
| 366 |
+
"this_arm_alone": 0.381
|
| 367 |
+
},
|
| 368 |
+
"stage_items_new_form_accuracy": {
|
| 369 |
+
"arms_off": 0.242,
|
| 370 |
+
"group_on": 0.237,
|
| 371 |
+
"this_arm_alone": 0.246
|
| 372 |
+
},
|
| 373 |
+
"second_seed": {
|
| 374 |
+
"own_stage_bpb_arms_off": 1.2339,
|
| 375 |
+
"own_stage_bpb_group_on": 0.3379,
|
| 376 |
+
"own_stage_bpb_this_arm_alone": 1.155,
|
| 377 |
+
"share_of_the_group_on_own_stage": -0.1737,
|
| 378 |
+
"share_of_the_group_on_own_stage_se": 0.002,
|
| 379 |
+
"share_of_the_group_on_web_text": -1e-05,
|
| 380 |
+
"alone_on_web_text": 6e-05,
|
| 381 |
+
"share_of_the_group_on_partner_stages": {
|
| 382 |
+
"s1_perspective": 0.0,
|
| 383 |
+
"s2_concept": -0.0,
|
| 384 |
+
"s3_rules": 0.0
|
| 385 |
+
},
|
| 386 |
+
"alone_on_partner_stages": {
|
| 387 |
+
"s1_perspective": -0.0013,
|
| 388 |
+
"s2_concept": -0.0015,
|
| 389 |
+
"s3_rules": 0.0005
|
| 390 |
+
},
|
| 391 |
+
"steps_trained": 800,
|
| 392 |
+
"stage_items_own_form_accuracy": {
|
| 393 |
+
"arms_off": 0.344,
|
| 394 |
+
"group_on": 0.594,
|
| 395 |
+
"this_arm_alone": 0.412
|
| 396 |
+
},
|
| 397 |
+
"stage_items_new_form_accuracy": {
|
| 398 |
+
"arms_off": 0.242,
|
| 399 |
+
"group_on": 0.271,
|
| 400 |
+
"this_arm_alone": 0.237
|
| 401 |
+
}
|
| 402 |
+
}
|
| 403 |
+
},
|
| 404 |
+
"examples": [
|
| 405 |
+
"Count by 3s: 1 3s make 3; 2 3s make 6; 3 3s make 9; 4 3s make 12; 5 3s make 15. So 3 times 5 is 15."
|
| 406 |
+
]
|
| 407 |
+
},
|
| 408 |
+
{
|
| 409 |
+
"id": "stages-1-4",
|
| 410 |
+
"name": "stages-1-4",
|
| 411 |
+
"base_model_id": "alephllm/mini-beatrix-3@step245674",
|
| 412 |
+
"step": 245674,
|
| 413 |
+
"params": 54771840,
|
| 414 |
+
"sites": 32,
|
| 415 |
+
"weights": [
|
| 416 |
+
"arms/stages-1-4/s1_perspective.safetensors",
|
| 417 |
+
"arms/stages-1-4/s2_concept.safetensors",
|
| 418 |
+
"arms/stages-1-4/s3_rules.safetensors",
|
| 419 |
+
"arms/stages-1-4/s4_arith.safetensors"
|
| 420 |
+
],
|
| 421 |
+
"mount": {
|
| 422 |
+
"type": "stack",
|
| 423 |
+
"members": [
|
| 424 |
+
"stages-1-4/s1_perspective",
|
| 425 |
+
"stages-1-4/s2_concept",
|
| 426 |
+
"stages-1-4/s3_rules",
|
| 427 |
+
"stages-1-4/s4_arith"
|
| 428 |
+
]
|
| 429 |
+
},
|
| 430 |
+
"precision": "fp32",
|
| 431 |
+
"template": {
|
| 432 |
+
"frame": "raw",
|
| 433 |
+
"template": "raw text, continued"
|
| 434 |
+
},
|
| 435 |
+
"decode": {
|
| 436 |
+
"temperature": 0.0,
|
| 437 |
+
"top_p": 1.0,
|
| 438 |
+
"max_new": 120,
|
| 439 |
+
"precision": "fp32"
|
| 440 |
+
},
|
| 441 |
+
"recipe": "trained as an always-on group on the frozen final core, in two steps. First the arms of stages 1 and 2: a run that attached one stage's arm at a time, each new arm over the earlier ones, every attached arm live and trainable under one pure Adam (lr 0.001), 800 steps per stage across the text of stages 1 to 4; the arms that run attached for stages 3 and 4 stayed nearly empty and were dropped. Then, with the arms of stages 1 and 2 held fixed and on, a new arm for stage 3 was attached and trained for 800 steps, and after it a new arm for stage 4 the same way. Each step gives a training arm 2 x 4096 bytes of its own stage's text with every attached arm on, and a quiet term at weight 2 on 2 rows that are not its text (one of web text, one of another member's stage text): the KL from the model with that arm switched off to the model with every arm on",
|
| 442 |
+
"title": "Stages 1 to 4, on together",
|
| 443 |
+
"behavior": "the arms of stage 1: perspective, stage 2: concepts, stage 3: rule chains, stage 4: arithmetic, always on together, as they were trained",
|
| 444 |
+
"family": "group",
|
| 445 |
+
"status": "two seeds",
|
| 446 |
+
"score": "stage text, arms off -> on: s1_perspective 0.554 -> 0.048; s2_concept 0.721 -> 0.112; s3_rules 0.930 -> 0.224; s4_arith 1.234 -> 0.338; web text +0.0017 bpb",
|
| 447 |
+
"measured": {
|
| 448 |
+
"stage_text_bpb": {
|
| 449 |
+
"s1_perspective": {
|
| 450 |
+
"arms_off": 0.5538,
|
| 451 |
+
"group_on": 0.0482,
|
| 452 |
+
"difference": -0.5057,
|
| 453 |
+
"difference_se": 0.0048
|
| 454 |
+
},
|
| 455 |
+
"s2_concept": {
|
| 456 |
+
"arms_off": 0.7212,
|
| 457 |
+
"group_on": 0.1123,
|
| 458 |
+
"difference": -0.6089,
|
| 459 |
+
"difference_se": 0.0038
|
| 460 |
+
},
|
| 461 |
+
"s3_rules": {
|
| 462 |
+
"arms_off": 0.9296,
|
| 463 |
+
"group_on": 0.2236,
|
| 464 |
+
"difference": -0.706,
|
| 465 |
+
"difference_se": 0.0052
|
| 466 |
+
},
|
| 467 |
+
"s4_arith": {
|
| 468 |
+
"arms_off": 1.2339,
|
| 469 |
+
"group_on": 0.3384,
|
| 470 |
+
"difference": -0.8955,
|
| 471 |
+
"difference_se": 0.0059
|
| 472 |
+
}
|
| 473 |
+
},
|
| 474 |
+
"web_text_difference": 0.00171,
|
| 475 |
+
"web_text_difference_se": 0.00035,
|
| 476 |
+
"steps_trained": {
|
| 477 |
+
"s1_perspective": 3200,
|
| 478 |
+
"s2_concept": 2400,
|
| 479 |
+
"s3_rules": 800,
|
| 480 |
+
"s4_arith": 800
|
| 481 |
+
},
|
| 482 |
+
"probe_suite_mean": {
|
| 483 |
+
"arms_off": 0.411,
|
| 484 |
+
"group_on": 0.419
|
| 485 |
+
},
|
| 486 |
+
"second_seed": {
|
| 487 |
+
"stage_text_bpb": {
|
| 488 |
+
"s1_perspective": {
|
| 489 |
+
"arms_off": 0.5538,
|
| 490 |
+
"group_on": 0.0479,
|
| 491 |
+
"difference": -0.506,
|
| 492 |
+
"difference_se": 0.0048
|
| 493 |
+
},
|
| 494 |
+
"s2_concept": {
|
| 495 |
+
"arms_off": 0.7212,
|
| 496 |
+
"group_on": 0.1212,
|
| 497 |
+
"difference": -0.6,
|
| 498 |
+
"difference_se": 0.0037
|
| 499 |
+
},
|
| 500 |
+
"s3_rules": {
|
| 501 |
+
"arms_off": 0.9296,
|
| 502 |
+
"group_on": 0.2222,
|
| 503 |
+
"difference": -0.7074,
|
| 504 |
+
"difference_se": 0.0054
|
| 505 |
+
},
|
| 506 |
+
"s4_arith": {
|
| 507 |
+
"arms_off": 1.2339,
|
| 508 |
+
"group_on": 0.3379,
|
| 509 |
+
"difference": -0.896,
|
| 510 |
+
"difference_se": 0.0059
|
| 511 |
+
}
|
| 512 |
+
},
|
| 513 |
+
"web_text_difference": 0.0011,
|
| 514 |
+
"web_text_difference_se": 0.00039,
|
| 515 |
+
"steps_trained": {
|
| 516 |
+
"s1_perspective": 3200,
|
| 517 |
+
"s2_concept": 2400,
|
| 518 |
+
"s3_rules": 800,
|
| 519 |
+
"s4_arith": 800
|
| 520 |
+
},
|
| 521 |
+
"probe_suite_mean": {
|
| 522 |
+
"arms_off": 0.411,
|
| 523 |
+
"group_on": 0.415
|
| 524 |
+
}
|
| 525 |
+
}
|
| 526 |
+
},
|
| 527 |
+
"examples": []
|
| 528 |
+
},
|
| 529 |
+
{
|
| 530 |
+
"id": "solo/s1_perspective",
|
| 531 |
+
"name": "solo/s1_perspective",
|
| 532 |
+
"group": null,
|
| 533 |
+
"base_model_id": "alephllm/mini-beatrix-3@step245674",
|
| 534 |
+
"step": 245674,
|
| 535 |
+
"params": 13692960,
|
| 536 |
+
"sites": 32,
|
| 537 |
+
"weights": [
|
| 538 |
+
"arms/solo/s1_perspective.safetensors"
|
| 539 |
+
],
|
| 540 |
+
"mount": {
|
| 541 |
+
"type": "anchor"
|
| 542 |
+
},
|
| 543 |
+
"precision": "fp32",
|
| 544 |
+
"template": {
|
| 545 |
+
"frame": "raw",
|
| 546 |
+
"template": "raw stage text, continued"
|
| 547 |
+
},
|
| 548 |
+
"decode": {
|
| 549 |
+
"temperature": 0.0,
|
| 550 |
+
"top_p": 1.0,
|
| 551 |
+
"max_new": 120,
|
| 552 |
+
"precision": "fp32"
|
| 553 |
+
},
|
| 554 |
+
"source": {
|
| 555 |
+
"file": "s1_perspective_fresh_seed1_d20261005_step800.safetensors",
|
| 556 |
+
"sha256": "ff72dd5a8061926b56e09c6da765c1d1ee44cbbd3d4e5e2c30793e04b369532a"
|
| 557 |
+
},
|
| 558 |
+
"recipe": "trained alone on the frozen final core: 800 steps of 2 x 4096 bytes of the stage's own text, pure Adam (lr 0.001), with a quiet term at weight 2 on held-out web text (the KL from the model with the arm off to the model with it on, one chunk beside every task chunk)",
|
| 559 |
+
"title": "Stage 1: perspective (solo)",
|
| 560 |
+
"behavior": "one small event retold from each side: seen from a person, said to them, told about them",
|
| 561 |
+
"family": "solo",
|
| 562 |
+
"status": "two seeds; trained alone: mount one solo arm at a time",
|
| 563 |
+
"score": "own stage text 0.554 -> 0.081 bpb; web text +0.0002 bpb",
|
| 564 |
+
"measured": {
|
| 565 |
+
"steps": 800,
|
| 566 |
+
"own_stage_bpb_arms_off": 0.5538,
|
| 567 |
+
"own_stage_bpb_arm_on": 0.0809,
|
| 568 |
+
"own_stage_difference": -0.4729,
|
| 569 |
+
"own_stage_difference_se": 0.0038,
|
| 570 |
+
"web_text_difference": 0.00018,
|
| 571 |
+
"web_text_difference_se": 0.00019,
|
| 572 |
+
"stage_items_own_form_accuracy": {
|
| 573 |
+
"arms_off": 0.55,
|
| 574 |
+
"arm_on": 0.75
|
| 575 |
+
},
|
| 576 |
+
"stage_items_new_form_accuracy": {
|
| 577 |
+
"arms_off": 0.463,
|
| 578 |
+
"arm_on": 0.412
|
| 579 |
+
},
|
| 580 |
+
"probe_suite_mean": {
|
| 581 |
+
"arms_off": 0.411,
|
| 582 |
+
"arm_on": 0.381
|
| 583 |
+
},
|
| 584 |
+
"second_seed": {
|
| 585 |
+
"steps": 800,
|
| 586 |
+
"own_stage_bpb_arms_off": 0.5538,
|
| 587 |
+
"own_stage_bpb_arm_on": 0.0802,
|
| 588 |
+
"own_stage_difference": -0.4737,
|
| 589 |
+
"own_stage_difference_se": 0.0038,
|
| 590 |
+
"web_text_difference": 0.00036,
|
| 591 |
+
"web_text_difference_se": 0.00016,
|
| 592 |
+
"stage_items_own_form_accuracy": {
|
| 593 |
+
"arms_off": 0.55,
|
| 594 |
+
"arm_on": 0.7
|
| 595 |
+
},
|
| 596 |
+
"stage_items_new_form_accuracy": {
|
| 597 |
+
"arms_off": 0.463,
|
| 598 |
+
"arm_on": 0.4
|
| 599 |
+
},
|
| 600 |
+
"probe_suite_mean": {
|
| 601 |
+
"arms_off": 0.411,
|
| 602 |
+
"arm_on": 0.385
|
| 603 |
+
}
|
| 604 |
+
}
|
| 605 |
+
},
|
| 606 |
+
"examples": [
|
| 607 |
+
"Rui dropped the acorn near the box. Seen from Rui: I dropped the acorn near the box. Said to Rui: you dropped the acorn near the box. Told about Rui: Rui dropped the acorn near the box. Three ways of saying, one thing that happened."
|
| 608 |
+
]
|
| 609 |
+
},
|
| 610 |
+
{
|
| 611 |
+
"id": "solo/s2_concept",
|
| 612 |
+
"name": "solo/s2_concept",
|
| 613 |
+
"group": null,
|
| 614 |
+
"base_model_id": "alephllm/mini-beatrix-3@step245674",
|
| 615 |
+
"step": 245674,
|
| 616 |
+
"params": 13692960,
|
| 617 |
+
"sites": 32,
|
| 618 |
+
"weights": [
|
| 619 |
+
"arms/solo/s2_concept.safetensors"
|
| 620 |
+
],
|
| 621 |
+
"mount": {
|
| 622 |
+
"type": "anchor"
|
| 623 |
+
},
|
| 624 |
+
"precision": "fp32",
|
| 625 |
+
"template": {
|
| 626 |
+
"frame": "raw",
|
| 627 |
+
"template": "raw stage text, continued"
|
| 628 |
+
},
|
| 629 |
+
"decode": {
|
| 630 |
+
"temperature": 0.0,
|
| 631 |
+
"top_p": 1.0,
|
| 632 |
+
"max_new": 120,
|
| 633 |
+
"precision": "fp32"
|
| 634 |
+
},
|
| 635 |
+
"source": {
|
| 636 |
+
"file": "s2_concept_fresh_seed2_d20261005_step800.safetensors",
|
| 637 |
+
"sha256": "dc76f7c7d713b34d23b553adbb0b6302ec1d1080d7ffe8cc9461c5f5e6a03938"
|
| 638 |
+
},
|
| 639 |
+
"recipe": "trained alone on the frozen final core: 800 steps of 2 x 4096 bytes of the stage's own text, pure Adam (lr 0.001), with a quiet term at weight 2 on held-out web text (the KL from the model with the arm off to the model with it on, one chunk beside every task chunk)",
|
| 640 |
+
"title": "Stage 2: concepts (solo)",
|
| 641 |
+
"behavior": "kinds, properties and differences: what a thing is a kind of, what its kind can do, how two things differ",
|
| 642 |
+
"family": "solo",
|
| 643 |
+
"status": "two seeds; trained alone: mount one solo arm at a time",
|
| 644 |
+
"score": "own stage text 0.721 -> 0.120 bpb; web text +0.0003 bpb",
|
| 645 |
+
"measured": {
|
| 646 |
+
"steps": 800,
|
| 647 |
+
"own_stage_bpb_arms_off": 0.7212,
|
| 648 |
+
"own_stage_bpb_arm_on": 0.1204,
|
| 649 |
+
"own_stage_difference": -0.6008,
|
| 650 |
+
"own_stage_difference_se": 0.0034,
|
| 651 |
+
"web_text_difference": 0.00029,
|
| 652 |
+
"web_text_difference_se": 0.00016,
|
| 653 |
+
"stage_items_own_form_accuracy": {
|
| 654 |
+
"arms_off": 0.298,
|
| 655 |
+
"arm_on": 0.845
|
| 656 |
+
},
|
| 657 |
+
"stage_items_new_form_accuracy": {
|
| 658 |
+
"arms_off": 0.678,
|
| 659 |
+
"arm_on": 0.729
|
| 660 |
+
},
|
| 661 |
+
"probe_suite_mean": {
|
| 662 |
+
"arms_off": 0.411,
|
| 663 |
+
"arm_on": 0.404
|
| 664 |
+
},
|
| 665 |
+
"second_seed": {
|
| 666 |
+
"steps": 800,
|
| 667 |
+
"own_stage_bpb_arms_off": 0.7212,
|
| 668 |
+
"own_stage_bpb_arm_on": 0.1199,
|
| 669 |
+
"own_stage_difference": -0.6013,
|
| 670 |
+
"own_stage_difference_se": 0.0035,
|
| 671 |
+
"web_text_difference": 0.00043,
|
| 672 |
+
"web_text_difference_se": 0.00018,
|
| 673 |
+
"stage_items_own_form_accuracy": {
|
| 674 |
+
"arms_off": 0.298,
|
| 675 |
+
"arm_on": 0.881
|
| 676 |
+
},
|
| 677 |
+
"stage_items_new_form_accuracy": {
|
| 678 |
+
"arms_off": 0.678,
|
| 679 |
+
"arm_on": 0.695
|
| 680 |
+
},
|
| 681 |
+
"probe_suite_mean": {
|
| 682 |
+
"arms_off": 0.411,
|
| 683 |
+
"arm_on": 0.389
|
| 684 |
+
}
|
| 685 |
+
}
|
| 686 |
+
},
|
| 687 |
+
"examples": [
|
| 688 |
+
"A whale is a kind of mammal. Most mammals can swim. A whale is not a beetle; they differ in kind."
|
| 689 |
+
]
|
| 690 |
+
},
|
| 691 |
+
{
|
| 692 |
+
"id": "solo/s3_rules",
|
| 693 |
+
"name": "solo/s3_rules",
|
| 694 |
+
"group": null,
|
| 695 |
+
"base_model_id": "alephllm/mini-beatrix-3@step245674",
|
| 696 |
+
"step": 245674,
|
| 697 |
+
"params": 13692960,
|
| 698 |
+
"sites": 32,
|
| 699 |
+
"weights": [
|
| 700 |
+
"arms/solo/s3_rules.safetensors"
|
| 701 |
+
],
|
| 702 |
+
"mount": {
|
| 703 |
+
"type": "anchor"
|
| 704 |
+
},
|
| 705 |
+
"precision": "fp32",
|
| 706 |
+
"template": {
|
| 707 |
+
"frame": "raw",
|
| 708 |
+
"template": "raw stage text, continued"
|
| 709 |
+
},
|
| 710 |
+
"decode": {
|
| 711 |
+
"temperature": 0.0,
|
| 712 |
+
"top_p": 1.0,
|
| 713 |
+
"max_new": 120,
|
| 714 |
+
"precision": "fp32"
|
| 715 |
+
},
|
| 716 |
+
"source": {
|
| 717 |
+
"file": "s3_rules_fresh_seed3_d20261005_step800.safetensors",
|
| 718 |
+
"sha256": "11bc779e8853bab9ecb6cdff3419673e39a59c55fc91e8842aaef6ffc23c3b47"
|
| 719 |
+
},
|
| 720 |
+
"recipe": "trained alone on the frozen final core: 800 steps of 2 x 4096 bytes of the stage's own text, pure Adam (lr 0.001), with a quiet term at weight 2 on held-out web text (the KL from the model with the arm off to the model with it on, one chunk beside every task chunk)",
|
| 721 |
+
"title": "Stage 3: rule chains (solo)",
|
| 722 |
+
"behavior": "if-then rules over made-up words, followed step by step to what follows",
|
| 723 |
+
"family": "solo",
|
| 724 |
+
"status": "two seeds; trained alone: mount one solo arm at a time",
|
| 725 |
+
"score": "own stage text 0.930 -> 0.372 bpb; web text +0.0009 bpb",
|
| 726 |
+
"measured": {
|
| 727 |
+
"steps": 800,
|
| 728 |
+
"own_stage_bpb_arms_off": 0.9296,
|
| 729 |
+
"own_stage_bpb_arm_on": 0.3724,
|
| 730 |
+
"own_stage_difference": -0.5572,
|
| 731 |
+
"own_stage_difference_se": 0.0054,
|
| 732 |
+
"web_text_difference": 0.00095,
|
| 733 |
+
"web_text_difference_se": 0.00011,
|
| 734 |
+
"stage_items_own_form_accuracy": {
|
| 735 |
+
"arms_off": 0.155,
|
| 736 |
+
"arm_on": 0.6
|
| 737 |
+
},
|
| 738 |
+
"stage_items_new_form_accuracy": {
|
| 739 |
+
"arms_off": 0.07,
|
| 740 |
+
"arm_on": 0.12
|
| 741 |
+
},
|
| 742 |
+
"probe_suite_mean": {
|
| 743 |
+
"arms_off": 0.411,
|
| 744 |
+
"arm_on": 0.407
|
| 745 |
+
},
|
| 746 |
+
"second_seed": {
|
| 747 |
+
"steps": 800,
|
| 748 |
+
"own_stage_bpb_arms_off": 0.9296,
|
| 749 |
+
"own_stage_bpb_arm_on": 0.3737,
|
| 750 |
+
"own_stage_difference": -0.5559,
|
| 751 |
+
"own_stage_difference_se": 0.0054,
|
| 752 |
+
"web_text_difference": 0.00079,
|
| 753 |
+
"web_text_difference_se": 0.00015,
|
| 754 |
+
"stage_items_own_form_accuracy": {
|
| 755 |
+
"arms_off": 0.155,
|
| 756 |
+
"arm_on": 0.58
|
| 757 |
+
},
|
| 758 |
+
"stage_items_new_form_accuracy": {
|
| 759 |
+
"arms_off": 0.07,
|
| 760 |
+
"arm_on": 0.12
|
| 761 |
+
},
|
| 762 |
+
"probe_suite_mean": {
|
| 763 |
+
"arms_off": 0.411,
|
| 764 |
+
"arm_on": 0.4
|
| 765 |
+
}
|
| 766 |
+
}
|
| 767 |
+
},
|
| 768 |
+
"examples": [
|
| 769 |
+
"If someone is hoream, then they are hortil. If someone is hortil, then they are babig. If someone is babig, then they are banng. Vin is hoream. So Vin is hortil. So Vin is babig. So Vin is banng."
|
| 770 |
+
]
|
| 771 |
+
},
|
| 772 |
+
{
|
| 773 |
+
"id": "solo/s4_arith",
|
| 774 |
+
"name": "solo/s4_arith",
|
| 775 |
+
"group": null,
|
| 776 |
+
"base_model_id": "alephllm/mini-beatrix-3@step245674",
|
| 777 |
+
"step": 245674,
|
| 778 |
+
"params": 13692960,
|
| 779 |
+
"sites": 32,
|
| 780 |
+
"weights": [
|
| 781 |
+
"arms/solo/s4_arith.safetensors"
|
| 782 |
+
],
|
| 783 |
+
"mount": {
|
| 784 |
+
"type": "anchor"
|
| 785 |
+
},
|
| 786 |
+
"precision": "fp32",
|
| 787 |
+
"template": {
|
| 788 |
+
"frame": "raw",
|
| 789 |
+
"template": "raw stage text, continued"
|
| 790 |
+
},
|
| 791 |
+
"decode": {
|
| 792 |
+
"temperature": 0.0,
|
| 793 |
+
"top_p": 1.0,
|
| 794 |
+
"max_new": 120,
|
| 795 |
+
"precision": "fp32"
|
| 796 |
+
},
|
| 797 |
+
"source": {
|
| 798 |
+
"file": "s4_arith_fresh_seed4_d20261005_step800.safetensors",
|
| 799 |
+
"sha256": "90774072859c74888b7e83298867cddbb7f12c7c9b9e9206a417c08545a6244d"
|
| 800 |
+
},
|
| 801 |
+
"recipe": "trained alone on the frozen final core: 800 steps of 2 x 4096 bytes of the stage's own text, pure Adam (lr 0.001), with a quiet term at weight 2 on held-out web text (the KL from the model with the arm off to the model with it on, one chunk beside every task chunk)",
|
| 802 |
+
"title": "Stage 4: arithmetic (solo)",
|
| 803 |
+
"behavior": "small arithmetic worked out in text",
|
| 804 |
+
"family": "solo",
|
| 805 |
+
"status": "two seeds; trained alone: mount one solo arm at a time",
|
| 806 |
+
"score": "own stage text 1.234 -> 0.389 bpb; web text +0.0007 bpb",
|
| 807 |
+
"measured": {
|
| 808 |
+
"steps": 800,
|
| 809 |
+
"own_stage_bpb_arms_off": 1.2339,
|
| 810 |
+
"own_stage_bpb_arm_on": 0.3889,
|
| 811 |
+
"own_stage_difference": -0.845,
|
| 812 |
+
"own_stage_difference_se": 0.0048,
|
| 813 |
+
"web_text_difference": 0.00069,
|
| 814 |
+
"web_text_difference_se": 0.00023,
|
| 815 |
+
"stage_items_own_form_accuracy": {
|
| 816 |
+
"arms_off": 0.344,
|
| 817 |
+
"arm_on": 0.487
|
| 818 |
+
},
|
| 819 |
+
"stage_items_new_form_accuracy": {
|
| 820 |
+
"arms_off": 0.242,
|
| 821 |
+
"arm_on": 0.242
|
| 822 |
+
},
|
| 823 |
+
"probe_suite_mean": {
|
| 824 |
+
"arms_off": 0.411,
|
| 825 |
+
"arm_on": 0.389
|
| 826 |
+
},
|
| 827 |
+
"second_seed": {
|
| 828 |
+
"steps": 800,
|
| 829 |
+
"own_stage_bpb_arms_off": 1.2339,
|
| 830 |
+
"own_stage_bpb_arm_on": 0.3884,
|
| 831 |
+
"own_stage_difference": -0.8455,
|
| 832 |
+
"own_stage_difference_se": 0.005,
|
| 833 |
+
"web_text_difference": 0.00056,
|
| 834 |
+
"web_text_difference_se": 0.00024,
|
| 835 |
+
"stage_items_own_form_accuracy": {
|
| 836 |
+
"arms_off": 0.344,
|
| 837 |
+
"arm_on": 0.487
|
| 838 |
+
},
|
| 839 |
+
"stage_items_new_form_accuracy": {
|
| 840 |
+
"arms_off": 0.242,
|
| 841 |
+
"arm_on": 0.208
|
| 842 |
+
},
|
| 843 |
+
"probe_suite_mean": {
|
| 844 |
+
"arms_off": 0.411,
|
| 845 |
+
"arm_on": 0.4
|
| 846 |
+
}
|
| 847 |
+
}
|
| 848 |
+
},
|
| 849 |
+
"examples": [
|
| 850 |
+
"Count by 3s: 1 3s make 3; 2 3s make 6; 3 3s make 9; 4 3s make 12; 5 3s make 15. So 3 times 5 is 15."
|
| 851 |
+
]
|
| 852 |
+
}
|
| 853 |
+
]
|
| 854 |
+
}
|
arms/solo/s1_perspective.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:3c9d862b4b6832ab8fc6bccf177c87882a1b0ab78e91b2c01a952f7b0f9328cd
|
| 3 |
+
size 54817760
|
arms/solo/s2_concept.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8a90104ddeace651612a0bf0015250cefbd104a29ece1cf6c064d4854cafa52d
|
| 3 |
+
size 54817632
|
arms/solo/s3_rules.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:d26218539c5f1328f30a2ce40c4d5ccc5cf01a64d03c50469183ad64c565553c
|
| 3 |
+
size 54817680
|
arms/solo/s4_arith.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:358c996148f9bb8c785f6fc2ec565ca5f08ba2cb252cb85b3ae388655e5626ed
|
| 3 |
+
size 54817552
|
arms/stages-1-4/s1_perspective.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:656d03203726ea77aa758a636424db6f21a39e379c34697fa10debbe017c48f9
|
| 3 |
+
size 54819000
|
arms/stages-1-4/s2_concept.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:436ce53452aa7264ef3fb5fea28b6a7077b843c4e65f1538932114b99fe380d5
|
| 3 |
+
size 54818880
|
arms/stages-1-4/s3_rules.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c76ee31256d3c36597b7791ab0deab31507d625ff68ccdac10f28a5c4a007a07
|
| 3 |
+
size 54818928
|
arms/stages-1-4/s4_arith.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:9c6080918b0ea2b107fa8392901cc26628934c1d8ec7c095db6cd1f86b212cc8
|
| 3 |
+
size 54818792
|
config.json
CHANGED
|
@@ -61,5 +61,12 @@
|
|
| 61 |
"AutoModelForCausalLM": "modeling_minibeatrix.MiniBeatrixForCausalLM"
|
| 62 |
},
|
| 63 |
"torch_dtype": "float32",
|
| 64 |
-
"use_cache": false
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
}
|
|
|
|
| 61 |
"AutoModelForCausalLM": "modeling_minibeatrix.MiniBeatrixForCausalLM"
|
| 62 |
},
|
| 63 |
"torch_dtype": "float32",
|
| 64 |
+
"use_cache": false,
|
| 65 |
+
"trunk": {
|
| 66 |
+
"craft": "mini-beatrix-3",
|
| 67 |
+
"step": 245674,
|
| 68 |
+
"base_model_id": "alephllm/mini-beatrix-3@step245674",
|
| 69 |
+
"note": "the core weights are the same with arms off and arms on; the arms are separate files"
|
| 70 |
+
},
|
| 71 |
+
"default_arm": null
|
| 72 |
}
|
modeling_minibeatrix.py
CHANGED
|
@@ -1,23 +1,43 @@
|
|
| 1 |
"""HF AutoModel wrapper for mini-beatrix-3 — the 32-block full-splat craft
|
| 2 |
(a CausalSplatHUB constellation and an anchored expert bank in every block;
|
| 3 |
-
64.4B-byte mission, completed 2026-10-05)
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
AutoModelForCausalLM
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
"""
|
| 12 |
from __future__ import annotations
|
| 13 |
|
|
|
|
|
|
|
|
|
|
| 14 |
import torch
|
| 15 |
from transformers import PretrainedConfig, PreTrainedModel
|
| 16 |
from transformers.generation import GenerationMixin
|
| 17 |
from transformers.modeling_outputs import CausalLMOutputWithPast
|
| 18 |
|
| 19 |
-
from .
|
|
|
|
| 20 |
from .alephlm import AlephLM
|
|
|
|
|
|
|
|
|
|
| 21 |
|
| 22 |
|
| 23 |
class MiniBeatrixConfig(PretrainedConfig):
|
|
@@ -33,7 +53,7 @@ class MiniBeatrixConfig(PretrainedConfig):
|
|
| 33 |
hub_K=64, hub_D=128, tau=0.1, bank_experts=3, bank_ff=1024,
|
| 34 |
head_K=256, head_D=256, gate_init=-3.0,
|
| 35 |
tie_embeddings=False, hub_chunk=256, hub_const=4,
|
| 36 |
-
hub_ckpt=0, **kwargs):
|
| 37 |
self.name = name
|
| 38 |
self.d_model = d_model
|
| 39 |
self.n_layers = n_layers
|
|
@@ -54,6 +74,11 @@ class MiniBeatrixConfig(PretrainedConfig):
|
|
| 54 |
self.hub_chunk = hub_chunk
|
| 55 |
self.hub_const = hub_const
|
| 56 |
self.hub_ckpt = hub_ckpt # inference: always 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
super().__init__(**kwargs)
|
| 58 |
|
| 59 |
def to_aleph(self) -> AlephLMConfig:
|
|
@@ -78,16 +103,31 @@ class MiniBeatrixForCausalLM(PreTrainedModel, GenerationMixin):
|
|
| 78 |
def __init__(self, config: MiniBeatrixConfig):
|
| 79 |
super().__init__(config)
|
| 80 |
self.model = AlephLM(config.to_aleph())
|
|
|
|
|
|
|
| 81 |
self.post_init()
|
| 82 |
|
| 83 |
def _init_weights(self, module):
|
| 84 |
-
pass # AlephLM initializes itself; loader must not re-init
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
|
|
|
|
| 86 |
def forward(self, input_ids=None, labels=None, attention_mask=None,
|
| 87 |
past_key_values=None, use_cache=None, **kwargs):
|
| 88 |
# attention_mask is safely ignored: causal model, right-padding
|
| 89 |
# with label masking is the trained convention. No KV cache in
|
| 90 |
-
# this wrapper; generate() recomputes the prefix each step
|
|
|
|
| 91 |
if input_ids.shape[1] > self.config.context:
|
| 92 |
input_ids = input_ids[:, -self.config.context:]
|
| 93 |
if labels is not None:
|
|
@@ -99,3 +139,154 @@ class MiniBeatrixForCausalLM(PreTrainedModel, GenerationMixin):
|
|
| 99 |
|
| 100 |
def prepare_inputs_for_generation(self, input_ids, **kwargs):
|
| 101 |
return {"input_ids": input_ids[:, -self.config.context:]}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
"""HF AutoModel wrapper for mini-beatrix-3 — the 32-block full-splat craft
|
| 2 |
(a CausalSplatHUB constellation and an anchored expert bank in every block;
|
| 3 |
+
64.4B-byte mission, completed 2026-10-05), packaged with its detachable
|
| 4 |
+
stage arms.
|
| 5 |
+
|
| 6 |
+
from transformers import AutoModelForCausalLM
|
| 7 |
+
m = AutoModelForCausalLM.from_pretrained("AbstractPhil/mini-beatrix-3",
|
| 8 |
+
trust_remote_code=True)
|
| 9 |
+
m.say("Who are you?") # arms off: the bare core
|
| 10 |
+
|
| 11 |
+
m.mount_arm("s3_rules") # arms on: one stage arm ...
|
| 12 |
+
m.mount_arm("stages-1-4") # ... or a stack of them
|
| 13 |
+
m.detach_arm() # bit-exact core restored
|
| 14 |
+
|
| 15 |
+
# arms on from the first call
|
| 16 |
+
m = AutoModelForCausalLM.from_pretrained("AbstractPhil/mini-beatrix-3",
|
| 17 |
+
trust_remote_code=True,
|
| 18 |
+
default_arm="stages-1-4")
|
| 19 |
+
|
| 20 |
+
The model reads raw UTF-8 bytes: input_ids are byte values 0..255. The
|
| 21 |
+
default weight file is bf16; the fp32 master weights load with
|
| 22 |
+
variant="fp32". The core's weights are the same with arms off and on: an
|
| 23 |
+
arm is a guest on the core, never a change to it.
|
| 24 |
"""
|
| 25 |
from __future__ import annotations
|
| 26 |
|
| 27 |
+
import json
|
| 28 |
+
import os
|
| 29 |
+
|
| 30 |
import torch
|
| 31 |
from transformers import PretrainedConfig, PreTrainedModel
|
| 32 |
from transformers.generation import GenerationMixin
|
| 33 |
from transformers.modeling_outputs import CausalLMOutputWithPast
|
| 34 |
|
| 35 |
+
from .arms import (SPECIALS, TEXT_STOPS, cut_specials_reply, cut_text_reply,
|
| 36 |
+
mount, render_specials_frame, render_text_frame)
|
| 37 |
from .alephlm import AlephLM
|
| 38 |
+
from .presets import AlephLMConfig
|
| 39 |
+
|
| 40 |
+
ARMS_INDEX = "arms/index.json"
|
| 41 |
|
| 42 |
|
| 43 |
class MiniBeatrixConfig(PretrainedConfig):
|
|
|
|
| 53 |
hub_K=64, hub_D=128, tau=0.1, bank_experts=3, bank_ff=1024,
|
| 54 |
head_K=256, head_D=256, gate_init=-3.0,
|
| 55 |
tie_embeddings=False, hub_chunk=256, hub_const=4,
|
| 56 |
+
hub_ckpt=0, trunk=None, default_arm=None, **kwargs):
|
| 57 |
self.name = name
|
| 58 |
self.d_model = d_model
|
| 59 |
self.n_layers = n_layers
|
|
|
|
| 74 |
self.hub_chunk = hub_chunk
|
| 75 |
self.hub_const = hub_const
|
| 76 |
self.hub_ckpt = hub_ckpt # inference: always 0
|
| 77 |
+
# the frozen core the packaged arms were trained on (provenance)
|
| 78 |
+
self.trunk = trunk or {"craft": "mini-beatrix-3", "step": 245674,
|
| 79 |
+
"base_model_id": "alephllm/mini-beatrix-3@step245674"}
|
| 80 |
+
# None = arms off at load; an arm id = that arm mounted at load
|
| 81 |
+
self.default_arm = default_arm
|
| 82 |
super().__init__(**kwargs)
|
| 83 |
|
| 84 |
def to_aleph(self) -> AlephLMConfig:
|
|
|
|
| 103 |
def __init__(self, config: MiniBeatrixConfig):
|
| 104 |
super().__init__(config)
|
| 105 |
self.model = AlephLM(config.to_aleph())
|
| 106 |
+
self._arm = None # the live handle, if an arm is mounted
|
| 107 |
+
self._index = None
|
| 108 |
self.post_init()
|
| 109 |
|
| 110 |
def _init_weights(self, module):
|
| 111 |
+
pass # AlephLM initializes itself; the loader must not re-init
|
| 112 |
+
|
| 113 |
+
@classmethod
|
| 114 |
+
def from_pretrained(cls, pretrained_model_name_or_path, *model_args, **kwargs):
|
| 115 |
+
"""Loads the bare core (arms off). With default_arm="<id>", passed
|
| 116 |
+
here or set in config.json, that arm is mounted once the core's
|
| 117 |
+
weights are in (arms on)."""
|
| 118 |
+
out = super().from_pretrained(pretrained_model_name_or_path, *model_args, **kwargs)
|
| 119 |
+
model = out[0] if isinstance(out, tuple) else out
|
| 120 |
+
if getattr(model.config, "default_arm", None):
|
| 121 |
+
model.mount_arm(model.config.default_arm)
|
| 122 |
+
return out
|
| 123 |
|
| 124 |
+
# ------------------------------------------------------------ core
|
| 125 |
def forward(self, input_ids=None, labels=None, attention_mask=None,
|
| 126 |
past_key_values=None, use_cache=None, **kwargs):
|
| 127 |
# attention_mask is safely ignored: causal model, right-padding
|
| 128 |
# with label masking is the trained convention. No KV cache in
|
| 129 |
+
# this wrapper; generate() recomputes the prefix each step — the
|
| 130 |
+
# model's own cached path is used by say() instead.
|
| 131 |
if input_ids.shape[1] > self.config.context:
|
| 132 |
input_ids = input_ids[:, -self.config.context:]
|
| 133 |
if labels is not None:
|
|
|
|
| 139 |
|
| 140 |
def prepare_inputs_for_generation(self, input_ids, **kwargs):
|
| 141 |
return {"input_ids": input_ids[:, -self.config.context:]}
|
| 142 |
+
|
| 143 |
+
def save_pretrained(self, *args, **kwargs):
|
| 144 |
+
if self._arm is not None:
|
| 145 |
+
raise RuntimeError(
|
| 146 |
+
"an arm is mounted: detach_arm() before saving, or the arm's "
|
| 147 |
+
"parameters would be written into the core's weight file")
|
| 148 |
+
return super().save_pretrained(*args, **kwargs)
|
| 149 |
+
|
| 150 |
+
# ------------------------------------------------------------ files
|
| 151 |
+
def _repo_file(self, relpath: str) -> str:
|
| 152 |
+
"""A file of this package: from the local snapshot if the model was
|
| 153 |
+
loaded from a directory, else fetched from the hub."""
|
| 154 |
+
root = str(getattr(self.config, "_name_or_path", "") or "")
|
| 155 |
+
local = os.path.join(root, *relpath.split("/"))
|
| 156 |
+
if root and os.path.isfile(local):
|
| 157 |
+
return local
|
| 158 |
+
if root and os.path.isdir(root):
|
| 159 |
+
raise FileNotFoundError(
|
| 160 |
+
f"{relpath} is missing from {root}: this model was loaded from a "
|
| 161 |
+
"directory, so the arms are served from that directory too — "
|
| 162 |
+
"copy arms/ next to config.json, or load by repo id")
|
| 163 |
+
from huggingface_hub import hf_hub_download
|
| 164 |
+
return hf_hub_download(root, relpath)
|
| 165 |
+
|
| 166 |
+
# ------------------------------------------------------------- arms
|
| 167 |
+
def arms(self) -> list:
|
| 168 |
+
"""The packaged arm index: one row per arm or stack, with what it
|
| 169 |
+
does, the text it was trained on and what it measured."""
|
| 170 |
+
if self._index is None:
|
| 171 |
+
with open(self._repo_file(ARMS_INDEX), encoding="utf-8") as f:
|
| 172 |
+
self._index = json.load(f)
|
| 173 |
+
return list(self._index.get("arms", []))
|
| 174 |
+
|
| 175 |
+
def arm_card(self, name: str) -> dict:
|
| 176 |
+
for row in self.arms():
|
| 177 |
+
if row.get("name") == name or row.get("id") == name:
|
| 178 |
+
return row
|
| 179 |
+
known = ", ".join(sorted(r.get("id", r.get("name", "?")) for r in self.arms()))
|
| 180 |
+
raise KeyError(f"no packaged arm {name!r}. Available: {known}")
|
| 181 |
+
|
| 182 |
+
@property
|
| 183 |
+
def arm(self):
|
| 184 |
+
"""The live arm handle, or None when the bare core is serving."""
|
| 185 |
+
return self._arm
|
| 186 |
+
|
| 187 |
+
def mount_arm(self, name: str, *, verify_base: bool = True):
|
| 188 |
+
"""Mount one packaged arm (or stack) on the core and return its
|
| 189 |
+
handle. Mounting a second arm detaches the first."""
|
| 190 |
+
card = self.arm_card(name)
|
| 191 |
+
if verify_base:
|
| 192 |
+
want = str((self.config.trunk or {}).get("base_model_id") or "")
|
| 193 |
+
got = str(card.get("base_model_id") or "")
|
| 194 |
+
if want and got and not (got.startswith(want) or want.startswith(got)):
|
| 195 |
+
raise ValueError(
|
| 196 |
+
f"arm {name!r} was trained on {got}, this core is {want} — "
|
| 197 |
+
"arms are trunk-bound (pass verify_base=False to override)")
|
| 198 |
+
if self._arm is not None:
|
| 199 |
+
self.detach_arm()
|
| 200 |
+
files = [self._repo_file(w) for w in card["weights"]]
|
| 201 |
+
self._arm = mount(self.model, card, files)
|
| 202 |
+
return self._arm
|
| 203 |
+
|
| 204 |
+
def detach_arm(self, *, verify: bool = True):
|
| 205 |
+
"""Restore the bare core. With verify=True the restored logits are
|
| 206 |
+
asserted BIT-EXACT against the pre-mount fingerprint."""
|
| 207 |
+
if self._arm is None:
|
| 208 |
+
return None
|
| 209 |
+
h, self._arm = self._arm, None
|
| 210 |
+
h.detach(verify=verify)
|
| 211 |
+
return h
|
| 212 |
+
|
| 213 |
+
# ------------------------------------------------------------ frames
|
| 214 |
+
def render(self, prompt: str, *, frame: str | None = None, history=None,
|
| 215 |
+
system: str | None = None) -> torch.Tensor:
|
| 216 |
+
"""Prompt bytes in a frame: 'specials' (the core's own chat frame),
|
| 217 |
+
'raw' (the bytes as they are) or 'text'."""
|
| 218 |
+
frame = frame or self.default_frame()
|
| 219 |
+
if frame == "specials":
|
| 220 |
+
ids = render_specials_frame(prompt, system=system, history=history)
|
| 221 |
+
elif frame == "text":
|
| 222 |
+
ids = render_text_frame(prompt, history=history)
|
| 223 |
+
elif frame == "raw":
|
| 224 |
+
ids = prompt.encode("utf-8")
|
| 225 |
+
else:
|
| 226 |
+
raise ValueError(f"unknown frame {frame!r} (raw | text | specials)")
|
| 227 |
+
return torch.tensor([list(ids)], dtype=torch.long, device=self.device)
|
| 228 |
+
|
| 229 |
+
def default_frame(self) -> str:
|
| 230 |
+
"""The live arm's own frame (the stage arms continue raw text); the
|
| 231 |
+
core's chat frame when nothing is mounted."""
|
| 232 |
+
if self._arm is not None:
|
| 233 |
+
return ((self._arm.card.get("template") or {}).get("frame")
|
| 234 |
+
or "raw")
|
| 235 |
+
return "specials"
|
| 236 |
+
|
| 237 |
+
@torch.no_grad()
|
| 238 |
+
def say(self, prompt: str, *, frame: str | None = None, history=None,
|
| 239 |
+
system: str | None = None, max_new: int = 120,
|
| 240 |
+
temperature: float = 0.0, top_p: float = 1.0,
|
| 241 |
+
use_cache: bool = True, raw: bool = False) -> str:
|
| 242 |
+
"""Render, continue and cut at the frame's stop (greedy by default).
|
| 243 |
+
Returns the continuation text."""
|
| 244 |
+
frame = frame or self.default_frame()
|
| 245 |
+
ids = self.render(prompt, frame=frame, history=history, system=system)
|
| 246 |
+
if getattr(self._arm, "reset_state", None):
|
| 247 |
+
self._arm.reset_state()
|
| 248 |
+
out = self._continue(ids, max_new=max_new, temperature=temperature,
|
| 249 |
+
top_p=top_p, use_cache=use_cache, frame=frame)
|
| 250 |
+
gen = bytes(int(b) for b in out[0, ids.shape[1]:].tolist())
|
| 251 |
+
if raw or frame == "raw":
|
| 252 |
+
return gen.decode("utf-8", errors="replace")
|
| 253 |
+
cut = (cut_specials_reply(gen) if frame == "specials"
|
| 254 |
+
else cut_text_reply(gen))
|
| 255 |
+
return cut.decode("utf-8", errors="replace").strip()
|
| 256 |
+
|
| 257 |
+
@torch.no_grad()
|
| 258 |
+
def _continue(self, idx: torch.Tensor, *, max_new: int, temperature: float,
|
| 259 |
+
top_p: float, use_cache: bool,
|
| 260 |
+
frame: str = "raw") -> torch.Tensor:
|
| 261 |
+
"""The core's own decode: constant-size cached state while the
|
| 262 |
+
sequence fits the position table. Stops early on the frame's own
|
| 263 |
+
stop bytes: [END] or a document boundary in every frame, and the
|
| 264 |
+
text frame's stop strings in that frame only."""
|
| 265 |
+
m = self.model
|
| 266 |
+
m.eval()
|
| 267 |
+
ctx = int(self.config.context)
|
| 268 |
+
stops = [bytes([SPECIALS["END"]]), bytes([SPECIALS["DOC"]])]
|
| 269 |
+
if frame == "text":
|
| 270 |
+
stops += [s.encode("utf-8") for s in TEXT_STOPS]
|
| 271 |
+
start = idx.shape[1]
|
| 272 |
+
if not (use_cache and idx.shape[1] < ctx):
|
| 273 |
+
for _ in range(max_new):
|
| 274 |
+
logits = self(input_ids=idx[:, -ctx:]).logits
|
| 275 |
+
nxt = m._sample(logits, temperature, top_p)
|
| 276 |
+
idx = torch.cat([idx, nxt], dim=1)
|
| 277 |
+
if _hit_stop(idx, start, stops):
|
| 278 |
+
break
|
| 279 |
+
return idx
|
| 280 |
+
logits, cache = m.prefill(idx)
|
| 281 |
+
for i in range(min(max_new, ctx - start)):
|
| 282 |
+
nxt = m._sample(logits, temperature, top_p)
|
| 283 |
+
idx = torch.cat([idx, nxt], dim=1)
|
| 284 |
+
if _hit_stop(idx, start, stops):
|
| 285 |
+
break
|
| 286 |
+
logits = m.decode_step(nxt, cache)
|
| 287 |
+
return idx
|
| 288 |
+
|
| 289 |
+
|
| 290 |
+
def _hit_stop(idx: torch.Tensor, start: int, stops) -> bool:
|
| 291 |
+
tail = bytes(int(b) for b in idx[0, start:].tolist())[-8:]
|
| 292 |
+
return any(tail.endswith(s) for s in stops)
|