mlboydaisuke commited on
Commit
b8a6ac3
·
verified ·
1 Parent(s): 6a28e1c

Re-export int8 per-block-32: the per-channel bundle had dead LM-head rows from vocab id ~65024 up (never emitted <|im_end|>)

Browse files
README.md CHANGED
@@ -20,12 +20,19 @@ Core AI is Apple's on-device ML runtime in iOS 27 / macOS 27 and the successor t
20
  This model has no row on [DeviceMark](https://devicemark.github.io/), the on-device LLM leaderboard.
21
  <!-- gen-cards:devicemark end -->
22
 
23
- # MiniCPM5-1B — Core AI (int8, runs on iPhone)
24
 
25
  Apple **Core AI** (`.aimodel`) conversion of [openbmb/MiniCPM5-1B](https://huggingface.co/openbmb/MiniCPM5-1B) —
26
  OpenBMB's 1.08B on-device LLM with **hybrid Think / No-Think reasoning** and **128K** context, reaching
27
  1B-class open-source SOTA. Runs fully on-device on **iPhone** and Apple Silicon Macs (GPU, pipelined engine).
28
 
 
 
 
 
 
 
 
29
  Part of the community Core AI model zoo: **https://github.com/john-rocky/coreai-model-zoo**
30
 
31
  <!-- gen-cards:use-it begin id=minicpm5-1b (managed by scripts/gen-cards — edit cards.json / QuickStart.swift, not this block) -->
@@ -79,29 +86,62 @@ conversation history; `streamResponse(to:)` yields tokens as they decode.
79
  - Measure in Release — Debug is ~3× slower on per-token host work
80
  <!-- gen-cards:use-it end -->
81
 
82
- ## On-device numbers (iPhone 17 Pro, A19 Pro)
83
-
84
- Measured with the zoo's `PipelinedBench` (random 128-token prompt, greedy):
85
-
86
- | | decode | prefill | quality | size | engine-ready |
87
- |---|---:|---:|---|---:|---:|
88
- | **`int8/`** (ship) | **66.8 tok/s** | 68.0 tok/s | **lossless** (24/24 token-exact vs HF fp32) | **1.0 GB** | 2.0 s |
89
-
90
- `int8` is **~2.2× faster than fp16** on iPhone (decode is memory-bandwidth-bound, so halving the
91
- weight read ≈ doubles throughput) at **no quality cost** — the device greedy output is token-for-token
92
- identical to the fp32 reference on the benchmark prompts. So int8 strictly dominates fp16 here.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
 
94
  ## Quantization
95
 
96
- Weight-only **symmetric per-channel int8** (absmax, no clipping — clipping craters the 130k-vocab LM
97
- head; absmax keeps it lossless), applied as a torch pre-export pass via `coreai-opt`; SDPA / RoPE /
98
- RMSNorm stay full precision. Same recipe family as the zoo's proven `sym8`.
99
 
100
  ```bash
101
  uv run coreai.llm.export openbmb/MiniCPM5-1B --experimental --compute-precision float16 \
102
- --compression-config minicpm5_int8sym.yaml
103
- # minicpm5_int8sym.yaml: quantization_config → op_state_spec.weight = {dtype: int8,
104
- # qscheme: symmetric, granularity: {type: per_channel, axis: 0}}
105
  ```
106
 
107
  ## Conversion notes
@@ -110,10 +150,14 @@ uv run coreai.llm.export openbmb/MiniCPM5-1B --experimental --compute-precision
110
  `llama` graph family, but Mistral's builder is architecturally identical for this config (GQA,
111
  no qkv bias, no qk-norm, explicit `head_dim` honored). One-line remap in the model registry.
112
  - **Chat EOS.** Base `eos_token` is `</s>`, but the chat template ends turns with `<|im_end|>`
113
- (id 130073). The bundle's tokenizer `eos_token` is set to `<|im_end|>` (as Qwen ships) so
114
- generation halts cleanly.
 
115
  - **Dynamic-shape bundle** → the Core AI pipelined engine (the iPhone path); a static iOS export
116
  routes to the static-shape engine instead, which this FM-format bundle doesn't target.
 
 
 
117
 
118
  ## Run
119
 
@@ -126,6 +170,20 @@ let session = LanguageModelSession(model: model)
126
  print(try await session.respond(to: "Explain on-device AI in one sentence."))
127
  ```
128
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
  ## License
130
 
131
  Apache-2.0 (upstream MiniCPM5 license). Model © OpenBMB — see
 
20
  This model has no row on [DeviceMark](https://devicemark.github.io/), the on-device LLM leaderboard.
21
  <!-- gen-cards:devicemark end -->
22
 
23
+ # MiniCPM5-1B — Core AI (int8 block-32, runs on iPhone)
24
 
25
  Apple **Core AI** (`.aimodel`) conversion of [openbmb/MiniCPM5-1B](https://huggingface.co/openbmb/MiniCPM5-1B) —
26
  OpenBMB's 1.08B on-device LLM with **hybrid Think / No-Think reasoning** and **128K** context, reaching
27
  1B-class open-source SOTA. Runs fully on-device on **iPhone** and Apple Silicon Macs (GPU, pipelined engine).
28
 
29
+ > **Revision note (2026-09-09).** This revision replaces the per-channel int8 bundle published as
30
+ > `5ad650f`. That bundle's LM head had **dead rows from vocab id ~65024 up** — every token there
31
+ > scored ~0 in the engine, `<|im_end|>` (130073) included — so a chat turn **never halted** (it ran
32
+ > to the token cap) and any answer needing a high-id token lost it. The card's "halts cleanly" line
33
+ > was wrong. Details and the measurements are under *Why per-block-32* below; pin this revision or
34
+ > newer.
35
+
36
  Part of the community Core AI model zoo: **https://github.com/john-rocky/coreai-model-zoo**
37
 
38
  <!-- gen-cards:use-it begin id=minicpm5-1b (managed by scripts/gen-cards — edit cards.json / QuickStart.swift, not this block) -->
 
86
  - Measure in Release — Debug is ~3× slower on per-token host work
87
  <!-- gen-cards:use-it end -->
88
 
89
+ ## Measured
90
+
91
+ | | decode | prefill | numerics | size |
92
+ |---|---:|---:|---|---:|
93
+ | **iPhone 17 Pro** (A19 Pro, `PipelinedBench`, Release) | **61.7 tok/s** | 65.6 tok/s | **24/24 token-exact** vs HF fp32 on the margin-clean alphabet prompt (min fp32 top-2 margin 0.841) **+ 6/6 including the stop** on the no-think turn `1+1=?` (`1+1=2` then `<|im_end|>`); engine ready 7.3 s cold | **1.1 GB** |
94
+ | **M4 Max** (macOS 27, `llm-benchmark`, 512p/1024g) | **246.6 tok/s** | 6649 tok/s | **16/16 token-exact** vs the fp32 oracle (margin-aware gate, min margin 0.913) + the same 6/6 stop gate | |
95
+
96
+ Halt check through the engine on the same phone-shaped bundle: the Think-mode turn `1+1=?` stops on
97
+ its own after 171 tokens (cap 400); the no-think turn after 6.
98
+
99
+ ⚠️ **iPhone context cap: prompt + generated tokens must stay under 1024.** The bundle declares a 131072
100
+ dynamic KV, and the shipped `CoreAIPipelinedEngine` caps iOS growing-KV capacity at 1024 (its guard
101
+ against the iOS compiler miscompiling growing-KV specializations at seq ≥ 2048). Chunk or trim the
102
+ history on iOS; macOS has no cap.
103
+
104
+ ## Why per-block-32 (what was wrong with the previous revision)
105
+
106
+ The previous bundle (`5ad650f`, int8 **per-channel** absmax) passed a 24-token greedy parity check and
107
+ still never ended a chat turn. Teacher-forcing it through the engine against the fp32 reference
108
+ located the defect in the LM head, by vocab id:
109
+
110
+ | token (id) | fp32 P | per-channel bundle | per-block-32 bundle (this revision) |
111
+ |---|---:|---:|---:|
112
+ | `.lineTo` (65023) | 0.978 | 0.993 | — |
113
+ | `粒子` (65039) | 0.977 | **0.0000** | 0.98 |
114
+ | ` chromosomal` (65528) | 0.383 | **0.0000** | 0.380 |
115
+ | ` OpenAI` (130051) | 0.929 | **0.0000** | 0.923 |
116
+ | `\n\n` after `</think>` (130063) | 0.9999 | **0.0000** | 0.9999 |
117
+ | `<|im_end|>` after `1+1=2` (130073) | 0.873 | **0.0000** (top-1 `.`) | 0.867 |
118
+
119
+ Every probed row at id ≥ 65039 is dead in the per-channel bundle and every probed row ≤ 65023 is
120
+ healthy (16 probes; the boundary lies in that 16-id window, which contains 65024 = 127 × 512). Rows
121
+ below it match fp32 to ~0.01 in probability, which is why free-running English prose looked fine.
122
+ A fresh per-channel export on the same toolchain (coreai-torch 0.4.1 / coreai-opt 0.2.1 /
123
+ coreai-core 1.0.0b2) reproduces the shipped bundle's logits to four decimals, so this is a property
124
+ of the per-channel int8 path for this 130560-row head, not a one-off; per-block-32 (this bundle),
125
+ the CLI's default int4 preset and fp16 (`--compression none`) are all clean on the same probes.
126
+ Which component owns the row cut-off (quantizer, converter or the runtime's per-channel int8 matmul)
127
+ is not established here.
128
+
129
+ The gate that catches it is now part of the zoo's `cli/coreai_verify.py`: `--chat no-think --prompt
130
+ "1+1=?"` makes the fp32 oracle's `<|im_end|>` a gated step — a bundle that runs past a stop the oracle
131
+ takes at margin 0.80 fails — and `--must-stop-within N` is the plain halt check. The old bundle is red
132
+ on both, this one green.
133
 
134
  ## Quantization
135
 
136
+ Weight-only **symmetric int8, per-block-32** (a scale per 32-wide block along the input dim; no
137
+ clipping), applied as a torch pre-export pass via `coreai-opt`; SDPA / RoPE / RMSNorm stay full
138
+ precision — the same YAML that ships the [2B](https://huggingface.co/mlboydaisuke/MiniCPM5-2B-CoreAI).
139
 
140
  ```bash
141
  uv run coreai.llm.export openbmb/MiniCPM5-1B --experimental --compute-precision float16 \
142
+ --compression-config minicpm5_int8sym_b32.yaml
143
+ # minicpm5_int8sym_b32.yaml: quantization_config → op_state_spec.weight = {dtype: int8,
144
+ # qscheme: symmetric, granularity: {type: per_block, block_size: 32}}
145
  ```
146
 
147
  ## Conversion notes
 
150
  `llama` graph family, but Mistral's builder is architecturally identical for this config (GQA,
151
  no qkv bias, no qk-norm, explicit `head_dim` honored). One-line remap in the model registry.
152
  - **Chat EOS.** Base `eos_token` is `</s>`, but the chat template ends turns with `<|im_end|>`
153
+ (id 130073). The bundle's tokenizer `eos_token` is set to `<|im_end|>` (as Qwen ships). Checked
154
+ through the engine with the chat template applied: the no-think turn `1+1=?` answers `1+1=2` and
155
+ stops at step 5, where the fp32 reference stops (margin 0.80).
156
  - **Dynamic-shape bundle** → the Core AI pipelined engine (the iPhone path); a static iOS export
157
  routes to the static-shape engine instead, which this FM-format bundle doesn't target.
158
+ - **Thinking.** The model thinks by default (`<think>…</think>` before the answer); pass
159
+ `enable_thinking=False` through the chat template for a direct answer. Give generation a
160
+ generous budget (the kit caps at 4096) — the think trace alone can run a few hundred tokens.
161
 
162
  ## Run
163
 
 
170
  print(try await session.respond(to: "Explain on-device AI in one sentence."))
171
  ```
172
 
173
+ ## Reproduce
174
+
175
+ Exporter, gate, card and port notes live in the
176
+ [Core AI model zoo](https://github.com/john-rocky/coreai-model-zoo):
177
+ [`models/minicpm5-1b/`](https://github.com/john-rocky/coreai-model-zoo/tree/main/models/minicpm5-1b),
178
+ [`conversion/export_minicpm5.py`](https://github.com/john-rocky/coreai-model-zoo/blob/main/conversion/export_minicpm5.py),
179
+ [`knowledge/minicpm5-1b.md`](https://github.com/john-rocky/coreai-model-zoo/blob/main/knowledge/minicpm5-1b.md).
180
+
181
+ ```bash
182
+ python3 conversion/zoo_convert.py show minicpm5-1b
183
+ python3 conversion/zoo_convert.py run minicpm5-1b
184
+ python3 cli/coreai_verify.py <bundle> --chat no-think --prompt "1+1=?" -n 16 --must-stop-within 16
185
+ ```
186
+
187
  ## License
188
 
189
  Apache-2.0 (upstream MiniCPM5 license). Model © OpenBMB — see
int8/metadata.json CHANGED
@@ -20,9 +20,9 @@
20
  "model_definition": "torch",
21
  "hf_model_id": "openbmb/MiniCPM5-1B"
22
  },
23
- "compression": "_minicpm5_int8sym",
24
  "compilation": {
25
- "date": "2026-06-26T17:39:31.612088+09:00",
26
  "targets": []
27
  }
28
- }
 
20
  "model_definition": "torch",
21
  "hf_model_id": "openbmb/MiniCPM5-1B"
22
  },
23
+ "compression": "minicpm5_int8sym_b32",
24
  "compilation": {
25
+ "date": "2026-09-09T22:31:10.485907+09:00",
26
  "targets": []
27
  }
28
+ }
int8/minicpm5_1b_int8.aimodel/main.hash CHANGED
@@ -1 +1 @@
1
- �$�Q���xw+�}��+������INr�6���1K
 
1
+ c���$��˰�_��� B��JW�,_��$ܯ
int8/minicpm5_1b_int8.aimodel/main.mlirb CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:bd24d951f785c00478772ba07d9bc52ba184f9fd8c9b494e72c136a1bbe3314b
3
- size 1082313882
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6380a69f24a0b714cbb0fd5f94dfe8204203e6cf054a578b2c5fc097241edcaf
3
+ size 1148614014
int8/minicpm5_1b_int8.aimodel/metadata.json CHANGED
@@ -1,8 +1,8 @@
1
  {
 
2
  "author" : "OpenBMB",
3
- "creationDate" : "20260720T175201Z",
4
  "assetVersion" : "2.0",
5
- "license" : "Apache-2.0",
6
- "producer" : "coreai-core 1.0.0b2",
7
- "description" : "MiniCPM5-1B is a 1.08B-parameter on-device causal language model from OpenBMB, with hybrid (Think \/ No-Think) reasoning and 128K context, reaching 1B-class open-source SOTA. Source: https:\/\/huggingface.co\/openbmb\/MiniCPM5-1B"
8
  }
 
1
  {
2
+ "license" : "Apache-2.0",
3
  "author" : "OpenBMB",
 
4
  "assetVersion" : "2.0",
5
+ "description" : "MiniCPM5-1B is a 1.08B-parameter on-device causal language model from OpenBMB, with hybrid (Think \/ No-Think) reasoning and 128K context, reaching 1B-class open-source SOTA. Source: https:\/\/huggingface.co\/openbmb\/MiniCPM5-1B",
6
+ "creationDate" : "20260909T133108Z",
7
+ "producer" : "coreai-core 1.0.0b2"
8
  }