Skip to content

fix: align visual-embedding feature selection with LLaVA semantics#113

Merged
kaiitunnz merged 2 commits into
mainfrom
kaiitunnz/fix/vlm-visual-feature-selection
Jul 23, 2026
Merged

fix: align visual-embedding feature selection with LLaVA semantics#113
kaiitunnz merged 2 commits into
mainfrom
kaiitunnz/fix/vlm-visual-feature-selection

Conversation

@kaiitunnz

@kaiitunnz kaiitunnz commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

Purpose

The visual-embedding path built LLaVA image embeddings by indexing the vision tower's hidden states directly, diverging from transformers' own LlavaModel.get_image_features: it never dropped the leading CLS token under the default vision-feature-select strategy, and it could not handle a list-valued vision_feature_layer. This aligns the selection with LLaVA semantics so the emitted embeddings match the model's canonical image features.

This change builds on #112 (merged), which relocated vision_tower / multi_modal_projector under self._model.model; the divergence is only reachable once that relocation is in place.

Changes

  • src/worker/executors/transformers_executor.py — add _select_vision_features, mirroring get_image_features (CLS crop on the default strategy; per-layer gather + last-dim concat for a list-valued vision_feature_layer), and route the visual-embedding block through it.
  • tests/worker/test_transformers_visual_embedding_features.py — cover int/list feature layer against default/full select strategy.

Design

Replicates only the feature-selection logic rather than calling base_model.get_image_features, which returns a BaseModelOutputWithPooling with image_sizes-driven splitting — a different shape and artifact contract than the current tensor path. The grouping/split and saved visual_embeddings.pt format are unchanged.

Test Plan

uv run pre-commit run --all-files
uv run pytest tests/ --ignore=tests/worker/test_mp_executor_cleanup_gpu.py

An isolated local-stack E2E builds current-branch server and GPU-worker images, brings up the stack with a single GPU worker, and submits a LLaVA visual-embedding workflow (llava-hf/llava-1.5-7b-hf) over one image. It waits for the task to reach a terminal state, downloads the resulting visual_embeddings.pt artifact, and inspects the embedding tensor to confirm the leading CLS token was dropped under the default strategy.

Test Result

  • uv run pre-commit run --all-files all passed.
  • uv run pytest tests/ --ignore=tests/worker/test_mp_executor_cleanup_gpu.py passed: 1416 tests, including the 4 new selection tests.
  • The local-stack E2E passed all 5 assertions: the GPU worker registered, the visual-embedding task was submitted and reached DONE, the visual_embeddings.pt artifact was downloadable, and the first embedding tensor had shape (1, 576, 4096) — sequence length 576, confirming the CLS token was cropped (for llava-1.5 the CLIP tower emits 576 patches plus one CLS token, so an un-cropped run would report 577).

Pre-submission Checklist
  • I have read the contribution guidelines.
  • I have run pre-commit run --all-files and fixed any issues.
  • I have added or updated tests covering my changes (if applicable).
  • I have verified that uv run pytest tests/ passes locally.
  • If I changed shared schemas or proto definitions, I have checked downstream compatibility across Server and Worker.
  • If I changed the SDK or CLI, I have verified the affected packages work (uv sync --all-packages --group ci --frozen).
  • If this is a breaking change, I have prefixed the PR title with [BREAKING] and described migration steps above.
  • I have updated documentation or config examples if user-facing behavior changed.

The visual-embedding path selected vision-tower hidden states directly,
diverging from transformers' LlavaModel.get_image_features: it never
cropped the leading CLS token under the "default" select strategy and
could not handle a list-valued vision_feature_layer. Mirror that logic
so the embeddings match LLaVA's own image features.

Signed-off-by: Noppanat Wadlom <noppanat.wad@gmail.com>
@kaiitunnz
kaiitunnz force-pushed the kaiitunnz/fix/vlm-visual-feature-selection branch from 678706c to 45a04f4 Compare July 23, 2026 12:44
Signed-off-by: Noppanat Wadlom <noppanat.wad@gmail.com>
@kaiitunnz
kaiitunnz marked this pull request as ready for review July 23, 2026 12:54
@kaiitunnz
kaiitunnz requested a review from timzsu as a code owner July 23, 2026 12:54

@timzsu timzsu 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.

LGTM.

@kaiitunnz
kaiitunnz merged commit 9b5763b into main Jul 23, 2026
12 checks passed
@kaiitunnz
kaiitunnz deleted the kaiitunnz/fix/vlm-visual-feature-selection branch July 23, 2026 13:29
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.

2 participants