Commit ·
c735e00
0
Parent(s):
D1A-E2B v0.6 Core ML: text model, 5 int8 chunks, L=512, with the reference runner (#224)
Browse files- .gitattributes +36 -0
- README.md +91 -0
- config.json +164 -0
- coreml/L512_00-07.mlpackage/Data/com.apple.CoreML/model.mlmodel +3 -0
- coreml/L512_00-07.mlpackage/Data/com.apple.CoreML/weights/weight.bin +3 -0
- coreml/L512_00-07.mlpackage/Manifest.json +18 -0
- coreml/L512_07-15.mlpackage/Data/com.apple.CoreML/model.mlmodel +3 -0
- coreml/L512_07-15.mlpackage/Data/com.apple.CoreML/weights/weight.bin +3 -0
- coreml/L512_07-15.mlpackage/Manifest.json +18 -0
- coreml/L512_15-22.mlpackage/Data/com.apple.CoreML/model.mlmodel +3 -0
- coreml/L512_15-22.mlpackage/Data/com.apple.CoreML/weights/weight.bin +3 -0
- coreml/L512_15-22.mlpackage/Manifest.json +18 -0
- coreml/L512_22-29.mlpackage/Data/com.apple.CoreML/model.mlmodel +3 -0
- coreml/L512_22-29.mlpackage/Data/com.apple.CoreML/weights/weight.bin +3 -0
- coreml/L512_22-29.mlpackage/Manifest.json +18 -0
- coreml/L512_29-35.mlpackage/Data/com.apple.CoreML/model.mlmodel +3 -0
- coreml/L512_29-35.mlpackage/Data/com.apple.CoreML/weights/weight.bin +3 -0
- coreml/L512_29-35.mlpackage/Manifest.json +18 -0
- d1a_config.json +142 -0
- embeddings.safetensors +3 -0
- head.safetensors +3 -0
- io.json +1 -0
- reference/d1a_coreml.py +184 -0
- reference/pipeline.py +80 -0
- reference/run.py +38 -0
- reference/workers.py +123 -0
- tokenizer.json +3 -0
- tokenizer_config.json +55 -0
.gitattributes
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
+
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
tokenizer.json filter=lfs diff=lfs merge=lfs -text
|
README.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: apache-2.0
|
| 3 |
+
base_model: google/gemma-4-E2B
|
| 4 |
+
tags: [coreml, apple-neural-engine, decision-model, d1a]
|
| 5 |
+
---
|
| 6 |
+
# D1A-E2B v0.6 · Core ML (text, ≤ 512 tokens)
|
| 7 |
+
|
| 8 |
+
D1A-E2B v0.6 for the Apple Neural Engine. D1A is a small decision model: a document and typed questions go in, and a
|
| 9 |
+
calibrated probability for every option comes out, in one pass, with no generated text ([jonpol01/d1a](https://github.com/jonpol01/d1a)).
|
| 10 |
+
This repository is the text model converted to Core ML; the conversion is described in jonpol01/d1a#224.
|
| 11 |
+
|
| 12 |
+
## Status
|
| 13 |
+
|
| 14 |
+
- **Correct at ≤ 512 tokens.** On 28 real requests (42 questions), no clear-cut answer changes against the fp32 PyTorch
|
| 15 |
+
rebuild of the same model, on an M1 Max and an M4.
|
| 16 |
+
- **Speed (median request):** M4 Neural Engine 0.96 s (MLX on the GPU: 0.20 s); M1 Max 9.7 s (MLX: 0.17 s). The M1's Neural Engine does not take these chunks, so on M1-class Macs they run on the CPU; the M4 runs them on its Neural Engine.
|
| 17 |
+
- **Use it** where the GPU should stay free or power matters (iPhone, iPad). On a Mac, the MLX build is faster.
|
| 18 |
+
- **Not included yet:** photo, voice and video (their encoders are separate models, still to convert), and inputs longer than
|
| 19 |
+
512 tokens (2,048-token programs give wrong answers on the M4 Neural Engine).
|
| 20 |
+
|
| 21 |
+
## Run it
|
| 22 |
+
|
| 23 |
+
```bash
|
| 24 |
+
pip install coremltools numpy torch transformers safetensors "d1a @ git+https://github.com/jonpol01/d1a"
|
| 25 |
+
hf download JohnP1/d1a-e2b-coreml --revision v0.6 --local-dir d1a-e2b-coreml
|
| 26 |
+
python d1a-e2b-coreml/reference/run.py d1a-e2b-coreml request.json
|
| 27 |
+
```
|
| 28 |
+
|
| 29 |
+
`request.json` is a System One request, for example:
|
| 30 |
+
|
| 31 |
+
```json
|
| 32 |
+
{"state": "Shoes arrived two weeks late and in the wrong size. Also I see two charges on my card.",
|
| 33 |
+
"questions": {"team": {"type": "choice", "instructions": "Which team should handle this?", "criteria": {"returns": null, "shipping": null, "billing": null}},
|
| 34 |
+
"urgent": {"type": "noul", "instructions": "Does this need a reply today?"}}}
|
| 35 |
+
```
|
| 36 |
+
|
| 37 |
+
The first run compiles the chunks into `coreml/.compiled/` (a few minutes); later runs load them.
|
| 38 |
+
|
| 39 |
+
## Files
|
| 40 |
+
|
| 41 |
+
| Path | What |
|
| 42 |
+
|---|---|
|
| 43 |
+
| `coreml/L512_<a>-<b>.mlpackage` | 5 chunks of the 35-layer text model, int8 weights (per block of 64). Chunk 7-15 outputs the shared keys and values that chunks 15-35 read. |
|
| 44 |
+
| `embeddings.safetensors` | the token and per-layer embedding tables (MLX affine-quantized, from JohnP1/d1a-e2b-mlx-q8@v0.6), read row by row on the host |
|
| 45 |
+
| `head.safetensors`, `d1a_config.json` | the pointer head, the temperature and the pad and delimiter ids |
|
| 46 |
+
| `config.json`, `tokenizer.json`, `tokenizer_config.json` | the Gemma 4 model config and tokenizer |
|
| 47 |
+
| `io.json` | each chunk's input names |
|
| 48 |
+
| `reference/` | Python reference: `run.py` (one request), `workers.py` (one process per chunk), `d1a_coreml.py` (host-side embeddings and masks) |
|
| 49 |
+
|
| 50 |
+
## How a request runs
|
| 51 |
+
|
| 52 |
+
1. Encode it with D1A's layout (`d1a.core.encoding.encode`) and right-pad it to 512 tokens with the pad token.
|
| 53 |
+
2. Look up the token embeddings (scaled by √hidden) and the per-layer embeddings (scaled by √256) on the host.
|
| 54 |
+
3. Build the block-causal masks (full and sliding), clamped to −1e4.
|
| 55 |
+
4. Run the chunks in order, each in its own process. Several large Neural Engine programs in one process can crash the
|
| 56 |
+
runtime.
|
| 57 |
+
5. Read the hidden states at each question's `<decide>` and `</opt>` positions, score them with the head, divide by the
|
| 58 |
+
temperature, and take the softmax.
|
| 59 |
+
|
| 60 |
+
macOS 15 / iOS 18 or later.
|
| 61 |
+
|
| 62 |
+
## D1A builds compared
|
| 63 |
+
|
| 64 |
+
Measured on 2026-10-11 on the same 28 real requests (42 questions, all ≤ 512 tokens), on an Apple M1 Max and a Mac mini M4.
|
| 65 |
+
|
| 66 |
+
| | E2B · MLX | E2B · Core ML | E4B · MLX | E4B · Core ML |
|
| 67 |
+
|---|---|---|---|---|
|
| 68 |
+
| Version | v0.6 | v0.6 | v0.6 | v0.6 |
|
| 69 |
+
| Repository | JohnP1/d1a-e2b-mlx-q8 | JohnP1/d1a-e2b-coreml | JohnP1/d1a-e4b-mlx-q8 | JohnP1/d1a-e4b-coreml |
|
| 70 |
+
| Runs on | Mac GPU | Neural Engine (iPhone, iPad, M4-class Macs) | Mac GPU | Neural Engine |
|
| 71 |
+
| Text | yes | yes | yes | yes |
|
| 72 |
+
| Photo, voice, video (16 frames, no sound) | yes | not yet (next) | yes | not yet |
|
| 73 |
+
| Input length | long documents | ≤ 512 tokens | long documents | ≤ 512 tokens |
|
| 74 |
+
| Questions | yes/no, choice, score, with a calibrated probability for each option | same | same | same |
|
| 75 |
+
| Languages | questions in English; documents in English or Japanese | same | same | same |
|
| 76 |
+
| PR labels, 487 newest PRs: type / severity | 87.5% / 77.2%; 0 of 30 P0/P1 found | same model | 90.6% / 79.1%; 13 of 30 P0/P1 | same model |
|
| 77 |
+
| Size | 3.8 GB + 1.0 GB media | 3.5 GB (1.8 GB chunks + 1.6 GB embeddings) | 6.6 GB + 1.0 GB media | 6.1 GB (3.9 GB chunks + 2.1 GB embeddings) |
|
| 78 |
+
| Median request, M4 (p95) | **0.20 s** (0.26) | 0.96 s (0.97) | 0.63 s (0.89) | 1.80 s (1.91) |
|
| 79 |
+
| Median request, M1 Max (p95) | **0.17 s** (0.23) | 9.7 s (10.0) ² | 0.81 s (1.33) | 2.93 s (2.95) |
|
| 80 |
+
| Load, M4 / M1 Max | 2 s / 9 s | 40 s / 247 s | 3 s / – | 52 s / 106 s |
|
| 81 |
+
| Answer changes vs fp32 PyTorch ¹ | 0 of 37 clear-cut (M4: 1 near-tie of 42) | 0 of 37 clear-cut (M4: 1 near-tie of 42) | 0 of 38 clear-cut | 0 of 38 clear-cut |
|
| 82 |
+
| Photo / voice / video, median (M1 Max) | 0.83 s over the 14 demo requests | – | – | – |
|
| 83 |
+
|
| 84 |
+
¹ Each build is compared with the fp32 PyTorch rebuild of the same v0.6 model. "Clear-cut" means the reference's top
|
| 85 |
+
answer leads by more than 0.05; a near-tie can flip either way.
|
| 86 |
+
² On the M1 Max's Neural Engine, the E2B chunks fall back to the CPU: each chunk takes the same time with or without the
|
| 87 |
+
Neural Engine. The M4 runs the same files on its Neural Engine.
|
| 88 |
+
|
| 89 |
+
## License
|
| 90 |
+
|
| 91 |
+
Apache-2.0. Base model: Gemma 4 by Google (Apache-2.0). D1A is built on Kev by Jared Palmer (Apache-2.0).
|
config.json
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"architectures": [
|
| 3 |
+
"Gemma4ForConditionalGeneration"
|
| 4 |
+
],
|
| 5 |
+
"audio_config": {
|
| 6 |
+
"_name_or_path": "",
|
| 7 |
+
"architectures": null,
|
| 8 |
+
"attention_chunk_size": 12,
|
| 9 |
+
"attention_context_left": 13,
|
| 10 |
+
"attention_context_right": 0,
|
| 11 |
+
"attention_invalid_logits_value": -1000000000.0,
|
| 12 |
+
"attention_logit_cap": 50.0,
|
| 13 |
+
"chunk_size_feed_forward": 0,
|
| 14 |
+
"conv_kernel_size": 5,
|
| 15 |
+
"dtype": "bfloat16",
|
| 16 |
+
"gradient_clipping": 10000000000.0,
|
| 17 |
+
"hidden_act": "silu",
|
| 18 |
+
"hidden_size": 1024,
|
| 19 |
+
"id2label": {
|
| 20 |
+
"0": "LABEL_0",
|
| 21 |
+
"1": "LABEL_1"
|
| 22 |
+
},
|
| 23 |
+
"initializer_range": 0.02,
|
| 24 |
+
"is_encoder_decoder": false,
|
| 25 |
+
"label2id": {
|
| 26 |
+
"LABEL_0": 0,
|
| 27 |
+
"LABEL_1": 1
|
| 28 |
+
},
|
| 29 |
+
"model_type": "gemma4_audio",
|
| 30 |
+
"num_attention_heads": 8,
|
| 31 |
+
"num_hidden_layers": 12,
|
| 32 |
+
"output_attentions": false,
|
| 33 |
+
"output_hidden_states": false,
|
| 34 |
+
"output_proj_dims": 1536,
|
| 35 |
+
"problem_type": null,
|
| 36 |
+
"residual_weight": 0.5,
|
| 37 |
+
"return_dict": true,
|
| 38 |
+
"rms_norm_eps": 1e-06,
|
| 39 |
+
"subsampling_conv_channels": [
|
| 40 |
+
128,
|
| 41 |
+
32
|
| 42 |
+
],
|
| 43 |
+
"use_clipped_linears": true
|
| 44 |
+
},
|
| 45 |
+
"audio_token_id": 258881,
|
| 46 |
+
"boa_token_id": 256000,
|
| 47 |
+
"boi_token_id": 255999,
|
| 48 |
+
"dtype": "bfloat16",
|
| 49 |
+
"eoa_token_id": 258883,
|
| 50 |
+
"eoa_token_index": 258883,
|
| 51 |
+
"eoi_token_id": 258882,
|
| 52 |
+
"eos_token_id": 1,
|
| 53 |
+
"image_token_id": 258880,
|
| 54 |
+
"initializer_range": 0.02,
|
| 55 |
+
"model_type": "gemma4",
|
| 56 |
+
"quantization": {
|
| 57 |
+
"group_size": 64,
|
| 58 |
+
"bits": 8,
|
| 59 |
+
"mode": "affine",
|
| 60 |
+
"language_model.model.embed_tokens_per_layer": {
|
| 61 |
+
"group_size": 64,
|
| 62 |
+
"bits": 4,
|
| 63 |
+
"mode": "affine"
|
| 64 |
+
}
|
| 65 |
+
},
|
| 66 |
+
"quantization_config": {
|
| 67 |
+
"group_size": 64,
|
| 68 |
+
"bits": 8,
|
| 69 |
+
"mode": "affine",
|
| 70 |
+
"language_model.model.embed_tokens_per_layer": {
|
| 71 |
+
"group_size": 64,
|
| 72 |
+
"bits": 4,
|
| 73 |
+
"mode": "affine"
|
| 74 |
+
}
|
| 75 |
+
},
|
| 76 |
+
"text_config": {
|
| 77 |
+
"attention_bias": false,
|
| 78 |
+
"attention_dropout": 0.0,
|
| 79 |
+
"attention_k_eq_v": false,
|
| 80 |
+
"bos_token_id": 2,
|
| 81 |
+
"dtype": "bfloat16",
|
| 82 |
+
"enable_moe_block": false,
|
| 83 |
+
"eos_token_id": 1,
|
| 84 |
+
"expert_intermediate_size": null,
|
| 85 |
+
"final_logit_softcapping": 30.0,
|
| 86 |
+
"global_head_dim": 512,
|
| 87 |
+
"head_dim": 256,
|
| 88 |
+
"hidden_activation": "gelu_pytorch_tanh",
|
| 89 |
+
"hidden_size": 1536,
|
| 90 |
+
"hidden_size_per_layer_input": 256,
|
| 91 |
+
"initializer_range": 0.02,
|
| 92 |
+
"intermediate_size": 6144,
|
| 93 |
+
"layer_types": [
|
| 94 |
+
"sliding_attention",
|
| 95 |
+
"sliding_attention",
|
| 96 |
+
"sliding_attention",
|
| 97 |
+
"sliding_attention",
|
| 98 |
+
"full_attention",
|
| 99 |
+
"sliding_attention",
|
| 100 |
+
"sliding_attention",
|
| 101 |
+
"sliding_attention",
|
| 102 |
+
"sliding_attention",
|
| 103 |
+
"full_attention",
|
| 104 |
+
"sliding_attention",
|
| 105 |
+
"sliding_attention",
|
| 106 |
+
"sliding_attention",
|
| 107 |
+
"sliding_attention",
|
| 108 |
+
"full_attention",
|
| 109 |
+
"sliding_attention",
|
| 110 |
+
"sliding_attention",
|
| 111 |
+
"sliding_attention",
|
| 112 |
+
"sliding_attention",
|
| 113 |
+
"full_attention",
|
| 114 |
+
"sliding_attention",
|
| 115 |
+
"sliding_attention",
|
| 116 |
+
"sliding_attention",
|
| 117 |
+
"sliding_attention",
|
| 118 |
+
"full_attention",
|
| 119 |
+
"sliding_attention",
|
| 120 |
+
"sliding_attention",
|
| 121 |
+
"sliding_attention",
|
| 122 |
+
"sliding_attention",
|
| 123 |
+
"full_attention",
|
| 124 |
+
"sliding_attention",
|
| 125 |
+
"sliding_attention",
|
| 126 |
+
"sliding_attention",
|
| 127 |
+
"sliding_attention",
|
| 128 |
+
"full_attention"
|
| 129 |
+
],
|
| 130 |
+
"max_position_embeddings": 131072,
|
| 131 |
+
"model_type": "gemma4_text",
|
| 132 |
+
"num_attention_heads": 8,
|
| 133 |
+
"num_experts": null,
|
| 134 |
+
"num_global_key_value_heads": null,
|
| 135 |
+
"num_hidden_layers": 35,
|
| 136 |
+
"num_key_value_heads": 1,
|
| 137 |
+
"num_kv_shared_layers": 20,
|
| 138 |
+
"pad_token_id": 0,
|
| 139 |
+
"rms_norm_eps": 1e-06,
|
| 140 |
+
"rope_parameters": {
|
| 141 |
+
"full_attention": {
|
| 142 |
+
"partial_rotary_factor": 0.25,
|
| 143 |
+
"rope_theta": 1000000.0,
|
| 144 |
+
"rope_type": "proportional"
|
| 145 |
+
},
|
| 146 |
+
"sliding_attention": {
|
| 147 |
+
"rope_theta": 10000.0,
|
| 148 |
+
"rope_type": "default"
|
| 149 |
+
}
|
| 150 |
+
},
|
| 151 |
+
"sliding_window": 512,
|
| 152 |
+
"tie_word_embeddings": true,
|
| 153 |
+
"top_k_experts": null,
|
| 154 |
+
"use_bidirectional_attention": null,
|
| 155 |
+
"use_cache": true,
|
| 156 |
+
"use_double_wide_mlp": true,
|
| 157 |
+
"vocab_size": 262144,
|
| 158 |
+
"vocab_size_per_layer_input": 262144
|
| 159 |
+
},
|
| 160 |
+
"tie_word_embeddings": true,
|
| 161 |
+
"transformers_version": "5.5.0.dev0",
|
| 162 |
+
"video_token_id": 258884,
|
| 163 |
+
"vision_soft_tokens_per_image": 280
|
| 164 |
+
}
|
coreml/L512_00-07.mlpackage/Data/com.apple.CoreML/model.mlmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:63cdc56003526dc6bc1922b1f5245e14fda32e472ff1c22f0b1bace80ba61235
|
| 3 |
+
size 254034
|
coreml/L512_00-07.mlpackage/Data/com.apple.CoreML/weights/weight.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:4932ca078e73e04eb9bacb44aafee7356ff9b418068c7a0d2bb3325dbcadfe34
|
| 3 |
+
size 287097152
|
coreml/L512_00-07.mlpackage/Manifest.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"fileFormatVersion": "1.0.0",
|
| 3 |
+
"itemInfoEntries": {
|
| 4 |
+
"15E6ABF0-3B7A-45EB-AEFE-575087ED5B8B": {
|
| 5 |
+
"author": "com.apple.CoreML",
|
| 6 |
+
"description": "CoreML Model Specification",
|
| 7 |
+
"name": "model.mlmodel",
|
| 8 |
+
"path": "com.apple.CoreML/model.mlmodel"
|
| 9 |
+
},
|
| 10 |
+
"186F9637-EC03-4170-A083-0E8F08B64E70": {
|
| 11 |
+
"author": "com.apple.CoreML",
|
| 12 |
+
"description": "CoreML Model Weights",
|
| 13 |
+
"name": "weights",
|
| 14 |
+
"path": "com.apple.CoreML/weights"
|
| 15 |
+
}
|
| 16 |
+
},
|
| 17 |
+
"rootModelIdentifier": "15E6ABF0-3B7A-45EB-AEFE-575087ED5B8B"
|
| 18 |
+
}
|
coreml/L512_07-15.mlpackage/Data/com.apple.CoreML/model.mlmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:9f64f99c757d484f0d57e5746f3ede35e2d44d377de494e72f8e9d532b9afb29
|
| 3 |
+
size 286085
|
coreml/L512_07-15.mlpackage/Data/com.apple.CoreML/weights/weight.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:a52d7199ef53e97e89fe64fc5b729742b3db3d4c90fa129372602b7fd3dd2cea
|
| 3 |
+
size 317972736
|
coreml/L512_07-15.mlpackage/Manifest.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"fileFormatVersion": "1.0.0",
|
| 3 |
+
"itemInfoEntries": {
|
| 4 |
+
"C34EF27C-FE7F-43D9-B3CB-EF07BA6ED05E": {
|
| 5 |
+
"author": "com.apple.CoreML",
|
| 6 |
+
"description": "CoreML Model Specification",
|
| 7 |
+
"name": "model.mlmodel",
|
| 8 |
+
"path": "com.apple.CoreML/model.mlmodel"
|
| 9 |
+
},
|
| 10 |
+
"E1CCD0C2-4CB7-4016-8EA6-D0C9512C59E7": {
|
| 11 |
+
"author": "com.apple.CoreML",
|
| 12 |
+
"description": "CoreML Model Weights",
|
| 13 |
+
"name": "weights",
|
| 14 |
+
"path": "com.apple.CoreML/weights"
|
| 15 |
+
}
|
| 16 |
+
},
|
| 17 |
+
"rootModelIdentifier": "C34EF27C-FE7F-43D9-B3CB-EF07BA6ED05E"
|
| 18 |
+
}
|
coreml/L512_15-22.mlpackage/Data/com.apple.CoreML/model.mlmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:98899d9bcfbe1941ae4533d9b2db80647f205ebe4d14a9a039f0850be19db094
|
| 3 |
+
size 183109
|
coreml/L512_15-22.mlpackage/Data/com.apple.CoreML/weights/weight.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ffb4791b587ae08b55f9030c8ba40f8dd2cb660d3cdd5e4416246396041cc120
|
| 3 |
+
size 473557760
|
coreml/L512_15-22.mlpackage/Manifest.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"fileFormatVersion": "1.0.0",
|
| 3 |
+
"itemInfoEntries": {
|
| 4 |
+
"8BF3EE60-129B-43DC-ADBC-76FCC2AE0F22": {
|
| 5 |
+
"author": "com.apple.CoreML",
|
| 6 |
+
"description": "CoreML Model Specification",
|
| 7 |
+
"name": "model.mlmodel",
|
| 8 |
+
"path": "com.apple.CoreML/model.mlmodel"
|
| 9 |
+
},
|
| 10 |
+
"D588AC66-1980-4F48-A80E-D22E2DC5FC04": {
|
| 11 |
+
"author": "com.apple.CoreML",
|
| 12 |
+
"description": "CoreML Model Weights",
|
| 13 |
+
"name": "weights",
|
| 14 |
+
"path": "com.apple.CoreML/weights"
|
| 15 |
+
}
|
| 16 |
+
},
|
| 17 |
+
"rootModelIdentifier": "8BF3EE60-129B-43DC-ADBC-76FCC2AE0F22"
|
| 18 |
+
}
|
coreml/L512_22-29.mlpackage/Data/com.apple.CoreML/model.mlmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:5d99163080dcb2cdf8fd583083db5e1b11a165a6ae47856186e5a45e455e6586
|
| 3 |
+
size 183113
|
coreml/L512_22-29.mlpackage/Data/com.apple.CoreML/weights/weight.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:99d4cc60a0b5d645595238fd279e7d2eb0c939de902e7f80bc9eb5f005ed3503
|
| 3 |
+
size 473557760
|
coreml/L512_22-29.mlpackage/Manifest.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"fileFormatVersion": "1.0.0",
|
| 3 |
+
"itemInfoEntries": {
|
| 4 |
+
"5CBC619A-F5EC-4532-AE9A-E730428289EC": {
|
| 5 |
+
"author": "com.apple.CoreML",
|
| 6 |
+
"description": "CoreML Model Weights",
|
| 7 |
+
"name": "weights",
|
| 8 |
+
"path": "com.apple.CoreML/weights"
|
| 9 |
+
},
|
| 10 |
+
"E85695E6-50B8-4D34-AFD3-8C9EAF6EF828": {
|
| 11 |
+
"author": "com.apple.CoreML",
|
| 12 |
+
"description": "CoreML Model Specification",
|
| 13 |
+
"name": "model.mlmodel",
|
| 14 |
+
"path": "com.apple.CoreML/model.mlmodel"
|
| 15 |
+
}
|
| 16 |
+
},
|
| 17 |
+
"rootModelIdentifier": "E85695E6-50B8-4D34-AFD3-8C9EAF6EF828"
|
| 18 |
+
}
|
coreml/L512_29-35.mlpackage/Data/com.apple.CoreML/model.mlmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c72de76354fb0154b891dd2ef7819eabfd556d4c54c59a243a8d8ff17dbdf1b9
|
| 3 |
+
size 163816
|
coreml/L512_29-35.mlpackage/Data/com.apple.CoreML/weights/weight.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:81a752d4ccecd24523c1e5bc5c5859c07d44233a75d229fe846912e44ef90cea
|
| 3 |
+
size 413443264
|
coreml/L512_29-35.mlpackage/Manifest.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"fileFormatVersion": "1.0.0",
|
| 3 |
+
"itemInfoEntries": {
|
| 4 |
+
"4D7E2B93-F4E3-4337-A629-BB3697573F32": {
|
| 5 |
+
"author": "com.apple.CoreML",
|
| 6 |
+
"description": "CoreML Model Weights",
|
| 7 |
+
"name": "weights",
|
| 8 |
+
"path": "com.apple.CoreML/weights"
|
| 9 |
+
},
|
| 10 |
+
"64D6F739-0E0A-4E79-97A2-DCFB02714BFA": {
|
| 11 |
+
"author": "com.apple.CoreML",
|
| 12 |
+
"description": "CoreML Model Specification",
|
| 13 |
+
"name": "model.mlmodel",
|
| 14 |
+
"path": "com.apple.CoreML/model.mlmodel"
|
| 15 |
+
}
|
| 16 |
+
},
|
| 17 |
+
"rootModelIdentifier": "64D6F739-0E0A-4E79-97A2-DCFB02714BFA"
|
| 18 |
+
}
|
d1a_config.json
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"format": "d1a-mlx",
|
| 3 |
+
"format_version": 1,
|
| 4 |
+
"base": "google/gemma-4-E2B",
|
| 5 |
+
"base_revision": "d29ff6b45f081a49ee2733a859c9c9c2d95d1a6f",
|
| 6 |
+
"source": "JohnP1/d1a-e2b",
|
| 7 |
+
"source_revision": "v0.6",
|
| 8 |
+
"adapter_sha256": "247f222314c8ceee158140d38a0be1c304da81719034ef6e71ee02506e334c1b",
|
| 9 |
+
"lora": 16,
|
| 10 |
+
"head_dim": 256,
|
| 11 |
+
"hidden_size": 1536,
|
| 12 |
+
"temperature": 1.447269237440378,
|
| 13 |
+
"leading_ids": [
|
| 14 |
+
2
|
| 15 |
+
],
|
| 16 |
+
"bos_id": 2,
|
| 17 |
+
"delimiter_ids": [
|
| 18 |
+
6,
|
| 19 |
+
7,
|
| 20 |
+
8,
|
| 21 |
+
9,
|
| 22 |
+
10
|
| 23 |
+
],
|
| 24 |
+
"pad_id": 0,
|
| 25 |
+
"dtype": "bfloat16",
|
| 26 |
+
"quantization": {
|
| 27 |
+
"group_size": 64,
|
| 28 |
+
"bits": 8,
|
| 29 |
+
"mode": "affine",
|
| 30 |
+
"language_model.model.embed_tokens_per_layer": {
|
| 31 |
+
"group_size": 64,
|
| 32 |
+
"bits": 4,
|
| 33 |
+
"mode": "affine"
|
| 34 |
+
},
|
| 35 |
+
"embeddings": true
|
| 36 |
+
},
|
| 37 |
+
"head": "head.safetensors",
|
| 38 |
+
"temperature_fit": {
|
| 39 |
+
"rows": [
|
| 40 |
+
"cal/rows.json",
|
| 41 |
+
"calpr/rows.json"
|
| 42 |
+
],
|
| 43 |
+
"n": 3178,
|
| 44 |
+
"method": "min micro mean NLL over a 121-point log grid 0.25..4",
|
| 45 |
+
"cross_validation": {
|
| 46 |
+
"raw": {
|
| 47 |
+
"n": 3178,
|
| 48 |
+
"ece": 0.0505561988092814,
|
| 49 |
+
"brier": 0.2952905648331223,
|
| 50 |
+
"nll": 0.5648973549274169,
|
| 51 |
+
"confident_error_rate": 0.02957835116425425,
|
| 52 |
+
"coverage_at_5pct_error": 0.5613593455003146
|
| 53 |
+
},
|
| 54 |
+
"out_of_fold": {
|
| 55 |
+
"n": 3178,
|
| 56 |
+
"ece": 0.023704548579686884,
|
| 57 |
+
"brier": 0.2896952220920997,
|
| 58 |
+
"nll": 0.5324486179012022,
|
| 59 |
+
"confident_error_rate": 0.017935808684707365,
|
| 60 |
+
"coverage_at_5pct_error": 0.5556954059156702
|
| 61 |
+
},
|
| 62 |
+
"ece_ci95": {
|
| 63 |
+
"raw": [
|
| 64 |
+
0.04102750015575881,
|
| 65 |
+
0.06377306530700594
|
| 66 |
+
],
|
| 67 |
+
"out_of_fold": [
|
| 68 |
+
0.016511069955034523,
|
| 69 |
+
0.03860363764230206
|
| 70 |
+
],
|
| 71 |
+
"delta": [
|
| 72 |
+
-0.04016350379193325,
|
| 73 |
+
-0.010727297381358629
|
| 74 |
+
]
|
| 75 |
+
},
|
| 76 |
+
"separated": true,
|
| 77 |
+
"temperatures": [
|
| 78 |
+
1.447269237440378,
|
| 79 |
+
1.447269237440378,
|
| 80 |
+
1.4142135623730947,
|
| 81 |
+
1.4810975522865641,
|
| 82 |
+
1.447269237440378
|
| 83 |
+
],
|
| 84 |
+
"folds": 5,
|
| 85 |
+
"seed": 0,
|
| 86 |
+
"samples": 1000,
|
| 87 |
+
"groups": 1670,
|
| 88 |
+
"unit": "source-stratified (source, group); sibling questions and variants stay together"
|
| 89 |
+
},
|
| 90 |
+
"interval": {
|
| 91 |
+
"level": 0.9,
|
| 92 |
+
"samples": 2000,
|
| 93 |
+
"seed": 0,
|
| 94 |
+
"lower": 1.381912879967776,
|
| 95 |
+
"upper": 1.5157165665103978,
|
| 96 |
+
"questions": 3178
|
| 97 |
+
},
|
| 98 |
+
"fit_rows": [
|
| 99 |
+
{
|
| 100 |
+
"rows": "cal/rows.json",
|
| 101 |
+
"suite": "evals/v7/decision-v7",
|
| 102 |
+
"split": "calibration",
|
| 103 |
+
"questions": 1148
|
| 104 |
+
},
|
| 105 |
+
{
|
| 106 |
+
"rows": "calpr/rows.json",
|
| 107 |
+
"suite": "evals/d1a/pr-labels",
|
| 108 |
+
"split": "development",
|
| 109 |
+
"questions": 2030
|
| 110 |
+
}
|
| 111 |
+
],
|
| 112 |
+
"training_suites": [
|
| 113 |
+
"evals/devtools-v1",
|
| 114 |
+
"evals/documents-v1",
|
| 115 |
+
"evals/hard-v1",
|
| 116 |
+
"evals/v7/decision-v7"
|
| 117 |
+
],
|
| 118 |
+
"training_partitions": [
|
| 119 |
+
"evals/d1a/ja-jglue:train",
|
| 120 |
+
"evals/d1a/pr-labels:train",
|
| 121 |
+
"evals/d1a/pr-labels:train-blast",
|
| 122 |
+
"evals/d1a/pr-labels:train-ja",
|
| 123 |
+
"evals/d1a/routing:factory-train",
|
| 124 |
+
"evals/d1a/routing:generic-train"
|
| 125 |
+
],
|
| 126 |
+
"same_corpus": [
|
| 127 |
+
"calpr/rows.json: SAME CORPUS: evals/d1a/pr-labels:development is an eval partition of evals/d1a/pr-labels, whose train partitions ['evals/d1a/pr-labels:train', 'evals/d1a/pr-labels:train-blast', 'evals/d1a/pr-labels:train-ja'] the checkpoint trained on"
|
| 128 |
+
],
|
| 129 |
+
"in_distribution": {
|
| 130 |
+
"allowed": true,
|
| 131 |
+
"problems": [
|
| 132 |
+
"cal/rows.json: evals/v7/decision-v7 is training data of the checkpoint",
|
| 133 |
+
"cal/rows.json: it pools 12 training source(s) ['agnews', 'amazon', 'banking77', 'boolq', 'compositional', 'dbpedia14', 'imdb', 'legacy_policy'] ...",
|
| 134 |
+
"cal/rows.json: it reads the calibration partition of evals/v7/decision-v7, a training corpus",
|
| 135 |
+
"calpr/rows.json: SAME CORPUS: evals/d1a/pr-labels:development is an eval partition of evals/d1a/pr-labels, whose train partitions ['evals/d1a/pr-labels:train', 'evals/d1a/pr-labels:train-blast', 'evals/d1a/pr-labels:train-ja'] the checkpoint trained on",
|
| 136 |
+
"cannot list the sources of --data /work/data/train-e2b-v06.jsonl (outside evals/, no sidecar train-e2b-v06.jsonl.json naming its sources), training data of d1"
|
| 137 |
+
]
|
| 138 |
+
}
|
| 139 |
+
},
|
| 140 |
+
"use_case_temperatures": {},
|
| 141 |
+
"use_case_temperature_fits": {}
|
| 142 |
+
}
|
embeddings.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:d6e0735da110bf28359f69f4aa48db25dd8385c629e2c411b1fa6bb8c61a1694
|
| 3 |
+
size 1749025704
|
head.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:5b56c4f9da06b75a359da342e6002c6c73896033925b88e9e13c3757878e7447
|
| 3 |
+
size 3148080
|
io.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"chunks": ["00-07", "07-15", "15-22", "22-29", "29-35"], "inputs": {"00-07": ["emb", "ple_tok", "pos", "mask_full", "mask_slide"], "07-15": ["h_in", "ple", "pos", "mask_full", "mask_slide"], "15-22": ["h_in", "ple", "pos", "mask_full", "mask_slide", "k_full_attention", "v_full_attention", "k_sliding_attention", "v_sliding_attention"], "22-29": ["h_in", "ple", "pos", "mask_full", "mask_slide", "k_full_attention", "v_full_attention", "k_sliding_attention", "v_sliding_attention"], "29-35": ["h_in", "ple", "pos", "mask_full", "mask_slide", "k_full_attention", "v_full_attention", "k_sliding_attention", "v_sliding_attention", "picks"]}}
|
reference/d1a_coreml.py
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""D1A (Gemma 4 text) -> Core ML pilot (jonpol01/d1a#224): rebuild a D1A MLX export as a torch module, split into
|
| 2 |
+
- host side: the two embedding tables (token embedding, per-layer embedding: 2.75B of E2B's 4.6B parameters) read
|
| 3 |
+
row by row from the export's safetensors (dequantized per request, never held whole), and
|
| 4 |
+
- Core: everything that computes (per-layer projection, 35 decoder layers, final norm, pointer head), which is what
|
| 5 |
+
gets converted to Core ML.
|
| 6 |
+
Inputs follow D1A's packed encoding (d1a.core.encoding.encode): one record, block-causal mask, positions restarting per
|
| 7 |
+
question after the state, readout at each question's <decide> and </opt> tokens."""
|
| 8 |
+
import json
|
| 9 |
+
import math
|
| 10 |
+
from pathlib import Path
|
| 11 |
+
|
| 12 |
+
import numpy as np
|
| 13 |
+
import torch
|
| 14 |
+
from safetensors import safe_open
|
| 15 |
+
from torch import nn
|
| 16 |
+
from transformers import AutoConfig
|
| 17 |
+
from transformers.models.gemma4 import modeling_gemma4 as G
|
| 18 |
+
|
| 19 |
+
PREFIX = "language_model.model."
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def dequant(packed, scales, biases, bits, group=64):
|
| 23 |
+
"""MLX affine dequantization: packed uint32 [out, in*bits/32] (lowest bits first) -> float32 [out, in]."""
|
| 24 |
+
p = packed.view(torch.int32).to(torch.int64) & 0xFFFFFFFF
|
| 25 |
+
per = 32 // bits
|
| 26 |
+
shifts = torch.arange(per, dtype=torch.int64) * bits
|
| 27 |
+
q = ((p[..., None] >> shifts) & ((1 << bits) - 1)).reshape(p.shape[0], -1).to(torch.float32)
|
| 28 |
+
s, b = scales.to(torch.float32), biases.to(torch.float32)
|
| 29 |
+
return (q.view(q.shape[0], -1, group) * s[..., None] + b[..., None]).reshape(q.shape[0], -1)
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
class _Shards:
|
| 33 |
+
"""model.safetensors or model-0000N-of-0000M.safetensors (via model.safetensors.index.json), read like one safe_open."""
|
| 34 |
+
|
| 35 |
+
def __init__(self, path):
|
| 36 |
+
idx = Path(path) / "model.safetensors.index.json"
|
| 37 |
+
if idx.exists():
|
| 38 |
+
wm = json.loads(idx.read_text())["weight_map"]
|
| 39 |
+
files = {f: safe_open(str(Path(path) / f), "pt") for f in sorted(set(wm.values()))}
|
| 40 |
+
self.where = {k: files[f] for k, f in wm.items()}
|
| 41 |
+
else: # a released Core ML bundle ships only the embedding tables, as embeddings.safetensors
|
| 42 |
+
name = "model.safetensors" if (Path(path) / "model.safetensors").exists() else "embeddings.safetensors"
|
| 43 |
+
f = safe_open(str(Path(path) / name), "pt")
|
| 44 |
+
self.where = {k: f for k in f.keys()}
|
| 45 |
+
|
| 46 |
+
def keys(self):
|
| 47 |
+
return self.where.keys()
|
| 48 |
+
|
| 49 |
+
def get_tensor(self, k):
|
| 50 |
+
return self.where[k].get_tensor(k)
|
| 51 |
+
|
| 52 |
+
def get_slice(self, k):
|
| 53 |
+
return self.where[k].get_slice(k)
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
class Export:
|
| 57 |
+
"""A D1A MLX export directory (config.json, d1a_config.json, model.safetensors, head.safetensors)."""
|
| 58 |
+
|
| 59 |
+
def __init__(self, path):
|
| 60 |
+
self.path = Path(path)
|
| 61 |
+
self.cfg = AutoConfig.from_pretrained(self.path)
|
| 62 |
+
self.tc = self.cfg.text_config
|
| 63 |
+
self.tc._attn_implementation = "eager"
|
| 64 |
+
self.d1a = json.loads((self.path / "d1a_config.json").read_text())
|
| 65 |
+
q = self.d1a["quantization"]
|
| 66 |
+
self.bits = {"default": (q["bits"], q["group_size"]), "embed_tokens_per_layer": (q[PREFIX.replace("model.", "") + "model.embed_tokens_per_layer"]["bits"] if False else q["language_model.model.embed_tokens_per_layer"]["bits"], q["language_model.model.embed_tokens_per_layer"]["group_size"])}
|
| 67 |
+
self.f = _Shards(self.path)
|
| 68 |
+
|
| 69 |
+
def bits_of(self, name):
|
| 70 |
+
return self.bits["embed_tokens_per_layer"] if name.endswith("embed_tokens_per_layer") else self.bits["default"]
|
| 71 |
+
|
| 72 |
+
def tensor(self, name):
|
| 73 |
+
"""A float32 tensor for `name` (without PREFIX), dequantized if it is stored quantized."""
|
| 74 |
+
full = PREFIX + name
|
| 75 |
+
keys = set(self.f.keys())
|
| 76 |
+
if full + ".weight" in keys and full + ".scales" in keys:
|
| 77 |
+
bits, group = self.bits_of(name)
|
| 78 |
+
return dequant(self.f.get_tensor(full + ".weight"), self.f.get_tensor(full + ".scales"), self.f.get_tensor(full + ".biases"), bits, group)
|
| 79 |
+
return self.f.get_tensor(full).to(torch.float32)
|
| 80 |
+
|
| 81 |
+
def text_model(self, dtype=torch.float32):
|
| 82 |
+
"""The Gemma 4 text trunk with every non-embedding weight loaded (embedding tables stay empty: host side)."""
|
| 83 |
+
with torch.device("meta"):
|
| 84 |
+
m = G.Gemma4TextModel(self.tc)
|
| 85 |
+
m = m.to_empty(device="cpu")
|
| 86 |
+
state = {}
|
| 87 |
+
keys = set(self.f.keys())
|
| 88 |
+
for name, p in m.named_parameters():
|
| 89 |
+
if name.startswith("embed_tokens"):
|
| 90 |
+
continue
|
| 91 |
+
base = name[: -len(".weight")] if name.endswith(".weight") else name
|
| 92 |
+
if PREFIX + base + ".scales" in keys:
|
| 93 |
+
state[name] = self.tensor(base)
|
| 94 |
+
else:
|
| 95 |
+
state[name] = self.f.get_tensor(PREFIX + name).to(torch.float32)
|
| 96 |
+
assert state[name].shape == p.shape, (name, state[name].shape, p.shape)
|
| 97 |
+
for name, b in m.named_buffers():
|
| 98 |
+
if PREFIX + name in keys:
|
| 99 |
+
state[name] = self.f.get_tensor(PREFIX + name).to(torch.float32)
|
| 100 |
+
missing, unexpected = m.load_state_dict(state, strict=False)
|
| 101 |
+
missing = [k for k in missing if not k.startswith("embed_tokens")]
|
| 102 |
+
assert not missing and not unexpected, (missing, unexpected)
|
| 103 |
+
# buffers transformers computes itself (rotary inv_freq, embed_scale): re-run their init on CPU
|
| 104 |
+
m.rotary_emb = G.Gemma4TextRotaryEmbedding(self.tc)
|
| 105 |
+
return m.to(dtype).eval()
|
| 106 |
+
|
| 107 |
+
def head(self):
|
| 108 |
+
h = safe_open(str(self.path / "head.safetensors"), "pt")
|
| 109 |
+
dp, d = h.get_tensor("q.weight").shape # pointer width, hidden size (E2B 1536, E4B 2560)
|
| 110 |
+
q, k = nn.Linear(d, dp), nn.Linear(d, dp)
|
| 111 |
+
q.weight.data, q.bias.data = h.get_tensor("q.weight").float(), h.get_tensor("q.bias").float()
|
| 112 |
+
k.weight.data, k.bias.data = h.get_tensor("k.weight").float(), h.get_tensor("k.bias").float()
|
| 113 |
+
return q, k
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
class HostEmbed:
|
| 117 |
+
"""Row lookups of the two embedding tables, dequantized per request: emb = E[ids] * sqrt(hidden) and
|
| 118 |
+
ple = E_pl[ids] * sqrt(ple_dim), reshaped [L, layers, ple_dim] (transformers' get_per_layer_inputs)."""
|
| 119 |
+
|
| 120 |
+
def __init__(self, ex):
|
| 121 |
+
self.ex, self.tc = ex, ex.tc
|
| 122 |
+
self.f = ex.f
|
| 123 |
+
|
| 124 |
+
def _rows(self, name, ids):
|
| 125 |
+
full = PREFIX + name
|
| 126 |
+
bits, group = self.ex.bits_of(name)
|
| 127 |
+
w, s, b = (self.f.get_slice(full + suf) for suf in (".weight", ".scales", ".biases"))
|
| 128 |
+
uniq, inv = np.unique(np.asarray(ids), return_inverse=True)
|
| 129 |
+
rows = [dequant(w[int(i): int(i) + 1], s[int(i): int(i) + 1], b[int(i): int(i) + 1], bits, group) for i in uniq]
|
| 130 |
+
return torch.cat(rows)[torch.as_tensor(inv)]
|
| 131 |
+
|
| 132 |
+
def __call__(self, ids):
|
| 133 |
+
emb = self._rows("embed_tokens", ids) * (self.tc.hidden_size ** 0.5)
|
| 134 |
+
ple = self._rows("embed_tokens_per_layer", ids) * (self.tc.hidden_size_per_layer_input ** 0.5)
|
| 135 |
+
return emb[None], ple.view(1, len(ids), self.tc.num_hidden_layers, self.tc.hidden_size_per_layer_input)
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
def masks_for(enc, window, L=None, dtype=torch.float32):
|
| 139 |
+
"""D1A's packed masks (d1a.backends.torch.branch_masks) for one record, padded to L: additive [1, 1, L, L]."""
|
| 140 |
+
seg, pos = enc["seg"], enc["pos"]
|
| 141 |
+
n = len(seg)
|
| 142 |
+
L = L or n
|
| 143 |
+
s = torch.full((L,), -1)
|
| 144 |
+
s[:n] = torch.tensor(seg)
|
| 145 |
+
p = torch.zeros(L, dtype=torch.long)
|
| 146 |
+
p[:n] = torch.tensor(pos)
|
| 147 |
+
causal = torch.tril(torch.ones(L, L, dtype=torch.bool))
|
| 148 |
+
same = (s[None, :] == s[:, None]) | (s[None, :] == 0)
|
| 149 |
+
allow = (causal & same & (s != -1)[None, :]) | torch.eye(L, dtype=torch.bool)
|
| 150 |
+
neg = torch.finfo(dtype).min
|
| 151 |
+
full = torch.zeros(L, L, dtype=dtype).masked_fill(~allow, neg)
|
| 152 |
+
far = (p[:, None] - p[None, :] >= window) & ~torch.eye(L, dtype=torch.bool)
|
| 153 |
+
slide = full.masked_fill(far, neg)
|
| 154 |
+
return full[None, None], slide[None, None], p[None]
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
class Core(nn.Module):
|
| 158 |
+
"""Everything that computes: per-layer projection, decoder layers, final norm, pointer head.
|
| 159 |
+
forward(emb [1,L,d], ple_tok [1,L,layers,256], pos [1,L] int, mask_full, mask_slide [1,1,L,L],
|
| 160 |
+
decide [Q] int, opts [Q,K] int, opt_valid [Q,K] float (1 used, 0 padding)) -> logits [Q,K]
|
| 161 |
+
(raw, temperature not applied; padded options get a large negative)."""
|
| 162 |
+
|
| 163 |
+
def __init__(self, text, head_q, head_k, silu_tanh=True):
|
| 164 |
+
super().__init__()
|
| 165 |
+
self.text, self.hq, self.hk = text, head_q, head_k
|
| 166 |
+
self.types = text.config.layer_types
|
| 167 |
+
self.scale = 1 / math.sqrt(head_q.out_features)
|
| 168 |
+
|
| 169 |
+
def forward(self, emb, ple_tok, pos, mask_full, mask_slide, decide, opts, opt_valid):
|
| 170 |
+
t = self.text
|
| 171 |
+
ple = t.project_per_layer_inputs(emb, ple_tok)
|
| 172 |
+
rope = {lt: t.rotary_emb(emb, pos, lt) for lt in t.unique_layer_types}
|
| 173 |
+
masks = {"full_attention": mask_full, "sliding_attention": mask_slide}
|
| 174 |
+
from collections import UserDict
|
| 175 |
+
kv = UserDict()
|
| 176 |
+
h = emb
|
| 177 |
+
for i, layer in enumerate(t.layers):
|
| 178 |
+
h = layer(h, ple[:, :, i, :], shared_kv_states=kv, position_embeddings=rope[self.types[i]],
|
| 179 |
+
attention_mask=masks[self.types[i]], position_ids=pos)
|
| 180 |
+
h = t.norm(h)[0] # [L, d]
|
| 181 |
+
q = self.hq(h[decide]) # [Q, dp]
|
| 182 |
+
k = self.hk(h[opts.reshape(-1)]).view(*opts.shape, -1) # [Q, K, dp]
|
| 183 |
+
z = (k * q[:, None, :]).sum(-1) * self.scale
|
| 184 |
+
return z + (opt_valid - 1.0) * 1e4
|
reference/pipeline.py
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Run a chunked D1A Core ML trunk end to end: host embeddings -> chunk 0 .. chunk N (h, per-layer inputs and the shared
|
| 2 |
+
keys/values passed along) -> hidden states at the picks -> pointer head on the host.
|
| 3 |
+
Usage as a library: Pipeline(export_dir, chunk_dir, L, units).probs(enc) -> [per-question probabilities]."""
|
| 4 |
+
import glob
|
| 5 |
+
import os
|
| 6 |
+
import re
|
| 7 |
+
import sys
|
| 8 |
+
import time
|
| 9 |
+
|
| 10 |
+
import coremltools as ct
|
| 11 |
+
import numpy as np
|
| 12 |
+
import torch
|
| 13 |
+
|
| 14 |
+
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) # d1a_coreml.py sits next to this file
|
| 15 |
+
from d1a_coreml import Export, HostEmbed, masks_for # noqa: E402
|
| 16 |
+
|
| 17 |
+
P_PICKS = 128
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
class Pipeline:
|
| 21 |
+
def __init__(self, export_dir, chunk_dir, L, units="CPU_AND_NE"):
|
| 22 |
+
self.ex = Export(export_dir)
|
| 23 |
+
self.host = HostEmbed(self.ex)
|
| 24 |
+
self.hq, self.hk = self.ex.head()
|
| 25 |
+
self.T = self.ex.d1a["temperature"]
|
| 26 |
+
self.L = L
|
| 27 |
+
cu = getattr(ct.ComputeUnit, units)
|
| 28 |
+
self.chunks = []
|
| 29 |
+
if chunk_dir.endswith(".mlpackage"): # one multifunction package: functions c00_07, c07_15, ...
|
| 30 |
+
names = ["c00_07", "c07_15", "c15_22", "c22_29", "c29_35"]
|
| 31 |
+
for name in names:
|
| 32 |
+
a, b = map(int, name[1:].split("_"))
|
| 33 |
+
m = ct.models.MLModel(chunk_dir, compute_units=cu, function_name=name)
|
| 34 |
+
fn = next(f for f in m.get_spec().description.functions if f.name == name)
|
| 35 |
+
self.chunks.append((a, b, m, [i.name for i in fn.input], [o.name for o in fn.output]))
|
| 36 |
+
return
|
| 37 |
+
files = sorted(glob.glob(f"{chunk_dir}/L{L}_*.mlpackage"))
|
| 38 |
+
for f in files:
|
| 39 |
+
a, b = map(int, re.search(r"_(\d+)-(\d+)\.mlpackage$", f).groups())
|
| 40 |
+
m = ct.models.MLModel(f, compute_units=cu)
|
| 41 |
+
spec = m.get_spec()
|
| 42 |
+
self.chunks.append((a, b, m, [i.name for i in spec.description.input], [o.name for o in spec.description.output]))
|
| 43 |
+
|
| 44 |
+
def hidden(self, enc):
|
| 45 |
+
L, n = self.L, len(enc["ids"])
|
| 46 |
+
# right-pad with the pad token's own embeddings (as d1a's torch backend pads ids), never zero rows: an all-zero row
|
| 47 |
+
# makes every RMSNorm compute 0 / sqrt(eps), and eps (1e-6) is subnormal in fp16, which the Neural Engine flushes
|
| 48 |
+
ids = list(enc["ids"]) + [self.ex.d1a.get("pad_id", 0)] * (L - n)
|
| 49 |
+
emb, ple = self.host(ids)
|
| 50 |
+
e = emb.numpy().astype(np.float16)
|
| 51 |
+
p = ple.numpy().astype(np.float16)
|
| 52 |
+
mf, ms, pos = masks_for(enc, self.ex.tc.sliding_window, L=L)
|
| 53 |
+
picks = list(enc["decide_idx"]) + [o for oo in enc["opt_idx"] for o in oo]
|
| 54 |
+
pk = np.zeros(P_PICKS, np.int32); pk[: len(picks)] = picks
|
| 55 |
+
common = {"pos": pos.numpy().astype(np.int32), "mask_full": mf.clamp(min=-1e4).numpy().astype(np.float16),
|
| 56 |
+
"mask_slide": ms.clamp(min=-1e4).numpy().astype(np.float16)}
|
| 57 |
+
state = {"emb": e, "ple_tok": p, "picks": pk}
|
| 58 |
+
for a, b, m, ins, outs in self.chunks:
|
| 59 |
+
feed = {k: np.ascontiguousarray(common[k] if k in common else state[k]) for k in ins}
|
| 60 |
+
feed = {k: (v.astype(np.float16) if v.dtype == np.float32 else v) for k, v in feed.items()} # chunk outputs come back fp32
|
| 61 |
+
out = {k: np.array(v, copy=True) for k, v in m.predict(feed).items()} # Core ML may reuse its output buffers on the next call
|
| 62 |
+
if "hidden" in out:
|
| 63 |
+
return torch.tensor(out["hidden"][: len(picks)], dtype=torch.float32)
|
| 64 |
+
state["h_in"] = out["h"]
|
| 65 |
+
if "ple_out" in out:
|
| 66 |
+
state["ple"] = out["ple_out"]
|
| 67 |
+
for k, v in out.items():
|
| 68 |
+
if k.endswith("_out") and k != "ple_out":
|
| 69 |
+
state[k[: -len("_out")]] = v
|
| 70 |
+
raise RuntimeError("no chunk returned hidden")
|
| 71 |
+
|
| 72 |
+
def probs(self, enc):
|
| 73 |
+
h = self.hidden(enc)
|
| 74 |
+
Q = len(enc["decide_idx"])
|
| 75 |
+
out, j = [], Q
|
| 76 |
+
with torch.no_grad():
|
| 77 |
+
for qi, oo in enumerate(enc["opt_idx"]):
|
| 78 |
+
z = (self.hk(h[j: j + len(oo)]) @ self.hq(h[qi])) / (self.hq.out_features ** 0.5)
|
| 79 |
+
out.append(torch.softmax(z / self.T, -1).numpy()); j += len(oo)
|
| 80 |
+
return out
|
reference/run.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Answer one System One request with a D1A Core ML bundle (this repository, downloaded), on the Neural Engine.
|
| 2 |
+
|
| 3 |
+
pip install coremltools numpy torch transformers safetensors "d1a @ git+https://github.com/jonpol01/d1a"
|
| 4 |
+
python reference/run.py <bundle dir> request.json
|
| 5 |
+
|
| 6 |
+
request.json is a /v1/systemone request: {"state": "...", "questions": {"id": {"type": "noul" | "choice" | "score", ...}}}.
|
| 7 |
+
The request must encode to 512 tokens or fewer. The first run compiles the chunks into coreml/.compiled/ (a few minutes);
|
| 8 |
+
later runs load them. Prints each question's options with their probabilities."""
|
| 9 |
+
import json
|
| 10 |
+
import os
|
| 11 |
+
import sys
|
| 12 |
+
|
| 13 |
+
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) # workers.py and d1a_coreml.py sit next to this file
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def main():
|
| 17 |
+
from transformers import AutoTokenizer
|
| 18 |
+
|
| 19 |
+
from d1a.core.api import SystemOneRequest, to_record
|
| 20 |
+
from d1a.core.encoding import encode
|
| 21 |
+
from workers import WorkerPipeline
|
| 22 |
+
|
| 23 |
+
bundle, request = sys.argv[1], sys.argv[2]
|
| 24 |
+
req = SystemOneRequest.model_validate({"model": "d1a-latest", **json.load(open(request))})
|
| 25 |
+
rec, meta = to_record(req)
|
| 26 |
+
enc = encode(AutoTokenizer.from_pretrained(bundle), rec)
|
| 27 |
+
if len(enc["ids"]) > 512:
|
| 28 |
+
raise SystemExit(f"the request encodes to {len(enc['ids'])} tokens; this build takes 512 or fewer")
|
| 29 |
+
pipe = WorkerPipeline(bundle, os.path.join(bundle, "coreml"), 512)
|
| 30 |
+
try:
|
| 31 |
+
for m, p in zip(meta, pipe.probs(enc)):
|
| 32 |
+
print(m["id"], {k: round(float(v), 4) for k, v in zip(m["keys"], p)})
|
| 33 |
+
finally:
|
| 34 |
+
pipe.close()
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
if __name__ == "__main__":
|
| 38 |
+
main()
|
reference/workers.py
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""One process per Core ML chunk (workaround: several long-context chunks in one process crash the Neural Engine runtime
|
| 2 |
+
on the second request on this Mac, while each chunk alone, or all on the GPU, runs fine). The parent runs the host side
|
| 3 |
+
(embeddings, masks, pointer head) and passes tensors along the chain through pipes.
|
| 4 |
+
WorkerPipeline(export_dir, chunk_dir, L, units).probs(enc)"""
|
| 5 |
+
import glob
|
| 6 |
+
import os
|
| 7 |
+
import multiprocessing as mp
|
| 8 |
+
import re
|
| 9 |
+
import shutil
|
| 10 |
+
import sys
|
| 11 |
+
|
| 12 |
+
import numpy as np
|
| 13 |
+
import torch
|
| 14 |
+
|
| 15 |
+
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) # d1a_coreml.py sits next to this file
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
BUCKETS = tuple(int(x) for x in os.environ.get("D1A_COREML_BUCKETS", "512").split(",")) # each length is its own ANE program; fewer keeps the ANE within its memory
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def compiled_chunks(chunk_dir):
|
| 22 |
+
"""-> [(first layer, end layer, .mlmodelc, its .mlpackage)]. A released bundle's coreml/L512_<a>-<b>.mlpackage are
|
| 23 |
+
compiled once, each as a one-function package (L512), into coreml/.compiled/; a build tree's compiled/*.mlmodelc
|
| 24 |
+
(multifunction, one function per length) are used as they are."""
|
| 25 |
+
import coremltools as ct
|
| 26 |
+
out = []
|
| 27 |
+
for f in sorted(glob.glob(f"{chunk_dir}/*.mlmodelc")):
|
| 28 |
+
a, b = map(int, re.search(r"(\d+)-(\d+)\.mlmodelc$", f).groups())
|
| 29 |
+
out.append((a, b, f, f.replace("compiled/", "buckets/").replace(".mlmodelc", ".mlpackage")))
|
| 30 |
+
if out:
|
| 31 |
+
return out
|
| 32 |
+
os.makedirs(f"{chunk_dir}/.compiled", exist_ok=True)
|
| 33 |
+
for src in sorted(glob.glob(f"{chunk_dir}/L*_*.mlpackage")):
|
| 34 |
+
L, a, b = map(int, re.search(r"L(\d+)_(\d+)-(\d+)\.mlpackage$", src).groups())
|
| 35 |
+
dst = f"{chunk_dir}/.compiled/{a:02d}-{b:02d}.mlmodelc"
|
| 36 |
+
if not os.path.exists(dst):
|
| 37 |
+
d = ct.utils.MultiFunctionDescriptor(); d.add_function(src, src_function_name="main", target_function_name=f"L{L}")
|
| 38 |
+
d.default_function_name = f"L{L}"; tmp = f"{chunk_dir}/.compiled/{a:02d}-{b:02d}.mlpackage"
|
| 39 |
+
ct.utils.save_multifunction(d, tmp); shutil.move(ct.models.utils.compile_model(tmp), dst); shutil.rmtree(tmp)
|
| 40 |
+
out.append((a, b, dst, src))
|
| 41 |
+
return out
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def _worker(path, spec_path, units, conn):
|
| 45 |
+
import coremltools as ct
|
| 46 |
+
cu = getattr(ct.ComputeUnit, units)
|
| 47 |
+
# a precompiled .mlmodelc (compiled once by coremltools.models.utils.compile_model): loading each length function
|
| 48 |
+
# from it never recompiles, where MLModel(.mlpackage) compiles the whole package again for every function
|
| 49 |
+
models = {L: ct.models.CompiledMLModel(path, compute_units=cu, function_name=f"L{L}") for L in BUCKETS}
|
| 50 |
+
spec = ct.utils.load_spec(spec_path)
|
| 51 |
+
fns = {f.name: f for f in spec.description.functions}
|
| 52 |
+
fn = fns.get(f"L{BUCKETS[0]}") or spec.description # a one-function package keeps its inputs in the description
|
| 53 |
+
conn.send(([i.name for i in fn.input], [o.name for o in fn.output]))
|
| 54 |
+
while True:
|
| 55 |
+
msg = conn.recv()
|
| 56 |
+
if msg is None:
|
| 57 |
+
break
|
| 58 |
+
L, feed = msg
|
| 59 |
+
import time as _t
|
| 60 |
+
t0 = _t.perf_counter()
|
| 61 |
+
out = models[L].predict(feed)
|
| 62 |
+
t1 = _t.perf_counter()
|
| 63 |
+
res = {k: np.array(v, dtype=np.float16 if v.dtype == np.float32 else v.dtype, copy=True) for k, v in out.items()}
|
| 64 |
+
res["_predict_ms"] = np.array((t1 - t0) * 1000)
|
| 65 |
+
conn.send(res)
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
class WorkerPipeline:
|
| 69 |
+
def __init__(self, export_dir, chunk_dir, L=None, units="CPU_AND_NE"):
|
| 70 |
+
from d1a_coreml import Export, HostEmbed
|
| 71 |
+
self.ex = Export(export_dir)
|
| 72 |
+
self.host = HostEmbed(self.ex)
|
| 73 |
+
self.hq, self.hk = self.ex.head()
|
| 74 |
+
self.T, self.L = self.ex.d1a["temperature"], L
|
| 75 |
+
ctx = mp.get_context("spawn")
|
| 76 |
+
self.chunks = []
|
| 77 |
+
for a, b, f, spec_path in compiled_chunks(chunk_dir):
|
| 78 |
+
parent, child = ctx.Pipe()
|
| 79 |
+
p = ctx.Process(target=_worker, args=(f, spec_path, units, child), daemon=True)
|
| 80 |
+
p.start()
|
| 81 |
+
self.chunks.append((a, b, p, parent))
|
| 82 |
+
self.io = getattr(self, "io", []) + [parent.recv()] # one at a time: concurrent first loads race in Core ML's compiler
|
| 83 |
+
|
| 84 |
+
def close(self):
|
| 85 |
+
for _, _, p, conn in self.chunks:
|
| 86 |
+
conn.send(None); p.join(5)
|
| 87 |
+
|
| 88 |
+
def hidden(self, enc):
|
| 89 |
+
from d1a_coreml import masks_for
|
| 90 |
+
self.last_predict_ms = []
|
| 91 |
+
n = len(enc["ids"])
|
| 92 |
+
L = next(b for b in BUCKETS if b >= n) # the smallest compiled length that fits
|
| 93 |
+
ids = list(enc["ids"]) + [self.ex.d1a.get("pad_id", 0)] * (L - n)
|
| 94 |
+
emb, ple = self.host(ids)
|
| 95 |
+
mf, ms, pos = masks_for(enc, self.ex.tc.sliding_window, L=L)
|
| 96 |
+
picks = list(enc["decide_idx"]) + [o for oo in enc["opt_idx"] for o in oo]
|
| 97 |
+
pk = np.zeros(128, np.int32); pk[: len(picks)] = picks
|
| 98 |
+
common = {"pos": pos.numpy().astype(np.int32), "mask_full": mf.clamp(min=-1e4).numpy().astype(np.float16),
|
| 99 |
+
"mask_slide": ms.clamp(min=-1e4).numpy().astype(np.float16)}
|
| 100 |
+
state = {"emb": emb.numpy().astype(np.float16), "ple_tok": ple.numpy().astype(np.float16), "picks": pk}
|
| 101 |
+
for (a, b, p, conn), (ins, outs) in zip(self.chunks, self.io):
|
| 102 |
+
conn.send((L, {k: (common[k] if k in common else state[k]) for k in ins}))
|
| 103 |
+
out = conn.recv()
|
| 104 |
+
self.last_predict_ms = getattr(self, "last_predict_ms", []) + [float(out.pop("_predict_ms"))]
|
| 105 |
+
if "hidden" in out:
|
| 106 |
+
return torch.tensor(out["hidden"][: len(picks)].astype(np.float32))
|
| 107 |
+
state["h_in"] = out["h"]
|
| 108 |
+
if "ple_out" in out:
|
| 109 |
+
state["ple"] = out["ple_out"]
|
| 110 |
+
for k, v in out.items():
|
| 111 |
+
if k.endswith("_out") and k != "ple_out":
|
| 112 |
+
state[k[: -len("_out")]] = v
|
| 113 |
+
raise RuntimeError("no chunk returned hidden")
|
| 114 |
+
|
| 115 |
+
def probs(self, enc):
|
| 116 |
+
h = self.hidden(enc)
|
| 117 |
+
Q = len(enc["decide_idx"])
|
| 118 |
+
out, j = [], Q
|
| 119 |
+
with torch.no_grad():
|
| 120 |
+
for qi, oo in enumerate(enc["opt_idx"]):
|
| 121 |
+
z = (self.hk(h[j: j + len(oo)]) @ self.hq(h[qi])) / (self.hq.out_features ** 0.5)
|
| 122 |
+
out.append(torch.softmax(z / self.T, -1).numpy()); j += len(oo)
|
| 123 |
+
return out
|
tokenizer.json
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:12bac982b793c44b03d52a250a9f0d0b666813da566b910c24a6da0695fd11e6
|
| 3 |
+
size 32170070
|
tokenizer_config.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"audio_token": "<|audio|>",
|
| 3 |
+
"backend": "tokenizers",
|
| 4 |
+
"boa_token": "<|audio>",
|
| 5 |
+
"boi_token": "<|image>",
|
| 6 |
+
"bos_token": "<bos>",
|
| 7 |
+
"eoa_token": "<audio|>",
|
| 8 |
+
"eoc_token": "<channel|>",
|
| 9 |
+
"eoi_token": "<image|>",
|
| 10 |
+
"eos_token": "<eos>",
|
| 11 |
+
"eot_token": "<turn|>",
|
| 12 |
+
"escape_token": "<|\"|>",
|
| 13 |
+
"etc_token": "<tool_call|>",
|
| 14 |
+
"etd_token": "<tool|>",
|
| 15 |
+
"etr_token": "<tool_response|>",
|
| 16 |
+
"extra_special_tokens": [
|
| 17 |
+
"<|video|>"
|
| 18 |
+
],
|
| 19 |
+
"image_token": "<|image|>",
|
| 20 |
+
"is_local": false,
|
| 21 |
+
"local_files_only": false,
|
| 22 |
+
"mask_token": "<mask>",
|
| 23 |
+
"model_max_length": 1000000000000000019884624838656,
|
| 24 |
+
"model_specific_special_tokens": {
|
| 25 |
+
"audio_token": "<|audio|>",
|
| 26 |
+
"boa_token": "<|audio>",
|
| 27 |
+
"boi_token": "<|image>",
|
| 28 |
+
"eoa_token": "<audio|>",
|
| 29 |
+
"eoc_token": "<channel|>",
|
| 30 |
+
"eoi_token": "<image|>",
|
| 31 |
+
"eot_token": "<turn|>",
|
| 32 |
+
"escape_token": "<|\"|>",
|
| 33 |
+
"etc_token": "<tool_call|>",
|
| 34 |
+
"etd_token": "<tool|>",
|
| 35 |
+
"etr_token": "<tool_response|>",
|
| 36 |
+
"image_token": "<|image|>",
|
| 37 |
+
"soc_token": "<|channel>",
|
| 38 |
+
"sot_token": "<|turn>",
|
| 39 |
+
"stc_token": "<|tool_call>",
|
| 40 |
+
"std_token": "<|tool>",
|
| 41 |
+
"str_token": "<|tool_response>",
|
| 42 |
+
"think_token": "<|think|>"
|
| 43 |
+
},
|
| 44 |
+
"pad_token": "<pad>",
|
| 45 |
+
"padding_side": "left",
|
| 46 |
+
"processor_class": "Gemma4Processor",
|
| 47 |
+
"soc_token": "<|channel>",
|
| 48 |
+
"sot_token": "<|turn>",
|
| 49 |
+
"stc_token": "<|tool_call>",
|
| 50 |
+
"std_token": "<|tool>",
|
| 51 |
+
"str_token": "<|tool_response>",
|
| 52 |
+
"think_token": "<|think|>",
|
| 53 |
+
"tokenizer_class": "GemmaTokenizer",
|
| 54 |
+
"unk_token": "<unk>"
|
| 55 |
+
}
|