<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>image retrieval - Hard Wired</title>
	<atom:link href="https://www.hardwired.dev/tag/image-retrieval/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.hardwired.dev</link>
	<description></description>
	<lastBuildDate>Sat, 11 Apr 2026 16:09:02 +0000</lastBuildDate>
	<language>cs</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://www.hardwired.dev/wp-content/uploads/2022/10/android-chrome-256x256-1-150x150.png</url>
	<title>image retrieval - Hard Wired</title>
	<link>https://www.hardwired.dev</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Multimodal Embedding &#038; Reranker Models with Sentence Transformers</title>
		<link>https://www.hardwired.dev/2026/04/10/multimodal-embedding-reranker-models-with-sentence-transformers/</link>
		
		<dc:creator><![CDATA[Yukiko Hesse]]></dc:creator>
		<pubDate>Fri, 10 Apr 2026 20:32:42 +0000</pubDate>
				<category><![CDATA[AI]]></category>
		<category><![CDATA[artificial intelligence]]></category>
		<category><![CDATA[computer vision]]></category>
		<category><![CDATA[cross-modal search]]></category>
		<category><![CDATA[embedding models]]></category>
		<category><![CDATA[image retrieval]]></category>
		<category><![CDATA[machine learning]]></category>
		<category><![CDATA[multimodal models]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[RAG]]></category>
		<category><![CDATA[reranker]]></category>
		<category><![CDATA[Semantic search]]></category>
		<category><![CDATA[sentence transformers]]></category>
		<guid isPermaLink="false">https://www.hardwired.dev/?p=3030</guid>

					<description><![CDATA[<p>Multimodal Embedding &#38; Reranker Models with Sentence Transformers Sentence Transformers is a Python library for using and training embedding and &#62;&#62;&#62;</p>
<p>The post <a href="https://www.hardwired.dev/2026/04/10/multimodal-embedding-reranker-models-with-sentence-transformers/">Multimodal Embedding & Reranker Models with Sentence Transformers</a> first appeared on <a href="https://www.hardwired.dev">Hard Wired</a>.</p>]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div><h1>Multimodal Embedding &amp; Reranker Models with Sentence Transformers</h1>
<p>Sentence Transformers is a Python library for using and training embedding and reranker models for applications like retrieval augmented generation, semantic search, and more. With the v5.4 update, you can now encode and compare texts, images, audio, and videos using the same familiar API. In this blogpost, I'll show you how to use these new multimodal capabilities for both embedding and reranking.</p>
</p>
<p>Multimodal embedding models map inputs from different modalities into a shared embedding space, while multimodal reranker models score the relevance of mixed-modality pairs. This opens up use cases like visual document retrieval, cross-modal search, and multimodal RAG pipelines.</p>
<h2>Table of Contents</h2>
<ul>
<li><a href="#what-are-multimodal-models">What are Multimodal Models?</a></li>
<li><a href="#installation">Installation</a></li>
<li><a href="#multimodal-embedding-models">Multimodal Embedding Models</a></li>
<li><a href="#loading-a-model">Loading a Model</a></li>
<li><a href="#encoding-images">Encoding Images</a></li>
<li><a href="#cross-modal-similarity">Cross-Modal Similarity</a></li>
<li><a href="#encoding-queries-and-documents">Encoding Queries and Documents</a></li>
<li><a href="#multimodal-reranker-models">Multimodal Reranker Models</a></li>
<li><a href="#ranking-mixed-modality-documents">Ranking Mixed-Modality Documents</a></li>
<li><a href="#predicting-pair-scores">Predicting Pair Scores</a></li>
<li><a href="#retrieve-and-rerank">Retrieve and Rerank</a></li>
<li><a href="#input-formats-and-configuration">Input Formats and Configuration</a></li>
<li><a href="#supported-input-types">Supported Input Types</a></li>
<li><a href="#checking-modality-support">Checking Modality Support</a></li>
<li><a href="#processor-and-model-kwargs">Processor and Model kwargs</a></li>
<li><a href="#supported-models">Supported Models</a></li>
<li><a href="#additional-resources">Additional Resources</a></li>
</ul>
<h2>What are Multimodal Models?</h2>
<p>Traditional embedding models convert text into fixed-size vectors. Multimodal embedding models extend this by mapping inputs from different modalities (text, images, audio, or video) into a shared embedding space. This means you can compare a text query against image documents (or vice versa) using the same similarity functions you're already familiar with.</p>
<p>Similarly, traditional reranker (Cross Encoder) models compute relevance scores between pairs of texts. Multimodal rerankers can score pairs where one or both elements are images, combined text-image documents, or other modalities.</p>
<p>For example, you can compare a text query against image documents, find video clips matching a description, or build RAG pipelines that work across modalities.</p>
<h2>Installation</h2>
<p>Multimodal models require some extra dependencies. Install the extras for the modalities you need (see <a href="https://sbert.net/docs/installation.html">Installation</a> for more details):</p>
<div class="codehilite">
<pre><span></span><code><span class="c1"># For image support</span>
pip<span class="w"> </span>install<span class="w"> </span>-U<span class="w"> </span><span class="s2">&quot;sentence-transformers[image]&quot;</span>

<span class="c1"># For audio support</span>
pip<span class="w"> </span>install<span class="w"> </span>-U<span class="w"> </span><span class="s2">&quot;sentence-transformers&quot;</span>

<span class="c1"># For video support</span>
pip<span class="w"> </span>install<span class="w"> </span>-U<span class="w"> </span><span class="s2">&quot;sentence-transformers&quot;</span>

<span class="c1"># Mix and match as needed</span>
pip<span class="w"> </span>install<span class="w"> </span>-U<span class="w"> </span><span class="s2">&quot;sentence-transformers[image,video,train]&quot;</span>
</code></pre>
</div>
<p>VLM-based models like Qwen3-VL-2B require a GPU with at least ~8 GB of VRAM. For the 8B variants, expect ~20 GB. If you don't have a local GPU, consider using a cloud GPU service or Google Colab. On CPU, these models will be extremely slow; text-only or CLIP models are better suited for CPU inference.</p>
<h2>Multimodal Embedding Models</h2>
<h3>Loading a Model</h3>
<p>Loading a multimodal embedding model works exactly like loading a text-only model:</p>
<div class="codehilite">
<pre><span></span><code><span class="kn">from</span> <span class="nn">sentence_transformers</span> <span class="kn">import</span> <span class="n">SentenceTransformer</span>

<span class="n">model</span> <span class="o">=</span> <span class="n">SentenceTransformer</span><span class="p">(</span><span class="s2">&quot;Qwen/Qwen3-VL-Embedding-2B&quot;</span><span class="p">,</span> <span class="n">revision</span><span class="o">=</span><span class="s2">&quot;refs/pr/23&quot;</span><span class="p">)</span>
</code></pre>
</div>
<p>The revision argument is required for now because the integration pull requests for these models are still pending. Once they're merged, you'll be able to load them without specifying a revision.</p>
<p>The model automatically detects which modalities it supports, so there's nothing extra to configure. See <a href="#processor-and-model-kwargs">Processor and Model kwargs</a> if you want to control things like image resolution or model precision.</p>
<h3>Encoding Images</h3>
<p>With a multimodal model loaded, <code>model.encode()</code> accepts images alongside text. Images can be provided as URLs, local file paths, or PIL Image objects (see <a href="#supported-input-types">Supported Input Types</a> for all accepted formats):</p>
<div class="codehilite">
<pre><span></span><code><span class="kn">from</span> <span class="nn">sentence_transformers</span> <span class="kn">import</span> <span class="n">SentenceTransformer</span>

<span class="n">model</span> <span class="o">=</span> <span class="n">SentenceTransformer</span><span class="p">(</span><span class="s2">&quot;Qwen/Qwen3-VL-Embedding-2B&quot;</span><span class="p">,</span> <span class="n">revision</span><span class="o">=</span><span class="s2">&quot;refs/pr/23&quot;</span><span class="p">)</span>

<span class="c1"># Encode images from URLs</span>
<span class="n">img_embeddings</span> <span class="o">=</span> <span class="n">model</span><span class="o">.</span><span class="n">encode</span><span class="p">([</span>
 <span class="s2">&quot;https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg&quot;</span><span class="p">,</span>
 <span class="s2">&quot;https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/bee.jpg&quot;</span><span class="p">,</span>
<span class="p">])</span>
<span class="nb">print</span><span class="p">(</span><span class="n">img_embeddings</span><span class="o">.</span><span class="n">shape</span><span class="p">)</span>
<span class="c1"># (2, 2048)</span>
</code></pre>
</div>
<h3>Cross-Modal Similarity</h3>
<p>You can compute similarities between text embeddings and image embeddings, since the model maps both into the same space:</p>
<div class="codehilite">
<pre><span></span><code><span class="kn">from</span> <span class="nn">sentence_transformers</span> <span class="kn">import</span> <span class="n">SentenceTransformer</span>

<span class="n">model</span> <span class="o">=</span> <span class="n">SentenceTransformer</span><span class="p">(</span><span class="s2">&quot;Qwen/Qwen3-VL-Embedding-2B&quot;</span><span class="p">,</span> <span class="n">revision</span><span class="o">=</span><span class="s2">&quot;refs/pr/23&quot;</span><span class="p">)</span>

<span class="c1"># Encode images</span>
<span class="n">img_embeddings</span> <span class="o">=</span> <span class="n">model</span><span class="o">.</span><span class="n">encode</span><span class="p">([</span>
 <span class="s2">&quot;https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg&quot;</span><span class="p">,</span>
 <span class="s2">&quot;https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/bee.jpg&quot;</span><span class="p">,</span>
<span class="p">])</span>

<span class="c1"># Encode text queries (one matching + one hard negative per image)</span>
<span class="n">text_embeddings</span> <span class="o">=</span> <span class="n">model</span><span class="o">.</span><span class="n">encode</span><span class="p">([</span>
 <span class="s2">&quot;A green car parked in front of a yellow building&quot;</span><span class="p">,</span>
 <span class="s2">&quot;A red car driving on a highway&quot;</span><span class="p">,</span>
 <span class="s2">&quot;A bee on a pink flower&quot;</span><span class="p">,</span>
 <span class="s2">&quot;A wasp on a wooden table&quot;</span><span class="p">,</span>
<span class="p">])</span>

<span class="c1"># Compute cross-modal similarities</span>
<span class="n">similarities</span> <span class="o">=</span> <span class="n">model</span><span class="o">.</span><span class="n">similarity</span><span class="p">(</span><span class="n">text_embeddings</span><span class="p">,</span> <span class="n">img_embeddings</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="n">similarities</span><span class="p">)</span>
<span class="c1"># tensor([[0.5115, 0.1078],</span>
<span class="c1"># [0.1999, 0.1108],</span>
<span class="c1"># [0.1255, 0.6749],</span>
<span class="c1"># [0.1283, 0.2704]])</span>
</code></pre>
</div>
<p>As expected, "A green car parked in front of a yellow building" is most similar to the car image (0.51), and "A bee on a pink flower" is most similar to the bee image (0.67). The hard negatives ("A red car driving on a highway", "A wasp on a wooden table") correctly receive lower scores.</p>
<p>You might notice that even the best matching scores (0.51, 0.67) aren't very close to 1.0. This is due to the <a href="https://arxiv.org/abs/2203.02053">modality gap</a>: embeddings from different modalities tend to cluster in separate regions of the space. Cross-modal similarities are typically lower than within-modal ones (e.g., text-to-text), but the relative ordering is preserved, so retrieval still works well.</p>
<h3>Encoding Queries and Documents</h3>
<p>For retrieval tasks, <code>encode_query()</code> and <code>encode_document()</code> are the recommended methods. Many retrieval models prepend different instruction prompts depending on whether the input is a query or a document, similar to how chat models might apply different system prompts depending on the goal. Model authors can specify their prompts in the model config, and <code>encode_query()</code> / <code>encode_document()</code> automatically load and apply the correct one:</p>
<ul>
<li><code>encode_query()</code> uses the model's "query" prompt (if available) and sets <code>task="query"</code>.</li>
<li><code>encode_document()</code> uses the first available prompt from "document", "passage", or "corpus", and sets <code>task="document"</code>.</li>
</ul>
<p>Under the hood, both are thin wrappers around <code>encode()</code>, they just handle prompt selection for you. Here's what cross-modal retrieval looks like:</p>
<div class="codehilite">
<pre><span></span><code><span class="kn">from</span> <span class="nn">sentence_transformers</span> <span class="kn">import</span> <span class="n">SentenceTransformer</span>

<span class="n">model</span> <span class="o">=</span> <span class="n">SentenceTransformer</span><span class="p">(</span><span class="s2">&quot;Qwen/Qwen3-VL-Embedding-2B&quot;</span><span class="p">,</span> <span class="n">revision</span><span class="o">=</span><span class="s2">&quot;refs/pr/23&quot;</span><span class="p">)</span>

<span class="c1"># Encode text queries with the query prompt</span>
<span class="n">query_embeddings</span> <span class="o">=</span> <span class="n">model</span><span class="o">.</span><span class="n">encode_query</span><span class="p">([</span>
 <span class="s2">&quot;Find me a photo of a vehicle parked near a building&quot;</span><span class="p">,</span>
 <span class="s2">&quot;Show me an image of a pollinating insect&quot;</span><span class="p">,</span>
<span class="p">])</span>

<span class="c1"># Encode document screenshots with the document prompt</span>
<span class="n">doc_embeddings</span> <span class="o">=</span> <span class="n">model</span><span class="o">.</span><span class="n">encode_document</span><span class="p">([</span>
 <span class="s2">&quot;https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg&quot;</span><span class="p">,</span>
 <span class="s2">&quot;https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/bee.jpg&quot;</span><span class="p">,</span>
<span class="p">])</span>

<span class="c1"># Compute similarities</span>
<span class="n">similarities</span> <span class="o">=</span> <span class="n">model</span><span class="o">.</span><span class="n">similarity</span><span class="p">(</span><span class="n">query_embeddings</span><span class="p">,</span> <span class="n">doc_embeddings</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="n">similarities</span><span class="p">)</span>
<span class="c1"># tensor([[0.3907, 0.1490],</span>
<span class="c1"># [0.1235, 0.4872]])</span>
</code></pre>
</div>
<p>These methods accept the same input types as <code>encode()</code> (images, URLs, multimodal dicts, etc.) and pass through the same parameters. For models without specialized query/document prompts, they behave identically to <code>encode()</code>.</p>
<h2>Multimodal Reranker Models</h2>
<p>Multimodal reranker (CrossEncoder) models score the relevance between pairs of inputs, where each element can be text, an image, audio, video, or a combination. They tend to outperform embedding models in terms of quality, but are slower since they process each pair individually. The currently available pretrained multimodal rerankers focus on text and image inputs, but the architecture supports any modality that the underlying model can handle.</p>
<h3>Ranking Mixed-Modality Documents</h3>
<p>The <code>rank()</code> method scores and ranks a list of documents against a query, supporting mixed modalities:</p>
<div class="codehilite">
<pre><span></span><code><span class="kn">from</span> <span class="nn">sentence_transformers</span> <span class="kn">import</span> <span class="n">CrossEncoder</span>

<span class="n">model</span> <span class="o">=</span> <span class="n">CrossEncoder</span><span class="p">(</span><span class="s2">&quot;Qwen/Qwen3-VL-Reranker-2B&quot;</span><span class="p">,</span> <span class="n">revision</span><span class="o">=</span><span class="s2">&quot;refs/pr/11&quot;</span><span class="p">)</span>

<span class="n">query</span> <span class="o">=</span> <span class="s2">&quot;A green car parked in front of a yellow building&quot;</span>
<span class="n">documents</span> <span class="o">=</span> <span class="p">[</span>
 <span class="c1"># Image documents (URL or local file path)</span>
 <span class="s2">&quot;https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg&quot;</span><span class="p">,</span>
 <span class="s2">&quot;https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/bee.jpg&quot;</span><span class="p">,</span>
 <span class="c1"># Text document</span>
 <span class="s2">&quot;A vintage Volkswagen Beetle painted in bright green sits in a driveway.&quot;</span><span class="p">,</span>
 <span class="c1"># Combined text + image document</span>
 <span class="p">{</span>
 <span class="s2">&quot;text&quot;</span><span class="p">:</span> <span class="s2">&quot;A car in a European city&quot;</span><span class="p">,</span>
 <span class="s2">&quot;image&quot;</span><span class="p">:</span> <span class="s2">&quot;https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg&quot;</span><span class="p">,</span>
 <span class="p">},</span>
<span class="p">]</span>

<span class="n">rankings</span> <span class="o">=</span> <span class="n">model</span><span class="o">.</span><span class="n">rank</span><span class="p">(</span><span class="n">query</span><span class="p">,</span> <span class="n">documents</span><span class="p">)</span>
<span class="k">for</span> <span class="n">rank</span> <span class="ow">in</span> <span class="n">rankings</span><span class="p">:</span>
 <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">rank</span><span class="p">[</span><span class="s1">&#39;score&#39;</span><span class="p">]</span><span class="si">:</span><span class="s2">.4f</span><span class="si">}</span><span class="se">\t</span><span class="s2">(document </span><span class="si">{</span><span class="n">rank</span><span class="p">[</span><span class="s1">&#39;corpus_id&#39;</span><span class="p">]</span><span class="si">}</span><span class="s2">)&quot;</span><span class="p">)</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd">0.9375 (document 0)</span>
<span class="sd">0.5000 (document 3)</span>
<span class="sd">-1.2500 (document 2)</span>
<span class="sd">-2.4375 (document 1)</span>
<span class="sd">&quot;&quot;&quot;</span>
</code></pre>
</div>
<p>The reranker correctly identifies the car image (document 0) as the most relevant result, followed by the combined text+image document about a car in a European city (document 3). The bee image (document 1) scores lowest.</p>
<p>Keep in mind that the modality gap can influence absolute scores: text-image pair scores may occupy a different range than text-text or image-image pair scores.</p>
<p>You can also check which modalities a reranker supports using <code>modalities</code> and <code>supports()</code>, just like with embedding models:</p>
<div class="codehilite">
<pre><span></span><code><span class="nb">print</span><span class="p">(</span><span class="n">model</span><span class="o">.</span><span class="n">modalities</span><span class="p">)</span>
<span class="c1"># [&#39;text&#39;, &#39;image&#39;, &#39;video&#39;, &#39;message&#39;]</span>

<span class="nb">print</span><span class="p">(</span><span class="n">model</span><span class="o">.</span><span class="n">supports</span><span class="p">(</span><span class="s2">&quot;image&quot;</span><span class="p">))</span>
<span class="c1"># True</span>

<span class="c1"># Check if the model supports a specific pair of modalities</span>
<span class="nb">print</span><span class="p">(</span><span class="n">model</span><span class="o">.</span><span class="n">supports</span><span class="p">((</span><span class="s2">&quot;image&quot;</span><span class="p">,</span> <span class="s2">&quot;text&quot;</span><span class="p">)))</span>
<span class="c1"># True</span>
</code></pre>
</div>
<h3>Predicting Pair Scores</h3>
<p>You can also use <code>predict()</code> to get raw relevance scores for specific pairs of inputs:</p>
<div class="codehilite">
<pre><span></span><code><span class="kn">from</span> <span class="nn">sentence_transformers</span> <span class="kn">import</span> <span class="n">CrossEncoder</span>

<span class="n">model</span> <span class="o">=</span> <span class="n">CrossEncoder</span><span class="p">(</span><span class="s2">&quot;jinaai/jina-reranker-m0&quot;</span><span class="p">,</span> <span class="n">trust_remote_code</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>

<span class="n">scores</span> <span class="o">=</span> <span class="n">model</span><span class="o">.</span><span class="n">predict</span><span class="p">([</span>
 <span class="p">(</span><span class="s2">&quot;A green car&quot;</span><span class="p">,</span> <span class="s2">&quot;https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg&quot;</span><span class="p">),</span>
 <span class="p">(</span><span class="s2">&quot;A bee on a flower&quot;</span><span class="p">,</span> <span class="s2">&quot;https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/bee.jpg&quot;</span><span class="p">),</span>
 <span class="p">(</span><span class="s2">&quot;A green car&quot;</span><span class="p">,</span> <span class="s2">&quot;https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/bee.jpg&quot;</span><span class="p">),</span>
<span class="p">])</span>
<span class="nb">print</span><span class="p">(</span><span class="n">scores</span><span class="p">)</span>
<span class="c1"># [0.9389156 0.96922314 0.46063158]</span>
</code></pre>
</div>
<h3>Retrieve and Rerank</h3>
<p>A common pattern is to use an embedding model for fast initial retrieval, then refine the top results with a reranker:</p>
<div class="codehilite">
<pre><span></span><code><span class="kn">from</span> <span class="nn">sentence_transformers</span> <span class="kn">import</span> <span class="n">SentenceTransformer</span><span class="p">,</span> <span class="n">CrossEncoder</span>

<span class="c1"># Step 1: Retrieve with an embedding model</span>
<span class="n">embedder</span> <span class="o">=</span> <span class="n">SentenceTransformer</span><span class="p">(</span><span class="s2">&quot;Qwen/Qwen3-VL-Embedding-2B&quot;</span><span class="p">,</span> <span class="n">revision</span><span class="o">=</span><span class="s2">&quot;refs/pr/23&quot;</span><span class="p">)</span>

<span class="n">query</span> <span class="o">=</span> <span class="s2">&quot;revenue growth chart&quot;</span>
<span class="n">query_embedding</span> <span class="o">=</span> <span class="n">embedder</span><span class="o">.</span><span class="n">encode_query</span><span class="p">(</span><span class="n">query</span><span class="p">)</span>

<span class="c1"># Pre-compute corpus embeddings (do this once, then store them)</span>
<span class="n">document_screenshots</span> <span class="o">=</span> <span class="p">[</span>
 <span class="s2">&quot;path/to/doc1.png&quot;</span><span class="p">,</span>
 <span class="s2">&quot;path/to/doc2.png&quot;</span><span class="p">,</span>
 <span class="c1"># ... potentially millions of document screenshots</span>
<span class="p">]</span>
<span class="n">corpus_embeddings</span> <span class="o">=</span> <span class="n">embedder</span><span class="o">.</span><span class="n">encode_document</span><span class="p">(</span><span class="n">document_screenshots</span><span class="p">,</span> <span class="n">show_progress_bar</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>

<span class="c1"># Simple cosine similarity retrieval, viable as long as embeddings fit in memory</span>
<span class="n">similarities</span> <span class="o">=</span> <span class="n">embedder</span><span class="o">.</span><span class="n">similarity</span><span class="p">(</span><span class="n">query_embedding</span><span class="p">,</span> <span class="n">corpus_embeddings</span><span class="p">)</span>
<span class="n">top_k_indices</span> <span class="o">=</span> <span class="n">similarities</span><span class="o">.</span><span class="n">argsort</span><span class="p">(</span><span class="n">descending</span><span class="o">=</span><span class="kc">True</span><span class="p">)[</span><span class="mi">0</span><span class="p">][:</span><span class="mi">10</span><span class="p">]</span>

<span class="c1"># Step 2: Rerank the top-k results with a reranker model</span>
<span class="n">reranker</span> <span class="o">=</span> <span class="n">CrossEncoder</span><span class="p">(</span><span class="s2">&quot;nvidia/llama-nemotron-rerank-vl-1b-v2&quot;</span><span class="p">,</span> <span class="n">trust_remote_code</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>

<span class="n">top_k_documents</span> <span class="o">=</span> <span class="p">[</span><span class="n">document_screenshots</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">top_k_indices</span><span class="p">]</span>
<span class="n">rankings</span> <span class="o">=</span> <span class="n">reranker</span><span class="o">.</span><span class="n">rank</span><span class="p">(</span><span class="n">query</span><span class="p">,</span> <span class="n">top_k_documents</span><span class="p">)</span>
<span class="k">for</span> <span class="n">rank</span> <span class="ow">in</span> <span class="n">rankings</span><span class="p">:</span>
 <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">rank</span><span class="p">[</span><span class="s1">&#39;score&#39;</span><span class="p">]</span><span class="si">:</span><span class="s2">.4f</span><span class="si">}</span><span class="se">\t</span><span class="si">{</span><span class="n">top_k_documents</span><span class="p">[</span><span class="n">rank</span><span class="p">[</span><span class="s1">&#39;corpus_id&#39;</span><span class="p">]]</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
</code></pre>
</div>
<p>Since the corpus embeddings are pre-computed, the initial retrieval is fast even over millions of documents. The reranker then provides more accurate scoring over the smaller candidate set.</p>
<h2>Input Formats and Configuration</h2>
<h3>Supported Input Types</h3>
<p>Multimodal models accept a variety of input formats. Here's a summary of what you can pass to <code>model.encode()</code>:</p>
<table>
<thead>
<tr>
<th>Modality</th>
<th>Accepted Formats</th>
</tr>
</thead>
<tbody>
<tr>
<td>Text</td>
<td>- Strings</td>
</tr>
<tr>
<td>Image</td>
<td>- PIL.Image.Image objects<br />- File paths (e.g. "./photo.jpg")<br />- URLs (e.g. "https://.../image.jpg")<br />- Numpy arrays, torch tensors</td>
</tr>
<tr>
<td>Audio</td>
<td>- File paths (e.g. "./audio.wav")<br />- URLs (e.g. "https://.../audio.wav")<br />- Numpy/torch arrays<br />- Dicts with "array" and "sampling_rate" keys<br />- torchcodec.AudioDecoder instances</td>
</tr>
<tr>
<td>Video</td>
<td>- File paths (e.g. "./video.mp4")<br />- URLs (e.g. "https://.../video.mp4")<br />- Numpy/torch arrays<br />- Dicts with "array" and "video_metadata" keys<br />- torchcodec.VideoDecoder instances</td>
</tr>
<tr>
<td>Multimodal</td>
<td>- Dicts mapping modality names to values,<br />e.g. <code>{"text": "a caption", "image": "https://.../image.jpg"}</code><br />Valid keys: "text", "image", "audio", "video"</td>
</tr>
<tr>
<td>Message</td>
<td>- List of message dicts with "role" and "content" keys,<br />e.g. <code>[{"role": "user", "content": [...]}]</code></td>
</tr>
</tbody>
</table>
<h3>Checking Modality Support</h3>
<p>You can check which modalities a model supports using the <code>modalities</code> property and <code>supports()</code> method:</p>
<div class="codehilite">
<pre><span></span><code><span class="kn">from</span> <span class="nn">sentence_transformers</span> <span class="kn">import</span> <span class="n">SentenceTransformer</span>

<span class="n">model</span> <span class="o">=</span> <span class="n">SentenceTransformer</span><span class="p">(</span><span class="s2">&quot;Qwen/Qwen3-VL-Embedding-2B&quot;</span><span class="p">,</span> <span class="n">revision</span><span class="o">=</span><span class="s2">&quot;refs/pr/23&quot;</span><span class="p">)</span>

<span class="c1"># List all supported modalities</span>
<span class="nb">print</span><span class="p">(</span><span class="n">model</span><span class="o">.</span><span class="n">modalities</span><span class="p">)</span>
<span class="c1"># [&#39;text&#39;, &#39;image&#39;, &#39;video&#39;, &#39;message&#39;]</span>

<span class="c1"># Check for a specific modality</span>
<span class="nb">print</span><span class="p">(</span><span class="n">model</span><span class="o">.</span><span class="n">supports</span><span class="p">(</span><span class="s2">&quot;image&quot;</span><span class="p">))</span>
<span class="c1"># True</span>
<span class="nb">print</span><span class="p">(</span><span class="n">model</span><span class="o">.</span><span class="n">supports</span><span class="p">(</span><span class="s2">&quot;audio&quot;</span><span class="p">))</span>
<span class="c1"># False</span>
</code></pre>
</div>
<p>The "message" modality indicates that the model accepts chat-style message inputs with interleaved content. In practice, you rarely need to use this directly. When you pass strings, URLs, or multimodal dicts, the model converts them to the appropriate message format internally. Sentence Transformers supports two message formats:</p>
<ol>
<li><strong>Structured</strong> (most VLMs, e.g. Qwen3-VL): Content is a list of typed dicts, e.g. <code>[{"type": "text", "text": "..."}, {"type": "image", "image": ...}]</code></li>
<li><strong>Flat</strong> (e.g. Deepseek-V3): Content is a direct value, e.g. <code>"some text"</code></li>
</ol>
<p>The format is auto-detected from the model's chat template.</p>
<p>Since all inputs get converted into the same message format internally, you can mix input types in a single <code>encode()</code> call:</p>
<div class="codehilite">
<pre><span></span><code><span class="n">embeddings</span> <span class="o">=</span> <span class="n">model</span><span class="o">.</span><span class="n">encode</span><span class="p">([</span>
 <span class="c1"># A text input</span>
 <span class="s2">&quot;A green car parked in front of a yellow building&quot;</span><span class="p">,</span>
 <span class="c1"># An image input (URL)</span>
 <span class="s2">&quot;https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg&quot;</span><span class="p">,</span>
 <span class="c1"># A combined text + image input</span>
 <span class="p">{</span>
 <span class="s2">&quot;text&quot;</span><span class="p">:</span> <span class="s2">&quot;A car in a European city&quot;</span><span class="p">,</span>
 <span class="s2">&quot;image&quot;</span><span class="p">:</span> <span class="s2">&quot;https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg&quot;</span><span class="p">,</span>
 <span class="p">},</span>
<span class="p">])</span>
</code></pre>
</div>
<h3>Processor and Model kwargs</h3>
<p>You may want to control image resolution bounds or model precision. Use <code>processor_kwargs</code> and <code>model_kwargs</code> when loading the model:</p>
<div class="codehilite">
<pre><span></span><code><span class="n">model</span> <span class="o">=</span> <span class="n">SentenceTransformer</span><span class="p">(</span>
 <span class="s2">&quot;Qwen/Qwen3-VL-Embedding-2B&quot;</span><span class="p">,</span>
 <span class="n">model_kwargs</span><span class="o">=</span><span class="p">{</span><span class="s2">&quot;attn_implementation&quot;</span><span class="p">:</span> <span class="s2">&quot;flash_attention_2&quot;</span><span class="p">,</span> <span class="s2">&quot;torch_dtype&quot;</span><span class="p">:</span> <span class="s2">&quot;bfloat16&quot;</span><span class="p">},</span>
 <span class="n">processor_kwargs</span><span class="o">=</span><span class="p">{</span><span class="s2">&quot;min_pixels&quot;</span><span class="p">:</span> <span class="mi">28</span> <span class="o">*</span> <span class="mi">28</span><span class="p">,</span> <span class="s2">&quot;max_pixels&quot;</span><span class="p">:</span> <span class="mi">600</span> <span class="o">*</span> <span class="mi">600</span><span class="p">},</span>
 <span class="n">revision</span><span class="o">=</span><span class="s2">&quot;refs/pr/23&quot;</span><span class="p">,</span>
<span class="p">)</span>
</code></pre>
</div>
<p><code>processor_kwargs</code> controls how inputs are preprocessed (e.g., image resolution bounds). Higher <code>max_pixels</code> means higher quality but more memory and compute. These are</p>

<div class="twitter-share"><a href="https://twitter.com/intent/tweet?url=https%3A%2F%2Fwww.hardwired.dev%2F2026%2F04%2F10%2Fmultimodal-embedding-reranker-models-with-sentence-transformers%2F&#038;via=hessevalentino" class="twitter-share-button">Tweet</a></div><p>The post <a href="https://www.hardwired.dev/2026/04/10/multimodal-embedding-reranker-models-with-sentence-transformers/">Multimodal Embedding & Reranker Models with Sentence Transformers</a> first appeared on <a href="https://www.hardwired.dev">Hard Wired</a>.</p>]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
