Instructions to use spybyscript/gliner25-decide-litert with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- LiteRT
How to use spybyscript/gliner25-decide-litert 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
- GLiNER2
How to use spybyscript/gliner25-decide-litert with GLiNER2:
from gliner2 import AutoExtractor extractor = AutoExtractor.from_pretrained("spybyscript/gliner25-decide-litert") # Extract entities text = "Apple CEO Tim Cook announced iPhone 15 in Cupertino yesterday." result = extractor.extract_entities(text, ["company", "person", "product", "location"]) print(result) - Notebooks
- Google Colab
- Kaggle
A concise card: the files, use, validation and the Galaxy S23 Ultra results
Browse files- README.md +107 -253
- SHA256SUMS +1 -1
README.md
CHANGED
|
@@ -14,40 +14,27 @@ tags:
|
|
| 14 |
|
| 15 |
# GLiNER2.5-Decide β LiteRT (Android)
|
| 16 |
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
| 20 |
|
| 21 |
-
|
| 22 |
-
The encoder files have four signatures for the classifier (`s128`, `s160`, `s256`, `s512`: logits at every position) and four
|
| 23 |
-
for the hidden states (`e128` to `e512`), all over one set of weights; the heads file holds the span heads and the
|
| 24 |
-
classifier (the span file and the count file hold the same heads, built for LiteRT's GPU); the token table is a separate float16 file the host maps. `config.json` is the full contract.
|
| 25 |
|
| 26 |
| File | Size | Contents |
|
| 27 |
|---|---|---|
|
| 28 |
-
| `gliner25_decide_wfp16.tflite` | 1028 MB |
|
| 29 |
-
| `gliner25_decide_fp16hw.tflite` | 1028 MB |
|
| 30 |
-
| `
|
| 31 |
-
| `
|
| 32 |
-
| `
|
| 33 |
-
| `gliner25_decide_embeddings_f16.bin` | 262 MB | float16 [128011, 1024], little-endian, row-major |
|
| 34 |
-
| `runtime/` | | `litert_span_runtime.py` (
|
| 35 |
| `npu/` | | Qualcomm NPU files (below) |
|
| 36 |
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
`runtime/litert_span_runtime.py` is gliner2's span extractor with its five trained modules (encoder, span
|
| 40 |
-
representation, count prediction, count embedding, classifier) on LiteRT; the schema handling, scoring and decoding are
|
| 41 |
-
the library's own code, and no PyTorch weights are loaded. Every public call works: `extract`, `classify_text`,
|
| 42 |
-
`extract_entities`, `extract_json`, `extract_relations`, and their `batch_*` and `*_long` forms.
|
| 43 |
-
|
| 44 |
-
- Classification, single- and multi-label, with label descriptions and several tasks in one call.
|
| 45 |
-
- Entities, structures (`extract_json`, with choice fields) and relations, through the span heads.
|
| 46 |
-
- **Sizes:** a request holds at most 512 tokens (schema and text); spans up to 8 words and up to 20 instances per
|
| 47 |
-
structure (the checkpoint's own limits); up to 32 fields or entity types per task. A larger request is refused with a
|
| 48 |
-
ValueError, never truncated: use the `*_long` calls with a chunk that fits, e.g. `extract_entities_long(text, labels,
|
| 49 |
-
chunk_size=300)`. The original runs with dynamic shapes.
|
| 50 |
-
- **Weights:** only their storage and compute precision change; nothing is retrained or pruned.
|
| 51 |
|
| 52 |
## Use
|
| 53 |
|
|
@@ -56,257 +43,124 @@ the library's own code, and no PyTorch weights are loaded. Every public call wor
|
|
| 56 |
import sys; sys.path.insert(0, "runtime")
|
| 57 |
from litert_span_runtime import LiteRTSpanExtractor
|
| 58 |
|
| 59 |
-
model = LiteRTSpanExtractor("."
|
| 60 |
model.classify_text("Please cancel my order.", {"intent": ["cancel", "refund", "track"]})
|
| 61 |
model.extract_entities("Alice founded Acme in Toronto in 2020.", ["person", "organization", "city"])
|
| 62 |
model.extract_relations("Alice founded Acme in Toronto.", ["founded", "located_in"])
|
| 63 |
-
|
| 64 |
-
# The span file and the count file in place of the heads file: the same results; the span heads take about half the CPU time
|
| 65 |
-
model = LiteRTSpanExtractor(".", span="gliner25_decide_span_wfp16.tflite", count="gliner25_decide_count_wfp16.tflite")
|
| 66 |
```
|
| 67 |
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
Tokenize with `tokenizer.json` and pack the prompt exactly as gliner2 2.0.0 does. Pick the smallest signature that holds the ids
|
| 73 |
-
(refuse more than 512), zero-pad the ids, look each one up in the table (row `id` at byte `id * 2048`) and upcast to float32 into
|
| 74 |
-
`inputs_embeds` [1, N, 1024]; `attention_mask` [1, N] is 1.0 for tokens and 0.0 for padding. `logits` [1, N] holds the classifier
|
| 75 |
-
at every position: read each task's `[L]` marker positions and take a softmax over them (sigmoid for multi-label tasks).
|
| 76 |
|
| 77 |
-
|
| 78 |
-
|
|
|
|
|
|
|
|
|
|
| 79 |
|
| 80 |
-
|
|
|
|
|
|
|
| 81 |
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
|
|
|
| 85 |
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
so both halves are computed once per word instead of once per span: a span's hidden layer is relu(A_i + B_(i+k)). Spans
|
| 89 |
-
that end past the bucket read zero padding; they also end past the text, which the library masks.
|
| 90 |
-
- **`f{W}_p{P}`** (P = 16 or 128 queries): `words` [1, W, 1024] and `queries` [P, 1024] (struct_proj flattened over
|
| 91 |
-
count and fields) -> `scores` [P, W, 8], the raw logits the library computes as einsum("lkd,bpd->bplk", span_rep,
|
| 92 |
-
struct_proj). The span head's last layer folds into the queries: q . (W2 h + b2) = (q W2) . h + q . b2, so a host that
|
| 93 |
-
needs only the scores never builds the [W, 8, 1024] span tensor.
|
| 94 |
-
- **`count`** and the classifier **`k32`**, **`k128`**, as in the heads file.
|
| 95 |
-
- Every tensor is [1, rows, features]. The GPU delegate rejected the heads file's FULLY_CONNECTED on 4-D inputs and its
|
| 96 |
-
BROADCAST_TO.
|
| 97 |
|
| 98 |
-
|
| 99 |
-
|
|
|
|
|
|
|
| 100 |
|
| 101 |
-
|
| 102 |
-
einsum (scores reach about 7,000). Through the reference runtime with both files in place of the heads file:
|
| 103 |
-
296 of 296 corpus requests identical (confidences within 0.0013), and 11 of 11 of FluidInference's fixtures.
|
| 104 |
|
| 105 |
## Validation
|
| 106 |
|
| 107 |
-
### Every call type against the library
|
| 108 |
-
|
| 109 |
`runtime/verify_parity.py` runs each request through gliner2 with the original weights and through the reference runtime
|
| 110 |
-
(LiteRT
|
| 111 |
-
(`runtime/parity_corpus.json`)
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
| `record_natural` | 16 | 16 | 1.0 | 0.000303 |
|
| 119 |
-
| `record_latent` | 24 | 24 | 1.0 | 0.000489 |
|
| 120 |
-
| `attributes` | 16 | 16 | 1.0 | 0.00045 |
|
| 121 |
-
| `classification_topic` | 40 | 40 | 1.0 | 0.000204 |
|
| 122 |
-
| `mixed` | 16 | 16 | 1.0 | 0.000207 |
|
| 123 |
-
| `relations_family` | 8 | 8 | 1.0 | 0.000234 |
|
| 124 |
-
| `classification_described` | 24 | 24 | 1.0 | 9.3e-05 |
|
| 125 |
-
| `record_anchorless` | 8 | 8 | 1.0 | 0.000269 |
|
| 126 |
-
| `structure_event` | 24 | 24 | 1.0 | 0.001315 |
|
| 127 |
-
| `classification_multi` | 16 | 16 | 1.0 | 0.000334 |
|
| 128 |
-
| `entities_described` | 16 | 16 | 1.0 | 0.000182 |
|
| 129 |
-
| `structure_order` | 8 | 8 | 1.0 | 0.000579 |
|
| 130 |
-
| `relations_event` | 8 | 8 | 1.0 | 0.000118 |
|
| 131 |
-
|
| 132 |
-
All 296 of 296 requests give identical results; FluidInference's eleven schema-path fixtures (from its
|
| 133 |
-
gliner2.5-base-v1 port) also match, 11 of 11. With the span file and the count file in place of the heads file: 296 of 296, and 11 of 11
|
| 134 |
-
fixtures. `validation-full.json` holds the numbers.
|
| 135 |
-
|
| 136 |
-
### The recorded classification calls (Mac, not a phone)
|
| 137 |
-
|
| 138 |
-
616 Decide calls (2,338 decisions) recorded from the gliner2 2.0.0 library on a synthetic English corpus of short
|
| 139 |
-
conversational statements, packed by the library's collator, padded to the smallest
|
| 140 |
-
bucket and decoded by a Python mirror of an on-device host. ai-edge-litert 2.2.0 on arm64 Mac (Apple M5 Max, macOS 27.0.1):
|
| 141 |
-
the CPU, and the Mac's GPU through the same ML Drift accelerator the phone uses (Metal here, OpenCL on Android) as a check of
|
| 142 |
-
the precision modes. These timings are Mac measurements, not phone numbers.
|
| 143 |
-
|
| 144 |
-
| Variant | Runtime | Labels as the library | Max probability drift | Mac ms per call p50 by bucket | Gate |
|
| 145 |
-
|---|---|---|---|---|---|
|
| 146 |
-
| `wfp16` | CPU (XNNPACK, 4 threads) | 2338/2338 (100.00%) | 0.00032 | 128: 58.3, 160: 68.1, 512: 248.6 | pass |
|
| 147 |
-
| `wfp16` | Mac GPU (Metal, ML Drift), FP32 precision | 2338/2338 (100.00%) | 0.00032 | 128: 21.0, 160: 23.8, 512: 76.1 | pass |
|
| 148 |
-
| `wfp16` | Mac GPU (Metal, ML Drift), default precision (FP16) | 2226/2338 (95.21%) | 0.30555 | 128: 16.3, 160: 20.5, 512: 49.6 | fail |
|
| 149 |
-
| `fp16hw` | CPU (XNNPACK, 4 threads) | 2338/2338 (100.00%) | 0.00032 | 128: 60.2, 160: 70.5, 512: 255.1 | pass |
|
| 150 |
-
| `fp16hw` | Mac GPU (Metal, ML Drift), FP32 precision | 2338/2338 (100.00%) | 0.00032 | 128: 22.1, 160: 25.7, 512: 79.8 | pass |
|
| 151 |
-
| `fp16hw` | Mac GPU (Metal, ML Drift), default precision (FP16) | 2332/2338 (99.74%) | 0.01158 | 128: 16.6, 160: 22.1, 512: 50.7 | pass |
|
| 152 |
-
|
| 153 |
-
The acceptance gate: label agreement β₯ 95 %, largest probability drift β€ 0.05. `validation.json` holds every number.
|
| 154 |
-
|
| 155 |
-
## On phones
|
| 156 |
-
|
| 157 |
-
### Galaxy Z Fold 8 (SM8850, 2026-10-28/29): classification on the GPU and the CPU
|
| 158 |
-
|
| 159 |
-
Snapdragon 8 Elite Gen 5 (SM8850), Adreno 840, Android 17, LiteRT 2.2.0. One request per signature from the
|
| 160 |
-
test set, warm, on a rested phone (it throttles within minutes of continuous runs):
|
| 161 |
-
|
| 162 |
-
| Setting | s128 | s160 | s512 | RAM taken | Labels as the library |
|
| 163 |
-
|---|---|---|---|---|---|
|
| 164 |
-
| `fp16hw`, GPU FP16 | 62 ms | 81 ms | 439 ms | about 1.0 GB | 9 / 9 |
|
| 165 |
-
| `fp16hw`, GPU FP16 with FP32 accumulation | 80 ms | 99 ms | 496 ms | about 1.05 GB | 9 / 9 |
|
| 166 |
-
| `wfp16`, GPU FP32 | 136 ms | 173 ms | 758 ms | about 1.9 GB | 9 / 9 |
|
| 167 |
-
| `wfp16`, CPU (4 threads, XNNPACK weight cache) | 120 ms | 155 ms | 972 ms | about 0.4β0.5 GB, plus droppable file pages | 9 / 9 |
|
| 168 |
|
| 169 |
-
-
|
| 170 |
-
- **On LiteRT 2.2.0, LOW GPU priority kept a rendering app's frames.** At the default, NORMAL or HIGH priority, each 512-token call held the app's renderer off the GPU for about 400 ms. LOW cost no frames but added about 95β130 ms to every call. On LiteRT 2.3.2 and a Galaxy S23 Ultra, LOW no longer keeps the frames (below).
|
| 171 |
-
- **The first load is the slowest.** It takes 6β8 s on the GPU. The program cache shortens later loads very little.
|
| 172 |
-
- **These are LiteRT 2.2.0's numbers.** On LiteRT 2.3.2 the GPU at LOW priority is slower on the same phone: 472 ms a call at s160 (2026-10-30; about 176 ms on 2.2.0) and 2.75 s at s512 (2026-11-01).
|
| 173 |
|
| 174 |
-
##
|
| 175 |
|
| 176 |
-
Android 16, LiteRT 2.3.2 with `qnn-runtime` 2.50.0. Each
|
| 177 |
-
after a 30 s rest,
|
| 178 |
-
|
| 179 |
-
priority. The FP16 columns use `fp16hw` for the encoder.
|
| 180 |
|
| 181 |
-
| Graph | CPU | GPU
|
| 182 |
-
|---|---|---|---|---|---|
|
| 183 |
-
| encoder `e128` / `e160` / `e256` / `e512` | 628 / 866 / 1,467 / 3,486 ms | 202 / 268 / 471 / 1,549 ms | 103 / 124 / 227 / 773 ms | 289 / 353 / 617 / 2,082 ms | 34 / 55 / 168 ms (precompiled; `e512` not compiled) |
|
| 184 |
-
| span file `w64` / `w128` / `w256` / `w512` | 43 / 105 / 221 / 450 ms | 19 / 26 / 42 / 79 ms | 10 / 15 / 25 / 47 ms | 27 / 35 / 58 / 102 ms | 7.3 / 6.0 / 14 / 42 ms |
|
| 185 |
-
| span file `f64` / `f128` / `f256` / `f512`, 16 queries | 26 / 55 / 111 / 220 ms | 15 / 18 / 28 / 46 ms | 8 / 10 / 16 / 27 ms | 23 / 25 / 38 / 62 ms | 3.8 / 5.6 / 14 / 44 ms |
|
| 186 |
-
| heads file `w64` / `w128` / `w256` / `w512` | 90 / 200 / 440 / 935 ms | does not compile | does not compile | does not compile | 7.4 / 13.7 / 24 / 56 ms |
|
| 187 |
-
| `count_embed`, 1 step Γ 8 fields / 20 steps Γ 32 fields | 2.1 / 169 ms (count file) | does not compile | does not compile | does not compile | 1.4 / 5.4 ms (heads file) |
|
| 188 |
-
| classifier, 32 / 128 rows | 0.9 / 3.8 ms | 1.6 / 2.4 ms | 1.2 / 1.5 ms | 2.8 / 2.8 ms | 0.8 / 1.3 ms |
|
| 189 |
-
| dirty memory, encoder / span file | 1.9 / 1.7 GB | 4.6 / 1.3 GB | 2.7 / 0.8 GB | 2.7 / 0.9 GB | 0.12 / 0.21 GB |
|
| 190 |
-
|
| 191 |
-
- **An extraction request of up to 128 tokens and 64 words** takes these times:
|
| 192 |
-
- about 115 ms on the GPU at FP16 and NORMAL priority;
|
| 193 |
-
- about 220 ms at FP32 and NORMAL;
|
| 194 |
-
- about 320 ms at FP16 and LOW;
|
| 195 |
-
- about 43 ms on the NPU;
|
| 196 |
-
- about 660 ms on the CPU with the span file, and 720 ms with the heads file.
|
| 197 |
-
|
| 198 |
-
`count_embed` runs on the CPU in every GPU case.
|
| 199 |
-
- **Answers on the phone's accelerators.** 32 recorded requests, two per call type, ran with the phone's outputs through the
|
| 200 |
-
rest of the pipeline:
|
| 201 |
-
- the encoder: unchanged 32 of 32 on the NPU, at GPU FP32 and on the CPU; at GPU FP16, 31 of 32 (the same relation
|
| 202 |
-
request with or without FP32 accumulation);
|
| 203 |
-
- the span file: unchanged 32 of 32 at GPU FP16 (confidences within 0.005) and on the NPU (within 0.0002);
|
| 204 |
-
- the heads file on the NPU: unchanged 32 of 32 (within 0.0006).
|
| 205 |
-
- **GPU priority beside a rendering app** (same phone and LiteRT version). An app rendered every frame on the same GPU
|
| 206 |
-
(Godot 4.7, Vulkan, 120 Hz) while the model ran three classification calls each round (`s160` and two `s256`, `fp16hw`
|
| 207 |
-
at GPU FP16, with the host's tokenizing and table lookup). Each setting ran 6 rounds; the two GPU settings ran twice:
|
| 208 |
-
|
| 209 |
-
| The model on | The app's longest frame per round (median) | The three calls |
|
| 210 |
-
|---|---|---|
|
| 211 |
-
| GPU, LOW priority | 596β602 ms | about 1.6 s (350 + 620 + 630 ms) |
|
| 212 |
-
| GPU, NORMAL priority | 228β229 ms | about 0.65 s (150 + 245 + 250 ms) |
|
| 213 |
-
| NPU (precompiled on the phone) | 81 ms | about 0.5 s (92 + 210 + 205 ms) |
|
| 214 |
-
| CPU, 4 threads | 48 ms | 4β5 s |
|
| 215 |
-
|
| 216 |
-
On this phone and LiteRT version, LOW priority does not let the app's rendering in. Either way the app stops for one
|
| 217 |
-
whole call, and LOW makes each call 2β3 times longer. On the GPU, use NORMAL and keep each call short (the smallest
|
| 218 |
-
signature that fits). The NPU keeps the frames.
|
| 219 |
-
|
| 220 |
-
## Qualcomm NPU (`npu/`)
|
| 221 |
-
|
| 222 |
-
Files for the Hexagon NPU of Snapdragon phones, through LiteRT's Qualcomm accelerator: LiteRT 2.3.2 with
|
| 223 |
-
`litert-npu-runtime-qualcomm` 2.3.2 and Qualcomm's `qnn-runtime` 2.50.0 (Qualcomm's AI Stack License). They are not part
|
| 224 |
-
of the GPU and CPU files above. Every one comes from `gliner25_decide_fp16hw.tflite`, because the NPU computes at FP16.
|
| 225 |
-
Inputs, outputs and host steps are the same as above; `npu/manifest.json` lists each file with its chip, signatures,
|
| 226 |
-
size, SHA-256 and the versions it was built with.
|
| 227 |
-
|
| 228 |
-
| Files | What they are |
|
| 229 |
-
|---|---|
|
| 230 |
-
| `npu/gliner25_decide_fp16hw_s128.tflite`, `_s160`, `_s256` (660 MB / 673 MB / 710 MB) | One signature each, cut from `fp16hw` (on LiteRT's CPU their logits equal the full file's). The phone compiles them for its own NPU at first use, so they serve any Snapdragon LiteRT supports: about 20 / 45 / 130 s on an SM8850, once per OS build. s512 is left out: compiling it on a phone ran out of memory on a Galaxy S26 (litert-community). |
|
| 231 |
-
| `npu/qualcomm/gliner25_decide_fp16hw_<chip>.tflite` | Precompiled on x86-64 Linux (ai-edge-litert 2.3.0, QAIRT 2.50.0.260828): all four signatures over one copy of the weights (`qualcomm_enable_weight_sharing=true`, the compiler's default O3). Each signature runs all 1,846 operations in one NPU partition, so nothing in the file falls back to the GPU or the CPU. The phone loads it without compiling. It runs only on its chip (`Build.SOC_MODEL`) and only with the runtime versions above. Building one takes 13β18 minutes and up to 24 GB of memory on the Linux host. |
|
| 232 |
-
|
| 233 |
-
| Chip | Snapdragon | Hexagon | Precompiled file | Size | Run on |
|
| 234 |
-
|---|---|---|---|---|---|
|
| 235 |
-
| SM8850 | 8 Elite Gen 5 | V81 | `npu/qualcomm/gliner25_decide_fp16hw_SM8850.tflite` | 877 MB | Galaxy Z Fold 8, 2026-11-01 |
|
| 236 |
-
| SM8750 | 8 Elite | V79 | `npu/qualcomm/gliner25_decide_fp16hw_SM8750.tflite` | 875 MB | compiled, not run on a phone yet |
|
| 237 |
-
| SM8650 | 8 Gen 3 | V75 | `npu/qualcomm/gliner25_decide_fp16hw_SM8650.tflite` | 860 MB | compiled, not run on a phone yet |
|
| 238 |
-
| SM8550 | 8 Gen 2 | V73 | `npu/qualcomm/gliner25_decide_fp16hw_SM8550.tflite` | 862 MB | Galaxy S23 Ultra, 2026-11-02 |
|
| 239 |
-
| SM8450 | 8 Gen 1 | V69 | `npu/qualcomm/gliner25_decide_fp16hw_SM8450.tflite` | 856 MB | compiled, not run on a phone yet |
|
| 240 |
-
|
| 241 |
-
### On a Galaxy Z Fold 8 (SM8850, 2026-11-01)
|
| 242 |
-
|
| 243 |
-
LiteRT 2.3.2 with `qnn-runtime` 2.50.0, Android 17. Each file ran the test requests that fit its signature, 10 calls
|
| 244 |
-
each, after a 60 s rest (thermal status 0 before and after). "Run" is the model alone, without the host's table lookup.
|
| 245 |
-
|
| 246 |
-
| File | Load | Run p50 | Largest logit difference from PyTorch FP32 | Labels as the library |
|
| 247 |
|---|---|---|---|---|
|
| 248 |
-
|
|
| 249 |
-
| `
|
| 250 |
-
|
|
| 251 |
-
|
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
|
| 267 |
-
|
|
| 268 |
-
|
|
| 269 |
-
|
|
| 270 |
-
|
|
| 271 |
-
|
|
| 272 |
-
|
| 273 |
-
- **Precompiled files run like the phone's own compile** here too, with the same logits.
|
| 274 |
-
- **Memory:** the four-signature file holds 50β69 MB of dirty memory and 853 MB of file pages. The GPU held 1.4 GB (s160)
|
| 275 |
-
and 2.0 GB (s512) of dirty memory. A compile on the phone holds about 1.3 GB of dirty memory in the process that
|
| 276 |
-
compiles; reloaded from LiteRT's compiler cache, the model holds about 50 MB, like a precompiled file.
|
| 277 |
-
- **s512:** 1.17 s a call on the NPU against 2.10 s on the GPU.
|
| 278 |
|
| 279 |
-
|
|
|
|
| 280 |
|
| 281 |
-
|
| 282 |
-
holds three more precompiled files: `gliner25_decide_fp16hw_se128-256_<chip>.tflite` (the classifier's `s128`/`s160`/`s256`
|
| 283 |
-
and the hidden states' `e128`/`e160`/`e256` over one copy of the weights, so one file serves both kinds of call) and
|
| 284 |
-
`gliner25_decide_heads_<chip>.tflite` (every signature of the heads file), and `gliner25_decide_span_<chip>.tflite`
|
| 285 |
-
(every signature of the span file, each in one NPU partition; 7β16 s to build). On an SM8550 the precompiled heads file loads in
|
| 286 |
-
0.5 s and holds 0.11 GB of dirty memory; compiled on the phone instead, the same file takes 63 s and 1.1 GB, with the same
|
| 287 |
-
outputs and speed.
|
| 288 |
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
| SM8550 | 8 Gen 2 | V73 | `npu/qualcomm/gliner25_decide_fp16hw_se128-256_SM8550.tflite` (778 MB) | `npu/qualcomm/gliner25_decide_heads_SM8550.tflite` (114 MB) | `npu/qualcomm/gliner25_decide_span_SM8550.tflite` (81 MB) | Galaxy S23 Ultra, 2026-11-02 |
|
| 295 |
-
| SM8450 | 8 Gen 1 | V69 | `npu/qualcomm/gliner25_decide_fp16hw_se128-256_SM8450.tflite` (777 MB) | `npu/qualcomm/gliner25_decide_heads_SM8450.tflite` (108 MB) | `npu/qualcomm/gliner25_decide_span_SM8450.tflite` (80 MB) | compiled, not run on a phone yet |
|
| 296 |
|
| 297 |
-
|
| 298 |
|
| 299 |
-
| |
|
| 300 |
|---|---|---|
|
| 301 |
-
|
|
| 302 |
-
|
|
| 303 |
-
|
|
| 304 |
-
|
|
| 305 |
-
|
|
| 306 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 307 |
|
| 308 |
## Files
|
| 309 |
|
| 310 |
-
`LICENSE` (Apache-2.0)
|
| 311 |
-
|
| 312 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
|
| 15 |
# GLiNER2.5-Decide β LiteRT (Android)
|
| 16 |
|
| 17 |
+
A LiteRT conversion of [fastino/GLiNER2.5-Decide](https://huggingface.co/fastino/GLiNER2.5-Decide) (revision
|
| 18 |
+
`bbe10ff77ebb238777c17d3a8ac9260e30929057`, Apache-2.0, by Fastino), converted by spybyscript. It carries every trained head:
|
| 19 |
+
classification, plus the span heads that extract entities, structures and relations. gliner2's own code decodes the
|
| 20 |
+
outputs. For the model itself (training, labels, benchmarks, intended use), see the
|
| 21 |
+
[original model card](https://huggingface.co/fastino/GLiNER2.5-Decide), copied here as `model-card.md`. Parts of the
|
| 22 |
+
graph code are ported from [litert-community/GLiNER2.5-Decide-LiteRT](https://huggingface.co/litert-community/GLiNER2.5-Decide-LiteRT).
|
| 23 |
|
| 24 |
+
## Files
|
|
|
|
|
|
|
|
|
|
| 25 |
|
| 26 |
| File | Size | Contents |
|
| 27 |
|---|---|---|
|
| 28 |
+
| `gliner25_decide_wfp16.tflite` | 1028 MB | encoder for the CPU and the GPU at FP32: `s128` / `s160` / `s256` / `s512` (classifier logits at every position) and `e128` β¦ `e512` (hidden states), one set of float16-stored weights |
|
| 29 |
+
| `gliner25_decide_fp16hw.tflite` | 1028 MB | the same encoder rewritten for FP16 hardware (GPU at FP16, NPU): LayerNorm power-of-two pre-scale, first feed-forward layer split in two |
|
| 30 |
+
| `gliner25_decide_span_wfp16.tflite` | 76 MB | span heads in a form LiteRT's GPU compiles: `w{W}` span representations, `f{W}_p{P}` fused span scores, `count`, classifier `k32` / `k128` |
|
| 31 |
+
| `gliner25_decide_count_wfp16.tflite` | 38 MB | count embedding `c{C}_m{M}` (CPU) |
|
| 32 |
+
| `gliner25_decide_heads_wfp16.tflite` | 105 MB | all span heads in the library's layout (CPU or NPU); the span file and the count file replace it |
|
| 33 |
+
| `gliner25_decide_embeddings_f16.bin` | 262 MB | token table, float16 [128011, 1024], little-endian, row-major |
|
| 34 |
+
| `runtime/` | | `litert_span_runtime.py` (reference runtime), `verify_parity.py`, `parity_corpus.json` |
|
| 35 |
| `npu/` | | Qualcomm NPU files (below) |
|
| 36 |
|
| 37 |
+
`config.json` lists every graph's signatures, shapes and host steps.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
|
| 39 |
## Use
|
| 40 |
|
|
|
|
| 43 |
import sys; sys.path.insert(0, "runtime")
|
| 44 |
from litert_span_runtime import LiteRTSpanExtractor
|
| 45 |
|
| 46 |
+
model = LiteRTSpanExtractor(".", span="gliner25_decide_span_wfp16.tflite", count="gliner25_decide_count_wfp16.tflite")
|
| 47 |
model.classify_text("Please cancel my order.", {"intent": ["cancel", "refund", "track"]})
|
| 48 |
model.extract_entities("Alice founded Acme in Toronto in 2020.", ["person", "organization", "city"])
|
| 49 |
model.extract_relations("Alice founded Acme in Toronto.", ["founded", "located_in"])
|
|
|
|
|
|
|
|
|
|
| 50 |
```
|
| 51 |
|
| 52 |
+
Every public gliner2 call works: `extract`, `classify_text`, `extract_entities`, `extract_json` and `extract_relations`,
|
| 53 |
+
plus their `batch_*` and `*_long` forms. The runtime runs gliner2's span extractor with the five trained modules on
|
| 54 |
+
LiteRT. Schema handling, scoring and decoding are the library's own code.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
|
| 56 |
+
**On a phone,** run the same graphs with LiteRT's `CompiledModel`:
|
| 57 |
+
1. Tokenize with `tokenizer.json` and pack the prompt as gliner2 2.0.0 does.
|
| 58 |
+
2. Look the ids up in the table (row `id` at byte `id * 2048`) into `inputs_embeds`.
|
| 59 |
+
3. Call the smallest signature that holds the request.
|
| 60 |
+
4. For classification, read each task's `[L]` marker positions from `logits`.
|
| 61 |
|
| 62 |
+
GPU settings: set the precision explicitly (FP32 for `wfp16`, FP16 for `fp16hw`), turn on constant tensor sharing, and use
|
| 63 |
+
NORMAL priority (see the S23 results). On the CPU, set XNNPACK's weight cache path. On a Snapdragon NPU, load the chip's
|
| 64 |
+
precompiled file.
|
| 65 |
|
| 66 |
+
**Fixed sizes.** The original runs with dynamic shapes; this conversion has fixed limits:
|
| 67 |
+
- a request holds at most 512 tokens (schema and text);
|
| 68 |
+
- spans up to 8 words, and up to 20 instances per structure (the checkpoint's own limits);
|
| 69 |
+
- up to 32 fields or entity types per task.
|
| 70 |
|
| 71 |
+
A larger request is refused with a ValueError, never truncated. For long text, use the `*_long` calls with a chunk that
|
| 72 |
+
fits. The weights change only in storage and compute precision.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
|
| 74 |
+
**The span file** computes the heads file's span representations with two exact rearrangements (only the order of float
|
| 75 |
+
sums differs):
|
| 76 |
+
- the first layer of the span head is computed once per word instead of once per span;
|
| 77 |
+
- `f{W}_p{P}` folds the last layer into the queries, so a host that needs only the scores skips the [W, 8, 1024] span tensor.
|
| 78 |
|
| 79 |
+
Every tensor is [1, rows, features], the shapes LiteRT's GPU delegate accepts.
|
|
|
|
|
|
|
| 80 |
|
| 81 |
## Validation
|
| 82 |
|
|
|
|
|
|
|
| 83 |
`runtime/verify_parity.py` runs each request through gliner2 with the original weights and through the reference runtime
|
| 84 |
+
(LiteRT CPU), then compares the structured results:
|
| 85 |
+
- **The corpus** (`runtime/parity_corpus.json`): 296 requests over 16 call types (entities, relations, structures, the
|
| 86 |
+
three record modes, attributes, single- and multi-label classification, mixed schemas). All 296 are identical, with
|
| 87 |
+
span F1 1.0 and confidences within 0.0013.
|
| 88 |
+
- **The same check with the span file and the count file:** also 296 of 296.
|
| 89 |
+
- **FluidInference's eleven schema-path fixtures:** 11 of 11.
|
| 90 |
+
- **2,338 recorded classification decisions:** `wfp16` gives the library's label on all of them, and `fp16hw` at FP16
|
| 91 |
+
precision on 2,332.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 92 |
|
| 93 |
+
`validation-full.json` and `validation.json` hold the numbers.
|
|
|
|
|
|
|
|
|
|
| 94 |
|
| 95 |
+
## Galaxy S23 Ultra (SM8550)
|
| 96 |
|
| 97 |
+
Snapdragon 8 Gen 2, Android 16, LiteRT 2.3.2 with `qnn-runtime` 2.50.0. Each graph ran on a request recorded from the
|
| 98 |
+
corpus: 10 calls after a 30 s rest, at thermal status 0. The CPU ran 4 threads; the GPU ran at NORMAL priority, timed as
|
| 99 |
+
whole calls. CPU and NPU times are the model alone.
|
|
|
|
| 100 |
|
| 101 |
+
| Graph | CPU | GPU FP16 (`fp16hw`) | GPU FP32 (`wfp16`) | NPU (precompiled) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 102 |
|---|---|---|---|---|
|
| 103 |
+
| encoder, 128 / 160 / 256 / 512 tokens | 628 / 866 / 1,467 / 3,486 ms | 103 / 124 / 227 / 773 ms | 202 / 268 / 471 / 1,549 ms | 34 / 55 / 168 / 1,149 ms |
|
| 104 |
+
| span file `w64` / `w128` / `w256` / `w512` | 43 / 105 / 221 / 450 ms | 10 / 15 / 25 / 47 ms | 19 / 26 / 42 / 79 ms | 7.3 / 6.0 / 14 / 42 ms |
|
| 105 |
+
| fused scores `f64` β¦ `f512`, 16 queries | 26 / 55 / 111 / 220 ms | 8 / 10 / 16 / 27 ms | 15 / 18 / 28 / 46 ms | 3.8 / 5.6 / 14 / 44 ms |
|
| 106 |
+
| count embedding, 1 step Γ 8 fields / 20 Γ 32 | 2.1 / 169 ms | not supported by LiteRT's GPU | not supported | 1.4 / 5.4 ms |
|
| 107 |
+
| classifier, 32 / 128 rows | 0.9 / 3.8 ms | 1.2 / 1.5 ms | 1.6 / 2.4 ms | 0.8 / 1.3 ms |
|
| 108 |
+
| dirty memory, encoder / span file | 1.9 / 1.7 GB | 2.7 / 0.8 GB | 4.6 / 1.3 GB | 0.12 / 0.21 GB |
|
| 109 |
+
|
| 110 |
+
- **The NPU's 512-token time** comes from the classification file; the hidden-state file stops at 256 tokens.
|
| 111 |
+
- **An extraction request** (up to 128 tokens and 64 words) takes about 43 ms on the NPU. On the GPU it takes about
|
| 112 |
+
115 ms at FP16 and 220 ms at FP32; on the CPU, about 660 ms.
|
| 113 |
+
- **Loading:** a precompiled NPU file loads in 0.5β1.7 s. The encoder's first GPU load takes 6β10 s.
|
| 114 |
+
- **Answers with the phone's outputs.** 32 recorded requests, two per call type, ran through the rest of the pipeline:
|
| 115 |
+
- the encoder: unchanged on the NPU, at GPU FP32 and on the CPU; at GPU FP16, 31 of 32 (one relation request changed);
|
| 116 |
+
- the span file: unchanged at GPU FP16 and on the NPU;
|
| 117 |
+
- the heads file: unchanged on the NPU.
|
| 118 |
+
- **GPU priority beside a rendering app.** An app rendered every frame (Godot 4.7, Vulkan, 120 Hz) while the model ran
|
| 119 |
+
three classification calls each round (`s160` and two `s256`, `fp16hw` at FP16):
|
| 120 |
+
|
| 121 |
+
| The model on | The app's longest frame per round | The three calls |
|
| 122 |
+
|---|---|---|
|
| 123 |
+
| GPU, NORMAL priority | 228 ms | about 0.65 s |
|
| 124 |
+
| GPU, LOW priority | 600 ms | about 1.6 s |
|
| 125 |
+
| NPU | 81 ms | about 0.5 s |
|
| 126 |
+
| CPU | 48 ms | 4β5 s |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 127 |
|
| 128 |
+
On this phone and LiteRT version, LOW does not let the app's rendering in; it only makes each call longer. On the GPU,
|
| 129 |
+
use NORMAL and the smallest signature that fits. The NPU keeps the frames.
|
| 130 |
|
| 131 |
+
## Qualcomm NPU files (`npu/`)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 132 |
|
| 133 |
+
The NPU path runs through LiteRT's Qualcomm accelerator:
|
| 134 |
+
- LiteRT 2.3.2 with `litert-npu-runtime-qualcomm` 2.3.2 and Qualcomm's `qnn-runtime` 2.50.0 (Qualcomm's AI Stack License);
|
| 135 |
+
- files precompiled on x86-64 Linux with QAIRT 2.50.0;
|
| 136 |
+
- each signature in one NPU partition;
|
| 137 |
+
- each file runs only on its chip (`Build.SOC_MODEL`) and with those runtime versions.
|
|
|
|
|
|
|
| 138 |
|
| 139 |
+
`npu/manifest.json` lists every file with its chip, signatures, size and SHA-256.
|
| 140 |
|
| 141 |
+
| File | Contents | Size |
|
| 142 |
|---|---|---|
|
| 143 |
+
| `npu/qualcomm/gliner25_decide_fp16hw_<chip>.tflite` | classification, `s128` β¦ `s512` | 856β877 MB |
|
| 144 |
+
| `npu/qualcomm/gliner25_decide_fp16hw_se128-256_<chip>.tflite` | classification and hidden states, 128β256 tokens | 777β792 MB |
|
| 145 |
+
| `npu/qualcomm/gliner25_decide_heads_<chip>.tflite` | the heads file | 108β115 MB |
|
| 146 |
+
| `npu/qualcomm/gliner25_decide_span_<chip>.tflite` | the span file | 80β81 MB |
|
| 147 |
+
| `npu/gliner25_decide_fp16hw_s128.tflite`, `_s160`, `_s256` | single-signature cuts that the phone compiles itself, once (on the S23: 21 / 41 / 117 s) | 660β710 MB |
|
| 148 |
+
|
| 149 |
+
| Chip | Snapdragon | Hexagon | Run on a phone |
|
| 150 |
+
|---|---|---|---|
|
| 151 |
+
| SM8850 | 8 Elite Gen 5 | V81 | classification file (Galaxy Z Fold 8) |
|
| 152 |
+
| SM8750 | 8 Elite | V79 | not yet |
|
| 153 |
+
| SM8650 | 8 Gen 3 | V75 | not yet |
|
| 154 |
+
| SM8550 | 8 Gen 2 | V73 | all four files (Galaxy S23 Ultra, the results above) |
|
| 155 |
+
| SM8450 | 8 Gen 1 | V69 | not yet |
|
| 156 |
|
| 157 |
## Files
|
| 158 |
|
| 159 |
+
- `LICENSE` (Apache-2.0) and `NOTICE.txt` (attribution and changes)
|
| 160 |
+
- `model-card.md`, the original card
|
| 161 |
+
- `config.json`, the contract
|
| 162 |
+
- `validation.json` and `validation-full.json`
|
| 163 |
+
- `SHA256SUMS`
|
| 164 |
+
- the tokenizer files
|
| 165 |
+
- `source/` (the checkpoint's configs)
|
| 166 |
+
- `runtime/` and `npu/`
|
SHA256SUMS
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30 LICENSE
|
| 2 |
c810d9c1789eef9890a1844136e77a9fb475c950df02a19ffdf3ecbbc50ec7be NOTICE.txt
|
| 3 |
-
|
| 4 |
98e61f6cec59c2238b2a80c4d3e829de88ba570ebd7ab22187672caa6737d9e7 config.json
|
| 5 |
43a132d3571d4a028013990f4866e266d01e35394819d89cbb1cd24d558608bc gliner25_decide_count_wfp16.tflite
|
| 6 |
f90f5d837721b3d4df7194a89ea18bbea5c449e6944fb6078664f1a8d20484fd gliner25_decide_embeddings_f16.bin
|
|
|
|
| 1 |
cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30 LICENSE
|
| 2 |
c810d9c1789eef9890a1844136e77a9fb475c950df02a19ffdf3ecbbc50ec7be NOTICE.txt
|
| 3 |
+
58a06d79fa17abe067a350bfb6e2b11f198ab5463d019e61570381e22eec0753 README.md
|
| 4 |
98e61f6cec59c2238b2a80c4d3e829de88ba570ebd7ab22187672caa6737d9e7 config.json
|
| 5 |
43a132d3571d4a028013990f4866e266d01e35394819d89cbb1cd24d558608bc gliner25_decide_count_wfp16.tflite
|
| 6 |
f90f5d837721b3d4df7194a89ea18bbea5c449e6944fb6078664f1a8d20484fd gliner25_decide_embeddings_f16.bin
|