[quantization] Add token embedding, vision prefill, and mm_fusion verification steps in Gemma4 static runtime - #834
Conversation
| The runtime's ``Gemma4VisionPrefillExportAdapter`` runs the vision tower | ||
| followed by the ``embed_vision`` projection. This function feeds | ||
| ``pixel_values`` and ``image_position_ids`` through both the runtime | ||
| adapter and HF's ``model.get_image_features(...)`` and asserts that the | ||
| projected visual soft tokens match. |
There was a problem hiding this comment.
There is mismatch in the description.
Actually, you test this assertion between runtime and quantized model not HF model.
There was a problem hiding this comment.
👍 Thank you for noticing! The docstring was updated to accurately describe the two-tier verification - PEIR (informational) against the HF FP model, and assert_close (hard assertion) against the quantized model - rather than claiming the function asserts exact match against HF.
| raise ValueError(f"Raw sequence length {seq_len_raw} exceeds max_seq {max_seq}") | ||
|
|
||
| padded_mask = torch.zeros((1, max_seq), dtype=torch.bool, device=runtime.device) | ||
| padded_mask[:, :seq_len_raw] = image_mask |
There was a problem hiding this comment.
This verification does not use visual_start_idx and num_visual_tokens, this differs from runtime.mm_fusion.
There was a problem hiding this comment.
👍 Added an explicit check that the image token positions in raw_input_ids match the static layout (visual_start_idx and num_visual_tokens). Previously, the masked_scatter reference path implicitly assumed the image tokens were contiguous at visual_start_idx (validated only by Step 1's build_static_inputs). Without this check, a layout mismatch would produce a confusing "mm_fusion mismatch" assertion error instead of a clear diagnostic. The new check raises a descriptive ValueError if the start position or token count doesn't match the static layout.
| # --- PEIR (Peak-Error-to-Interval Ratio) --- | ||
| from tico.quantization.evaluation.metric import compute_peir | ||
|
|
||
| peir = compute_peir(rt_visual_embeds, hf_visual_embeds) |
There was a problem hiding this comment.
The first argument should be reference value: hf_visual_embeds.
There was a problem hiding this comment.
👍 Thanks for catching that! Fixed the compute_peir argument order in verify_step_vision_prefill. The compute_peir(base, target) function is asymmetric — base is the reference used for both the numerator and the interval denominator, while target is the value being compared (used only in the numerator). The original code had the arguments swapped (compute_peir(rt_visual_embeds, hf_visual_embeds)), which incorrectly computed the interval denominator from the quantized runtime output instead of the HF FP reference. The fix swaps them to compute_peir(hf_visual_embeds, rt_visual_embeds), ensuring the interval is computed from the FP reference as the metric's design intends.
…ification steps in Gemma4 static runtime Validate runtime adapters against HF reference for embedding, vision, and fusion. Co-authored-by: Cline TICO-DCO-1.0-Signed-off-by: d.savchenkov <d.savchenkov@partner.samsung.com>
Related issue: #768
Related PR: #822
What
This PR adds three new side-by-side verification steps to the
StaticGemma4Runtimefor the Gemma4 E2B model:token_embedding,vision_prefill, andmm_fusion. Each step independently validates a runtime adapter's output against the corresponding HFtransformersreference implementation, ensuring numerical equivalence before the static runtime is used for actual inference. It also refactors allverify_step_*functions to return their computed results, eliminating redundant recomputation across steps.Why
The
StaticGemma4Runtimeis being built incrementally, one workflow step at a time. Before proceeding to prefill, decode, and generation, we need confidence that each upstream component (token embedding, vision tower, multimodal fusion) produces identical results to the HF reference. Side-by-side validation catches subtle bugs early — such as dtype mismatches, incorrect embedding scaling, or wrong fusion semantics — rather than debugging them through the full prefill pipeline. The refactoring to return results (instead ofbool) eliminates redundanttoken_embeddingandvision_prefillcalls that were previously duplicated in themm_fusionstep.Key Design Decisions
Return computed results, not
bool: Since verification failures raise exceptions (assert_close,raise ValueError), returningTruewas meaningless. Eachverify_step_*now returns its runtime-computed tensor (or batch dict), allowing downstream steps to reuse it without recomputation.vision_prefilluses PEIR, not exact match, for HF comparison: The runtime's vision adapter wraps a quantized vision tower (with fake Q-DQ ops), so exact equality against the FP HF model is not expected. PEIR (Peak-Error-to-Interval Ratio) quantifies the deviation. An additional exact-match assertion against the quantized model'sget_image_featuresensures the adapter itself introduces no error beyond quantization.mm_fusioncomparesfixed_slot_fusevs HFmasked_scatter: The runtime uses a statictorch.cat-based fusion (fixed contiguous slot), while HF uses dynamicmasked_scatter. The verification re-runs the processor to recover rawinput_idswith image tokens, builds the image mask, and appliesmasked_scatterto confirm both paths produce identical fused embeddings.pixel_valuescast to model dtype: The HF processor outputs float32, but the quantized vision tower weights are BFloat16. Thevision_prefillstep castspixel_valuestomodel.dtypebefore passing to both runtime and HF reference sides.mm_fusionacceptstext_embedsandvisual_embedsas parameters rather than recomputing them internally, following the "reuse results" principle.Changes
tico/quantization/recipes/debug/static_gemma4_runtime.pyverify_step_build_static_inputs: Changed return type frombooltodict[str, torch.Tensor], returns thebatchdict.verify_step_token_embedding: ValidatesGemma4TokenEmbeddingExportAdapteroutput against HFGemma4TextScaledWordEmbedding, including a sanity check thatsqrt(hidden_size)embedding scale is applied. Returnstext_embedstensor.verify_step_vision_prefill: ValidatesGemma4VisionPrefillExportAdapteroutput against HFmodel.get_image_features().pooler_output(with PEIR metric) and quantized model'sget_image_features(exact match). Castspixel_valuesto model dtype. Returnsvisual_embedstensor.verify_step_mm_fusion: ValidatesGemma4MMFusionExportAdapter(fixed_slot_fuse) against HFmasked_scatterreference. Acceptstext_embedsandvisual_embedsas parameters. Returnsfused_embedstensor.run_static_gemma4_runtime: Updated to chain return values —batchfrom Step 1,text_embedsfrom Step 2,visual_embedsfrom Step 3, passed to Step 4. Removed redundantbuild_static_inputscall.Tests
test/quantization/recipes/integration/test_static_gemma4_runtime_helpers.pycontinue to pass (tests_normalize_valid_token_maskand_validate_padding_layouthelper functions).build_static_inputs): All checks passed.token_embedding): All checks passed.vision_prefill): PEIR = 7.142857e-03, all checks passed.mm_fusion): All checks passed.Example Script
$ python ./tico/quantization/examples/inspector.py \ --mode static-gemma4-runtime \ --config ./tico/quantization/examples/configs/static_gemma4_runtime.yaml [run_static_gemma4_runtime] Loading model: google/gemma-4-e2b-it Warning: You are sending unauthenticated requests to the HF Hub. Please set a HF_TOKEN to enable higher rate limits and faster downloads. Loading weights: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████| 1951/1951 [00:00<00:00, 4382.96it/s] [run_static_gemma4_runtime] Creating StaticGemma4Runtime ... [run_static_gemma4_runtime] Step 1: verify build_static_inputs [verify_step_build_static_inputs] All checks passed. [run_static_gemma4_runtime] Step 2: verify token_embedding [verify_step_token_embedding] All checks passed. [run_static_gemma4_runtime] Step 3: verify vision_prefill [verify_step_vision_prefill] PEIR = 7.142857e-03 [verify_step_vision_prefill] All checks passed. [run_static_gemma4_runtime] Step 4: verify mm_fusion [verify_step_mm_fusion] All checks passed. [run_static_gemma4_runtime] SKIP: prefill (not implemented in this PR) [run_static_gemma4_runtime] SKIP: decode (not implemented in this PR) [run_static_gemma4_runtime] SKIP: generation (not implemented in this PR) [run_static_gemma4_runtime] Done.