[EXPERIMENT][WIP][OpenVINO] Add support for FG-CLIP2 - #1955
Draft
mlukasze wants to merge 1 commit into
Draft
Conversation
Adds Fgclip2OpenVINOConfig (subclassing SiglipOpenVINOConfig), a dedicated DummyFgclip2VisionInputGenerator for the spatial_shapes input, and a Fgclip2ModelPatcher working around an upstream buffer-materialization bug (position_ids/mask1/mask2 not recomputed by from_pretrained). - Registers fgclip2 in TasksManager for feature-extraction and zero-shot-image-classification tasks. - OVModelForZeroShotImageClassification.forward() updated to support the spatial_shapes input required by this model's SigLIP2-NaFlex-style vision tower. - Exports at a FIXED canonical resolution (documented limitation -- true arbitrary-resolution NaFlex dynamism is out of scope for this change). - Default weight-only INT4 quantization config added (group_size_fallback="adjust", since intermediate_size=4304 isn't evenly divisible by the default group_size=128). Weight-only INT8 also validated. Full/activation-based INT8 quantization confirmed architecturally incompatible with the fixed-resolution export (documented, no default config registered). - Validated FP16/INT8-WO/INT4-WO exports and inference on CPU and GPU. - Tests added: utils_tests.py (MODEL_NAMES, expected INT8 node counts, OVModel class mapping, REMOTE_CODE_MODELS), test_modeling.py (dedicated test method in the existing OVModelForZeroShotImageClassificationIntegrationTest class), test_export.py and test_exporters_cli.py (SUPPORTED_ARCHITECTURES entries + AutoModelForCausalLM loading branch, since fgclip2's auto_map entry only exposes CausalLM-style loading). - Docs: added FG-CLIP2 row to docs/source/openvino/models.mdx. A tiny-model creation script is provided out-of-band at agent-results/optimum-intel/create_tiny_fgclip2.py for a human maintainer to run and upload to optimum-intel-internal-testing/tiny-random-Fgclip2Model -- until that upload happens, the newly added tests referencing that Hub id will fail with RepositoryNotFoundError (expected/known state, not a code defect). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
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. |
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.
What does this PR do?
Adds OpenVINO export and inference support for FG-CLIP2 (
qihoo360/fg-clip2-so400m,model_type=fgclip2), a bilingual (EN/中文) dual-tower zero-shot image classification model with a SigLIP2-NaFlex-style contrastive vision/text embedding architecture (customtrust_remote_codemodeling, loaded viaFgclip2Model).Closes #84.
Architecture summary
image_embeds/text_embeds/logits_per_image/logits_per_text, reusing the existingOVModelForZeroShotImageClassificationruntime class (no new OV runtime class needed).pixel_valuesis pre-patchified to(batch, num_patches, num_channels * patch_size * patch_size), and aspatial_shapes(batch, 2) side-tensor drives a per-sampleF.interpolatecall that resizes learned positional embeddings to the image's grid.sqrt(num_patches) x sqrt(num_patches)patch grid) so the per-sample interpolation loop traces to one static shape -- the same trade-off the existingsiglip(fixed-resolution) OV config already makes. Arbitrary-resolution NaFlex dynamism is out of scope for this PR.modeling_fgclip2.py(position_ids/mask1/mask2inFgclip2TextEmbeddingsare precomputed at__init__time as non-persistent buffers/plain attributes, sofrom_pretrained's fast-init path never (re)materializes them, causing anIndexErrorin plain PyTorch, independent of OpenVINO) is worked around inFgclip2ModelPatcherfor the export path.Accuracy validation (perception-mode fallback, not WWB)
FG-CLIP2's task (
zero-shot-image-classification) iscanonical_type=perception, so the standard WWB text-generation pipeline does not apply. Accuracy was validated byaccuracy-supervisorvia manual image/text embedding cosine-similarity against the eager PyTorch reference, on 4 real image/caption pairs, CPU + iGPU (GPU.0):Verdict:
ok(this is the first accuracy run for this model_type, so it establishes the baseline rather than regressing against one). Full breakdown:agent-results/accuracy-supervisor/verdict.json/agent-results/accuracy-supervisor/summary.md.Known limitations / follow-ups
walk_typeis pinned to the model's own default ("short") for the exported text tower variant -- a compile-time Python string kwarg, not a tensor branch.Broadcastshape-mismatch during statistics collection. Weight-only INT8/INT4 are the supported quantization paths (no default_DEFAULT_INT8_FQ_CONFIGSentry is registered; documented inline inconfiguration.py).group_sizesweep finding (not implemented in this PR, out of scope): the current default INT4 recipe (group_size=128,group_size_fallback="adjust", sinceintermediate_size=4304is not evenly divisible by 128) shows meaningful fidelity loss (91.8% image-embed cosine similarity, 75% top-1 agreement). A CPU-onlygroup_sizesweep byaccuracy-supervisorfoundgroup_size=16(an exact divisor ofintermediate_size=4304, avoiding the fallback path entirely) recovers image-embed cosine similarity to 96.8% and top-1 caption agreement to 100%, with no regression on any other measured axis (text-embed cosine also improves slightly, 99.59% vs 99.25%).group_size=32also fully recovers top-1 agreement (94.9% image cosine). Recommend a human maintainer or a follow-up iteration consider registeringgroup_size=16(or32) as the default INT4 weight-compression group size forfgclip2in_DEFAULT_4BIT_WQ_CONFIGS, instead of the generic128+adjustfallback. Full sweep data:agent-results/accuracy-supervisor/verdict.json(known_issue.experiment).Pending human action -- tiny test fixture upload
The tiny test fixture
optimum-intel-internal-testing/tiny-random-Fgclip2Modelreferenced by the new test entries has not been uploaded to the Hub yet (this agent has no upload access). A standalone, no-argument creation script is provided atagent-results/optimum-intel/create_tiny_fgclip2.py(realFgclip2Modelclass, random weights, scaled-down but shape/divisibility-invariant-preserving config). A human maintainer needs to run it and upload the result to that Hub id before the newly added tests (test_modeling.py,test_export.py,test_exporters_cli.py) can pass in CI. All new test code paths were validated locally against a freshly generated tiny model (temporarily pointingMODEL_NAMES["fgclip2"]at the local directory, reverted before commit) --2 passed(seetest_run.log). Running against the real (not-yet-uploaded) Hub id fails deterministically withRepositoryNotFoundError(401), which is the expected/documented blocked state, not a code defect.Quality gate results
check_pr_quality.py: black PASS, syntax PASS, test_coverage PASS (hard gates). mutation shows a pre-existing warning unrelated to this change (37 locations inmodel_configs.py/model_patcher.pyall belong toVideoChatFlashQwenConfig/QwenModelPatcher, not touched by this diff). base_class PASS (no recent breaking upstream changes to the base classes used here).scan_open_prs.py: no duplicate/open PRs found forfgclip2/clip.Mandatory Reuse Audit
Fgclip2OpenVINOConfigSiglipOpenVINOConfiglogits_per_image/logits_per_text/text_embeds/image_embedsoutput contract, same fixed-resolution vision-input trade-offclass Fgclip2OpenVINOConfig(SiglipOpenVINOConfig))DUMMY_INPUT_GENERATOR_CLASSES(addsspatial_shapes) andinputs(declarespixel_values/spatial_shapesshapes matching the pre-patchified NaFlex-style vision tower); noattention_maskinput, matchingsiglip's own conventionDummyFgclip2VisionInputGeneratorDummyVisionInputGenerator(base),DummyDeepseekOCR2VisionTilesInputGenerator/DummyGemma4UnifiedVisionInputGenerator(closest sibling patterns with non-standard vision dummy inputs)class DummyFgclip2VisionInputGenerator(DummyVisionInputGenerator))pixel_values(batch, num_patches, channels*patch*patch) instead of(batch, channels, height, width), plus a synthetic constantspatial_shapestensor; pinsbatch_size=1because the vision-tower embeddings module unrolls a Python loop overspatial_shapes.shape[0]at trace time, baking image batch size into the graph -- none of the inspected siblings need thisFgclip2ModelPatcherModelPatcher(base),SAMModelPatcher/QwenModelPatcher(closest siblings that also mutatemodel.config/precompute tensors in__init__)ModelPatcher, no shared logic reused beyond the base contract)position_ids/mask1/mask2forFgclip2TextEmbeddingsto work around an upstreamfrom_pretrainedfast-init bug specific to this checkpoint's custom modeling code -- not a general pattern applicable elsewhereOVModelForZeroShotImageClassification.forward(extended, not replaced)clip/siglipcode path)clip/siglip; conditionally extended (if "spatial_shapes" in self.input_names) rather than forked into a new classspatial_shapesinput, defaulted to the fixed canonical grid size derived frompixel_values.shape[1]when the caller does not supply one -- a no-op for every existing model that does not declare aspatial_shapesinput_DEFAULT_4BIT_WQ_CONFIGS["qihoo360/fg-clip2-so400m"]entrygroup_size_fallback="adjust"needed becauseintermediate_size=4304is not divisible by the defaultgroup_size=128(see Known limitations above for the follow-up sweep)No symbol in this diff is a renamed copy or a bare
super()-only override; every override adds a concrete, documented, model-specific behavioral delta.Regression check
No shared runtime files (
convert.py,modeling_decoder.py,modeling_diffusion.py,modeling_seq2seq.py,modeling_visual_language.py) were modified.model_patcher.py,model_configs.py, andinput_generators.pywere extended with new, additive classes only (no existing class/function body was modified) -- a regression run against an existing model of the same family was not required, andcheck_pr_quality.py'sbase_classcheck confirms no upstream base-class drift affects this change.Installation instructions
Exporting cmd-line
optimum-cli export openvino -m qihoo360/fg-clip2-so400m --task zero-shot-image-classification ov_fgclip2Inference script
Before submitting