Skip to content

model, mtmd: fix gemma4 vision handling - #28335

Merged
ngxson merged 2 commits into
masterfrom
xsn/gemma4_vision_fix
Sep 4, 2026
Merged

model, mtmd: fix gemma4 vision handling#28335
ngxson merged 2 commits into
masterfrom
xsn/gemma4_vision_fix

Conversation

@ngxson

@ngxson ngxson commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Overview

Fix #28318

Recap which model has what:

Model use_bidirectional_attention Embedding dim (hidden_size) Per-layer embedding dim (PLE) Notes
gemma-4-E2B-it null → causal 1536 256 35 layers, 20 KV-shared
gemma-4-E4B-it null → causal 2560 256 42 layers, 18 KV-shared
gemma-4-12B-it "vision" → bidirectional 3840 0 unified (encoder-free)
gemma-4-26B-A4B-it "vision" → bidirectional 2816 0 MoE, 128 experts / top-8 (read from base repo; -it is identical)
gemma-4-31B-it "vision" → bidirectional 5376 0 dense, 60 layers
gemma-4-31B-it-assistant null 1024 (drafter) · backbone_hidden_size 5376 0 4-layer MTP drafter; projects from the 31B backbone
gemma-4-E2B/E4B/12B/26B-A4B-it-assistant null not fetched expect same pattern: small drafter width + backbone_hidden_size = target's dim

Requirements

@ngxson
ngxson requested review from a team, CISC and ggerganov as code owners September 3, 2026 17:22
@github-actions github-actions Bot added model Model specific mtmd Related to multimodal functionality (video/image/audio) labels Sep 3, 2026
@ngxson

ngxson commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator Author

/bot review

@ggml-gh-bot

ggml-gh-bot Bot commented Sep 3, 2026

Copy link
Copy Markdown
Automated code review

Review of PR #28335 (model, mtmd: fix gemma4 vision handling)

I reviewed the diff plus surrounding code (llama-kv-cache.cpp mask machinery, llama-model.cpp memory creation for gemma4, mtmd-helper.cpp decode path). The swa_full_non_causal bool to llama_non_causal_type enum refactor is a clean generalization, and the per-cache override in llama_kv_cache::set_input_kq_mask is correct for gemma4: it is routed to llama_kv_cache_iswa (llama-model.cpp:2602+), where the base cache is constructed with LLAMA_SWA_TYPE_NONE (llama-kv-cache-iswa.cpp:98) and the SWA cache with hparams.swa_type, so dense layers stay causal while SWA layers go bidirectional. The deepseek4 change is a mechanical rename with unchanged behavior.

Blocking / important

(point 1) Image chunks larger than n_ubatch abort the process for the bidirectional gemma4 models. tools/mtmd/clip.cpp:1639 raises the image budget from 280 to 1120 tokens, while mtmd_helper_decode_image_chunk decodes the whole chunk (up to n_batch = 2048 by default) in one llama_decode call with causal_attn = false. llama_context::decode then hits GGML_ASSERT((cparams.causal_attn || cparams.n_ubatch >= n_tokens_all)) (llama-context.cpp:1724), and with the default n_ubatch = 512 (common/common.h:452) any gemma4 image producing more than 512 tokens - e.g. roughly a 1024x1024 photo - hard-aborts llama-server/mtmd-cli instead of returning an error. The old 280 cap kept this unreachable; the new 1120 cap makes it the common case. The TODO at mtmd-helper.cpp:99 acknowledges the requirement, but nothing enforces it. Suggest either checking llama_n_ubatch(lctx) >= n_tokens in the helper and failing gracefully (with a hint to raise --ubatch-size), or clamping the per-model token budget to what the context can decode non-causally. Note that simply splitting the chunk is not a fix: tokens across split ubatches are not bidirectional to each other.

Will slow the review

(point 2) Hardcoded hidden sizes in mtmd_decode_use_non_causal (tools/mtmd/mtmd.cpp:2176-2178). Gating causality on n_embd_text != 1536 && n_embd_text != 2560 duplicates model-config knowledge (use_bidirectional_attention in the HF config) in a second place, keyed by a number rather than by config. A future gemma4 vision variant that reuses one of those widths would silently get the wrong mask. At minimum, please add a short comment naming where the numbers come from (E2B/E4B use_bidirectional_attention = null); longer term, storing that config value in the GGUF at conversion time and reading it here (via llama_model_meta_val_str) would remove the guesswork entirely and let the llama-side non_causal_type and the mtmd-side decision share one source of truth.

(point 3) Token budget values need a reference. The removed comment at clip.cpp:1636-1637 documented why the old minimum of 40 existed (poor quality with small images). The new (70, 1120) pair has no provenance. Please confirm 70/1120 match the reference image processor config for gemma4, and state in the PR or a one-line comment where they come from - also noting that this case block is shared by both GEMMA4V and GEMMA4UV, so the same budget applies to the unified 12B/26B/31B models.

Nits

(point 4) The stale // TODO: is causal == true correct? might need some changes above build_attn_inp_kv_iswa() in src/models/gemma4.cpp can now be removed or updated - this PR is what resolves that question.

(point 5) The comment "only the SWA cache (or the SWA layers of a single cache) become non-causal" (llama-kv-cache.cpp:1757-1758) overstates what the code does: for a single cache with swa_type != NONE that also holds dense layers, the dense layers would incorrectly become non-causal, since the override keys off the cache's swa_type, not per-layer. No current SWA_ONLY arch hits that (gemma4 uses the iswa split), but the comment should not claim support the code does not have.

(point 6) ctx->n_embd_text is -1 when the mtmd context was created without a text model, which makes the GEMMA4V check return non-causal. Harmless in practice (decode needs an lctx anyway), but if that path is ever reachable it defaults to the more dangerous branch.

Everything else in the diff looks correct: the in_span change is behavior-preserving for deepseek4, the enum default (ALL = 0) matches the old false default for all other archs, and print_info addition is fine. Style and comment conventions (ASCII, concise) are respected.

This review was generated automatically by pi coding agent using zai-org/GLM-5.3. It may contain mistakes. Maintainers make the final call.

@ngxson
ngxson merged commit 163a407 into master Sep 4, 2026
23 of 26 checks passed
@ggerganov
ggerganov deleted the xsn/gemma4_vision_fix branch September 4, 2026 10:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

model Model specific mtmd Related to multimodal functionality (video/image/audio)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

mtmd: Gemma 4 E2B/E4B image tokens are decoded non-causally, the models expect causal attention

3 participants