跳到正文
原文
Hugging Face Blog·· 2026-04-09精选AI 评分66

Sentence Transformers v5.4 多模态嵌入与重排序模型使用指南

Multimodal Embedding & Reranker Models with Sentence Transformers

AI 导读

Hugging Face 发布 Sentence Transformers v5.4,可在同一套 API 下编码和比较文本、图像、音频与视频,用于视觉文档检索、跨模态搜索和多模态 RAG。

推荐理由

官方博客给出 Sentence Transformers v5.4 多模态嵌入与重排序的完整代码用法和模型清单,可直接照抄跑通流程。

正文 · AI 翻译

Sentence Transformers 是一个 Python 库,用于使用和训练嵌入及重排序模型,应用于检索增强生成、语义搜索等场景。通过 v5.4 更新,你现在可以使用同样熟悉的 API 对文本、图像、音频和视频进行编码和比较。在这篇博客文章中,我将向你展示如何将这些新的多模态能力用于嵌入和重排序。

多模态嵌入模型将来自不同模态的输入映射到一个共享的嵌入空间,而多模态重排序模型则对混合模态对的相关性进行评分。这开启了诸如视觉文档检索、跨模态搜索和多模态 RAG 流水线等用例。

如果你想训练自己的多模态模型,请查看配套博客文章:使用 Sentence Transformers 训练和微调多模态嵌入与重排序模型。

目录

什么是多模态模型?

传统嵌入模型将文本转换为 固定大小的向量。多模态嵌入模型对此进行了扩展,将来自不同模态(文本、图像、音频或视频)的输入映射到一个共享的嵌入空间。这意味着你可以使用已经熟悉的相同相似度函数,将文本查询与图像文档进行比较(反之亦然)。

类似地,传统重排序(Cross Encoder)模型计算文本对之间的相关性分数。多模态重排序模型可以对其中一个或两个元素为图像、文本-图像组合文档或其他模态的配对进行评分。

例如,你可以将文本查询与图像文档进行比较,找到与描述匹配的视频片段,或构建跨模态工作的 RAG 流水线。

安装

多模态模型需要一些额外的依赖项。为你需要的模态安装相应的扩展(详情请参阅安装):

# 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]"

像 Qwen3-VL-2B 这样的基于 VLM 的模型需要至少约 8 GB 显存的 GPU。对于 8B 变体,预计需要约 20 GB。如果你没有本地 GPU,可以考虑使用云 GPU 服务或 Google Colab。在 CPU 上,这些模型会极其缓慢;纯文本或 CLIP 模型更适合 CPU 推理。

多模态嵌入模型

加载模型

加载多模态嵌入模型与加载纯文本模型完全一样:

from sentence_transformers import SentenceTransformer

model = SentenceTransformer("Qwen/Qwen3-VL-Embedding-2B")

如果模型的集成拉取请求仍未合并,某些模型目前可能需要一个 revision 参数。一旦它们被合并,你就可以像上面那样,无需指定 revision 即可加载它们。

模型会自动检测它支持哪些模态,因此无需额外配置。如果你想控制图像分辨率或模型精度等内容,请参阅 Processor 和 Model kwargs。

编码图像

加载多模态模型后,model.encode() 可以接受图像和文本。图像可以作为 URL、本地文件路径或 PIL Image 对象提供(所有接受的格式见支持的输入类型):

from sentence_transformers import SentenceTransformer

model = SentenceTransformer("Qwen/Qwen3-VL-Embedding-2B")

# Encode images from URLs
img_embeddings = model.encode([
    "https://hf-mirror.com/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg",
    "https://hf-mirror.com/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")

# Encode images
img_embeddings = model.encode([
    "https://hf-mirror.com/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg",
    "https://hf-mirror.com/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]])

正如预期的那样,“A green car parked in front of a yellow building”与汽车图像最相似(0.51),而“A bee on a pink flower”与蜜蜂图像最相似(0.67)。难负例(“A red car driving on a highway”、“A wasp on a wooden table”)正确地获得了较低的分数。

你可能会注意到,即使是最佳匹配分数(0.51、0.67)也不是很接近 1.0。这是由于模态鸿沟:不同模态的嵌入往往聚集在空间的不同区域。跨模态相似度通常低于模态内相似度(例如,文本到文本),但相对顺序得以保留,因此检索仍然有效。

编码查询和文档

对于检索任务,encode_query() 和 encode_document() 是推荐使用的方法。许多检索模型会根据输入是查询还是文档来前置不同的指令提示,类似于聊天模型可能根据目标应用不同的系统提示。模型作者可以在模型配置中指定他们的提示,encode_query() / encode_document() 会自动加载并应用正确的提示:

  • encode_query() 使用模型的 "query" 提示(如果可用)并设置 task="query"。
  • encode_document() 使用来自 "document"、"passage" 或 "corpus" 的第一个可用提示,并设置 task="document"。

在底层,两者都是 encode() 的薄封装,它们只是替你处理提示选择。以下是跨模态检索的样子:

from sentence_transformers import SentenceTransformer

model = SentenceTransformer("Qwen/Qwen3-VL-Embedding-2B")

# 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://hf-mirror.com/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg",
    "https://hf-mirror.com/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]])

这些方法接受与 encode() 相同的输入类型(图像、URL、多模态字典等)并传递相同的参数。对于没有专门查询/文档提示的模型,它们的行为与 encode() 相同。

多模态重排序模型

多模态重排序(CrossEncoder)模型对输入对之间的相关性进行评分,其中每个元素可以是文本、图像、音频、视频或其组合。它们在质量上往往优于嵌入模型,但由于逐个处理每一对输入而速度较慢。目前可用的预训练多模态重排序器专注于文本和图像输入,但该架构支持底层模型能够处理的任何模态。

对混合模态文档进行排序

rank() 方法根据查询对一组文档进行评分和排序,支持混合模态:

from sentence_transformers import CrossEncoder

model = CrossEncoder("Qwen/Qwen3-VL-Reranker-2B")

query = "A green car parked in front of a yellow building"
documents = [
    # Image documents (URL or local file path)
    "https://hf-mirror.com/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg",
    "https://hf-mirror.com/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://hf-mirror.com/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 和 supports() 检查重排序器支持哪些模态:

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

预测配对分数

你还可以使用 predict() 获取特定输入对的原始相关性分数:

from sentence_transformers import CrossEncoder

model = CrossEncoder("jinaai/jina-reranker-m0", trust_remote_code=True)

scores = model.predict([
    ("A green car", "https://hf-mirror.com/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg"),
    ("A bee on a flower", "https://hf-mirror.com/datasets/huggingface/documentation-images/resolve/main/bee.jpg"),
    ("A green car", "https://hf-mirror.com/datasets/huggingface/documentation-images/resolve/main/bee.jpg"),
])
print(scores)
# [0.9389156  0.96922314 0.46063158]

检索并重排序

常见模式是使用嵌入模型进行快速初步检索,然后用重排序器对顶部结果进行精排:

from sentence_transformers import SentenceTransformer, CrossEncoder

# Step 1: Retrieve with an embedding model
embedder = SentenceTransformer("Qwen/Qwen3-VL-Embedding-2B")

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)

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 对象
- 文件路径(例如 "./photo.jpg")
- URL(例如 "https://.../image.jpg")
- Numpy 数组、torch 张量
音频 - 文件路径(例如 "./audio.wav")
- URL(例如 "https://.../audio.wav")
- Numpy/torch 数组
- 带有 "array" 和 "sampling_rate" 键的字典
- torchcodec.AudioDecoder 实例
视频 - 文件路径(例如 "./video.mp4")
- URL(例如 "https://.../video.mp4")
- Numpy/torch 数组
- 带有 "array" 和 "video_metadata" 键的字典
- torchcodec.VideoDecoder 实例
多模态 - 将模态名称映射到值的字典,
例如 {"text": "a caption", "image": "https://.../image.jpg"}
有效键:"text"、"image"、"audio"、"video"
消息 - 带有 "role" 和 "content" 键的消息字典列表,
例如 [{"role": "user", "content": [...]}]

检查模态支持

你可以使用 modalities 属性和 supports() 方法检查模型支持哪些模态:

from sentence_transformers import SentenceTransformer

model = SentenceTransformer("Qwen/Qwen3-VL-Embedding-2B")

# 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

"message" 模态表示模型接受带有交错内容的聊天风格消息输入。实际上,你很少需要直接使用它。当你传入字符串、URL 或多模态字典时,模型会在内部将它们转换为适当的消息格式。Sentence Transformers 支持两种消息格式:

  • 结构化(大多数 VLM,例如 Qwen3-VL):内容是带类型的字典列表,例如 [{"type": "text", "text": "..."}, {"type": "image", "image": ...}]
  • 扁平(例如 Deepseek-V3):内容是直接的值,例如 "some text"

该格式会从模型的聊天模板中自动检测。

由于所有输入在内部都会被转换为相同的消息格式,你可以在单次 encode() 调用中混合使用不同的输入类型:

embeddings = model.encode([
    # A text input
    "A green car parked in front of a yellow building",
    # An image input (URL)
    "https://hf-mirror.com/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg",
    # A combined text + image input
    {
        "text": "A car in a European city",
        "image": "https://hf-mirror.com/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg",
    },
])

如果你需要传入原始消息输入,请点击此处

如果模型不遵循这两种格式,而你需要完全控制,你可以直接传入带有 role 和 content 键的原始消息字典:

embeddings = model.encode([
    [
        {
            "role": "user",
            "content": [
                {"type": "image", "image": "https://hf-mirror.com/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg"},
                {"type": "text", "text": "Describe this vehicle."},
            ],
        }
    ],
])

这会绕过自动格式转换,将消息直接传给处理器的 apply_chat_template()。

处理器与模型 kwargs

你可能想要控制图像分辨率边界或模型精度。在加载模型时使用 processor_kwargs 和 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},
)
  • processor_kwargs 控制输入如何被预处理(例如图像分辨率边界)。更高的 max_pixels 意味着更高质量,但也会占用更多内存和计算资源。这些参数会直接传给 AutoProcessor.from_pretrained(...)。
  • model_kwargs 控制底层模型如何加载(例如精度、注意力实现)。这些参数会直接传给相应的 AutoModel.from_pretrained(...) 调用(例如 AutoModel、AutoModelForCausalLM、AutoModelForSequenceClassification 等,具体取决于模型模块的配置)。

有关这些 kwargs 的更多细节,请参阅 SentenceTransformer API 参考文档。

在 Sentence Transformers v5.4 中,tokenizer_kwargs 已重命名为 processor_kwargs,以反映多模态模型使用的是处理器而非仅分词器。旧名称仍被接受,但已弃用。

支持的模型

以下是 v5.4 中支持的多模态模型,也可在 v5.4 集成合集 中找到:

支持的多模态嵌入模型

模型 参数 模态 修订版本
Qwen/Qwen3-VL-Embedding-2B 2B 文本、图像、视频 无需 revision
Qwen/Qwen3-VL-Embedding-8B 8B 文本、图像、视频 无需 revision
nvidia/llama-nemotron-embed-vl-1b-v2 1.7B 文本、图像 无需 revision
nvidia/omni-embed-nemotron-3b 4.7B 文本、图像 无需 revision
LCO-Embedding/LCO-Embedding-Omni-3B 5B 文本、图像、音频、视频 无需 revision
LCO-Embedding/LCO-Embedding-Omni-7B 9B 文本、图像、音频、视频 无需 revision
BidirLM/BidirLM-Omni-2.5B-Embedding 2.5B 文本、图像、音频 无需 revision
BAAI/BGE-VL-base 0.1B 文本、图像 无需 revision
BAAI/BGE-VL-large 0.4B 文本、图像 无需 revision
BAAI/BGE-VL-MLLM-S1 8B 文本、图像 无需 revision
BAAI/BGE-VL-MLLM-S2 8B 文本、图像 无需 revision
BAAI/BGE-VL-v1.5-zs 8B 文本、图像 无需 revision
BAAI/BGE-VL-v1.5-mmeb 8B 文本、图像 无需 revision
BAAI/BGE-VL-Screenshot 4B 文本、图像 无需 revision
royokong/e5-v 8B 文本、图像 无需 revision
eagerworks/eager-embed-v1 4B 文本、图像 revision="refs/pr/2"
nomic-ai/nomic-embed-multimodal-3b 5B 文本、图像 revision="refs/pr/4"
nomic-ai/nomic-embed-multimodal-7b 9B 文本、图像 revision="refs/pr/3"
Haon-Chen/e5-omni-3B 5B 文本、图像、音频、视频 revision="refs/pr/2"
Haon-Chen/e5-omni-7B 9B 文本、图像、音频、视频 revision="refs/pr/1"

支持的多模态重排序模型

模型 参数量 模态 版本
Qwen/Qwen3-VL-Reranker-2B 2B 文本、图像、视频 无需 revision
Qwen/Qwen3-VL-Reranker-8B 8B 文本、图像、视频 无需 revision
nvidia/llama-nemotron-rerank-vl-1b-v2 2B 文本、图像 无需 revision
jinaai/jina-reranker-m0 2B 文本、图像 无需 revision

纯文本重排序模型(同样新增于 v5.4)

模型 参数量 版本
Qwen/Qwen3-Reranker-0.6B 0.6B 无需 revision
Qwen/Qwen3-Reranker-4B 4B 无需 revision
Qwen/Qwen3-Reranker-8B 8B 无需 revision
mixedbread-ai/mxbai-rerank-base-v2 0.5B 无需 revision
mixedbread-ai/mxbai-rerank-large-v2 2B 无需 revision
ContextualAI/ctxl-rerank-v2-instruct-multilingual-1b 1B revision="refs/pr/2"
ContextualAI/ctxl-rerank-v2-instruct-multilingual-2b 3B revision="refs/pr/1"
ContextualAI/ctxl-rerank-v2-instruct-multilingual-6b 7B revision="refs/pr/1"

点击此处查看纯文本重排序器使用示例

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 模型

较旧的 CLIP 模型继续受到支持:

模型 ImageNet 零样本 Top-1 准确率 说明
sentence-transformers/clip-ViT-L-14 75.4
sentence-transformers/clip-ViT-B-16 68.1
sentence-transformers/clip-ViT-B-32 63.3
sentence-transformers/clip-ViT-B-32-multilingual-v1 N/A 多语言文本编码器,支持 50+ 种语言

这些简单的 CLIP 模型在低资源硬件上依然表现良好。

点击此处查看 CLIP 使用示例

from sentence_transformers import SentenceTransformer

model = SentenceTransformer("sentence-transformers/clip-ViT-L-14")

images = [
    "https://hf-mirror.com/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg",
    "https://hf-mirror.com/datasets/huggingface/documentation-images/resolve/main/bee.jpg",
    "https://hf-mirror.com/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"

其他资源

文档

训练

要了解如何在你自己的数据上微调这些多模态模型,请参阅配套博客文章:使用 Sentence Transformers 训练和微调多模态嵌入与重排序模型。

HF Mirror Hub

配套博客文章

本文的训练配套内容及相关的 Sentence Transformers 指南:

来源:Hugging Face Blog · hf-mirror.com