多模态向量化:Sentence Transformers 实战
展示如何用 Sentence Transformers 做跨模态嵌入和排序,对 RAG、搜索等应用开发直接有用。
展示如何用 Sentence Transformers 做跨模态嵌入和排序,对 RAG、搜索等应用开发直接有用。
多模态嵌入模型将不同模态的输入映射到共享的嵌入空间,而多模态重排序模型则对混合模态输入对的相关性进行评分。由此可以实现视觉文档检索、跨模态搜索和多模态 RAG 流水线等应用场景。
如果你想训练自己的多模态模型,请参阅配套博文:使用 Sentence Transformers 训练和微调多模态嵌入与重排序模型。
什么是多模态模型?
多模态嵌入模型 加载模型 编码图像 跨模态相似度 编码查询和文档
跨模态相似度
编码查询和文档
多模态重排序模型 对混合模态文档进行排序 预测输入对分数
对混合模态文档进行排序
预测输入对分数
输入格式与配置 支持的输入类型 检查模态支持情况 Processor 和模型 kwargs
支持的输入类型
检查模态支持情况
Processor 和模型 kwargs
传统嵌入模型将文本转换为固定大小的向量。多模态嵌入模型在此基础上更进一步,将不同模态的输入(文本、图像、音频或视频)映射到共享的嵌入空间。这意味着,你可以使用已经熟悉的相似度函数,将文本查询与图像文档进行比较,反之亦然。
类似地,传统的重排序(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]"
基于 VLM 的模型(例如 Qwen3-VL-2B)需要配备至少约 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 和模型 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://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")
# 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]])
不出所料,"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://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]])
这些方法接受与 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://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)
"""
重新排序模型正确地将汽车图像(文档 0)识别为最相关的结果,其次是关于欧洲城市中汽车的组合文本+图像文档(文档 3)。蜜蜂图像(文档 1)得分最低。请注意,模态差异可能会影响绝对分数:文本-图像对的分数范围可能与文本-文本或图像-图像对的分数范围不同。
你也可以使用 modalities 和 supports() 方法检查重新排序模型支持的模态,就像使用嵌入模型一样:
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([
("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]
一种常见的模式是先使用嵌入模型进行快速初始检索,然后用重新排序模型优化前k个结果:
from sentence_transformers import SentenceTransformer, CrossEncoder
# 第1步:使用嵌入模型检索
embedder = SentenceTransformer("Qwen/Qwen3-VL-Embedding-2B")
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步:用重新排序模型对top-k结果重新排序
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() 的内容总结:
你可以使用 modalities 属性和 supports() 方法检查模型支持的模态:
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("Qwen/Qwen3-VL-Embedding-2B")
# 列出所有支持的模态
print(model.modalities)
# ['text', 'image', 'video', 'message']
# 检查特定模态
print(model.supports("image"))
# True
print(model.supports("audio"))
# False
"message"模态表示模型接受聊天风格的消息输入,支持交错的内容。实际上,你很少需要直接使用它。当你传递字符串、URL或多模态字典时,模型会在内部将它们转换为适当的消息格式。Sentence Transformers 支持两种消息格式:
结构化(大多数视觉语言模型,如 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 和 content 键的原始消息字典:
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()。
你可能想要控制图像分辨率范围或模型精度。加载模型时使用 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 集成合集中获得:
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 模型在低资源硬件上仍然表现良好。
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)
Sentence Transformer > 使用方法
Sentence Transformer > 预训练模型
Cross Encoder > 使用方法
Cross Encoder > 预训练模型
要了解如何使用自己的数据微调这些多模态模型,请参阅配套博文:使用 Sentence Transformers 训练和微调多模态嵌入与重排序模型。
Sentence Transformer > 训练概述
Sentence Transformer > 训练示例
Cross Encoder > 训练概述
Cross Encoder > 训练示例
Sparse Encoder > 训练概述
Sparse Encoder > 训练示例
Hub 上的 Sentence Transformers 模型
Hub 上的 Sentence Transformers 数据集
v5.4 集成合集
本文的配套训练文章及相关 Sentence Transformers 指南:
使用 Sentence Transformers 训练和微调多模态嵌入与重排序模型:本文直接配套的训练文章,包含视觉文档检索的完整演练。
使用 Sentence Transformers 训练和微调嵌入模型:面向纯文本双编码器嵌入模型的通用训练指南。
使用 Sentence Transformers 训练和微调重排序模型:Cross Encoder(重排序器)训练,适用于纯文本和多模态重排序器。
使用 Sentence Transformers 训练和微调稀疏嵌入模型:用于稀疏检索的 SPLADE 训练。
🪆 Matryoshka 嵌入模型简介:可变大小的嵌入;配套训练文章中也将其应用于多模态模型。
使用 Sentence Transformers 将 Static Embedding Models 的训练速度提升 400 倍:对 CPU 友好的文本嵌入模型。
通过二值和标量嵌入量化,显著提升检索速度并降低成本:对嵌入向量进行训练后压缩。
本文提及的模型 10
本文提及的合集 1
博客中的更多文章
使用 Sentence Transformers 训练和微调多模态嵌入与重排序模型
Falcon 2:一个拥有 110 亿参数的预训练语言模型和 VLM,使用超过 50000 亿个 token 和 11 种语言训练
很棒的 sentenceTransforners 多模态入门介绍!谢谢!
· 注册或登录后发表评论
本文提及的模型 10
本文提及的合集 1