Publish parse Hexagon-NPU bundle (v81) + model card
Browse files- .gitattributes +1 -0
- README.md +116 -0
- v81/doc_sample.png +3 -0
- v81/nemotron-parse.json +35 -0
- v81/parse_cross_proj.bin +3 -0
- v81/parse_decoder_step.bin +3 -0
- v81/parse_neck.bin +3 -0
- v81/parse_radio_preproc.bin +3 -0
- v81/radio_enc_1024.bin +3 -0
- v81/tokenizer.json +0 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,4 @@ saved_model/**/* 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
|
|
|
|
|
|
| 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 |
+
v81/doc_sample.png filter=lfs diff=lfs merge=lfs -text
|
README.md
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: other
|
| 3 |
+
license_name: nvidia-open-model-license
|
| 4 |
+
license_link: https://huggingface.co/nvidia/NVIDIA-Nemotron-Parse-v1.2
|
| 5 |
+
tags:
|
| 6 |
+
- qhexrt
|
| 7 |
+
- hexagon
|
| 8 |
+
- npu
|
| 9 |
+
- qnn
|
| 10 |
+
- vlm
|
| 11 |
+
- ocr
|
| 12 |
+
- document-understanding
|
| 13 |
+
base_model: nvidia/NVIDIA-Nemotron-Parse-v1.2
|
| 14 |
+
pipeline_tag: image-text-to-text
|
| 15 |
+
---
|
| 16 |
+
|
| 17 |
+
# Nemotron-Parse-v1.2 β Hexagon NPU (QHexRT)
|
| 18 |
+
|
| 19 |
+
Document OCR + layout parse (`nvidia/NVIDIA-Nemotron-Parse-v1.2`) compiled to run on the Qualcomm
|
| 20 |
+
Hexagon NPU through the [QHexRT](https://github.com/) runtime. Give it a document image; it returns the
|
| 21 |
+
text plus layout markup (`<class_Section-header>` / `<class_Text>` β¦) and bounding-box tokens.
|
| 22 |
+
|
| 23 |
+
It runs as a QHexRT **AED-VLM** plan: a RADIO ViT-H vision encoder feeds an MBart attention-encoder-decoder
|
| 24 |
+
that greedily decodes the page. Only the two heavy graphs (vision, decoder) run on the NPU; the glue is host C++.
|
| 25 |
+
|
| 26 |
+
## Arch
|
| 27 |
+
|
| 28 |
+
- **Hexagon v81** (`dsp_arch=v81`, `soc_model=87`). Bundle dir: `v81/`.
|
| 29 |
+
- **Arch-pinned:** a context binary bakes in `dsp_arch` + `soc_model`, so these `.bin`s load **only** on v81.
|
| 30 |
+
A different Hexagon arch needs a re-compile into a sibling `v79/` / `v83/` dir (load failure, not wrong output).
|
| 31 |
+
- Precision: **fp16** on both graphs (no quantization yet; W8A16 weight-only is the future perf/memory lever).
|
| 32 |
+
|
| 33 |
+
## Pipeline β what runs where
|
| 34 |
+
|
| 35 |
+
| stage | where | detail |
|
| 36 |
+
|---|---|---|
|
| 37 |
+
| RADIO preprocess | host C++ | CLIP mean/std normalize, `im_to_patches` 16Γ16, embedder 768β1280, 8 prefix (cls/register) tokens, CPE pos-embed β `embed[3336,1280]` |
|
| 38 |
+
| **vision encoder** | **NPU graph** `radio_enc_1024` | C-RADIOv2-H, 32-block ViT-H (1280-dim, 16 heads), full attention. `embed[3336,1280] β vit_feat[3336,1280]`, fp16, 2D I/O |
|
| 39 |
+
| neck | host C++ | drop 8 prefix β conv1βLN β window conv2βLN + summary token β **833 cross-attn memory tokens** `mem[833,1024]` |
|
| 40 |
+
| cross-KV | host C++ | project `mem` by each decoder layer's `encoder_attn.{k,v}_proj` β the cross-attention K/V, bound once |
|
| 41 |
+
| **decoder step** | **NPU graph** `parse_decoder_step` | MBart AED, 10 layers, d1024/16 heads. Single-step **fixed-MDL=512 KV cache**, in-graph int32 embed + in-graph untied lm-head β `logits[52352]`, fp16. No `position_ids` (MBart has no positional embedding) |
|
| 42 |
+
| decode loop | host C++ | greedy argmax, self-KV ping-pong, seed = eos = `decoder_start_token_id` = 2, stop on predicted eos β detok β text + layout markup |
|
| 43 |
+
|
| 44 |
+
No custom op-package (RADIO ViT + AED decoder are native QNN ops) β **no `ADSP_LIBRARY_PATH` needed**.
|
| 45 |
+
|
| 46 |
+
## Files (`v81/`)
|
| 47 |
+
|
| 48 |
+
| file | role | size |
|
| 49 |
+
|---|---|---|
|
| 50 |
+
| `nemotron-parse.json` | QHexRT manifest (the declarative run plan) | 2.4 KB |
|
| 51 |
+
| `radio_enc_1024.bin` | vision encoder context binary (RADIO ViT-H, fp16) | 1329.1 MB |
|
| 52 |
+
| `parse_decoder_step.bin` | MBart AED decoder-step context binary (KV-cache + in-graph lm-head, fp16) | 512.7 MB |
|
| 53 |
+
| `parse_radio_preproc.bin` | host blob: input-conditioner + patch-generator weights (CLIP norm, embedder 768β1280, prefix, CPE pos) | 21.0 MB |
|
| 54 |
+
| `parse_neck.bin` | host blob: neck weights (conv1, ln1, conv2, ln2, sum_proj, ln3) | 44.3 MB |
|
| 55 |
+
| `parse_cross_proj.bin` | host blob: `encoder_attn.{k,v}_proj` weight+bias Γ10 layers (cross-KV projection) | 84.0 MB |
|
| 56 |
+
| `tokenizer.json` | tokenizer (vocab 52352) | 4.0 MB |
|
| 57 |
+
| `doc_sample.png` | device-gate fixture document | 0.1 MB |
|
| 58 |
+
|
| 59 |
+
Total β **2.0 GB**. The QNN runtime libs (`libQnnHtp.so`, `libQnnSystem.so`, the v81 HTP skel) come from the
|
| 60 |
+
QAIRT SDK β they are **not** in this repo.
|
| 61 |
+
|
| 62 |
+
## Run (on-device, over adb)
|
| 63 |
+
|
| 64 |
+
Stage the QAIRT aarch64 runtime libs + the `qhx_generate` tool into `/data/local/tmp/wq` once (see the QHexRT
|
| 65 |
+
`docs/DEPLOY.md`), then:
|
| 66 |
+
|
| 67 |
+
```bash
|
| 68 |
+
hf download runanywhere/nemotron_parse_HNPU --local-dir nemotron_parse_HNPU
|
| 69 |
+
adb push nemotron_parse_HNPU/v81 /data/local/tmp/wq/nemotron_parse # PowerShell + native paths on Windows
|
| 70 |
+
|
| 71 |
+
adb shell "cd /data/local/tmp/wq && LD_LIBRARY_PATH=. \
|
| 72 |
+
./qhx_generate nemotron_parse/nemotron-parse.json libQnnHtp.so libQnnSystem.so nemotron_parse \
|
| 73 |
+
512 '' nemotron_parse/doc_sample.png"
|
| 74 |
+
```
|
| 75 |
+
|
| 76 |
+
Tool arg order is invariant: `<tool> <manifest> libQnnHtp.so libQnnSystem.so <artifacts_root> <max_new> "<prompt>" [image]`.
|
| 77 |
+
The **image is the input**; the text prompt is ignored (the host-op runs its own greedy decode to eos). `max_new`
|
| 78 |
+
caps the decode at the MDL (512 β 511 tokens β a paragraph / simple page).
|
| 79 |
+
|
| 80 |
+
## Device validation (Hexagon v81, S25 reference device)
|
| 81 |
+
|
| 82 |
+
Validated per the QHexRT three-gate rubric (offline oracle β on-device raw-I/O parity β end-to-end over multiple inputs):
|
| 83 |
+
|
| 84 |
+
- **Vision, on-device raw-I/O parity:** `embed[3336,1280]` F16 β `vit_feat`, cosine **0.999030** vs the offline
|
| 85 |
+
reference at RES=1024 (3336-token ViT). (Offline oracle cos 1.0007 vs `vit.forward_features`.)
|
| 86 |
+
- **Decoder:** offline fixed-MDL(512) step cosine **1.000001** vs the full-seq `NemotronParseDecoder`; device-functional
|
| 87 |
+
(loads, emits `logits[52352]` + all self-KV out β the KV ring works).
|
| 88 |
+
- **End-to-end** on `doc_sample.png` β reads the document text **exactly** vs the pure-greedy gold (65-token chain,
|
| 89 |
+
seed 2 β eos 2): title + body text + `<class_Section-header>`/`<class_Text>` layout markup + bbox tokens. Confirmed
|
| 90 |
+
on **3 additional synthetic documents**.
|
| 91 |
+
- Text content is **exact**; bounding-box y-coordinates drift ~0.03 (fp16 coordinate-regression precision, a
|
| 92 |
+
`greedy_tol`-class tolerance β not a text error).
|
| 93 |
+
|
| 94 |
+
### Measured performance (v81)
|
| 95 |
+
|
| 96 |
+
| phase | time |
|
| 97 |
+
|---|---|
|
| 98 |
+
| vision encoder (3336-wide self-attention) | ~1986 ms |
|
| 99 |
+
| decode | ~14 ms/token |
|
| 100 |
+
| end-to-end (`doc_sample.png`, 65 tokens) | ~15.7 s |
|
| 101 |
+
|
| 102 |
+
## Caveats
|
| 103 |
+
|
| 104 |
+
- **Fixed device resolution 1024Γ832.** The processor resizes by longest edge then white-pads; any-aspect document
|
| 105 |
+
is letterboxed to 1024Γ832 on the host (patch grid stays 64Γ52). Other resolutions need a re-export.
|
| 106 |
+
- **Output cap β 511 tokens** (MDL=512) β a paragraph or a simple page. A full multi-page document needs MDL raised
|
| 107 |
+
and the decoder re-exported.
|
| 108 |
+
- **Memory:** the 3336-wide vision self-attention spills ~5.5 GB DDR, so a ~12 GB device (S25) is required.
|
| 109 |
+
- **fp16, not quantized.** Bounding-box coordinates are the only place fp16 shows (~0.03 drift); text is exact.
|
| 110 |
+
- Greedy (temp=0) is the parity metric; the source config's `repetition_penalty=1.1` is a sampler-quality knob,
|
| 111 |
+
identical to pure greedy on short documents.
|
| 112 |
+
|
| 113 |
+
## License
|
| 114 |
+
|
| 115 |
+
Governed by the source model's license (`nvidia/NVIDIA-Nemotron-Parse-v1.2`, NVIDIA Open Model License). This repo
|
| 116 |
+
ships only the compiled Hexagon context binaries + host weights + manifest, not the original checkpoints.
|
v81/doc_sample.png
ADDED
|
Git LFS Details
|
v81/nemotron-parse.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"schema_version": 1,
|
| 3 |
+
"_comment": "NB-9 NVIDIA-Nemotron-Parse-v1.2 document OCR / layout-parse as a QHexRT AED-VLM family (image -> RADIO ViT-H vision -> host neck -> MBart AED decoder -> autoregressive greedy). The nemotron_parse_generate host-op runs the whole pipeline: host RADIO preprocess (LongestMaxSize-resize + white-pad to 1024x832, /255, OPENAI-CLIP norm, patch_generator im_to_patches 16x16 -> embedder 768->1280 + 8 prefix + CPE pos_embed) -> NPU vision graph radio_enc_1024 (32 RADIO blocks, 2D I/O embed[3336,1280] -> vit_feat[3336,1280], fp16) -> host neck (drop 8 prefix -> conv1/LN1 -> window conv2/LN2 + summary token -> 833 cross-attn memory tokens[833,1024]) -> host cross-KV (mem @ decoder.encoder_attn.{k,v}_proj per layer) -> NPU decoder step graph parse_decoder_step (single-step fixed-MDL=512 KV cache: input_ids[1] int32 in-graph embed, attention_mask[512], k/v_cache_self_N_in/out[512,1024], k/v_cache_cross_N[833,1024], in-graph lm-head -> logits[52352]) greedy decode loop (asr_whisper pattern: seed=eos=decoder_start_token_id=2, right-aligned mask, self-KV ping-pong, NO position_ids). No custom op-package (RADIO ViT + AED decoder are native QNN ops) -> no ADSP_LIBRARY_PATH needed (like Whisper). Host weight blobs (parse_radio_preproc.bin, parse_neck.bin, parse_cross_proj.bin) + tokenizer.json in fixture_dir. Offline-gated: preprocess cos 1.0008, neck cos 1.000066, cross-KV cos 1.000000; both graphs device-functional (vision Gate-B cos 0.999030, decoder loads + emits logits). Graph names are the compiled binary's real internal names (radio_enc_1024_f16 / parse_decoder_step_f16 from build_f16.sh); confirm with qhx_meta after compile.",
|
| 4 |
+
"model": { "name": "nemotron-parse", "family": "vlm", "dsp_arch": "v79" },
|
| 5 |
+
"params": {
|
| 6 |
+
"vocab": 52352,
|
| 7 |
+
"n_layers": 10,
|
| 8 |
+
"hidden": 1024,
|
| 9 |
+
"head_dim": 64,
|
| 10 |
+
"max_ctx": 512,
|
| 11 |
+
"eos_token_id": 2
|
| 12 |
+
},
|
| 13 |
+
"artifacts": {
|
| 14 |
+
"contexts": {
|
| 15 |
+
"vision": { "bin": "radio_enc_1024.bin" },
|
| 16 |
+
"decoder": { "bin": "parse_decoder_step.bin" }
|
| 17 |
+
},
|
| 18 |
+
"tokenizer": "tokenizer.json",
|
| 19 |
+
"fixture_dir": "."
|
| 20 |
+
},
|
| 21 |
+
"plan": {
|
| 22 |
+
"steps": [
|
| 23 |
+
{
|
| 24 |
+
"host": "nemotron_parse_generate",
|
| 25 |
+
"params": {
|
| 26 |
+
"vision": "radio_enc_1024_f16",
|
| 27 |
+
"decoder": "parse_decoder_step_f16",
|
| 28 |
+
"res": 1024,
|
| 29 |
+
"decoder_start": 2,
|
| 30 |
+
"eos": 2
|
| 31 |
+
}
|
| 32 |
+
}
|
| 33 |
+
]
|
| 34 |
+
}
|
| 35 |
+
}
|
v81/parse_cross_proj.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:99c064845844826b7adb7caa3a216bb9fb691e7a69fd8ceb3fe1802d1d198be8
|
| 3 |
+
size 83968000
|
v81/parse_decoder_step.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ae6924eba1bfe6b4d215999a0ae9383b2a50a5c7c664d4e3fff386ff73720be5
|
| 3 |
+
size 512659456
|
v81/parse_neck.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:066c5afabfb38130fda03195a8b8ad389a797fb97a75a7c89d430b976649d858
|
| 3 |
+
size 44340240
|
v81/parse_radio_preproc.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:53d17ca48c9b709453ad59546fa062f0afe79c0a2d5d078e836daec7a01ece21
|
| 3 |
+
size 21017600
|
v81/radio_enc_1024.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e9287a8f01692ef6c3f9a3b5ee38abe526028ea7b7df927c93c8df2dd8a3b6c7
|
| 3 |
+
size 1329102848
|
v81/tokenizer.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|