Hugging Face 推出多模态句嵌入模型

Hugging Face Blog··作者 Tom Aarsen

关键信息

如 Qwen3-VL-Embedding-2B 模型需要至少 8GB 显存的 GPU;CPU 推理速度极慢。安装时需根据所需模态选择额外依赖(例如 `sentence-transformers[image]`)。目前加载这些模型仍需指定 `revision` 参数,因为相关集成请求尚未合并。

资讯摘要

Hugging Face 发布了新的多模态模型,可将文本和图像映射到统一的嵌入空间中,从而实现跨不同输入类型之间的相似性比较。用户可以使用同一个模型编码图像和文本,并直接计算跨模态相似度。例如,文本查询可以匹配图像文档,系统会根据语义对齐正确识别相关结果。

安装时需为每种模态(图像、音频、视频)安装特定扩展包。如 Qwen3-VL-Embedding-2B 这类模型需要大量显存资源,而 CLIP 等轻量模型更适合 CPU 使用。博客提供了清晰的代码示例,展示如何加载模型、编码混合输入以及计算文本与图像嵌入之间的相似度。

Hugging Face 推出多模态句嵌入模型

资讯正文

多模态嵌入模型将来自不同模态的输入映射到共享的嵌入空间,而多模态重排序模型则对混合模态对的相关性进行评分。这为视觉文档检索、跨模态搜索以及多模态RAG(检索增强生成)流程等应用场景打开了可能性。

什么是多模态模型?

安装说明

多模态嵌入模型

多模态重排序模型

检索与重排序

输入格式与配置

支持的模型

额外资源

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

同样,传统重排序模型(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]"

基于视觉语言模型(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="refs/pr/23")

目前必须指定revision参数,因为这些模型的集成拉取请求仍在处理中。一旦合并完成,您将无需再指定版本即可加载。

该模型会自动检测其支持的模态,因此无需额外配置。如果想控制图像分辨率或模型精度等参数,请参考Processor和Model的kwargs选项。

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

# 从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)

你可以计算文本嵌入和图像嵌入之间的相似性,因为该模型会将两者映射到同一空间中:

# 编码图像

# 编码文本查询(每张图像对应一个匹配项和一个困难负样本)

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 不够近。这是由于模态差距造成的:不同模态的嵌入往往在空间中聚集成不同的区域。跨模态相似度通常低于同模态的相似度(例如文本到文本),但相对排序得以保留,因此检索仍然非常有效。

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

encode_query() 使用模型的 "query" 提示(如果可用),并设置 task="query"。

encode_document() 使用第一个可用的提示,顺序为 "document"、"passage" 或 "corpus",并设置 task="document"。

在底层,这两个方法都是 encode() 的轻量封装,它们只是为你处理提示选择的问题。以下是跨模态检索的实际操作方式:

# 使用查询提示编码文本查询

query_embeddings = model.encode_query([

"帮我找一张车辆停在建筑物附近的照片",

"给我看一张传粉昆虫的图片",

# 使用文档提示编码文档截图

doc_embeddings = model.encode_document([

# 计算相似性

similarities = model.similarity(query_embeddings, doc_embeddings)

# tensor([[0.3907, 0.1490],

# [0.1235, 0.4872]])

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

多模态重排序器(CrossEncoder)模型用于评分输入对之间的相关性,其中每个元素可以是文本、图像、音频、视频或组合形式。这类模型在质量上通常优于嵌入模型,但速度较慢,因为它们需要逐对处理每组输入。目前可用的预训练多模态重排序器主要聚焦于文本和图像输入,但其架构支持任何底层模型能够处理的模态。

该方法对文档列表进行评分和排序,以匹配查询,并支持混合模态:

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 = [

# 图像文档(URL 或本地文件路径)

# 文本文档

"A vintage Volkswagen Beetle painted in bright green sits in a driveway.",

# 文本+图像组合文档

{

"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")))

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

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

# 第一步:使用嵌入模型检索

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]

# 第二步:使用重排序模型对前 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)

print(f"{rank['score']:.4f}\t{top_k_documents[rank['corpus_id']]}")

由于语料库嵌入已经预先计算好,即使在数百万文档中进行初始检索也很快。随后的重排序器会对较小的候选集提供更精确的评分。

多模态模型可以接受多种输入格式。以下是你可以传递给 model.encode() 的内容总结:

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

# 列出所有支持的模态

# 检查特定模态

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([

# 文本输入

# 图像输入(URL)

# 文本+图像组合输入

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

如果某个模型不遵循上述任一格式,并且你需要完全控制,可以直接传递包含 role 和 content 键的原始消息字典:

"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},

revision="refs/pr/23",

processor_kwargs

processor_kwargs 控制输入如何预处理(例如图像分辨率限制)。更高的 max_pixels 值意味着更高画质,但也占用更多内存和计算资源。这些参数会直接传递给 AutoProcessor.from_pretrained(...)。

model_kwargs 控制底层模型如何加载(例如精度、注意力实现方式)。这些参数会直接传递给相应的 AutoModel.from_pretrained(...) 调用(例如 AutoModel、AutoModelForCausalLM、AutoModelForSequenceClassification 等,具体取决于模型模块的配置)。

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

在 Sentence Transformers v5.4 中,tokenizer_kwargs 已更名为 processor_kwargs。

为了体现多模态模型使用处理器而非仅使用分词器的特点,旧名称仍被接受但已弃用。

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

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

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)

# [ 7.3077507 -2.6217823 8.724761 -2.2488995]

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 模型在资源受限的硬件上依然表现良好。

点击此处查看 CLIP 的使用示例

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

images = [

"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)

# tensor([[0.2208, 0.1042, 0.0617, 0.0907], 第一张图片(汽车)最相似于 "A green car"

# [0.1205, 0.2303, 0.0632, 0.0917], 第二张图片(蜜蜂)最相似于 "A bee on a flower"

# [0.1107, 0.0196, 0.2425, 0.1162]]) 第三张图片(多只猫)最相似于 "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 > 训练示例

来源与参考

  1. 原始链接
  2. Multimodal Embedding & Reranker Models with Sentence Transformers

收录于 2026-04-10