Add Gemma 4 text model builder support (dense gemma-4-12b-it + 26B-A4B MoE) - #2473
Open
Thiago Pereira Rocha (thpereir) wants to merge 4 commits into
Open
Add Gemma 4 text model builder support (dense gemma-4-12b-it + 26B-A4B MoE)#2473Thiago Pereira Rocha (thpereir) wants to merge 4 commits into
Thiago Pereira Rocha (thpereir) wants to merge 4 commits into
Conversation
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
Adds Gemma4Model builder for the text component of Gemma4Unified (Gemma4UnifiedForConditionalGeneration). Verified against HuggingFace: full 48-layer fp32 parity gives logit correlation 1.0 and exact top-1 match. Handles the architecture's deviations from Gemma3: - Per-layer head dims (sliding 256 / full 512) and KV heads (8 / 1), emitted as concrete per-layer KV cache shapes so the runtime's DefaultKeyValueCache auto-detects the heterogeneous layout. - attention_k_eq_v: full-attention layers share the K projection as V (no v_proj), plus a scaleless value RMSNorm (v_norm) on all layers. - Proportional RoPE on full layers (partial rotary on the global head dim with a zero-padded NoPE tail), default RoPE on sliding layers. - Per-layer layer_scalar residual multiplier. - No-offset RMSNorm (weight used directly, unlike Gemma1/2/3). - Text-only weight loader that remaps the model.language_model.* prefix and avoids loading the vision/audio towers. Emits model_type "gemma4_text" to match the runtime's model type list. Co-Authored-By: Claude <noreply@anthropic.com>
Add Gemma4MoEModel subclassing the dense Gemma4Model. Every layer runs a parallel dense MLP and a fused-QMoE expert block, combined by Add. Uses the fused QMoE op path (gelu + swiglu_fusion=1 + normalize_routing_weights) with an explicit router pre-projection subgraph feeding raw logits, and folds per_expert_scale into the expert down_proj weights offline. Co-Authored-By: Claude <noreply@anthropic.com>
…nature) Co-Authored-By: Claude <noreply@anthropic.com>
Thiago Pereira Rocha (thpereir)
force-pushed
the
gemma4-base-upstream
branch
from
August 27, 2026 20:20
36e7dbe to
140e038
Compare
This was referenced Aug 27, 2026
…tub test - make_moe_init: set both moe_attrs["op_type"] and ["moe_op_type"] to the resolved op. The shared emitters read "op_type" while the gemma parallel-FFN path reads "moe_op_type"; setting only one left quantized experts on the float "MoE" op, which rejects uint8 weights. - Gemma4MoEModel.__init__: hide config.moe_intermediate_size across super().__init__ so the base sets self.intermediate_size to the dense size directly, instead of reassigning it afterward (CodeQL "overwriting attribute"). - test_qmoe_weights.py: add Gemma4Model/Gemma4MoEModel to the stubbed `builders` class list so the builder-CLI import test matches builder.py's imports. Co-Authored-By: Claude <noreply@anthropic.com>
Thiago Pereira Rocha (thpereir)
force-pushed
the
gemma4-base-upstream
branch
from
August 27, 2026 21:08
140e038 to
e8e9a35
Compare
Thiago Pereira Rocha (thpereir)
marked this pull request as ready for review
August 28, 2026 14:28
Thiago Pereira Rocha (thpereir)
requested a review
from a team
as a code owner
August 28, 2026 14:28
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
Copilot started reviewing on behalf of
Thiago Pereira Rocha (thpereir)
August 28, 2026 14:28
View session
Contributor
There was a problem hiding this comment.
Pull request overview
Adds Python model-builder support for Google Gemma 4 text models (dense + MoE), integrating new Gemma4 builder classes into the dispatcher and extending unit tests to validate Gemma4-specific RoPE/KV-cache geometry and MoE wiring.
Changes:
- Introduces
Gemma4ModelandGemma4MoEModelbuilders with Gemma4-specific RoPE cache handling, per-layer KV-cache geometry, and (for MoE) parallel dense+expert FFN construction. - Wires Gemma4 architectures into
create_model()dispatch and exports the new builders from the package. - Adds focused unit tests for dense Gemma4 and Gemma4 MoE builder structure/parity, plus updates QMoE weight tests to include Gemma4 builders.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| test/python/models/test_gemma4moe_builder.py | New unit tests covering Gemma4 MoE builder (attrs/op selection, parallel FFN combine, router path, per-expert scale folding). |
| test/python/models/test_gemma4_builder.py | New unit tests covering dense Gemma4 builder (per-layer KV shapes, RoPE cache parity vs HF reference). |
| test/python/builder/test_qmoe_weights.py | Extends builder CLI test allowlist to include Gemma4 builders. |
| src/python/py/models/builders/gemma.py | Implements Gemma4Model and Gemma4MoEModel builder logic (RoPE, KV cache geometry, layer scalar, MoE FFN). |
| src/python/py/models/builders/base.py | Ensures MoE op-type is consistently available under both moe_attrs["op_type"] and moe_attrs["moe_op_type"]. |
| src/python/py/models/builders/init.py | Exports the new Gemma4 builder classes. |
| src/python/py/models/builder.py | Adds dispatcher branches for Gemma4 architectures and emits warnings about precision and unsupported towers. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+546
to
+567
| def make_gemma4_rmsnorm(self, name, root_input, weight, with_scale=True): | ||
| # Standalone Gemma4 RMSNorm (SimplifiedLayerNormalization, fp32 accumulation) | ||
| # on a [B, S, H] tensor. `weight` is the HF parameter (or None if scaleless). | ||
| weight_name = f"model.{name}.weight" | ||
| if with_scale: | ||
| self.make_initializer(weight + self.layernorm_attrs["add_offset"], weight_name, to=self.io_dtype) | ||
| else: | ||
| self.make_initializer(torch.ones(self.hidden_size), weight_name, to=self.io_dtype) | ||
| # Node paths use '/' separators (initializer names keep the '.'-joined form). | ||
| ln_name = f"/model/{name.replace('.', '/')}/SimplifiedLayerNormalization" | ||
| output = f"{ln_name}/output_0" | ||
| self.make_node( | ||
| "SimplifiedLayerNormalization", | ||
| inputs=[root_input, weight_name], | ||
| outputs=[output], | ||
| name=ln_name, | ||
| epsilon=self.layernorm_attrs["epsilon"], | ||
| axis=-1, | ||
| stash_type=1, | ||
| ) | ||
| self.make_value(output, self.io_dtype, shape=["batch_size", "sequence_length", self.hidden_size]) | ||
| return output |
Comment on lines
+539
to
+544
| def make_layer(self, layer_id, layer): | ||
| # Stash the full layer so make_mlp can reach the router/experts/extra norms | ||
| # (the parent only passes layer.mlp to make_mlp). | ||
| self._current_layer = layer | ||
| super().make_layer(layer_id, layer) | ||
| self._current_layer = None |
Comment on lines
+472
to
+476
| moe_intermediate_size = config.moe_intermediate_size | ||
| del config.moe_intermediate_size | ||
| super().__init__(config, io_dtype, onnx_dtype, ep, cache_dir, extra_options) | ||
| config.moe_intermediate_size = moe_intermediate_size | ||
| self.moe_intermediate_size = moe_intermediate_size |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds model-builder support for Google's Gemma 4 text models, producing GenAI-compatible ONNX:
gemma-4-12b-it(Gemma4UnifiedForConditionalGeneration) via a newGemma4Model.gemma-4-26B-A4B-it(Gemma4ForConditionalGeneration) via a newGemma4MoEModel, including the parallel dense-MLP-alongside-experts topology and the float (MoE) / quantized (QMoE) op-type split.Both inherit from the existing
Gemma3Modeland override only the hooks that differ:{"full_attention": {...}, "sliding_attention": {...}}) instead of the flatrope_parameters["rope_type"]form — handled via amake_rope_initoverride.intermediate_sizeis pinned to the dense size while the expert size is tracked separately asmoe_intermediate_size.Scope / relationship to prior work
This is the text builder layer. It complements the already-merged Gemma 4 C++ runtime / multimodal processor. It supersedes the non-functional scaffolding stub in #2088 (which prints "not yet end-to-end functional" and raises
NotImplementedError) — here the dense and MoE text paths build end-to-end and are unit-tested. See #2062 for the architecture background.Tests
test/python/models/test_gemma4_builder.py— dense Gemma4 builder.test/python/models/test_gemma4moe_builder.py— MoE builder incl.moe_op_type(MoE vs QMoE) and dense/expert intermediate-size wiring.All 16 tests pass locally.
Follow-ups (separate stacked PRs)
Draft: opening for early review while the stacked quantization PRs are prepared.