Repository navigation
npu: read a pack's vision component (vision_weight.q4nx): every tensor placed, geometry from the tensors (engine#1788) - #2040
Conversation
…r placed, geometry from the tensors (engine#1788) FastFlowLM's MiniCPM-V 4.7 packs ship the vision tower as a second container beside model.q4nx, and nothing in the engine opened it. npu/vision_pack.h reads it: all 347 tensors BF16 and accounted for, the tower's geometry derived from the tensors' own shapes, and every disagreement refused by name. Both published packs state two MLP widths in config.json that their tensors do not have (4304 vs 4608, 17216 vs 17280); those are reported and overruled, never used to size anything. 1bit npu-pack vision <pack dir> prints the result. A reader only: no forward runs the tower yet. Test: npu_vision_pack (70 checks on crafted containers; +8 on a published pack via ONEBIT_MINICPM_PACK). Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
|
ready for review at d51f4ef |
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT: READY at d51f4ef
This is the vision half of F1, and the scoping is right: "First scoped PR of the device-free vision half; the issue stays open and my comment there lists what remains." And the premise is the one my own census confirmed from the other side — "FastFlowLM's MiniCPM-V 4.7 packs are two conta[iners]" — which is exactly the multi-component shape I hit on Gemma4-E2B-IT-NPU2 (model.q4nx 4.67 GB text + vision_weight.q4nx 337 MB + audio_weight.q4nx 952 MB).
"Every tensor placed" is the right property to establish before any forward runs, because a vision component's failure mode is not a crash: it is a tower that runs with a tensor in the wrong slot and produces plausible pooler output. Placing every tensor, and saying which ones have no home, converts that into an inventory question.
Two asks, both from the F1 census (engine#1786):
vision_weight.q4nxshould be reported through the same genre arm asmodel.q4nx— the census found 13 self-describing containers, 14 gguf-converted and 2 with no metadata at all (d3-edge,d3-flash), so the vision container may be a different genre from its text sibling in the same pack; a reader that assumes they match will read one of them blind;- report the component's
dimswhen it has them — for the self-describing genre the geometry is already in the header, and for Gemma4-E2B the vision tower's own hidden/block/merger numbers are the ones the forward needs.
No other asks.
…dding and carry the model's width (engine#1788) The first commit called config.json stale about two MLP widths. It is not: 4304 and 17216 are the model's widths (the source GGUF's ffn tensors), and the container stores them 4608 and 17280 wide with the extra rows all zero. Measured by rebuilding all 347 tensors of both published packs from their source mmproj GGUFs: 263 bit-identical, 84 identical in the source's extent and +0.0 in the padding, 0 different. The reader still sizes everything from the tensor. It now checks that every value past the stated width is zero in every block, carries the model's width (Geometry::model_intermediate), says so when the tail is not zero, and refuses a config that states an MLP wider than its tensors. Docs and comments corrected. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
|
New head ready for review at a8c38f4 |
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT at a8c38f4: READY
Checked at a8c38f4. A three-way merge onto main is clean. ci_targets_check is at baseline 9 with none new, tests_referenced_check passes, and copyright rc is 0.
-
npu_vision_pack_test: "78 checks, 0 failed". WithONEBIT_MINICPM_PACK=~/scratch-packs/MiniCPM-V-4.7-1B-NPU2the published-pack arm runs too: "87 checks, 0 failed", with both padding notes. -
The reader on the four
vision_weight.q4nxfiles on this box:pack rc result MiniCPM-V-4.7-1B-NPU2 0 347 tensors, all BF16, 0 B unaccounted; 27 blocks at 1152 (16 × 72), MLP 4304 (4608 stored), vit_merger 17216 (17280 stored), merger → 1024; source mmproj-model-f16.ggufwith the sha256 the description quotesMiniCPM-V-4.7-35B-A3B-NPU2 0 the same tower, merger → 2048 (its text hidden size) gemma4-e2b-npu2 1 refused: "has no tensor model.visual.patch_embed.proj.weight" Qwen3.8-27B-NPU2 refused "patch_embed.proj.weight is [1152, 3, 2, 16, 16], not [hidden, channels, patch, patch]" So the two towers it's for read with consistent geometry, and two different towers are refused by name, not misread.
-
The padding check can fail. With
is_zeromade to return true (any tail accepted as padding), the suite fails "not padding: one nonzero value past the stated width keeps the tensor's width" and "… the note says so". Restored, it's 78/78.
Read: is_zero treats ±0.0 as zero. A stated width larger than the tensor is refused. A smaller one is taken as padding only when every element past it is zero in fc1 rows, fc1 bias and fc2 columns, across every block; otherwise the tensor's width stands, with a note. The correction at a8c38f4 (the widths are padded, config.json is right) is the strongest part of the PR, because it rests on a bit-exact rebuild from the source GGUF the container names, with the other model's source as a control.
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT at 56110f5: READY
The new head is a merge of main (#2024, #2029, #2033 and others). The PR's own diff is identical to a8c38f4, which I passed, and a three-way merge onto current main is clean. At 56110f5, npu_vision_pack_test with ONEBIT_MINICPM_PACK=~/scratch-packs/MiniCPM-V-4.7-1B-NPU2 gives "87 checks, 0 failed".
PR Reviewer Guide 🔍Here are some key observations to aid the review process:
|
Refs #1788 (F6, MiniCPM-V 4.7). First scoped PR of the device-free vision half; the issue stays open and my comment there lists what remains.
What was missing
FastFlowLM's MiniCPM-V 4.7 packs are two containers:
model.q4nxandvision_weight.q4nx(the vision tower). Nothing in the engine opened the second one; the facts about it in #1788 were read by hand with a Python parser. On main:What this adds
npu/vision_pack.h: a reader for the vision component, and1bit npu-pack vision <pack dir>to run it.config.jsonis consulted only for what tensors cannot say (head count, image size, the unpadded MLP widths) and refused where the two cannot both be right (heads × head_dim ≠ width, image ≠ grid × patch, another width / block count / position count, texthidden_size≠ what the merger projects to, an MLP stated wider than its tensors).config.jsonstates); the container stores them 4608 and 17280 wide with the extra rows zero. Every size comes from the tensor; the reader checks that everything past the stated width is zero in every block and carries the model's width beside the stored one.It is a reader only. No forward runs the tower yet.
Correction at
a8c38f4(second commit)The first commit, and the #1788 thread before it, called
config.jsonstale about those two widths. That was wrong, and I found it by doing the check the issue's done-condition 1 asks for: the self-check against a named reference.The 1B container's
__metadata__namesmmproj-model-f16.ggufwith a SHA-256. That is the file of that name inopenbmb/MiniCPM-V-4.7-1B-gguf(downloaded,sha256summatches:fb9cc047…5a900). I rebuilt every pack tensor from the GGUF and compared BF16 bit patterns:So the source's ffn tensors are 4304 and 17216 wide, and the pack pads them to the next multiple of 128 with
+0.0.attn.qkvis the source'sattn_q,attn_k,attn_vstacked in that order;merger.normisv.post_ln. That comparison was a script run by hand (it needs 2.2 GB of source files), not a test in the tree; the docs say so.Measured on this box (both published packs, device-free)
The 35B-A3B pack prints the same tower with
merger 4608 -> 4608 -> 2048and no provenance lines: its container carries no__metadata__.Test
npu_vision_pack(new, registered in ctest, built withonebit): crafted containers with the published packs' tensor names and shape relations, one edit per arm.Mutants of the reader, each rebuilt and run:
config.jsonlinear_fc2columnsCI does not have the packs, so the published-pack arm runs only where
ONEBIT_MINICPM_PACKis set; the crafted arms run everywhere.Not in this PR
A host forward for this tower, image preprocessing and slicing, the
<|image_pad|>join, and anything on the device. Our llama.cpp fork has aminicpmv4_6projector but nominicpmv4_7one (which is what both source GGUFs declare), so there is no forward oracle on the box yet.Docs:
docs/npu.md, "A pack's vision component".🤖 Generated with Claude Code