Gemma-4 small (E2B/E4B): emit vision-tower shapes in HF export map - #4860
Open
lokic233 wants to merge 1 commit into
Open
Gemma-4 small (E2B/E4B): emit vision-tower shapes in HF export map#4860lokic233 wants to merge 1 commit into
lokic233 wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Code Review
This pull request adds support for the vision tower (multimodal E2B / E4B) in the GEMMA4_SMALL_HF_WEIGHTS_TO_SHAPE function within hf_shape.py. It maps the shapes of the vision tower components, including the patch embedder, position embedding table, standardization parameters, projection layers, and encoder layers, when a vision_config is present. Additionally, a comprehensive suite of unit tests has been introduced in hf_shape_test.py to validate this mapping behavior. There are no review comments, and I have no additional feedback to provide.
lokic233
marked this pull request as ready for review
August 12, 2026 21:08
lokic233
requested review from
A9isha,
NuojCheng,
RissyRan,
SurbhiJainUSC,
abhinavclemson,
aireenmei,
bvandermoon,
darisoy,
dipannita08,
gagika,
gobbleturk,
hengtaoguo,
huytransformer,
igorts-git,
jiangjy1982,
khatwanimohit,
parambole,
richjames0,
shralex,
shuningjin,
vipannalla and
xibinliu
as code owners
August 12, 2026 21:08
GEMMA4_SMALL_HF_WEIGHTS_TO_SHAPE returned only language-model / PLE shapes and
emitted zero model.vision_tower.* / model.embed_vision.* keys, while
GEMMA4_SMALL_MAXTEXT_TO_HF_PARAM_MAPPING already maps the full vision encoder.
Since utils._process requires every mapped HF target to be present in the shape
map, 'to_huggingface use_multimodal=true' on a converted gemma4-e2b/e4b
checkpoint died on the first vision tensor:
ValueError: HF path 'model.vision_tower.patch_embedder.input_proj.weight'
not found in hf_shape_map
Add the vision block to GEMMA4_SMALL_HF_WEIGHTS_TO_SHAPE, gated on vision_config
presence (matching the mapping's 'use_multimodal and vcfg' gate) and mirroring
the GEMMA4_HF_WEIGHTS_TO_SHAPE (26B/31B) vision convention: each clipped-linear
vision projection emits a '.linear.weight'; attention q/k/v/o + q_norm/k_norm,
the four layernorms, gate/up/down MLP, patch-embedder input_proj +
position_embedding_table, and the embed_vision projection. std_scale / std_bias
are emitted only under standardize=true (E2B/E4B ship standardize=false).
Also emit the four scalar activation clip bounds per clipped-linear projection
(input_min/input_max/output_min/output_max, shape [] not [1]) that
GEMMA4_SMALL_MAXTEXT_TO_HF_PARAM_MAPPING maps under use_clipped_linears_for_vit.
The shape fn sees only the HF config (not the MaxText config), so it emits the
bound shapes whenever the vision block is present: _process is driven by the
param map, so the extra keys are a harmless superset when clipped-linears is off
and required coverage when it is on. A rank-1 [1] shape would make the HF loader
reinitialize the bound to a non-finite sentinel.
Add tests/unit/hf_shape_test.py: fail-closed coverage (every mapped vision HF
target, including the 448 clip bounds when the running param map supports them,
exists in the shape map), clip-bound scalar-shape, std-conditional, and
text-only-config checks.
Text-only and 26B/31B export paths are unaffected.
Signed-off-by: Loki Chen <dengcchi@meta.com>
lokic233
force-pushed
the
navi-gemma4-small-vision-hf-export
branch
from
August 13, 2026 14:16
d4eabe0 to
7eb0f94
Compare
This was referenced Aug 13, 2026
Open
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.
Description
GEMMA4_SMALL_HF_WEIGHTS_TO_SHAPE(used bygemma4-e2b/gemma4-e4b) returned only language-model / PLEshapes and emitted zero
model.vision_tower.*/model.embed_vision.*keys, whileGEMMA4_SMALL_MAXTEXT_TO_HF_PARAM_MAPPINGmaps the full vision encoder. Becausecheckpoint_conversion/utils/utils.py::_processrequires every mapped HF target to be present in the shapemap,
to_huggingface use_multimodal=trueon a converted Gemma-4 E2B/E4B checkpoint died on the first visiontensor:
This adds the vision block to
GEMMA4_SMALL_HF_WEIGHTS_TO_SHAPE, gated onvision_configpresence:GEMMA4_HF_WEIGHTS_TO_SHAPE(26B/31B) convention: each clipped-linear vision projection emitsa
.linear.weight; attention q/k/v/o + q_norm/k_norm, four layernorms, gate/up/down MLP, patch-embedderinput_proj+position_embedding_table,embed_visionprojection.(
input_min/input_max/output_min/output_max) thatGEMMA4_SMALL_MAXTEXT_TO_HF_PARAM_MAPPINGmaps underuse_clipped_linears_for_vit(the E2B/E4B image-parity path). Bounds are scalar[](a rank-1[1]shapemakes the HF loader reinitialize the bound to a non-finite sentinel). The shape fn receives only the HF
config (not the MaxText config), and
_processiterates the param map, so emitting the bound shapeswhenever the vision block is present is a harmless superset when clipped-linears is off and required
coverage when it is on.
std_scale/std_biasare emitted only understandardize=true(E2B/E4B shipstandardize=false).Scope: this PR completes the vision HF export shape map for gemma4-small. Shared-layer text K/V /
k_norm are intentionally omitted on the HF side (HF transformers
Gemma4TextAttentionitself omits them forKV-shared layers), so this is HF-faithful; the serving-side shared-layer checkpoint-completeness invariant is
a separate concern and is not part of this PR.
FIXES: to_huggingface multimodal export for gemma4-e2b / gemma4-e4b.
Tests
tests/unit/hf_shape_test.py(CPU, no gated checkpoint):test_shape_map_covers_every_mapped_vision_target— fail-closed: every mapped vision HF target has a shape.test_shape_map_covers_clipped_mapping_targets— fail-closed incl. the 448 clip bounds when the param map maps them.test_clip_bound_keys_present_and_scalar— clip bounds present and scalar[].test_vision_tower_keys_present,test_std_keys_only_when_standardize,test_text_only_config_emits_no_vision_keys.Validated composed with #4790 (which adds the clip-bound mapping): mapping<->shape coverage = 0 missing
across clipped ON/OFF x standardize ON/OFF.
pyink/pylint(10.00/10)/codespellclean.Related work
GEMMA4_SMALL_MAXTEXT_TO_HF_PARAM_MAPPING(incl. clip-bound mapping this PR covers).Checklist
Authored with Navi on behalf of Loki Chen (@lokic233).