The era of search and retrieval that mixes text, images, audio and video is already here. Can you imagine being able to search an image with a phrase, then refine results with a reranker that understands text+image? Hugging Face and Sentence Transformers introduce multimodal support that makes this practical and accessible.
What are multimodal models?
Traditional embedding models turn text into fixed vectors. Multimodal models extend that idea: they map inputs from different modalities (text, image, audio, video) into the same embedding space. The result? You can directly compare a text query with documents that are images or screenshots using the same similarity functions you already know.
Multimodal rerankers (CrossEncoders) do the opposite: they receive mixed pairs (text+image, image+image, etc.) and return a relevance score per pair. They usually provide higher quality than embeddings, but are slower because they process each pair individually.
Practical note: cross-modal similarities are often lower in magnitude than text-to-text. Don’t be alarmed if the top scores don’t approach 1.0; what matters is the relative ordering.
Quick installation
Install only what you need depending on modality:
# Image
pip install -U sentence-transformers[image]
# Audio
pip install -U sentence-transformers[audio]
# Video
pip install -U sentence-transformers[video]
# Mix as needed
pip install -U sentence-transformers[image,video,train]
Tip: large VLMs (e.g. Qwen3-VL-2B) require a GPU with at least ~8 GB VRAM; 8B variants need ~20 GB. If you don't have a local GPU, use a cloud GPU or Google Colab. On CPU they will be very slow: for CPU, CLIP or text-only models perform better.
Multimodal embedding models: loading and use
Loading a multimodal model is as simple as with a text model:
from sentence_transformers import SentenceTransformer
model = SentenceTransformer('Qwen/Qwen3-VL-Embedding-2B', revision='refs/pr/23')
For now some integrations require the revision argument until PRs are merged. The model automatically detects supported modalities. model.encode() accepts images (URLs, local paths or PIL objects), text and multimodal dicts.
Example of encoding and cross-modal similarity:
# Encode images
img_embeddings = model.encode([
'https://.../car.jpg',
'https://.../bee.jpg',
])
# Encode text
text_embeddings = model.encode([
'A green car parked in front of a yellow building',
'A red car driving on a highway',
'A bee on a pink flower',
'A wasp on a wooden table',
])
# Similarities
similarities = model.similarity(text_embeddings, img_embeddings)
print(similarities)
You'll see that the best matches remain in order, even if absolute values are moderated by the modality gap.
encode_query / encode_document
For retrieval tasks it's recommended to use encode_query() and encode_document(). Many models include different prompts for queries and documents; these methods automatically apply the correct prompt before generating embeddings.
Multimodal rerankers (CrossEncoder)
Multimodal rerankers score mixed pairs. They're ideal to refine short candidate lists. Example usage with rank:
from sentence_transformers import CrossEncoder
model = CrossEncoder('Qwen/Qwen3-VL-Reranker-2B', revision='refs/pr/11')
query = 'A green car parked in front of a yellow building'
documents = [
'https://.../car.jpg',
'https://.../bee.jpg',
'A vintage Volkswagen Beetle painted in bright green sits in a driveway.',
{'text': 'A car in a European city', 'image': 'https://.../car.jpg'},
]
rankings = model.rank(query, documents)
The reranker usually orders correctly (car image on top, bee below), but remember score ranges can vary depending on whether the pair is text-image or text-text.
You can also use predict() to get raw scores for specific pairs.
Retrieve and rerank: recommended pattern
An effective and scalable pattern:
- Initial retrieval with an embeddings model (fast, indexable). Precompute embeddings for the corpus.
- Rerank the top-k with a multimodal CrossEncoder to get precision.
Short example:
# Step 1: fast embedder
embedder = SentenceTransformer('Qwen/Qwen3-VL-Embedding-2B', revision='refs/pr/23')
query_embedding = embedder.encode_query('revenue growth chart')
corpus_embeddings = embedder.encode_document(document_screenshots, show_progress_bar=True)
# obtain top_k via similarity
# Step 2: reranker
reranker = CrossEncoder('nvidia/llama-nemotron-rerank-vl-1b-v2', trust_remote_code=True, revision='refs/pr/9')
rankings = reranker.rank(query, top_k_documents)
Advantage: precomputed embeddings make search over millions of documents viable; the reranker is applied only to a small subset.
Input formats and preprocessing control
Models accept many formats. Practical summary:
- Text: strings.
- Image: PIL, local paths, URLs, numpy arrays, torch tensors.
- Audio: paths, URLs, arrays, dicts with
arrayandsampling_rate. - Video: paths, URLs, arrays, dicts with metadata.
- Multimodal: dicts like
{ 'text': '...', 'image': 'https://...' }. - Message: lists of dicts with
roleandcontentfor chat-style inputs.
You can control preprocessing and loading with processor_kwargs and model_kwargs:
model = SentenceTransformer(
'Qwen/Qwen3-VL-Embedding-2B',
model_kwargs={'attn_implementation': 'flash_attention_2', 'torch_dtype': 'bfloat16'},
processor_kwargs={'min_pixels': 28*28, 'max_pixels': 600*600},
revision='refs/pr/23',
)
processor_kwargs adjusts image resolution and quality (higher max_pixels means more memory and time). model_kwargs controls precision, attention and other model loading parameters.
Supported models (v5.4) and lightweight alternatives
Highlighted multimodal models in v5.4:
- Qwen/Qwen3-VL-Embedding-2B (Text, Image, Video) — revision='refs/pr/23'
- Qwen/Qwen3-VL-Embedding-8B (Text, Image, Video) — revision='refs/pr/11'
- nvidia/llama-nemotron-embed-vl-1b-v2 (Text, Image)
- nvidia/omni-embed-nemotron-3b (Text, Image)
Multimodal rerankers:
- Qwen/Qwen3-VL-Reranker-2B
- Qwen/Qwen3-VL-Reranker-8B
- nvidia/llama-nemotron-rerank-vl-1b-v2
- jinaai/jina-reranker-m0
If you have limited hardware, CLIP models remain a solid option for CPU:
- sentence-transformers/clip-ViT-L-14
- sentence-transformers/clip-ViT-B-16
Best practices and technical considerations
- Precompute corpus embeddings and use indexes (FAISS, Milvus) to scale.
- Use batching and FP16/bfloat16 when hardware allows to reduce memory.
- Keep the modality gap in mind: normalize expectations about score magnitudes, but trust the ordering.
- Check what modalities a model supports with
model.modalitiesandmodel.supports('image'). - If you need fine control over chat-style messages, pass raw
messagedicts to avoid automatic conversion. - If you plan to train/finetune, a dedicated guide is coming soon (Hugging Face announces a post about multimodal training soon).
The arrival of multimodal embeddings and rerankers in Sentence Transformers makes building RAG pipelines and cross-modal search much more direct. The takeaway? You can start experimenting today without reinventing the wheel: try CLIP on CPU, move to VLMs on GPU, and combine fast retrieval with reranking for production.
Original source
https://huggingface.co/blog/multimodal-sentence-transformers
