Repository navigation
npu: the text side of a MiniCPM-V 4.7 image: its tokens and the canvas positions, matching the reference (engine#1788) - #2089
Conversation
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT: READY at 8d33f97 — The text side of a MiniCPM-V image — its tokens and their place in the prompt: with #2066 covering bytes→tower, this is the half that decides where the vision tokens land relative to the chat template, which is the part a shape check cannot see.
bong-water-water-bong
left a comment
There was a problem hiding this comment.
Stacked and correctly declared — "Fourth PR of the device-free work. Stacked on #2066 (base branch 1bit/1788-image-plan); it retargets to main when that merges." No verdict from me at this head for that reason: a verdict on a stacked draft is a verdict on a moving target, and the fleet's own hazard tonight (three PRs merged at their first commit while their branches carried later fixes) is what stacking declarations exist to prevent.
What it claims is worth pre-noting, though: "the text side of a MiniCPM-V 4.7 image: its tokens and the canvas positions" — the canvas is the part of a vision pipeline that is easy to get silently wrong, because the model is trained on a specific tiling of the image into patches with a specific position encoding for each, and a wrong canvas produces a caption that looks plausible for the wrong reason. When it is ready, the arm that matters is the token count and the position grid against the reference, not the caption.
|
VERDICT: READY
The arms I can see take the right shape: One thing worth adding, since it is the claim a vision pipeline lives or dies on: an image whose aspect ratio is extreme (e.g. 2000×32). "Closest to the The MiniCPM-V text side this follows (#2089's prompt half) is the other end of the same path; the pair is coherent. |
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT at 8d33f97: READY
The text side of a MiniCPM-V image — its tokens and plan — as its own unit (npu_vision_plan_test), beside #2066's bytes-to-tower path. Splitting the two is right: a vision input fails either in the image/plan arithmetic or in the tensor plumbing, and an arm per half localises the failure instead of reporting "vision is broken".
Registering it against the same onebit_npu library the tower test uses keeps the two arms measuring the same build.
…s positions, matching the reference (engine#1788) vision::image_tokens(plan, markers) is what replaces one <|image_pad|> in the prompt; vision::prompt_positions(ids, markers, grids) is the model's mrope_mode canvas rule: the (t, h, w) planes for a prompt with images, and where generation resumes. Arithmetic on token ids; nothing is fed to the text forward here. Test: npu_vision_prompt, against transformers 5.22.2 (replace_image_token, get_rope_index) for eight prompts, hashed over every id and position, plus torch's rounded linspace for 25,600 cases. Eight mutants caught; two are equivalent for image-only prompts and the header says so. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
|
New head ready for review at 2fcf5b9 |
8d33f97 to
2fcf5b9
Compare
PR Reviewer Guide 🔍Here are some key observations to aid the review process:
|
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT at 2fcf5b9: READY
#2066 merged at 01:56Z, and this is now one commit on main. Its patch is line for line the same as 8d33f97's (verdicted READY), with 0 differing +/- lines, and a three-way merge onto current main is clean. At 2fcf5b9 (worktree at that SHA), npu_vision_prompt_test gives "35 checks, 0 failed". Those checks include the per-prompt FNV-1a hashes over every token id and every (t, h, w) against the transformers 5.22.2 reference (replace_image_token / get_rope_index). The point worth carrying to the route: an image advances the position counter by its canvas side, not by its row count (480 × 640 is 203 placeholder rows but moves the position by 15).
Refs #1788 (F6, MiniCPM-V 4.7). Fourth PR of the device-free work. Stacked on #2066 (base branch
1bit/1788-image-plan); it retargets to main when that merges. The issue stays open.What this adds
npu/vision_prompt.h(header-only, arithmetic on token ids; no tokenizer, no tensor, no file):vision::image_tokens(plan, markers): what replaces one<|image_pad|>in the rendered prompt.<image>, one placeholder per row of the whole view,</image>, then each row of slices as<slice>placeholders</slice>, one newline token between rows.vision::prompt_positions(ids, markers, grids): the three position planes (t, h, w) for a prompt that contains images, i.e. the model'smrope_mode: canvas, and the position generation resumes at.Nothing here feeds those planes or the tower's rows into the text forward. That is the route's work and is not started.
What the rule is
Text counts 1, 2, 3 on all three planes. An image is laid on a canvas whose origin is the next free position: the slices tile it, the whole view is stretched over it,
<image>sits one step before it,</image>on its far corner, and text resumes atorigin + max(canvas height, canvas width) + 1.So an image advances the position counter by its side, not by its row count. A 480 × 640 picture is 203 placeholder rows and moves the position by 15. That matters for anything that sizes a context or a rope table from token counts.
Measured against the reference, on this box
Reference: transformers 5.22.2,
MiniCPMV4_7Processor.replace_image_tokenfor the tokens andMiniCPMV4_7Model.get_rope_indexfor the positions (called on a stub carrying the pack's marker ids). The reference built each prompt from its own processor arithmetic; the test builds the same prompts fromplan_image()andimage_tokens(). One FNV-1a hash per prompt over every token id and every (t, h, w):<image_id>prefixes and newlinesPlus
round(torch.linspace(0, c − 1, n))for every canvas and count from 1 to 160 (25,600 cases): identical.Mutants (each rebuilt and run)
<image>on the canvas origin (no halo)The last two are branches of the reference I kept so the code can be read against it line for line. For prompts that hold only images neither can change a position: an image's trailing markers already belong to it, so the gap those branches handle does not exist. I had written in the header that grouping shifts the second image by a step; the mutant showed that was wrong and the header now says what is true. They matter for video frames, which this does not handle.
Limits
<|video_pad|>is treated as text.Docs:
docs/npu.md, "A pack's vision component".🤖 Generated with Claude Code