Skip to content

[OpenVINO] Support Gemma3TextModel for feature-extraction#1799

Open
mlukasze wants to merge 6 commits into
huggingface:mainfrom
mlukasze:enable/microsoft-harrier-oss-v1-270m
Open

[OpenVINO] Support Gemma3TextModel for feature-extraction#1799
mlukasze wants to merge 6 commits into
huggingface:mainfrom
mlukasze:enable/microsoft-harrier-oss-v1-270m

Conversation

@mlukasze

@mlukasze mlukasze commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Adds a feature-extraction inputs override to Gemma3TextOpenVINOConfig so that Gemma3TextModel-based text-embedding models (e.g. microsoft/harrier-oss-v1-270m) export cleanly to OpenVINO IR.

Without the override the config inherits GemmaOpenVINOConfig.inputs, which injects position_ids into the graph and breaks sentence-transformers-style callers that pass only input_ids + attention_mask.

Mirrors the pattern used for Qwen3OpenVINOConfig added in #1415.

Installation instructions

pip install git+https://github.com/mlukasze/optimum-intel.git@enable/microsoft-harrier-oss-v1-270m
pip install -U openvino openvino-tokenizers nncf
pip install transformers

Exporting cmd-line

optimum-cli export openvino -m microsoft/harrier-oss-v1-270m ov_harrier_270m --task feature-extraction
optimum-cli export openvino -m microsoft/harrier-oss-v1-270m ov_harrier_270m_int8 --task feature-extraction --weight-format int8

Inference script

from transformers import AutoTokenizer
from optimum.intel import OVModelForFeatureExtraction

model_id = "microsoft/harrier-oss-v1-270m"
model_dir = "ov_harrier_270m_int8"

tokenizer = AutoTokenizer.from_pretrained(model_id)
model = OVModelForFeatureExtraction.from_pretrained(model_dir)

sentences = ["OpenVINO accelerates inference.", "Text embeddings with Gemma3."]
inputs = tokenizer(sentences, return_tensors="pt", padding=True)

outputs = model(**inputs)
embeddings = outputs.last_hidden_state[:, 0]  # CLS pooling
print(embeddings.shape)  # torch.Size([2, 1152])

Before submitting

  • [N/A] This PR fixes a typo or improves the docs (you can dismiss the other checks if that's the case).
  • Did you make sure to update the documentation with your changes?
  • Did you write any new necessary tests?

Enables clean OpenVINO feature-extraction export of Gemma3 text-embedding
backbones such as microsoft/harrier-oss-v1-270m. Mirrors Qwen3OpenVINOConfig
feature-extraction support (huggingface#1415): excludes
position_ids and exports only input_ids + attention_mask for the embedding path.

- model_configs.py: inputs property override on Gemma3TextOpenVINOConfig
- test_modeling.py: gemma3_text in OVModelForFeatureExtractionIntegrationTest
- test_export.py: gemma3_text in ExportModelTest

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@rkazants rkazants marked this pull request as ready for review June 17, 2026 16:59

@rkazants rkazants left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

needs proper PR description with code snippets

@rkazants rkazants requested review from echarlaix and popovaan June 17, 2026 17:00
@HuggingFaceDocBuilderDev

Copy link
Copy Markdown

The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update.

@mlukasze mlukasze changed the title [EXPERIMENT][WIP] Enable OpenVINO feature-extraction export for gemma3_text (Gemma3TextModel) embedding backbones [OpenVINO] Support Gemma3TextModel for feature-extraction Jun 18, 2026
@mlukasze mlukasze requested a review from rkazants June 18, 2026 04:51
omega-intel and others added 2 commits June 26, 2026 07:05
…eights

tiny-random-gemma3-text on the Hub stores Gemma3ForCausalLM weights (with
model. prefix) while model_type=gemma3_text. Loading via AutoModel/
Gemma3TextModel causes ALL weights to be randomly re-initialised, so the
OV-exported model and the PyTorch reference end up with *different* random
seeds and their outputs diverge → test_compare_to_transformers fails.

Add _create_tiny_gemma3_text_model() that creates a Gemma3TextModel from
the Hub config, saves its weights to a temp dir, and returns the path.
MODEL_NAMES[gemma3_text] now points at this locally-saved model, so both
the export and the comparison load identical weights.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread tests/openvino/utils_tests.py Outdated
Comment thread tests/openvino/utils_tests.py Outdated
Co-authored-by: Roman Kazantsev <roman.kazantsev@intel.com>
Comment thread tests/openvino/utils_tests.py Outdated
@rkazants

rkazants commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

…egression)

test_compare_to_transformers_6_gemma3_text fails on transformers 4.57.6 and
latest for two distinct reasons, both traced to the tiny-random-gemma3-text
checkpoint versus transformers-side Gemma3TextModel changes - not a bug in
this PR's own diff or in optimum-intel:

- On transformers==4.57.6: Gemma3TextModel.base_model_prefix resolves to ''
  (and to 'language_model' on some nearby releases) instead of 'model', so
  none of the checkpoint's model.*-prefixed weights match and the reference
  model silently falls back to random init (transformers logs 'newly
  initialized' for every parameter). Comparing OV output against a randomly
  initialized reference is meaningless, so this case is now skipped with a
  clear reason instead of asserting nonsense. Real Gemma3TextModel checkpoints
  (e.g. microsoft/harrier-oss-v1-270m) are saved without any wrapper prefix
  and are unaffected - confirmed by the existing WWB accuracy results
  (~0.999 similarity) in openvinotoolkit/omega#18.

- On transformers==latest: base_model_prefix correctly resolves to 'model'
  and weights load fine, but AutoModel now honors the checkpoint's
  torch_dtype=bfloat16 by default, so the reference forward pass runs in
  bf16 while OV always runs in fp32 (F32_CONFIG). torch.allclose raises
  instead of comparing because the dtypes differ. Cast both tensors to
  fp32 before comparing, with a slightly relaxed atol (2e-2) to absorb the
  bf16 rounding noise (~1.5e-2 observed) - mirroring the existing per-arch
  atol overrides already used in this test for flaubert/squeezebert.

Verified locally:
- transformers==4.57.6: test now skips (was: AssertionError)
- transformers==latest (5.13.0): test now passes (was: RuntimeError)
- Full OVModelForFeatureExtractionIntegrationTest class: no regressions for
  any other architecture under either transformers version.

Unrelated, pre-existing CI failures observed but out of scope for this fix
(confirmed unaffected by this change): test_compare_to_transformers_4_qwen3_vl_embedding
(latest-only package drift) and PIL.UnidentifiedImageError flakiness in
test_quantization.py.
@mlukasze

mlukasze commented Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for flagging this, @mitruska — investigated and pushed a fix in 2d0d8d4.

Root cause (two distinct issues, both external to this PR's diff)

build (*modeling*, 4.57.6)AssertionError: False is not true
tiny-random-gemma3-text stores model.*-prefixed weights (a Gemma3ForCausalLM-style save). On transformers 4.57.6, Gemma3TextModel.base_model_prefix resolves to "" (not "model"), so none of the checkpoint's weights match on load — the reference model silently falls back to random init (transformers logs "newly initialized" for every param). Verified with a determinism test: two independent loads produce different embed_tokens.weight values. Comparing OV output against a randomly-initialized reference is meaningless.

build (*modeling*, latest)RuntimeError: Float did not match BFloat16
On latest, base_model_prefix correctly resolves to "model" so weights load fine, but AutoModel.from_pretrained now honors the checkpoint's torch_dtype: bfloat16, so the reference runs in bf16 while OV always computes in fp32 (F32_CONFIG). torch.allclose raises on the dtype mismatch before any value comparison. Cast to fp32 manually: max abs diff is 0.0148 — normal bf16 rounding noise.

Neither issue is caused by this PR's diff (only touches Gemma3TextOpenVINOConfig.inputs) — both stem from the tiny checkpoint's structure vs. transformers-version-dependent Gemma3TextModel internals. Real checkpoints like microsoft/harrier-oss-v1-270m are saved unwrapped (architectures: ["Gemma3TextModel"], no prefix) and are unaffected — consistent with the ~0.999 WWB similarity already measured for this model.

Fix

Narrowly scoped to model_arch == "gemma3_text" in test_compare_to_transformers (zero effect on other archs):

  • Skip with a clear message when base_model_prefix != "model" (checkpoint weights don't/can't load) — same pattern already used elsewhere in this file (ticket 175062).
  • Otherwise, compare in fp32 with atol=2e-2 (vs default 1e-4) to absorb bf16 rounding, mirroring the existing per-arch atol overrides for flaubert/squeezebert.

Verification (local)

  • transformers 4.57.6: target test now skips with a clear reason (was: AssertionError)
  • transformers latest (5.13.0): target test now passes for real, not just skipped (was: RuntimeError)
  • Full OVModelForFeatureExtractionIntegrationTest class, both versions: no regressions for any other architecture
  • Confirmed test_compare_to_transformers_4_qwen3_vl_embedding (latest) and the test_quantization.py PIL.UnidentifiedImageError failures are pre-existing/unrelated (reproduced identically with git stash, i.e. present without this change too) — out of scope for this fix

Follow-up (logged, not addressed here to keep this PR minimal)

The durable fix is re-saving tiny-random-gemma3-text as a bare Gemma3TextModel (unprefixed, no baked-in torch_dtype) to match real checkpoints structurally. I don't have push access to optimum-intel-internal-testing, so I've prepared (and verified) a ready-to-run script for a maintainer: happy to share if useful — just let me know.

CI on the new commit is currently action_required (needs a maintainer to approve running workflows on this fork PR) — flagging in case that needs a manual click on your end.

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.

5 participants