Sentence Transformersによるマルチモーダル埋め込みとリランカーモデル
Sentence Transformers v5.4は、テキスト、画像、音声、動画を共通の埋め込み空間で扱えるマルチモーダル埋め込み・リランカーモデルを導入し、クロスモーダル検索やマルチモーダルRAGパイプラインなどの新たな応用を可能にした。
キーポイント
マルチモーダル埋め込みモデルの拡張
従来のテキスト専用モデルから拡張され、テキスト、画像、音声、動画を共通の埋め込み空間にマッピングし、異なるモダリティ間での類似性比較を可能にした。
マルチモーダルリランカーモデルの導入
テキスト間だけでなく、画像や複合ドキュメントを含む混合モダリティペアの関連性スコアを計算できるリランカーモデルを提供し、検索精度を向上させる。
統一されたAPIインターフェース
既存のSentence Transformers APIと互換性を保ちながら、マルチモーダル機能を統合し、開発者が容易に導入できる設計となっている。
実用的な応用シナリオ
視覚的文書検索、クロスモーダル検索、マルチモーダルRAGパイプラインなど、具体的なユースケースを想定した機能設計がされている。
ハードウェア要件と推奨環境
Qwen3-VL-2Bモデルは約8GBのVRAMを必要とし、8Bバリアントは約20GBを要する。GPUがない場合はクラウドサービスやGoogle Colabの利用が推奨され、CPUでは極めて低速になるためテキスト専用モデルやCLIPモデルが適している。
マルチモーダル埋め込みモデルの使用方法
SentenceTransformersを使用してモデルをロードし、model.encode()で画像をエンコードできる。テキストと画像の埋め込みは同じ空間にマッピングされるため、クロスモーダル類似度を計算可能で、検索タスクに活用できる。
モダリティギャップの影響
異なるモダリティ(テキストと画像)の埋め込みは空間内で別々の領域にクラスタリングされるため、クロスモーダル類似度は1.0に近づきにくいが、相対的な順序は保持されるため検索は有効に機能する。
影響分析・編集コメントを表示
影響分析
このアップデートは、マルチモーダルAIの実用化を加速させる重要な進展であり、特にRAG(Retrieval Augmented Generation)システムの能力を大幅に拡張する。異なるデータ形式を統一的に扱えることで、より豊かなコンテキスト理解に基づくAIアプリケーションの開発が促進される。
編集コメント
Hugging FaceのSentence Transformersがマルチモーダル対応に本格的に踏み出した点は、実務家にとって非常に実用的なアップデート。既存コードベースとの互換性を重視した設計が評価できる。
記事に戻る Sentence Transformersによるマルチモーダル埋め込み&リランカーモデル
Upvote 6 ![]()



Sentence Transformersは、検索拡張生成(Retrieval Augmented Generation)、意味的検索(semantic search)などのアプリケーション向けに、埋め込み(embedding)モデルとリランカー(reranker)モデルを使用・学習するためのPythonライブラリです。v5.4アップデートにより、同じ使い慣れたAPIを使って、テキスト、画像、音声、動画をエンコード(encode)して比較できるようになりました。このブログ記事では、これらの新しいマルチモーダル(multimodal)機能を埋め込みとリランキングの両方で使用する方法を紹介します。
マルチモーダル埋め込みモデルは、異なるモダリティ(modality)からの入力を共有の埋め込み空間(embedding space)にマッピングします。一方、マルチモーダルリランカーモデルは、異なるモダリティのペアの関連性をスコアリングします。これにより、視覚的文書検索(visual document retrieval)、クロスモーダル検索(cross-modal search)、マルチモーダルRAG(Retrieval Augmented Generation)パイプラインなどのユースケースが開かれます。
目次
マルチモーダルモデルとは?
マルチモーダル埋め込みモデル モデルのロード
画像のエンコード
クロスモーダル類似度
クエリとドキュメントのエンコード
マルチモーダルリランカーモデル 混合モダリティドキュメントのランキング
ペアスコアの予測
検索とリランク
入力形式と設定 サポートされる入力タイプ
モダリティサポートの確認
プロセッサとモデルのkwargs
サポートされるモデル
追加リソース
マルチモーダルモデルとは?
従来の埋め込みモデルは、テキストを固定サイズのベクトルに変換します。マルチモーダル埋め込みモデルはこれを拡張し、異なるモダリティ(テキスト、画像、音声、動画)からの入力を共有の埋め込み空間にマッピングします。これは、すでに使い慣れている同じ類似度関数を使って、テキストクエリを画像ドキュメントと比較できる(またはその逆も可能)ことを意味します。
同様に、従来のリランカー(クロスエンコーダ:Cross Encoder)モデルは、テキストのペア間の関連性スコアを計算します。マルチモーダルリランカーは、一方または両方の要素が画像、テキストと画像を組み合わせたドキュメント、またはその他のモダリティであるペアをスコアリングできます。
例えば、テキストクエリを画像ドキュメントと比較したり、説明に一致する動画クリップを見つけたり、モダリティをまたいで動作するRAGパイプラインを構築したりできます。
マルチモーダルモデルにはいくつかの追加依存関係が必要です。必要なモダリティのエクストラをインストールしてください(詳細はインストールを参照):
画像サポート用 pip install -U "sentence-transformers[image]" # 音声サポート用 pip install -U "sentence-transformers[audio]" # 動画サポート用 pip install -U "sentence-transformers[video]" # 必要に応じて組み合わせる pip install -U "sentence-transformers[image,video,train]"
Qwen3-VL-2BのようなVLM(Vision-Language Model)ベースのモデルは、少なくとも約8 GBのVRAMを搭載したGPUが必要です。8Bバリアントでは約20 GBを想定してください。ローカルにGPUがない場合は、クラウドGPUサービスやGoogle Colabの使用を検討してください。CPU上では、これらのモデルは極端に遅くなります。テキストのみのモデルやCLIP(Contrastive Language-Image Pre-training)モデルは、CPU推論により適しています。
マルチモーダル埋め込みモデル
モデルのロード
マルチモーダル埋め込みモデルのロードは、テキストのみのモデルのロードと全く同じように動作します:
from sentence_transformers import SentenceTransformer model = SentenceTransformer("Qwen/Qwen3-VL-Embedding-2B", revision="refs/pr/23")
モデルは自動的にサポートするモダリティを検出するため、追加で設定するものはありません。画像解像度やモデル精度などを制御したい場合は、プロセッサとモデルのkwargsを参照してください。
画像のエンコード
マルチモーダルモデルをロードすると、model.encode()メソッドは画像を受け入れるようになります。画像はURL、ローカルファイルパス、またはPIL.Imageオブジェクトとして渡すことができます:
from sentence_transformers import SentenceTransformer model = SentenceTransformer("Qwen/Qwen3-VL-Embedding-2B", revision="refs/pr/23") # URLから画像をエンコード img_embeddings = model.encode([ "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg", "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/bee.jpg", ]) print(img_embeddings.shape) # (2, 2048)
クロスモーダル類似度
モデルが両方を同じ空間にマッピングするため、テキスト埋め込みと画像埋め込みの間の類似度を計算できます:
from sentence_transformers import SentenceTransformer model = SentenceTransformer("Qwen/Qwen3-VL-Embedding-2B", revision="refs/pr/23") # 画像をエンコード img_embeddings = model.encode([ "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg", "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/bee.jpg", ]) # テキストクエリをエンコード(画像ごとに一致するもの1つ+難しいネガティブ1つ) text_embeddings = model.encode([ "黄色い建物の前に駐車された緑の車", "高速道路を走る赤い車", "ピンクの花の上の蜂", "木製のテーブルの上のスズメバチ", ]) # クロスモーダル類似度を計算 similarities = model.similarity(text_embeddings, img_embeddings) print(similarities) # tensor([[0.5115, 0.1078], # [0.1999, 0.1108], # [0.1255, 0.6749], # [0.1283, 0.2704]])
予想通り、「黄色い建物の前に駐車された緑の車」は車の画像に最も類似しており(0.51)、「ピンクの花の上の蜂」は蜂の画像に最も類似しています(0.67)。難しいネガティブ(「高速道路を走る赤い車」、「木製のテーブルの上のスズメバチ」)は正しく低いスコアを受け取っています。
最適なマッチングスコア(0.51, 0.67)でさえ、1.0にあまり近くないことに気付くかもしれません。これはモダリティギャップ(modality gap)によるものです:異なるモダリティからの埋め込みは、空間の別々の領域にクラスタリングする傾向があります。クロスモーダル類似度は通常、モダリティ内の類似度(例:テキスト対テキスト)よりも低くなりますが、相対的な順序は保持されるため、検索は依然としてうまく機能します。
クエリとドキュメントのエンコード
検索タスクでは、encode_query()メソッドとencode_document()メソッドを使用できます。これらは、それぞれクエリとドキュメントに適したプロンプトを適用します。デフォルトでは、encode_document()は画像をドキュメントとして扱いますが、task="document"引数を渡すことで、テキストドキュメントにも使用できます。
内部的には、両方ともencode()の薄いラッパーです:
from sentence_transformers import SentenceTransformer model = SentenceTransformer("Qwen/Qwen3-VL-Embedding-2B", revision="refs/pr/23") # クエリプロンプトでテキストクエリをエンコード query_embeddings = model.encode_query([ "建物の近くに駐車された車両の写真を見つけてください", "花粉媒介昆虫の画像を見せてください", ]) # ドキュメントプロンプトでドキュメントスクリーンショットをエンコード doc_embeddings = model.encode_document([ "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg", "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/bee.jpg", ]) # 類似度を計算 similarities = model.similarity(query_embeddings, doc_embeddings) print(similarities) # tensor([[0.3907, 0.1490], # [0.1235, 0.4872]])
これらのメソッドは、encode()と同じ入力タイプを受け入れます。
マルチモーダルリランカーモデル
マルチモーダルリランカー(クロスエンコーダ:CrossEncoder)モデルは、各要素がテキスト、画像、音声、動画、またはそれらの組み合わせである可能性がある入力ペア間の関連性をスコアリングします。これらは品質の点で埋め込みモデルを上回る傾向がありますが、各ペアを個別に処理するため、より遅くなります。現在利用可能な事前学習済みマルチモーダルリランカーはテキストと画像の入力に焦点を当てていますが、アーキテクチャは基盤モデルが扱える任意のモダリティをサポートします。
混合モダリティドキュメントのランキング
from sentence_transformers import CrossEncoder model = CrossEncoder("Qwen/Qwen3-VL-Reranker-2B", revision="refs/pr/11") query = "黄色い建物の前に駐車された緑の車" documents = [ # 画像ドキュメント(URLまたはローカルファイルパス) "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg", "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/bee.jpg", # テキストドキュメント "明るい緑に塗られたビンテージのフォルクスワーゲン・ビートルが車道に停まっている。", # テキスト+画像を組み合わせたドキュメント { "text": "ヨーロッパの都市にある車", "image": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg", }, ] rankings = model.rank(query, documents) for rank in rankings: print(f"{rank['score']:.4f}\t(document {rank['corpus_id']})") """ 0.9375 (document 0) 0.5000 (document 3) -1.2500 (document 2) -2.4375 (document 1) """
リランカーは、車の画像(ドキュメント0)を最も関連性の高い結果として正しく識別し、次にヨーロッパの都市にある車についてのテキスト+画像を組み合わせたドキュメント(ドキュメント3)が続きます。蜂の画像(ドキュメント1)は最も低いスコアです。モダリティギャップが絶対スコアに影響を与える可能性があることに注意してください:テキスト-画像ペアのスコアは、テキスト-テキストや画像-画像ペアのスコアとは異なる範囲を占める可能性があります。
modalities属性を使用して、リランカーがサポートするモダリティを確認することもできます:
print(model.modalities) # ['text', 'image', 'video', 'message'] print(model.supports("image")) # True # モデルが特定のモダリティのペアをサポートするか確認 print(model.supports(("image", "text"))) # True
ペアスコアの予測
predict()メソッドを使用して、ペアのスコアを直接取得することもできます:
from sentence_transformers import CrossEncoder model = CrossEncoder("jinaai/jina-reranker-m0", trust_remote_code=True) scores = model.predict([ ("緑の車", "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg"), ("花の上の蜂", "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/bee.jpg"), ("緑の車", "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/bee.jpg"), ]) print(scores) # [0.9389156 0.96922314 0.46063158]
検索とリランク
一般的なパターンは、高速な初期検索に埋め込みモデルを使用し、上位結果をリランカーで洗練することです:
from sentence_transformers import SentenceTransformer, CrossEncoder
ステップ1: 埋め込みモデルによる検索
embedder = SentenceTransformer("Qwen/Qwen3-VL-Embedding-2B", revision="refs/pr/23")
query = "revenue growth chart"
query_embedding = embedder.encode_query(query)
コーパス埋め込みを事前計算(一度実行し、保存する)
document_screenshots = [
"path/to/doc1.png",
"path/to/doc2.png",
# ... 数百万のドキュメントスクリーンショットの可能性
]
corpus_embeddings = embedder.encode_document(document_screenshots, show_progress_bar=True)
単純なコサイン類似度による検索、埋め込みがメモリに収まる限り有効
similarities = embedder.similarity(query_embedding, corpus_embeddings)
top_k_indices = similarities.argsort(descending=True)[0][:10]
ステップ2: リランカーモデルで上位k件の結果を再ランク付け
reranker = CrossEncoder(
"nvidia/llama-nemotron-rerank-vl-1b-v2",
trust_remote_code=True,
revision="refs/pr/9",
)
top_k_documents = [document_screenshots[i] for i in top_k_indices]
rankings = reranker.rank(query, top_k_documents)
for rank in rankings:
print(f"{rank['score']:.4f}\t{top_k_documents[rank['corpus_id']]}")
コーパス埋め込みは事前計算されているため、数百万のドキュメントに対しても初期検索は高速です。リランカーはその後、より小さな候補セットに対してより正確なスコアリングを提供します。
入力形式と設定
サポートされる入力タイプ
マルチモーダルモデルは様々な入力形式を受け入れます。以下はmodel.encode()に渡すことができるものの概要です。
受け入れられる形式
- PIL.Image.Image
"https://.../image.jpg"
- ファイルパス(例:"./audio.wav"
"https://.../audio.wav"
"sampling_rate"
torchcodec.AudioDecoder
- ファイルパス(例:"./video.mp4"
"https://.../video.mp4"
"video_metadata"
torchcodec.VideoDecoder
- モダリティ名と値をマッピングする辞書、例:{"text": "a caption", "image": "https://.../image.jpg"}
- "role"を持つメッセージ辞書のリスト
[{"role": "user", "content": [...]}]
モダリティサポートの確認
modalities属性を使用して、モデルがサポートするモダリティを確認できます。
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("Qwen/Qwen3-VL-Embedding-2B", revision="refs/pr/23")
サポートされるすべてのモダリティをリスト表示
print(model.modalities) # ['text', 'image', 'video', 'message']
特定のモダリティを確認
print(model.supports("image")) # True
print(model.supports("audio")) # False
構造化(ほとんどのVLM(Vision-Language Model)、例:Qwen3-VL): コンテンツは型付き辞書のリスト、例:[{"type": "text", "text": "..."}, {"type": "image", "image": ...}]
フラット(例:Deepseek-V3): コンテンツは直接の値、例:"some text"
形式はモデルのチャットテンプレートから自動検出されます。
すべての入力は内部的に同じメッセージ形式に変換されるため、単一のencode()呼び出しで入力タイプを混在させることができます。
embeddings = model.encode([
# テキスト入力
"A green car parked in front of a yellow building",
# 画像入力(URL)
"https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg",
# テキスト+画像の組み合わせ入力
{
"text": "A car in a European city",
"image": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg",
},
])
モデルがどちらの形式にも従わず、完全な制御が必要な場合は、roleを持つ生のメッセージ辞書を渡すことができます。
embeddings = model.encode([
[
{
"role": "user",
"content": [
{"type": "image", "image": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg"},
{"type": "text", "text": "Describe this vehicle."},
],
}
],
])
これは自動形式変換をバイパスし、メッセージをプロセッサのapply_chat_template()に直接渡します。
プロセッサとモデルのkwargs
画像解像度の境界やモデル精度を制御したい場合があります。processor_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はAutoProcessor.from_pretrained(...)に渡されます。
model_kwargsはAutoModel.from_pretrained(...)に渡されます。
埋め込みモデルの場合、AutoModelForCausalLMの代わりにAutoModelForSequenceClassificationが使用されます。
これらのkwargsの詳細については、SentenceTransformer APIリファレンスドキュメントを参照してください。
Sentence Transformers v5.4では、tokenizer_kwargsはprocessor_kwargsに統合されました。
サポートされるモデル
以下はv5.4でサポートされるマルチモーダルモデルで、v5.4統合コレクションでも利用可能です。
サポートされるマルチモーダル埋め込みモデル
Qwen/Qwen3-VL-Embedding-2B
テキスト、画像、動画
revision="refs/pr/23"
Qwen/Qwen3-VL-Embedding-8B
テキスト、画像、動画
revision="refs/pr/11"
nvidia/llama-nemotron-embed-vl-1b-v2
revision="refs/pr/6"
nvidia/omni-embed-nemotron-3b
revision="refs/pr/3"
サポートされるマルチモーダルリランカーモデル
Qwen/Qwen3-VL-Reranker-2B
テキスト、画像、動画
revision="refs/pr/11"
Qwen/Qwen3-VL-Reranker-8B
テキスト、画像、動画
revision="refs/pr/9"
nvidia/llama-nemotron-rerank-vl-1b-v2
revision="refs/pr/9"
jinaai/jina-reranker-m0
テキスト専用リランカーモデル(v5.4で新規追加)
Qwen/Qwen3-Reranker-0.6B
revision="refs/pr/24"
Qwen/Qwen3-Reranker-4B
revision="refs/pr/11"
Qwen/Qwen3-Reranker-8B
revision="refs/pr/11"
mixedbread-ai/mxbai-rerank-base-v2
mixedbread-ai/mxbai-rerank-large-v2
from sentence_transformers import CrossEncoder
model = CrossEncoder("mixedbread-ai/mxbai-rerank-base-v2")
query = "How do I bake sourdough bread?"
documents = [
"Sourdough bread requires a starter made from flour and water, fermented over several days.",
"The history of bread dates back to ancient Egypt around 8000 BCE.",
"To bake sourdough, mix your starter with flour, water, and salt, then let it rise overnight.",
"Rye bread is a popular alternative to wheat-based breads in Northern Europe.",
]
pairs = [(query, doc) for doc in documents]
scores = model.predict(pairs)
print(scores) # [ 7.3077507 -2.6217823 8.724761 -2.2488995]
rankings = model.rank(query, documents)
for rank in rankings:
print(f"{rank['score']:.4f}\t{documents[rank['corpus_id']]}")
8.7248 To bake sourdough, mix your starter with flour, water, and salt, then let it rise overnight.
7.3078 Sourdough bread requires a starter made from flour and water, fermented over several days.
-2.2489 Rye bread is a popular alternative to wheat-based breads in Northern Europe.
-2.6218 The history of bread dates back to ancient Egypt around 8000 BCE.
古いCLIPモデルも引き続きサポートされています。
ImageNet Zero-Shot Top-1 Accuracy
sentence-transformers/clip-ViT-L-14
sentence-transformers/clip-ViT-B-16
sentence-transformers/clip-ViT-B-32
sentence-transformers/clip-ViT-B-32-multilingual-v1
多言語テキストエンコーダ、50以上の言語
これらのシンプルなCLIPモデルは、リソースが限られたハードウェアでも良好に機能します。
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("sentence-transformers/clip-ViT-L-14")
images = [
"https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg",
"https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/bee.jpg",
"https://huggingface.co/datasets/huggingface/cats-image/resolve/main/cats_image.jpeg"
]
texts = ["A green car", "A bee on a flower", "Some cats on a couch", "One cat sitting in the window"]
image_embeddings = model.encode(images)
text_embeddings = model.encode(texts)
print(image_embeddings.shape, text_embeddings.shape) # (3, 768) (4, 768)
similarities = model.similarity(image_embeddings, text_embeddings)
print(similarities)
tensor([[0.2208, 0.1042, 0.0617, 0.0907], 最初の画像(車)は「A green car」に最も類似
[0.1205, 0.2303, 0.0632, 0.0917], 2番目の画像(蜂)は「A bee on a flower」に最も類似
[0.1107, 0.0196, 0.2425, 0.1162]]) 3番目の画像(複数の猫)は「Some cats on a couch」に最も類似
追加リソース
Sentence Transformer > 使用方法
Sentence Transformer > 事前学習済みモデル
Cross Encoder > 使用方法
Cross Encoder > 事前学習済みモデル
今後数週間でマルチモーダルモデルのトレーニングとファインチューニングに関するブログ投稿を公開する予定ですので、ご期待ください!それまでの間、事前学習済みモデルで推論を試したり、トレーニングドキュメントを使用してトレーニングを試みたりすることができます。
Sentence Transformer > トレーニング概要
Sentence Transformer > トレーニング例
Cross Encoder > トレーニング概要
Cross Encoder > トレーニング例
Sparse Encoder > トレーニング概要
Sparse Encoder > トレーニング例
Hugging Face Hub
Sentence Transformersモデル on the Hub
Sentence Transformersデータセット on the Hub
v5.4統合コレクション
原文を表示
Back to Articles Multimodal Embedding & Reranker Models with Sentence Transformers
Upvote 6 ![]()



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.
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.
Table of Contents
What are Multimodal Models?
Multimodal Embedding Models Loading a Model
Encoding Images
Cross-Modal Similarity
Encoding Queries and Documents
Multimodal Reranker Models Ranking Mixed-Modality Documents
Predicting Pair Scores
Retrieve and Rerank
Input Formats and Configuration Supported Input Types
Checking Modality Support
Processor and Model kwargs
Supported Models
Additional Resources
What are Multimodal Models?
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.
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.
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.
Multimodal models require some extra dependencies. Install the extras for the modalities you need (see Installation for more details):
For image support pip install -U "sentence-transformers[image]" # For audio support pip install -U "sentence-transformers[audio]" # For video support pip install -U "sentence-transformers[video]" # Mix and match as needed pip install -U "sentence-transformers[image,video,train]"
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.
Multimodal Embedding Models
Loading a Model
Loading a multimodal embedding model works exactly like loading a text-only model:
from sentence_transformers import SentenceTransformer model = SentenceTransformer("Qwen/Qwen3-VL-Embedding-2B", revision="refs/pr/23")
The model automatically detects which modalities it supports, so there's nothing extra to configure. See Processor and Model kwargs if you want to control things like image resolution or model precision.
Encoding Images
With a multimodal model loaded, model.encode()
from sentence_transformers import SentenceTransformer model = SentenceTransformer("Qwen/Qwen3-VL-Embedding-2B", revision="refs/pr/23") # Encode images from URLs img_embeddings = model.encode([ "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg", "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/bee.jpg", ]) print(img_embeddings.shape) # (2, 2048)
Cross-Modal Similarity
You can compute similarities between text embeddings and image embeddings, since the model maps both into the same space:
from sentence_transformers import SentenceTransformer model = SentenceTransformer("Qwen/Qwen3-VL-Embedding-2B", revision="refs/pr/23") # Encode images img_embeddings = model.encode([ "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg", "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/bee.jpg", ]) # Encode text queries (one matching + one hard negative per image) 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", ]) # Compute cross-modal similarities similarities = model.similarity(text_embeddings, img_embeddings) print(similarities) # tensor([[0.5115, 0.1078], # [0.1999, 0.1108], # [0.1255, 0.6749], # [0.1283, 0.2704]])
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.
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 modality gap: 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.
Encoding Queries and Documents
For retrieval tasks, encode_query()
encode_document()
encode_document()
encode_document()
task="document"
Under the hood, both are thin wrappers around encode()
from sentence_transformers import SentenceTransformer model = SentenceTransformer("Qwen/Qwen3-VL-Embedding-2B", revision="refs/pr/23") # Encode text queries with the query prompt query_embeddings = model.encode_query([ "Find me a photo of a vehicle parked near a building", "Show me an image of a pollinating insect", ]) # Encode document screenshots with the document prompt doc_embeddings = model.encode_document([ "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg", "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/bee.jpg", ]) # Compute similarities similarities = model.similarity(query_embeddings, doc_embeddings) print(similarities) # tensor([[0.3907, 0.1490], # [0.1235, 0.4872]])
These methods accept the same input types as encode()
Multimodal Reranker Models
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.
Ranking Mixed-Modality Documents
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 = [ # Image documents (URL or local file path) "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg", "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/bee.jpg", # Text document "A vintage Volkswagen Beetle painted in bright green sits in a driveway.", # Combined text + image document { "text": "A car in a European city", "image": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg", }, ] rankings = model.rank(query, documents) for rank in rankings: print(f"{rank['score']:.4f}\t(document {rank['corpus_id']})") """ 0.9375 (document 0) 0.5000 (document 3) -1.2500 (document 2) -2.4375 (document 1) """
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. 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.
You can also check which modalities a reranker supports using modalities
print(model.modalities) # ['text', 'image', 'video', 'message'] print(model.supports("image")) # True # Check if the model supports a specific pair of modalities print(model.supports(("image", "text"))) # True
Predicting Pair Scores
You can also use predict()
from sentence_transformers import CrossEncoder model = CrossEncoder("jinaai/jina-reranker-m0", trust_remote_code=True) scores = model.predict([ ("A green car", "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg"), ("A bee on a flower", "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/bee.jpg"), ("A green car", "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/bee.jpg"), ]) print(scores) # [0.9389156 0.96922314 0.46063158]
Retrieve and Rerank
A common pattern is to use an embedding model for fast initial retrieval, then refine the top results with a reranker:
from sentence_transformers import SentenceTransformer, CrossEncoder # Step 1: Retrieve with an embedding model embedder = SentenceTransformer("Qwen/Qwen3-VL-Embedding-2B", revision="refs/pr/23") query = "revenue growth chart" query_embedding = embedder.encode_query(query) # Pre-compute corpus embeddings (do this once, then store them) document_screenshots = [ "path/to/doc1.png", "path/to/doc2.png", # ... potentially millions of document screenshots ] corpus_embeddings = embedder.encode_document(document_screenshots, show_progress_bar=True) # Simple cosine similarity retrieval, viable as long as embeddings fit in memory similarities = embedder.similarity(query_embedding, corpus_embeddings) top_k_indices = similarities.argsort(descending=True)[0][:10] # Step 2: Rerank the top-k results with a reranker model reranker = CrossEncoder( "nvidia/llama-nemotron-rerank-vl-1b-v2", trust_remote_code=True, revision="refs/pr/9", ) top_k_documents = [document_screenshots[i] for i in top_k_indices] rankings = reranker.rank(query, top_k_documents) for rank in rankings: print(f"{rank['score']:.4f}\t{top_k_documents[rank['corpus_id']]}")
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.
Input Formats and Configuration
Supported Input Types
Multimodal models accept a variety of input formats. Here's a summary of what you can pass to model.encode()
Accepted Formats
- PIL.Image.Image
"https://.../image.jpg"
- File paths (e.g. "./audio.wav"
"https://.../audio.wav"
"sampling_rate"
torchcodec.AudioDecoder
- File paths (e.g. "./video.mp4"
"https://.../video.mp4"
"video_metadata"
torchcodec.VideoDecoder
- Dicts mapping modality names to values, e.g. {"text": "a caption", "image": "https://.../image.jpg"}
- List of message dicts with "role"
[{"role": "user", "content": [...]}]
Checking Modality Support
You can check which modalities a model supports using the modalities
from sentence_transformers import SentenceTransformer model = SentenceTransformer("Qwen/Qwen3-VL-Embedding-2B", revision="refs/pr/23") # List all supported modalities print(model.modalities) # ['text', 'image', 'video', 'message'] # Check for a specific modality print(model.supports("image")) # True print(model.supports("audio")) # False
Structured (most VLMs, e.g. Qwen3-VL): Content is a list of typed dicts, e.g. [{"type": "text", "text": "..."}, {"type": "image", "image": ...}]
Flat (e.g. Deepseek-V3): Content is a direct value, e.g. "some text"
The format is auto-detected from the model's chat template.
Since all inputs get converted into the same message format internally, you can mix input types in a single encode()
embeddings = model.encode([ # A text input "A green car parked in front of a yellow building", # An image input (URL) "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg", # A combined text + image input { "text": "A car in a European city", "image": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg", }, ])
If a model doesn't follow either format and you need full control, you can pass raw message dicts with role
embeddings = model.encode([ [ { "role": "user", "content": [ {"type": "image", "image": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg"}, {"type": "text", "text": "Describe this vehicle."}, ], } ], ])
This bypasses the automatic format conversion and passes the messages directly to the processor's apply_chat_template()
Processor and Model kwargs
You may want to control image resolution bounds or model precision. Use processor_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
AutoProcessor.from_pretrained(...)
AutoModel.from_pretrained(...)
AutoModelForCausalLM
AutoModelForSequenceClassification
See the SentenceTransformer API Reference documentation for more details on these kwargs.
In Sentence Transformers v5.4, tokenizer_kwargs
processor_kwargs
Supported Models
Here are the multimodal models supported in v5.4, also available in the v5.4 integrations collection:
Supported Multimodal Embedding Models
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
revision="refs/pr/6"
nvidia/omni-embed-nemotron-3b
revision="refs/pr/3"
Supported Multimodal Reranker Models
Qwen/Qwen3-VL-Reranker-2B
Text, Image, Video
revision="refs/pr/11"
Qwen/Qwen3-VL-Reranker-8B
Text, Image, Video
revision="refs/pr/9"
nvidia/llama-nemotron-rerank-vl-1b-v2
revision="refs/pr/9"
jinaai/jina-reranker-m0
Text-Only Reranker Models (also new in v5.4)
Qwen/Qwen3-Reranker-0.6B
revision="refs/pr/24"
Qwen/Qwen3-Reranker-4B
revision="refs/pr/11"
Qwen/Qwen3-Reranker-8B
revision="refs/pr/11"
mixedbread-ai/mxbai-rerank-base-v2
mixedbread-ai/mxbai-rerank-large-v2
from sentence_transformers import CrossEncoder model = CrossEncoder("mixedbread-ai/mxbai-rerank-base-v2") query = "How do I bake sourdough bread?" documents = [ "Sourdough bread requires a starter made from flour and water, fermented over several days.", "The history of bread dates back to ancient Egypt around 8000 BCE.", "To bake sourdough, mix your starter with flour, water, and salt, then let it rise overnight.", "Rye bread is a popular alternative to wheat-based breads in Northern Europe.", ] pairs = [(query, doc) for doc in documents] scores = model.predict(pairs) print(scores) # [ 7.3077507 -2.6217823 8.724761 -2.2488995] rankings = model.rank(query, documents) for rank in rankings: print(f"{rank['score']:.4f}\t{documents[rank['corpus_id']]}") # 8.7248 To bake sourdough, mix your starter with flour, water, and salt, then let it rise overnight. # 7.3078 Sourdough bread requires a starter made from flour and water, fermented over several days. # -2.2489 Rye bread is a popular alternative to wheat-based breads in Northern Europe. # -2.6218 The history of bread dates back to ancient Egypt around 8000 BCE.
The older CLIP models continue to be supported:
ImageNet Zero-Shot Top-1 Accuracy
sentence-transformers/clip-ViT-L-14
sentence-transformers/clip-ViT-B-16
sentence-transformers/clip-ViT-B-32
sentence-transformers/clip-ViT-B-32-multilingual-v1
Multilingual text encoder, 50+ languages
These simple CLIP models still work well on lower-resource hardware.
from sentence_transformers import SentenceTransformer model = SentenceTransformer("sentence-transformers/clip-ViT-L-14") images = [ "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg", "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/bee.jpg", "https://huggingface.co/datasets/huggingface/cats-image/resolve/main/cats_image.jpeg" ] texts = ["A green car", "A bee on a flower", "Some cats on a couch", "One cat sitting in the window"] image_embeddings = model.encode(images) text_embeddings = model.encode(texts) print(image_embeddings.shape, text_embeddings.shape) # (3, 768) (4, 768) similarities = model.similarity(image_embeddings, text_embeddings) print(similarities) # tensor([[0.2208, 0.1042, 0.0617, 0.0907], First image (car) is most similar to "A green car" # [0.1205, 0.2303, 0.0632, 0.0917], Second image (bee) is most similar to "A bee on a flower" # [0.1107, 0.0196, 0.2425, 0.1162]]) Third image (multiple cats) is most similar to "Some cats on a couch"
Additional Resources
Sentence Transformer > Usage
Sentence Transformer > Pretrained Models
Cross Encoder > Usage
Cross Encoder > Pretrained Models
I'll be releasing a blogpost for training and finetuning multimodal models in the coming weeks, so stay tuned! In the meantime, you can experiment with inference on the pretrained models, or try to experiment with training using the training documentation:
Sentence Transformer > Training Overview
Sentence Transformer > Training Examples
Cross Encoder > Training Overview
Cross Encoder > Training Examples
Sparse Encoder > Training Overview
Sparse Encoder > Training Examples
Hugging Face Hub
Sentence Transformers models on the Hub
Sentence Transformers datasets on the Hub
v5.4 Integrations Collection
関連記事
Gemini Embedding 2 の一般提供:エージェント型マルチモーダル RAG を実現する統合モデル
Google はテキスト、画像、動画、音声、文書を単一の意味空間にマッピングする統合モデル「Gemini Embedding 2」の一般提供を開始した。この新モデルにより、開発者は1回のリクエストで複数のマルチモーダル入力を処理でき、エージェント型 RAG の性能が大幅に向上する。
Amazon Nova Multimodal Embeddingsで強化する動画意味検索
Amazonは、動画内の特定シーンを高速・高精度に検索できる「Nova Multimodal Embeddings」を発表した。スポーツ放送局やスタジオが、プレイヤーの得点シーンや特定俳優のシーンを瞬時に抽出できるようになる。
【AIニュース】ImageGenはAGIへの道を進んでいる
AnthropicのようなエンタープライズAI重視の潮流の中で、GPT-Image-2は創造的な応用を推進し、AGI実現への重要な一歩を示している。