Repository navigation
gemma4: fix 16x audio soft-token magnitude on NPU via single-source scaling - #726
Merged
Merged
Conversation
The gemma4 16x weight reparameterization was encoded twice: once in gemma4_scale_factor() and again as a hardcoded branch in save_tensor_with_header(). The two had to be kept in lockstep by hand, and gemma4_scale_factor() omitted the projection weights entirely. Add the vision/audio projection basenames to GEMMA4_DIV_BASENAMES and collapse the tensor_io branch to call gemma4_scale_factor(), so a single function decides every gemma4 weight's factor. Factors are identical for all converted weights (verified over 2133 names), so stored weights are byte-for-byte unchanged. Signed-off-by: Noah Cylich <noahcylich@gmail.com>
The NPU audio and vision encoders are traced directly from the in-memory torch module, which holds the original projection weights, while the soft-token 1/16 lives only in the converted embed_*_proj.weights files that the .mlpackage never reads. gemma4 multimodal embedders have no post-projection norm (only a pre-projection one), so the projection magnitude is the soft-token magnitude and nothing renormalizes it: both audio and vision soft tokens came out 16x too large on NPU. Reparameterize the projection weights with the same gemma4_scale_factor used at conversion time, applied as a reversible context manager around the NPU trace so the shared module is restored before CPU IR capture. Verified at the .mlpackage level: reparam divides both encoders' output by exactly 1/16 (cosine 1.0), matching the CPU path. Signed-off-by: Noah Cylich <noahcylich@gmail.com>
ncylich
force-pushed
the
gemma4-npu-scale-single-source
branch
from
June 16, 2026 01:38
57844ec to
cf453c4
Compare
kar-m
previously approved these changes
Jun 16, 2026
kar-m
left a comment
Collaborator
There was a problem hiding this comment.
tested pre and post PR NPU performance, audio is fixed from a bad regression, vision is just a tiny bit better post-PR. LGTM
Collaborator
|
@ncylich just resolve the conflicts here |
…le-source Signed-off-by: Noah Cylich <noahcylich@gmail.com>
ncylich
force-pushed
the
gemma4-npu-scale-single-source
branch
from
June 16, 2026 19:27
7481862 to
3f88749
Compare
ncylich
force-pushed
the
gemma4-npu-scale-single-source
branch
from
June 16, 2026 20:06
ef7f4e1 to
91573de
Compare
Signed-off-by: Noah Cylich <noahcylich@gmail.com>
ncylich
force-pushed
the
gemma4-npu-scale-single-source
branch
from
June 16, 2026 20:06
91573de to
713374e
Compare
Signed-off-by: Noah Cylich <noahcylich@gmail.com>
Signed-off-by: Noah Cylich <noahcylich@gmail.com>
kar-m
approved these changes
Jun 16, 2026
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.
Problem
a8f634a6(#723) folded the gemma4 audio soft-token1/16out of the runtime graph and into the convertedembed_audio_proj.weightsfile. Correct for the CPU path (it binds that scaled file at runtime), but silently wrong for the NPU path: the audio.mlpackageis traced directly from the in-memory torch module, which holds the original, unscaled projection weight, and the.mlpackagenever reads that converted file.gemma4 multimodal embedders have no post-projection norm (only a pre-projection one) — verified:
embed_vision/embed_audioexpose onlyembedding_projection+embedding_pre_projection_norm, and noembed_*_post_proj_norm.weightsis produced. So the projection magnitude is the soft-token magnitude and nothing renormalizes it. Both audio and vision NPU soft tokens were 16x too large (vision was previously assumed safe via a post-projection RMSNorm — that norm does not exist for this family, so the assumption was wrong).The deeper issue is structural: the
÷16reparam lived in two disjoint copies (the canonicalgemma4_scale_factor()and a hardcoded block intensor_io.py), and a third weight consumer (NPU baked weights) consulted neither.Fix
Make
gemma4_scale_factor()the single source of truth, consumed by all three weight-emission paths.GEMMA4_DIV_BASENAMESand collapse the hardcodedtensor_io.pygemma4 block to callgemma4_scale_factor(). Factors are identical for every converted weight (verified over 2133 names), so stored weights are byte-for-byte unchanged.npu_reparamfield onComponentModuleSpec, keeping the family-agnostic NPU pipeline free of gemma4 specifics.Verification
gemma4_scale_factormatches the old hardcoded block on all 2133 real converted filenames (0 mismatches) → CPU conversion output unchanged..mlpackagevs torch adapter, sametest.wavinputs): RMS(NPU)/RMS(torch unscaled) = 0.0626 ≈ 1/16; RMS(NPU)/RMS(torch ÷16) = 1.002 ≈ 1.0; cosine identical scaled-vs-unscaled → change is magnitude-only..mlpackageratio, with vs without reparam): RMS ratio = 0.062500 = exactly 1/16, cosine 1.000000. Confirmed gemma4 vision has no post-projection norm (so it was 16x hot too, now matches CPU).test_transpile_weight_compat,test_component_partition,test_optimize_gemma4_attention).Deploy
Convert output is unchanged, so no reconvert is required. Affected gemma4 NPU bundles must be re-transpiled (
--npu) to pick up the corrected audio and vision.mlpackages.