Instructions to use litert-community/MiniCPM5-2B with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- LiteRT
How to use litert-community/MiniCPM5-2B with LiteRT:
# No code snippets available yet for this library. # To use this model, check the repository files and the library's documentation. # Want to help? PRs adding snippets are welcome at: # https://github.com/huggingface/huggingface.js
- Notebooks
- Google Colab
- Kaggle
Card: correctness, thinking control, conversion notes and manifest for the recommended files
#3
by mlboydaisuke - opened
- README.md +56 -1
- litertlm_manifest.json +289 -0
README.md
CHANGED
|
@@ -25,7 +25,7 @@ This repository hosts the [**LiteRT-LM**](https://ai.google.dev/edge/litert-lm)
|
|
| 25 |
|
| 26 |
## Available Models
|
| 27 |
|
| 28 |
-
Recommend to use below both **CPU and GPU
|
| 29 |
**Requires litert-lm ≥ 0.16** (thought channel + `ThinkingConfig`); measured here on 0.17.0.
|
| 30 |
|
| 31 |
| File | Recipe | Size |
|
|
@@ -35,6 +35,8 @@ Recommend to use below both **CPU and GPU compatable** models, originally from [
|
|
| 35 |
|
| 36 |
The **int4 file is the phone file** (smaller, fastest GPU decode on every platform measured) — best used for direct answers or short reasoning; see the thinking-mode note below. **int8 is the file for reasoning that has to complete**: its thinking chains are ~3–4× shorter than int4's on the same questions and terminate where int4 runs into the token budget. int8's main weight section is 2.33 GB, above the single-section mmap ceiling of default-entitlement iOS apps, so it is a desktop / Android build.
|
| 37 |
|
|
|
|
|
|
|
| 38 |
Further, below are some CPU-only models for exploration:
|
| 39 |
* `minicpm_wi4c_wi8_afp32.litertlm`: This model features mixed INT4/INT8 weight-only quantization with FP32 activations (afp32). MLP projections use channelwise INT4 with Hadamard rotation; all remaining weights (attention, embedding, and lmhead) use channelwise INT8.
|
| 40 |
* `minicpm_wi8_afp32.litertlm`: This model features weight-only INT8 quantization (wi8) with FP32 activations (afp32).
|
|
@@ -86,8 +88,44 @@ Install `uv` and run the model directly from the LiteRT-LM command line:
|
|
| 86 |
```bash
|
| 87 |
uv tool install litert-lm
|
| 88 |
uvx litert-lm run --from-huggingface-repo=litert-community/MiniCPM5-2B MiniCPM5-2B_int4.litertlm --prompt="What is the capital of France?"
|
|
|
|
|
|
|
|
|
|
| 89 |
```
|
| 90 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
## Performance
|
| 92 |
|
| 93 |
`litert-lm benchmark` (litert-lm 0.17.0), Apple M4 Max, `-p 256 -d 256 --runs 3 --cache no --max-num-tokens 1024`, quiet machine, serialized, ≥300 s rest before each GPU reading; each backend confirmed to generate real text before its number was recorded:
|
|
@@ -110,6 +148,21 @@ Galaxy S26 (SM-S942Q, Snapdragon SM8850, Adreno; `litert_lm_advanced_main` from
|
|
| 110 |
|
| 111 |
GPU wins prefill (5–10× on the phone, 11× on the Mac) and time-to-first-token everywhere; on Adreno the int4 GPU decode edge over the same-device CPU is modest (~1.1×), while the int8 file's fp32 activations bring its GPU decode level with its CPU.
|
| 112 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 113 |
## Links
|
| 114 |
|
| 115 |
- 🤗 Original model (BF16): [openbmb/MiniCPM5-2B](https://huggingface.co/openbmb/MiniCPM5-2B)
|
|
@@ -123,6 +176,8 @@ GPU wins prefill (5–10× on the phone, 11× on the Mac) and time-to-first-toke
|
|
| 123 |
|
| 124 |
Released under the **Apache-2.0 License**, consistent with the upstream [openbmb/MiniCPM5-2B](https://huggingface.co/openbmb/MiniCPM5-2B).
|
| 125 |
|
|
|
|
|
|
|
| 126 |
## Citation
|
| 127 |
|
| 128 |
```bibtex
|
|
|
|
| 25 |
|
| 26 |
## Available Models
|
| 27 |
|
| 28 |
+
Recommend to use below both **CPU and GPU compatible** models, originally from [mlboydaisuke/MiniCPM5-2B-LiteRT](https://huggingface.co/mlboydaisuke/MiniCPM5-2B-LiteRT)
|
| 29 |
**Requires litert-lm ≥ 0.16** (thought channel + `ThinkingConfig`); measured here on 0.17.0.
|
| 30 |
|
| 31 |
| File | Recipe | Size |
|
|
|
|
| 35 |
|
| 36 |
The **int4 file is the phone file** (smaller, fastest GPU decode on every platform measured) — best used for direct answers or short reasoning; see the thinking-mode note below. **int8 is the file for reasoning that has to complete**: its thinking chains are ~3–4× shorter than int4's on the same questions and terminate where int4 runs into the token budget. int8's main weight section is 2.33 GB, above the single-section mmap ceiling of default-entitlement iOS apps, so it is a desktop / Android build.
|
| 37 |
|
| 38 |
+
Both files carry the checkpoint's own `chat_template.jinja` byte for byte and declare the `thought` channel, so `enable_thinking`, the tool-calling format, and the runtime's `ThinkingConfig` all work unchanged (details under *Conversion notes*).
|
| 39 |
+
|
| 40 |
Further, below are some CPU-only models for exploration:
|
| 41 |
* `minicpm_wi4c_wi8_afp32.litertlm`: This model features mixed INT4/INT8 weight-only quantization with FP32 activations (afp32). MLP projections use channelwise INT4 with Hadamard rotation; all remaining weights (attention, embedding, and lmhead) use channelwise INT8.
|
| 42 |
* `minicpm_wi8_afp32.litertlm`: This model features weight-only INT8 quantization (wi8) with FP32 activations (afp32).
|
|
|
|
| 88 |
```bash
|
| 89 |
uv tool install litert-lm
|
| 90 |
uvx litert-lm run --from-huggingface-repo=litert-community/MiniCPM5-2B MiniCPM5-2B_int4.litertlm --prompt="What is the capital of France?"
|
| 91 |
+
|
| 92 |
+
# local file, GPU backend
|
| 93 |
+
litert-lm run ./MiniCPM5-2B_int4.litertlm --backend gpu --cache no --prompt "..."
|
| 94 |
```
|
| 95 |
|
| 96 |
+
### Controlling thinking
|
| 97 |
+
|
| 98 |
+
Thinking is the model's **default**: with no `ThinkingConfig` it decides for itself and, in practice, reasons before every answer (100–700 characters on trivial questions, thousands on math). To control it:
|
| 99 |
+
|
| 100 |
+
- **Give it a generous output budget (≥ 2048 tokens; 4096 for math).** Truncated mid-thought it produces no final answer at all.
|
| 101 |
+
- The bundle declares the `thought` channel (`<think>` / `</think>`), so the streamed text contains only the answer, and runtimes that expose `ThinkingConfig` can cap or read the reasoning separately.
|
| 102 |
+
- `enable_thinking=false` (via `ThinkingConfig` or the conversation's extra context — both reach the template) switches the model to **direct answers**: two- to seven-token replies on the gate questions, ~10× faster turns, and the GSM8K numbers below. `enable_thinking=true` pre-fills the think opener explicitly. On the CLI: `--thinking false` / `--thinking-budget N`.
|
| 103 |
+
- Sampling: OpenBMB recommends `temperature 1.0, top_p 0.95`; the gates below are greedy.
|
| 104 |
+
- Prompt format is ChatML (`<|im_start|>role\n…<|im_end|>\n`), 4096-token KV budget, six prefill signatures (1024, 256, 64, 16, 4, 1).
|
| 105 |
+
|
| 106 |
+
## Correctness
|
| 107 |
+
|
| 108 |
+
Both files score **8/8 on an 8-question sanity gate on both CPU and GPU** (Apple M4 Max), with the reasoning arriving on the thought channel and only the final answer in the streamed text. The bf16 PyTorch model scores 8/8 on the same gate with thinking on or left to the model, and **6/8 with thinking forced off** ("opposite of hot" → "Cool.", the rhyme line → "Green.") — read any thinking-off result against that, not against 8/8.
|
| 109 |
+
|
| 110 |
+
**GSM8K** (first 100 test questions, greedy, 0-shot chain-of-thought prompt, **thinking off** — the protocol OpenBMB's own MiniCPM5 cards use, max 2048 new tokens, identical prompt and extraction on every row):
|
| 111 |
+
|
| 112 |
+
| Configuration | GSM8K |
|
| 113 |
+
|---|---|
|
| 114 |
+
| bf16 PyTorch (MPS), upstream template | **92 %** |
|
| 115 |
+
| int8, CPU | **91 %** |
|
| 116 |
+
| int4, CPU | 86 % |
|
| 117 |
+
| int4, GPU (Metal) | 87 % |
|
| 118 |
+
|
| 119 |
+
int8 is at parity (7 of its 9 misses are the bf16 model's own). int4 costs about five points on this 42-layer model; the GPU's default fp16 activations cost nothing measurable in no-think mode.
|
| 120 |
+
|
| 121 |
+
**Thinking mode is where int4 shows its damage.** On ten GSM8K questions with thinking on and a 3584-token budget, the bf16 model closes its reasoning on 9/10 with ~3,000-character chains and int8 reproduces that question for question (9/10, median ~3,200 characters on CPU), while **int4 closes 0/10 on CPU** (median ~13,700 characters — it keeps re-checking and runs into the budget; the answer is usually right inside the thought text but never gets emitted). On the Metal GPU with the runtime's default fp16 activations the same int4 file happened to close 10/10 with ~8,000-character chains, but that is fp16 rounding steering the trajectory, not a property to rely on. If your use needs the reasoning to finish, use int8 or turn thinking off on int4.
|
| 122 |
+
|
| 123 |
+
On a **Galaxy S26 (SM-S942Q, Snapdragon SM8850, Adreno)** both files generate correctly on GPU and CPU with **full OpenCL delegation — 1873/1873 nodes on every prefill signature and decode, zero rejected ops** — and the runtime separates the reasoning on-device (`[thought] … [/thought]`, then the answer).
|
| 124 |
+
|
| 125 |
+
On an **iPhone 17 Pro** the int4 file passes the same 8-question gate on both backends (on-device byte count verified against the source file): **Metal GPU 7/8** (init 5.7 s) and **CPU 7/8** (init 2.2 s); the one miss on each leg is the rhyme line inside the 8-question composite prompt, answered "green" — the same answer the bf16 model gives with thinking off, so a prompt-format artifact rather than conversion damage.
|
| 126 |
+
|
| 127 |
+
Multi-turn: three-turn conversations (introduce a name and a city, an arithmetic question, then "which city do I live in?") hold on both files under all three thinking modes with the name and city recalled — the template's history rendering stays consistent with what the runtime already streamed.
|
| 128 |
+
|
| 129 |
## Performance
|
| 130 |
|
| 131 |
`litert-lm benchmark` (litert-lm 0.17.0), Apple M4 Max, `-p 256 -d 256 --runs 3 --cache no --max-num-tokens 1024`, quiet machine, serialized, ≥300 s rest before each GPU reading; each backend confirmed to generate real text before its number was recorded:
|
|
|
|
| 148 |
|
| 149 |
GPU wins prefill (5–10× on the phone, 11× on the Mac) and time-to-first-token everywhere; on Adreno the int4 GPU decode edge over the same-device CPU is modest (~1.1×), while the int8 file's fp32 activations bring its GPU decode level with its CPU.
|
| 150 |
|
| 151 |
+
## Conversion notes
|
| 152 |
+
|
| 153 |
+
Converted with [`litert-torch`](https://github.com/google-ai-edge/litert-torch) 0.9.3 / litert-converter 0.4.0 / ai-edge-quantizer 0.9.0 / litert-lm-builder 0.16.1 from a pristine released stack (reproduction: [hf-to-litertlm](https://github.com/john-rocky/hf-to-litertlm), `bash scripts/reproduce_llm.sh minicpm5-2b`). Four things make these files run on both backends:
|
| 154 |
+
|
| 155 |
+
- **The chat template is the checkpoint's `chat_template.jinja`, byte for byte**, embedded on the runtime's jinja path — the same packaging as [litert-community/MiniCPM5-1B](https://huggingface.co/litert-community/MiniCPM5-1B). That is what keeps `enable_thinking` and the tool-calling format available to the app; the `thought` channel is declared alongside it.
|
| 156 |
+
- **Start token `<s>`** is correct for this family: the template's own `{{ bos_token }}` renders empty at runtime and the engine prepends the metadata start token, so the model sees exactly one `<s>` as it does upstream. Stops are the model's `</s>` and `<|im_end|>`.
|
| 157 |
+
- **int4 needed a zero-scale fix.** Decoder layer 0's MLP contains 13 all-zero rows; blockwise quantization emits a zero scale for each of their blocks, which the CPU (XNNPACK) path refuses to load (`unsupported scale value (0.000000) … for INT4 tensor`) while the GPU path silently accepts. The scales were replaced in place by a tiny positive epsilon (the quantized values in those blocks are zero, so the dequantized weights are unchanged) — 3,328 bytes of a 1.55 GB file.
|
| 158 |
+
- **int8 declares fp32 activations in-bundle** (`prefer_activation_type = "fp32"` in the bundle's `model.toml`; weights untouched). With the runtime's default fp16 GPU activations, the int8 model's reasoning on one gate question ran 2000+ tokens without closing `</think>` (it fails the 8-question gate); with fp32 declared it closes in ~450 tokens and passes 8/8, at a cost of ~14 % GPU decode speed. On a ten-question thinking-on GSM8K subset the two dtypes were closer (fp16 finished 10/10, fp32 7/10), so this is a measured trade for the gate, not a cure. The int4 file keeps the fp16 default: it passes the gate there, and declaring fp32 only reproduces the CPU reference's non-terminating chains (see Correctness).
|
| 159 |
+
|
| 160 |
+
Also: the 130560×2048 input-embedding table sits in its own section (embedder externalised); the tokenizer is embedded as the upstream `tokenizer.json` (byte-level BPE survives intact); int4 is blockwise-32 with OCTAV clipping on the linears and int8 on the embedding, int8 is dynamic per-channel on linears and embedding. A block-128 int4 variant was built and rejected: it loses 8 GSM8K points (79 vs 87, thinking off), fails the 8-question gate on CPU, and does not shorten the thinking chains.
|
| 161 |
+
|
| 162 |
+
## Machine-readable manifest
|
| 163 |
+
|
| 164 |
+
`litertlm_manifest.json` in this repo describes both recommended files (sha256, size, sections, context length, verified backends, the measured rows above with their conditions, and known issues) for tooling that picks a file per device.
|
| 165 |
+
|
| 166 |
## Links
|
| 167 |
|
| 168 |
- 🤗 Original model (BF16): [openbmb/MiniCPM5-2B](https://huggingface.co/openbmb/MiniCPM5-2B)
|
|
|
|
| 176 |
|
| 177 |
Released under the **Apache-2.0 License**, consistent with the upstream [openbmb/MiniCPM5-2B](https://huggingface.co/openbmb/MiniCPM5-2B).
|
| 178 |
|
| 179 |
+
**Changes from the original work** (for the two recommended files): weights converted from safetensors bf16 to LiteRT flatbuffers and quantized as described above; 13 all-zero weight rows' quantization scales set to an epsilon (no numeric change); tokenizer and chat template repackaged into the `.litertlm` bundle with runtime metadata (thought channel, stop tokens, activation-dtype preference). No fine-tuning.
|
| 180 |
+
|
| 181 |
## Citation
|
| 182 |
|
| 183 |
```bibtex
|
litertlm_manifest.json
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"manifest_schema": "0.1.2",
|
| 3 |
+
"repo": "litert-community/MiniCPM5-2B",
|
| 4 |
+
"generated": "2026-09-08",
|
| 5 |
+
"generator": "make_manifest.py",
|
| 6 |
+
"model": {
|
| 7 |
+
"display_name": "MiniCPM5-2B",
|
| 8 |
+
"base_model": "openbmb/MiniCPM5-2B",
|
| 9 |
+
"architecture": "Dense LlamaForCausalLM (42L, hidden 2048, GQA 16:2, untied 130560-vocab embeddings, rope_theta 5e6, no rope scaling), 2.52B params; hybrid-reasoning chat template (enable_thinking) carried verbatim with a declared thought channel",
|
| 10 |
+
"parameters_b": 2.52,
|
| 11 |
+
"license": "apache-2.0",
|
| 12 |
+
"context_length": 4096,
|
| 13 |
+
"capabilities": {
|
| 14 |
+
"vision": false,
|
| 15 |
+
"audio": false,
|
| 16 |
+
"thinking": {
|
| 17 |
+
"declared": true,
|
| 18 |
+
"channel": {
|
| 19 |
+
"start": "<think>",
|
| 20 |
+
"end": "</think>"
|
| 21 |
+
}
|
| 22 |
+
},
|
| 23 |
+
"channels": [
|
| 24 |
+
{
|
| 25 |
+
"name": "thought",
|
| 26 |
+
"start": "<think>",
|
| 27 |
+
"end": "</think>"
|
| 28 |
+
}
|
| 29 |
+
]
|
| 30 |
+
}
|
| 31 |
+
},
|
| 32 |
+
"variants": [
|
| 33 |
+
{
|
| 34 |
+
"file": "MiniCPM5-2B_int4.litertlm",
|
| 35 |
+
"sha256": "9858563beafbc6d5e0d25fcee3827541515296a9302ed3d088b16a58d4fbe7b8",
|
| 36 |
+
"size_bytes": 1553670064,
|
| 37 |
+
"sections": [
|
| 38 |
+
{
|
| 39 |
+
"type": "LlmMetadataProto",
|
| 40 |
+
"size_bytes": 9346
|
| 41 |
+
},
|
| 42 |
+
{
|
| 43 |
+
"type": "HF_Tokenizer_Zlib",
|
| 44 |
+
"size_bytes": 2048362
|
| 45 |
+
},
|
| 46 |
+
{
|
| 47 |
+
"type": "TFLiteModel",
|
| 48 |
+
"size_bytes": 1281048576,
|
| 49 |
+
"model_type": "tf_lite_prefill_decode"
|
| 50 |
+
},
|
| 51 |
+
{
|
| 52 |
+
"type": "TFLiteModel",
|
| 53 |
+
"size_bytes": 270524336,
|
| 54 |
+
"model_type": "tf_lite_embedder"
|
| 55 |
+
}
|
| 56 |
+
],
|
| 57 |
+
"quantization": "blockwise-32 int4 + OCTAV on linears, int8 embedding (externalized embedder section); 1,664 all-zero-block scales (13 dead layer-0 MLP rows) set to an epsilon in place after export; GPU activations at the runtime default (fp16)",
|
| 58 |
+
"backends": [
|
| 59 |
+
"cpu",
|
| 60 |
+
"gpu"
|
| 61 |
+
],
|
| 62 |
+
"default_backend": "gpu",
|
| 63 |
+
"recommended": [
|
| 64 |
+
{
|
| 65 |
+
"platform": "android",
|
| 66 |
+
"device_class": "flagship",
|
| 67 |
+
"backend": "gpu",
|
| 68 |
+
"reason": "measured on one Galaxy S26 with one binary (2026-09-08, 205-token prompt, 2 runs per backend, cooled below 42 C before each run): gpu prefill 400.7-411.1 tok/s vs cpu 39.0-72.0, TTFT 0.56 s vs 2.9-5.3 s, decode 16.1-18.6 vs 15.6-15.8 tok/s (a modest 1.0-1.2x), peak VmHWM 1.13-1.14 GB vs 1.96-2.12 GB; full LITERT_CL delegation (1873/1873 nodes on every subgraph) and the gate answer verified through the thought channel. The gpu wins on time-to-first-token and memory; decode is close to a wash"
|
| 69 |
+
},
|
| 70 |
+
{
|
| 71 |
+
"platform": "macos",
|
| 72 |
+
"backend": "gpu",
|
| 73 |
+
"reason": "Apple M4 Max, litert-lm 0.17.0 benchmark -p 256 -d 256 --runs 3 --cache no, generation gated per backend first: gpu 1699 tok/s prefill / 92.8 tok/s decode vs cpu 149 / 31.1"
|
| 74 |
+
},
|
| 75 |
+
{
|
| 76 |
+
"platform": "ios",
|
| 77 |
+
"backend": "gpu",
|
| 78 |
+
"reason": "iPhone 17 Pro, 8-question on-device gate at a 2048-token budget: Metal gpu 7/8 (init 5.7 s) and cpu 7/8 (init 2.2 s), both G41_DONE; the one miss on each leg is the rhyme line of the composite prompt, which the bf16 model also misses with thinking off. No speed rows were taken on the phone"
|
| 79 |
+
}
|
| 80 |
+
],
|
| 81 |
+
"requirements": {
|
| 82 |
+
"peak_ram_mb": 2122,
|
| 83 |
+
"platform_notes": [
|
| 84 |
+
"Requires litert-lm >= 0.16 (thought channel + ThinkingConfig); measured on 0.17.0",
|
| 85 |
+
"Hybrid-reasoning model: with no ThinkingConfig it thinks before every answer; budget max output tokens >= 2048 (4096 for math). enable_thinking=false (ThinkingConfig or conversation extra_context) switches to direct answers",
|
| 86 |
+
"Thinking-mode caveat measured on this file: on GSM8K-class questions its reasoning chains run ~4x longer than int8's and do not close inside 3584 tokens on the CPU reference (0/10 closed; int8 9/10, bf16 9/10). Use int8 when the reasoning must complete, or turn thinking off",
|
| 87 |
+
"iPhone 17 Pro: passes the on-device gate on gpu and cpu (init 5.7 s / 2.2 s); the int8 file does not fit default-entitlement iOS (2.33 GB main section)",
|
| 88 |
+
"The 13 all-zero MLP rows in decoder layer 0 make raw blockwise-int4 exports fail to load on XNNPACK (scale 0); this file carries the epsilon fix"
|
| 89 |
+
]
|
| 90 |
+
},
|
| 91 |
+
"measured": [
|
| 92 |
+
{
|
| 93 |
+
"device": "Apple M4 Max",
|
| 94 |
+
"os": "macOS",
|
| 95 |
+
"backend": "cpu",
|
| 96 |
+
"runtime": "litert-lm 0.17.0",
|
| 97 |
+
"prompt_tokens": 256,
|
| 98 |
+
"decode_tokens": 256,
|
| 99 |
+
"prefill_tps": 148.58,
|
| 100 |
+
"decode_tps": 31.12,
|
| 101 |
+
"ttft_s": 1.76,
|
| 102 |
+
"load_s": 4.52,
|
| 103 |
+
"max_num_tokens": 1024,
|
| 104 |
+
"cache": "no",
|
| 105 |
+
"runs": 3,
|
| 106 |
+
"date": "2026-09-08",
|
| 107 |
+
"source": "litert-lm benchmark -p 256 -d 256 --runs 3 --cache no --max-num-tokens 1024, quiet machine, backend gated on a real generation first"
|
| 108 |
+
},
|
| 109 |
+
{
|
| 110 |
+
"device": "Apple M4 Max",
|
| 111 |
+
"os": "macOS",
|
| 112 |
+
"backend": "gpu",
|
| 113 |
+
"runtime": "litert-lm 0.17.0",
|
| 114 |
+
"prompt_tokens": 256,
|
| 115 |
+
"decode_tokens": 256,
|
| 116 |
+
"prefill_tps": 1699.15,
|
| 117 |
+
"decode_tps": 92.79,
|
| 118 |
+
"ttft_s": 0.16,
|
| 119 |
+
"load_s": 3.65,
|
| 120 |
+
"max_num_tokens": 1024,
|
| 121 |
+
"cache": "no",
|
| 122 |
+
"runs": 3,
|
| 123 |
+
"date": "2026-09-08",
|
| 124 |
+
"source": "litert-lm benchmark -p 256 -d 256 --runs 3 --cache no --max-num-tokens 1024, >=300 s rest before the GPU cell, backend gated on a real generation first"
|
| 125 |
+
},
|
| 126 |
+
{
|
| 127 |
+
"device": "Galaxy S26 (SM-S942Q, Qualcomm SM8850, Adreno)",
|
| 128 |
+
"backend": "gpu",
|
| 129 |
+
"runtime": "litert_lm_advanced_main v0.16.0, LiteRT CL delegate",
|
| 130 |
+
"prompt_tokens": 205,
|
| 131 |
+
"decode_tokens": 1021,
|
| 132 |
+
"prefill_tps": "400.7-411.1",
|
| 133 |
+
"decode_tps": "16.14-18.56",
|
| 134 |
+
"ttft_s": 0.56,
|
| 135 |
+
"load_s": 11.2,
|
| 136 |
+
"peak_memory_mb": 1142,
|
| 137 |
+
"cache": "no",
|
| 138 |
+
"runs": 2,
|
| 139 |
+
"date": "2026-09-08",
|
| 140 |
+
"source": "205-token prompt with --benchmark, 2 runs (engine init 11.2 / 13.1 s), device cooled below 42 C before each run; GPU gate full LITERT_CL delegation (1873/1873 nodes on every subgraph, 0 blocking XNNPack), generation verified on the gate prompt; the cpu row of the same date is the same-device control"
|
| 141 |
+
},
|
| 142 |
+
{
|
| 143 |
+
"device": "Galaxy S26 (SM-S942Q, Qualcomm SM8850, Adreno)",
|
| 144 |
+
"backend": "cpu",
|
| 145 |
+
"runtime": "litert_lm_advanced_main v0.16.0 (XNNPACK CPU)",
|
| 146 |
+
"prompt_tokens": 205,
|
| 147 |
+
"decode_tokens": 1021,
|
| 148 |
+
"prefill_tps": "39.0-72.0",
|
| 149 |
+
"decode_tps": "15.56-15.79",
|
| 150 |
+
"ttft_s": "2.9-5.3",
|
| 151 |
+
"load_s": 5.7,
|
| 152 |
+
"peak_memory_mb": 2122,
|
| 153 |
+
"cache": "no",
|
| 154 |
+
"runs": 2,
|
| 155 |
+
"date": "2026-09-08",
|
| 156 |
+
"source": "same-device CPU control: identical binary, prompt file and flags, only --backend differs; 205-token prompt with --benchmark, 2 runs (engine init 5.7 / 3.1 s), cooled before each run"
|
| 157 |
+
}
|
| 158 |
+
],
|
| 159 |
+
"known_issues": [
|
| 160 |
+
"Thinking mode: reasoning chains are ~4x longer than int8/bf16 on GSM8K-class questions and frequently hit a 3584-token budget without emitting the answer (CPU reference 0/10 closed; the Metal GPU's default fp16 activations happen to close 22/30). Quantization damage of int4 on this 42-layer model, not a runtime bug; block-128 int4 was tried and is worse (GSM8K 79 vs 87)"
|
| 161 |
+
]
|
| 162 |
+
},
|
| 163 |
+
{
|
| 164 |
+
"file": "MiniCPM5-2B_int8.litertlm",
|
| 165 |
+
"sha256": "58da116a6eb563185eb5e8d0b1f1e1116b632a516cadc0285b6da35b9f886a86",
|
| 166 |
+
"size_bytes": 2603278256,
|
| 167 |
+
"sections": [
|
| 168 |
+
{
|
| 169 |
+
"type": "LlmMetadataProto",
|
| 170 |
+
"size_bytes": 9346
|
| 171 |
+
},
|
| 172 |
+
{
|
| 173 |
+
"type": "HF_Tokenizer_Zlib",
|
| 174 |
+
"size_bytes": 2048362
|
| 175 |
+
},
|
| 176 |
+
{
|
| 177 |
+
"type": "TFLiteModel",
|
| 178 |
+
"size_bytes": 2330645824,
|
| 179 |
+
"model_type": "tf_lite_prefill_decode"
|
| 180 |
+
},
|
| 181 |
+
{
|
| 182 |
+
"type": "TFLiteModel",
|
| 183 |
+
"size_bytes": 270524336,
|
| 184 |
+
"model_type": "tf_lite_embedder"
|
| 185 |
+
}
|
| 186 |
+
],
|
| 187 |
+
"quantization": "dynamic int8 on linears + embedding (externalized embedder section); prefer_activation_type = fp32 declared in the bundle for the GPU executor",
|
| 188 |
+
"backends": [
|
| 189 |
+
"cpu",
|
| 190 |
+
"gpu"
|
| 191 |
+
],
|
| 192 |
+
"default_backend": "gpu",
|
| 193 |
+
"recommended": [
|
| 194 |
+
{
|
| 195 |
+
"platform": "android",
|
| 196 |
+
"device_class": "flagship",
|
| 197 |
+
"backend": "gpu",
|
| 198 |
+
"reason": "measured on one Galaxy S26 with one binary (2026-09-08, 205-token prompt, 2 runs per backend, cooled before each run): gpu prefill 150.2-159.5 tok/s vs cpu 102.8-157.2, decode 10.86-12.75 vs 11.67-11.75 tok/s (a wash), TTFT 1.4 s vs 1.4-2.1 s, peak VmHWM 0.94-1.10 GB vs 2.90 GB; full LITERT_CL delegation and the gate answer verified through the thought channel. With fp32 activations declared the gpu no longer wins decode; it is recommended for the 2.6x lower process memory"
|
| 199 |
+
},
|
| 200 |
+
{
|
| 201 |
+
"platform": "macos",
|
| 202 |
+
"backend": "gpu",
|
| 203 |
+
"reason": "Apple M4 Max, litert-lm 0.17.0 benchmark -p 256 -d 256 --runs 3 --cache no, generation gated per backend first: gpu 1405 tok/s prefill / 74.7 tok/s decode vs cpu 161 / 30.0"
|
| 204 |
+
}
|
| 205 |
+
],
|
| 206 |
+
"requirements": {
|
| 207 |
+
"peak_ram_mb": 2896,
|
| 208 |
+
"platform_notes": [
|
| 209 |
+
"Requires litert-lm >= 0.16 (thought channel + ThinkingConfig); measured on 0.17.0",
|
| 210 |
+
"Hybrid-reasoning model: with no ThinkingConfig it thinks before every answer; budget max output tokens >= 2048 (4096 for math). enable_thinking=false switches to direct answers",
|
| 211 |
+
"fp32 activations are declared in-bundle: with the runtime's default fp16 GPU activations this model's reasoning on one gate question ran 2000+ tokens without closing </think> (7/8, degenerate); with fp32 it closes in ~450 tokens and passes 8/8, at ~14 % GPU decode cost (Mac). The declaration is a measured trade for the gate, not a general cure",
|
| 212 |
+
"Not for default-entitlement iOS apps: the main weight section is 2.33 GB, above the ~2 GiB single-section mmap limit",
|
| 213 |
+
"GSM8K (thinking off, n=100, greedy): 91 vs bf16 92 - at parity; with thinking on it reproduces the bf16 model question for question on a 10-question subset (9/10 chains close, ~3.2k characters)"
|
| 214 |
+
]
|
| 215 |
+
},
|
| 216 |
+
"measured": [
|
| 217 |
+
{
|
| 218 |
+
"device": "Apple M4 Max",
|
| 219 |
+
"os": "macOS",
|
| 220 |
+
"backend": "cpu",
|
| 221 |
+
"runtime": "litert-lm 0.17.0",
|
| 222 |
+
"prompt_tokens": 256,
|
| 223 |
+
"decode_tokens": 256,
|
| 224 |
+
"prefill_tps": 160.87,
|
| 225 |
+
"decode_tps": 30.02,
|
| 226 |
+
"ttft_s": 1.62,
|
| 227 |
+
"load_s": 15.03,
|
| 228 |
+
"max_num_tokens": 1024,
|
| 229 |
+
"cache": "no",
|
| 230 |
+
"runs": 3,
|
| 231 |
+
"date": "2026-09-08",
|
| 232 |
+
"source": "litert-lm benchmark -p 256 -d 256 --runs 3 --cache no --max-num-tokens 1024, quiet machine, backend gated on a real generation first"
|
| 233 |
+
},
|
| 234 |
+
{
|
| 235 |
+
"device": "Apple M4 Max",
|
| 236 |
+
"os": "macOS",
|
| 237 |
+
"backend": "gpu",
|
| 238 |
+
"runtime": "litert-lm 0.17.0",
|
| 239 |
+
"prompt_tokens": 256,
|
| 240 |
+
"decode_tokens": 256,
|
| 241 |
+
"prefill_tps": 1405.16,
|
| 242 |
+
"decode_tps": 74.65,
|
| 243 |
+
"ttft_s": 0.2,
|
| 244 |
+
"load_s": 3.0,
|
| 245 |
+
"max_num_tokens": 1024,
|
| 246 |
+
"cache": "no",
|
| 247 |
+
"runs": 3,
|
| 248 |
+
"date": "2026-09-08",
|
| 249 |
+
"source": "litert-lm benchmark -p 256 -d 256 --runs 3 --cache no --max-num-tokens 1024, >=300 s rest before the GPU cell, backend gated on a real generation first"
|
| 250 |
+
},
|
| 251 |
+
{
|
| 252 |
+
"device": "Galaxy S26 (SM-S942Q, Qualcomm SM8850, Adreno)",
|
| 253 |
+
"backend": "gpu",
|
| 254 |
+
"runtime": "litert_lm_advanced_main v0.16.0, LiteRT CL delegate",
|
| 255 |
+
"prompt_tokens": 205,
|
| 256 |
+
"decode_tokens": 1021,
|
| 257 |
+
"prefill_tps": "150.2-159.5",
|
| 258 |
+
"decode_tps": "10.86-12.75",
|
| 259 |
+
"ttft_s": "1.35-1.44",
|
| 260 |
+
"load_s": 6.3,
|
| 261 |
+
"peak_memory_mb": 1098,
|
| 262 |
+
"cache": "no",
|
| 263 |
+
"runs": 2,
|
| 264 |
+
"date": "2026-09-08",
|
| 265 |
+
"source": "205-token prompt with --benchmark, 2 runs, device cooled below 42 C before each run; GPU gate full LITERT_CL delegation (1873/1873 nodes on every subgraph), generation verified on the gate prompt through the thought channel (engine init 6.3 / 4.0 s); the cpu row of the same date is the same-device control"
|
| 266 |
+
},
|
| 267 |
+
{
|
| 268 |
+
"device": "Galaxy S26 (SM-S942Q, Qualcomm SM8850, Adreno)",
|
| 269 |
+
"backend": "cpu",
|
| 270 |
+
"runtime": "litert_lm_advanced_main v0.16.0 (XNNPACK CPU)",
|
| 271 |
+
"prompt_tokens": 205,
|
| 272 |
+
"decode_tokens": 1021,
|
| 273 |
+
"prefill_tps": "102.8-157.2",
|
| 274 |
+
"decode_tps": "11.67-11.75",
|
| 275 |
+
"ttft_s": "1.4-2.1",
|
| 276 |
+
"load_s": 0.27,
|
| 277 |
+
"peak_memory_mb": 2896,
|
| 278 |
+
"cache": "no",
|
| 279 |
+
"runs": 2,
|
| 280 |
+
"date": "2026-09-08",
|
| 281 |
+
"source": "same-device CPU control: identical binary, prompt file and flags, only --backend differs; 205-token prompt with --benchmark, 2 runs (engine init 0.27 / 0.30 s), cooled before each run"
|
| 282 |
+
}
|
| 283 |
+
],
|
| 284 |
+
"known_issues": [
|
| 285 |
+
"GPU with the runtime's default fp16 activations (i.e. if the in-bundle fp32 declaration is overridden): one 8-question gate item's reasoning ran 2000+ tokens without closing </think>. Keep the declared fp32 activations for this file"
|
| 286 |
+
]
|
| 287 |
+
}
|
| 288 |
+
]
|
| 289 |
+
}
|