Skip to content

Add gemma4 OpenVINO NPU support - #746

Open
xiaoyu-work wants to merge 3 commits into
mainfrom
fix/gemma4-openvino-npu
Open

xiaoyu-work wants to merge 3 commits into
mainfrom
fix/gemma4-openvino-npu

Conversation

@xiaoyu-work

Copy link
Copy Markdown
Member

Add gemma4 OpenVINO NPU support

Emit an OpenVINO-compatible rank-4 attention graph, preserve opset 23 Attention semantics, and use canonical Gather extraction for per-layer inputs so NPUW can compile repeated decoder blocks.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: d7d02d41-4f2e-4bdd-ae7d-0d22fbc6d3bd
Drop checkpoint K/V projection and norm tensors for layers that borrow a shared KV cache, including packed quantized sidecars that have no target module.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: d7d02d41-4f2e-4bdd-ae7d-0d22fbc6d3bd
Remove one-off requires_* capability flags and branch directly on the OpenVINO execution provider where the Gemma4 and opset workarounds are applied.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: d7d02d41-4f2e-4bdd-ae7d-0d22fbc6d3bd
@xiaoyu-work
xiaoyu-work requested review from a team and a lite review from Copilot September 23, 2026 19:07
@github-actions

Copy link
Copy Markdown

Performance Comparison

Comparing 783e03b5 → cc982302

Model Metric Baseline Current Delta
bert (feature-extraction) model_size_bytes 359 KB 359 KB +0.0% ⚪
bert (feature-extraction) num_nodes 68 68 +0.0% ⚪
falcon model_size_bytes 364 KB 364 KB +0.0% ⚪
falcon num_nodes 66 66 +0.0% ⚪
gemma2 model_size_bytes 428 KB 428 KB +0.0% ⚪
gemma2 num_nodes 105 105 +0.0% ⚪
gpt2 model_size_bytes 324 KB 324 KB +0.0% ⚪
gpt2 num_nodes 54 54 +0.0% ⚪
llama model_size_bytes 425 KB 425 KB +0.0% ⚪
llama num_nodes 60 60 +0.0% ⚪
llama (static-cache) model_size_bytes 425 KB 425 KB +0.0% ⚪
llama (static-cache) num_nodes 56 56 +0.0% ⚪
mamba (ssm-text-generation) model_size_bytes 296 KB 296 KB +0.0% ⚪
mamba (ssm-text-generation) num_nodes 94 94 +0.0% ⚪
phi3 model_size_bytes 421 KB 421 KB +0.0% ⚪
phi3 num_nodes 58 58 +0.0% ⚪
phi3 (static-cache) model_size_bytes 421 KB 421 KB +0.0% ⚪
phi3 (static-cache) num_nodes 54 54 +0.0% ⚪
qwen2 model_size_bytes 425 KB 425 KB +0.0% ⚪
qwen2 num_nodes 60 60 +0.0% ⚪
qwen2 (static-cache) model_size_bytes 425 KB 425 KB +0.0% ⚪
qwen2 (static-cache) num_nodes 56 56 +0.0% ⚪
qwen3_5_moe (hybrid-text-generation) model_size_bytes 506 KB 506 KB +0.0% ⚪
qwen3_5_moe (hybrid-text-generation) num_nodes 265 265 +0.0% ⚪
qwen3_5_text (hybrid-text-generation) model_size_bytes 458 KB 458 KB +0.0% ⚪
qwen3_5_text (hybrid-text-generation) num_nodes 127 127 +0.0% ⚪
qwen3_5_vl (hybrid-qwen-vl) model_size_bytes 977 KB 977 KB +0.0% ⚪
qwen3_5_vl (hybrid-qwen-vl) num_nodes 450 450 +0.0% ⚪
t5 (seq2seq) model_size_bytes 836 KB 836 KB +0.0% ⚪
t5 (seq2seq) num_nodes 176 176 +0.0% ⚪
whisper (speech-to-text) model_size_bytes 1008 KB 1008 KB +0.0% ⚪
whisper (speech-to-text) num_nodes 128 128 +0.0% ⚪

No performance regressions.

@github-actions

Copy link
Copy Markdown

🏗️ Architecture Diff

Comparing 783e03b5 → cc982302

Model Sub-model Changes Status
bert (feature-extraction) model 0 ⚪
falcon model 0 ⚪
gemma2 model 0 ⚪
gemma4 (gemma4) decoder 0 ⚪
gemma4 (gemma4) embedding 0 ⚪
gemma4 (gemma4) vision_encoder 0 ⚪
gemma4_text model 0 ⚪
gpt2 model 0 ⚪
llama model 0 ⚪
llama (static-cache) model 0 ⚪
mamba (ssm-text-generation) model 0 ⚪
phi3 model 0 ⚪
phi3 (static-cache) model 0 ⚪
qwen model 0 ⚪
qwen (static-cache) model 0 ⚪
qwen2 model 0 ⚪
qwen2 (static-cache) model 0 ⚪
qwen2_moe model 0 ⚪
qwen2_moe (static-cache) model 0 ⚪
qwen3 model 0 ⚪
qwen3 (static-cache) model 0 ⚪
qwen3_5_moe (hybrid-text-generation) model 0 ⚪
qwen3_5_text (hybrid-text-generation) model 0 ⚪
qwen3_5_vl (hybrid-qwen-vl) decoder 0 ⚪
qwen3_5_vl (hybrid-qwen-vl) embedding 0 ⚪
qwen3_5_vl (hybrid-qwen-vl) vision_encoder 0 ⚪
qwen3_moe model 0 ⚪
qwen3_moe (static-cache) model 0 ⚪
qwen3_next (hybrid-text-generation) model 0 ⚪
t5 (seq2seq) decoder 0 ⚪
t5 (seq2seq) encoder 0 ⚪
whisper (speech-to-text) decoder 0 ⚪
whisper (speech-to-text) encoder 0 ⚪

No architecture changes detected. ✅


Legend: ⚪ No change · 🔵 Minor (attrs/inits) · 🟡 Moderate (nodes added/removed) · 🔴 Major (interface changed)

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Critical OpenVINO RoPE and dtype-boundary issues remain, and runtime parity coverage is missing.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 3 High severity · 1 Medium severity

Open (4)
What changed in this PR

Adds OpenVINO/NPU support for Gemma4 through rank-4 attention graphs, float32 boundaries, shared-KV preprocessing, and opset lowering.

Changes:

  • Adds OpenVINO decoder and embedding I/O handling.
  • Implements OpenVINO attention with RoPE and KV broadcasting.
  • Adds graph, preprocessing, and opset-lowering tests.
File Summary
src/​mobius/​tasks/​_gemma4.py Adds OpenVINO I/O handling; text-only and multimodal boundaries remain incomplete.
src/​mobius/​models/​gemma4.py Adds OpenVINO attention and shared-KV handling; RoPE interleave compatibility and runtime parity require changes.
src/​mobius/​models/​gemma4_test.py Adds topology and preprocessing tests; runtime prefill/decode coverage is still needed.
src/​mobius/​_builder.py Adds OpenVINO opset lowering.
src/​mobius/​_builder_test.py Adds opset-lowering test coverage.

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +1164 to +1167
first, second = op.Split(states, [head_dim // 2, head_dim // 2], axis=-1, _outputs=2)
rotated = op.Concat(op.Neg(second), first, axis=-1)
left = op.Mul(states, cos)
right = op.Mul(rotated, sin)
op = builder.op
caps = ep_capabilities()
is_openvino = caps.name == "openvino"
decoder_io_dtype = ir.DataType.FLOAT if is_openvino else config.dtype
Comment on lines +827 to +835
cast_outputs = ep_capabilities().name == "openvino"

def component_output(value: ir.Value) -> ir.Value:
if not cast_outputs or value.dtype == ir.DataType.FLOAT:
return value
output = op.Cast(value, to=ir.DataType.FLOAT)
output.type = ir.DataType.FLOAT
output.shape = value.shape
return output
Comment on lines +477 to +482
assert counts["Attention"] == config.num_hidden_layers
assert counts["RotaryEmbedding"] == 0
assert counts["Softmax"] == 0
assert counts["CumSum"] == 1
assert inputs_embeds.dtype == ir.DataType.FLOAT
assert logits.dtype == ir.DataType.FLOAT

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants