news 2026/9/20 22:16:54

sentence-transformers MultiVectorEncoder 完全指南:基于 MaxSim 晚期交互的多向量检索模型

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
sentence-transformers MultiVectorEncoder 完全指南:基于 MaxSim 晚期交互的多向量检索模型
  • 人工智能
  • NLP
  • Embedding
  • 微调

【免费下载链接】sentence-transformers

State-of-the-Art Embeddings, Retrieval, and Reranking

项目地址:https://gitcode.com/gh_mirrors/se/sentence-transformers
点击查看免费下载

本文是 sentence-transformers 仓库中MultiVectorEncoder(多向量编码器,又称 ColBERT / 晚期交互模型)的 API 级使用指南。它以 model.md 为骨架,结合 model.py 的源码实现,讲解如何加载多向量模型、用encode_query/encode_document生成逐 token 向量、用similarity/similarity_pairwise进行 MaxSim 打分,以及如何使用模型卡数据类MultiVectorEncoderModelCardData。读完本文,你将掌握多向量检索的完整调用链,并能直接上手语义检索、视觉文档检索(ColPali 风格)等实战场景。

MultiVectorEncoder:一句话理解它是什么

MultiVectorEncoder是 sentence-transformers 中用于加载或创建多向量 / 晚期交互(late-interaction,ColBERT 风格)嵌入模型的类,定义于 sentence_transformers/multi_vector_encoder/model.py。

它与传统的SentenceTransformer有本质区别:

  • SentenceTransformer对每个输入只产生一个向量(句子级嵌入);
  • MultiVectorEncoder对每个输入产生一个向量序列,即每个 token 一个向量
  • 查询与文档之间的打分使用MaxSim 晚期交互算子:对每个查询 token,取它与所有文档 token 相似度的最大值,再对所有查询 token 求和。

这一设计保留了单向量模型丢弃的token 级匹配信息,通常能带来更强的检索效果,代价是更大的索引占用(每个文档都存储为一串 token 向量而非单个向量)。同时,它是视觉文档检索(ColPali 风格)的事实标准:文本查询可以直接匹配页面图像,完全跳过 OCR,相关说明见 docs/multi_vector_encoder/usage/usage.rst。

核心构造参数

MultiVectorEncoder.__init__的核心参数如下(完整签名见 model.py):

参数类型 / 取值默认值说明
model_name_or_pathstrNone磁盘路径则从本地加载;否则尝试下载预训练多向量模型;再失败则尝试用该名称从 Hugging Face Hub 构造模型
moduleslist[nn.Module]None按顺序串行调用的 torch 模块列表,可用于从零搭建自定义多向量模型
device"cuda"/"cpu"/"mps"/"npu"None计算设备;为None时自动检测可用 GPU
promptsdict[str, str]None标准 prompts 字典,由 encode 方法前置到输入。ColBERT 风格模型需提供{"query": "[Q] ", "document": "[D] "}(或模型自身的前缀 token)
default_prompt_namestrNone默认使用的 prompt 名称;未设置则不应用任何 prompt
cache_folderstrNone模型存储路径,也可通过环境变量SENTENCE_TRANSFORMERS_HOME设置
trust_remote_codeboolFalse是否允许加载 Hub 上自带建模代码的自定义模型
revisionstrNone指定的模型版本
local_files_onlyboolFalse是否仅使用本地文件
tokenbool/strNoneHugging Face 认证 token
model_kwargsdictNone透传给底层 Transformers 模型的关键字参数
processor_kwargsdictNone透传给 HF processor / tokenizer 的关键字参数
config_kwargsdictNone透传给 HF config 的关键字参数
model_card_dataMultiVectorEncoderModelCardDataNone模型卡数据对象
backend"torch"/"onnx"/"openvino""torch"推理后端
similarity_fn_name"maxsim"/"meanmaxsim""maxsim"相似度函数名

注意:query_lengthdocument_lengthquery_expansionskiplist_words等长度 / 扩展 / 掩码旋钮并不直接挂在模型上,而是位于底层模块TransformerMultiVectorMask上,并在保存时写入 checkpoint 配置,见 model.py。

默认模块流水线

从测试 tests/multi_vector_encoder/test_model.py 可以看到,一个从裸 HF backbone 构建的MultiVectorEncoder默认由 4 个模块组成:

  1. Transformer(backbone,产出上下文化 token 嵌入);
  2. Dense(token 级投影,将隐藏维度投影到多向量维度,默认输出 128 维,bias=Falseactivation_function=nn.Identity()module_input_name="token_embeddings");
  3. MultiVectorMask(计算打分掩码,见下节);
  4. Normalize(token 级 L2 归一化,module_input_name="token_embeddings")。

默认情况下query_expansion=None(经典 ColBERT 技巧属于显式配方选择而非默认行为),skiplist_words=[](空掩码列表)。你可以在构造时传入modules=...自定义投影(如不同的输出维度)。

编码:encode、encode_query 与 encode_document

为什么检索要用不对称的 query / document 拆分

多向量检索中,查询与文档的预处理策略刻意不同,这正是encode_queryencode_document存在的意义(见 model.py):

  • encode_query:若未显式指定 prompt,自动使用模型 prompts 字典中的"query"项;设置task="query",从而插入查询前缀 token、使用query_length作为最大序列长度,并在开启query_expansion时将输入扩展到指定长度;
  • encode_document:自动使用"document"/"passage"/"corpus"中第一个可用的 prompt;设置task="document",插入文档前缀 token、使用document_length,并应用文档侧 skiplist(如标点符号)将其从输出中剔除。

文档侧的task与掩码行为由MultiVectorMask模块实现,见 sentence_transformers/multi_vector_encoder/modules/multi_vector_mask.py:

  • 真实 token(tokenizer 的attention_mask)参与打分;task="query"且开启查询扩展时,扩展位置也参与打分——这是 ColBERT 的核心技巧:即使 Transformer 的注意力没有看到扩展 token,它们也贡献 MaxSim;
  • skiplist_tasks(默认仅"document",无 task 的输入视为 document)中的 token 应用 skiplist 剔除;
  • 非 query 任务且设置了keep_only_token_ids时,额外限制为这些 token ID(典型场景是 ColPali 风格的图像 patch token,可让文档索引体积减半)。

encode 的完整参数

encode是底层通用方法,encode_query/encode_document都委托给它(model.py)。核心参数:

参数默认值说明
inputs必填字符串、字符串列表或多模态输入(dict、图像、数组)
prompt_name/promptNone使用的 prompt;prompt字符串优先于prompt_name
batch_size32前向传播批大小,必须为正整数
show_progress_barNone(自动)是否显示进度条
output_value"token_embeddings""token_embeddings"(默认)返回按打分掩码切片的逐 token 嵌入;None返回原始逐输入模块输出字典(含token_embeddingsattention_mask及自定义模块写入的额外键),不做归一化与转换
convert_to_numpyFalseTrue时返回numpy.ndarray列表并逐批移到 CPU;多进程编码(pool或 device 列表)总是返回 CPU 结果
deviceNone单个设备、设备列表(多进程编码)或None(使用模型当前设备)
normalize_embeddingsFalse返回前对每个 token 向量做 L2 归一化;流水线中已有 token 级Normalize时无操作
poolNone通过start_multi_process_pool创建的多进程池
chunk_sizeNone多进程编码的块大小
token_poolingNone按次调用的 token 池化,应用于tasks中匹配的 task(默认仅 document);若模型流水线已内置池化则会叠加
taskNone"query""document",决定前缀 / 长度 / 掩码策略

两个值得注意的行为(均有源码与报错支撑):

  • 由于多向量嵌入长度可变,无法堆叠encode没有convert_to_tensor参数——传了会直接报错,提示改用convert_to_numpy=True(model.py);
  • 默认返回值为每个输入一个 2D 张量,形状(num_tokens_i, embedding_dim);传入单个字符串时外层列表会被解包,直接返回裸 2D 张量。

快速上手示例

from sentence_transformers import MultiVectorEncoder # 1. 加载预训练多向量模型 model = MultiVectorEncoder("lightonai/LateOn") queries = ["What is the capital of France?"] documents = [ "Paris is the capital of France.", "Berlin is the capital of Germany.", ] # 2. 编码查询与文档(注意不对称的 encode_query / encode_document 拆分) query_embeddings = model.encode_query(queries) document_embeddings = model.encode_document(documents) # 每个元素是形状为 (num_tokens_i, embedding_dim) 的 2D 张量,长度随输入变化 print(query_embeddings[0].shape) # torch.Size([10, 128]) # 3. 用 MaxSim 打分 scores = model.similarity(query_embeddings, document_embeddings) print(scores) # tensor([[9.1129, 8.8769]], device='cuda:0')

打分:similarity 与 similarity_pairwise

model.similarity返回全对(all-pairs)MaxSim 分数矩阵,model.similarity_pairwise返回配对分数向量(model.py):

scores = model.similarity(query_embeddings, document_embeddings) print(scores.shape) # torch.Size([1, 2]),1 个查询对 2 个文档 pairwise = model.similarity_pairwise([query_embeddings[0], query_embeddings[0]], document_embeddings) print(pairwise.shape) # torch.Size([2])

两者都遵循模型的similarity_fn_name

  • "maxsim"(默认):sum_i max_j (a_i . b_j),即对每个查询 token 取与任意文档 token 的最大相似度再求和;
  • "meanmaxsim":MaxSim 除以查询的真实 token 数,分数落在逐 token 相似度区间(归一化嵌入约为[-1, 1]),与查询长度无关。若模型用长度归一化打分训练,应设为"meanmaxsim"以保持训练 / 评估一致。

similarity_fn_name惰性初始化的属性(model.py):首次访问时若未显式设置则取"maxsim"。setter 会校验取值,只接受"maxsim""meanmaxsim";传"xtr"会明确报错——XTR 是训练期打分,其全局 top-k 依赖 batch 构成,不适用于模型级相似度;传cosine/dot等单向量相似度也会被拒绝,因为它们无法作用于长度参差的逐 token 嵌入。

底层 MaxSim 实现

打分的底层实现位于 sentence_transformers/util/similarity.py 的maxsim/maxsim_pairwise/mean_maxsim/mean_maxsim_pairwise函数。源码揭示了几个关键的工程细节:

  • 打分在文档所在设备上进行(文档是大头,查询便宜可搬移);可通过device参数指定打分设备;
  • 长文本按chunk_elements元素预算分块打分,默认 1 亿元素预算(约 400 MB,bf16 / fp16 减半),防止中间张量撑爆显存;
  • 被掩码的文档 token 用dtype 最小值填充参与 max 而不是乘 0,避免 padding token 因负相似度而"赢得"max(与 PyLate 的乘 0 方案相比更严谨,见 similarity.py);
  • 查询 token 的求和始终在 float32 中累积——MaxSim 分数量级可达 O(查询 token 数),bf16 网格太粗会淹没相近分数;
  • 完全无有效 token 的文档会得到约_EMPTY_DOCUMENT_SCORE的哨兵分数,排在所有真实文档之下。

多模态输入与视觉文档检索

部分多向量模型支持文本之外的输入,最典型的是用于视觉文档检索的页面图像。可使用model.modalitiesmodel.supports()检查模态支持(示例见 docs/multi_vector_encoder/usage/usage.rst):

from sentence_transformers import MultiVectorEncoder model = MultiVectorEncoder("vidore/colqwen2.5-v0.2") # 列出所有支持的模态 print(model.modalities) # ['text', 'image'] # 检查特定模态 print(model.supports("image")) # True print(model.supports("audio")) # False

图像文档以 URL、本地路径或 PIL 图像传入,编码方式与文本完全一致,随后用 MaxSim 计算跨模态分数:

from sentence_transformers import MultiVectorEncoder # 1. 加载同时支持文本与图像的模型 model = MultiVectorEncoder("vidore/colqwen2.5-v0.2") queries = [ "What is the variable represented on the y-axis of the graph?", "Total outlay is maximum in which year?", ] # 2. 图像文档以 URL、本地路径或 PIL 图像传入 images = [ "https://huggingface.co/datasets/sentence-transformers/example-documents/resolve/main/doc1.jpg", "https://huggingface.co/datasets/sentence-transformers/example-documents/resolve/main/doc2.jpg", ] # 3. 图像文档与文本文档编码方式相同 query_embeddings = model.encode_query(queries) document_embeddings = model.encode_document(images) # 4. 计算跨模态 MaxSim 分数 scores = model.similarity(query_embeddings, document_embeddings) print(scores)

相关实战脚本可参考 examples/multi_vector_encoder/applications/README.md:semantic_search.py一次性编码语料后用 MaxSim 检索;retrieve_rerank.py用双编码器先召回再用多向量模型精排;heatmap.pytext_similarity_map.py利用 MaxSim 分数可追溯到具体 token / 图像 patch 的特性做可解释性可视化;token_pooling.pyHierarchicalTokenPooling压缩文档索引。

前缀 token 与 prompts

多向量模型的前缀 token 以模型的"query""document"prompts 存储,可以直接检查每个方法前置了什么:

from sentence_transformers import MultiVectorEncoder model = MultiVectorEncoder("lightonai/mLateOn") print(model.prompts) # {'query': '[Q] ', 'document': '[D] '}

不同 checkpoint 的前缀不同:原版 ColBERT 格式的模型复用保留词表项,例如answerdotai/answerai-colbert-small-v1的 prompts 为{'query': '[unused0] ', 'document': '[unused1] '}。而 PyLate v3 与 Stanford-NLP ColBERT 等旧格式 checkpoint 中保存的query_prefix/document_prefix(或artifact.metadata中的query_token_id/doc_token_id)会在加载时自动提升为 prompts,并通过_register_prefix_tokens(model.py)注册为特殊 token——否则像[unused0]这样的保留 token 以文本前置会被切碎成['[', 'unused', '##0', ']'],与训练时的 token 插入行为不一致。

模型加载:五种来源透明兼容

MultiVectorEncoder可以从以下来源透明加载,自动检测格式(对应_get_model_type_load_default_modules的多条加载路径,见 model.py):

from sentence_transformers import MultiVectorEncoder # 1. 本库原生格式(用本库训练的多向量模型;PyLate 也基于同一 schema,checkpoint 可无差别加载) model = MultiVectorEncoder("lightonai/LateOn") model = MultiVectorEncoder("lightonai/mLateOn") model = MultiVectorEncoder("LiquidAI/LFM2-ColBERT-350M") # 2. 部分原生 checkpoint 自带自定义架构代码,需要 trust_remote_code model = MultiVectorEncoder("perplexity-ai/pplx-embed-v1-late-0.6b", trust_remote_code=True) # 3. Stanford-NLP ColBERT 格式:通过 `HF_ColBERT` 架构标记自动检测, # 内联投影权重与特殊 token 从 artifact.metadata 读取 model = MultiVectorEncoder("colbert-ir/colbertv2.0") model = MultiVectorEncoder("answerdotai/answerai-colbert-small-v1") # 4. transformers 原生晚期交互检索器(`*ForRetrieval` 架构,如 ColPali / ColQwen2 / # ColModernVBert)自动检测:投影与归一化在模型内部完成,查询与图像文档由 processor 格式化 model = MultiVectorEncoder("vidore/colqwen2-v1.0-hf") # 5. 裸 Transformer:自动追加随机初始化的投影层,需要训练后才能使用 model = MultiVectorEncoder("answerdotai/ModernBERT-base")

各路径的关键行为:

  • 原生格式:读取config_sentence_transformers.json的模块配置,其中model_type == "ColBERT"的 PyLate v3 保存会被归一化为"MultiVectorEncoder"并按标准配置加载;
  • Stanford-NLP ColBERTconfig.jsonarchitectures == ["HF_ColBERT"]触发专用路径——从仓库根的linear.weight读取内联投影权重、从artifact.metadata恢复特殊 token / 长度 / 扩展配置,并默认以string.punctuation预置 skiplist(与原始mask_punctuation默认行为一致);旧格式的query_expansion平面字段(do_query_expansionattend_to_expansion_tokens等)会被翻译成新的 dict 形态,query_length落入扩展配置,回退到 ColBERT 规范默认值 32;
  • *ForRetrieval架构(如 ColPali / ColQwen2):投影、L2 归一化、padding 置零都在模型内部完成,processor 已内置查询前缀与视觉 prompt,因此只追加MultiVectorMask,无需 Dense / Normalize;
  • 裸 Transformer:追加随机初始化的 token 级投影(输出 128 维)并提示"需要训练才有用",可通过modules=...自定义。

SentenceTransformercheckpoint 转换(_load_converted_modules)时,句子级Pooling与句子级Normalize会被移除,若存在句子级Dense头则重定向到 token 级(保留学习到的投影权重),否则追加随机投影;转换后的模型相似度函数自动设为"maxsim"(model.py)。

MultiVectorEncoderModelCardData:模型卡元数据

MultiVectorEncoderModelCardData是用于生成模型卡的 dataclass,定义于 sentence_transformers/multi_vector_encoder/model_card.py,继承自BaseModelCardData。核心字段:

字段示例说明
language"en"["en", "de", "nl"]模型语言
license"apache-2.0"/"mit"/"cc-by-nc-sa-4.0"模型许可证
model_name"MultiVectorEncoder based on answerdotai/ModernBERT-base"模型的展示名
model_id"tomaarsen/mve-modernbert-base-ms-marco"推送到 Hub 时的模型 ID
train_datasets/eval_datasets[{"name": "MS MARCO", "id": "microsoft/ms_marco"}]训练 / 评估数据集
task_name"semantic search with late interaction"任务人类可读名称,注册模型时若未设置自动取该值
tags["sentence-transformers", "multi-vector", "colbert", "late-interaction"]模型标签
local_files_onlyTrue/False是否不访问 Hub 查询数据集与基础模型信息
generate_widget_examplesTrue/False是否从评估 / 训练数据集生成 widget 示例

模型注册(register_model)时自动设置pipeline_tag="feature-extraction",并将ir_model置为True——晚期交互总是检索任务,base 会按位置从第一个数据集列取查询、第二个列取文档生成 widget 示例。get_model_specific_metadata会写入output_dimensionality(逐 token 向量维度)与人类化的similarity_fn_name"MaxSim"/"MeanMaxSim"),并当query_expansion"fixed"策略时把query_length覆盖为扩展长度(固定扩展把每个查询钉在自己的长度上)。

run_usage_snippet会真实运行一次检索并生成用法片段:第一个示例作为查询、其余作为文档,输出逐 token 形状与相似度矩阵;generate_usage_snippet生成可直接复制的代码块。模型卡模板位于 model_card_template.md。安装codecarbon后模型卡可自动记录碳排放信息。

实战要点与进阶阅读

  • 检索一定用不对称 APIencode_query/encode_document会自动处理前缀 token([Q]/[D])、最大长度与文档侧 skiplist;只有需要显式覆盖task时才直接用encode
  • GPU 上保持张量而非 numpy:默认convert_to_numpy=False时嵌入留在设备上,similarity打分无需搬运,在加速器上收益数倍;语料过大放不进显存时再开convert_to_numpy=True
  • 推理加速:PyTorch 后端可组合 fp16 / bf16、Flash Attention(attn_implementation="flash_attention_2",配合model[0].unpad_inputs控制输入去 padding)、model.compile(dynamic=True);也可切 ONNX / OpenVINO 后端,详见 docs/multi_vector_encoder/usage/efficiency.rst。注意非 attend 查询扩展模型(如 Stanford 系 checkpoint)加载时拒绝 Flash Attention,需用"sdpa"
  • 索引与精排:多向量索引与模型无关(索引存储encode_document的输出),可将本库MultiVectorEncoder与外部晚期交互索引(如 PyLate 的 PLAID)配合使用;也可在 examples/multi_vector_encoder 中查看语义检索、召回 + 精排、可解释性与索引压缩的完整脚本;
  • 训练:训练多向量模型可参考 examples/multi_vector_encoder/training 下的对比损失(contrastive)、缓存对比(cached contrastive)、蒸馏(KD)与 LoRA 示例,对应的CachedMultiVectorMultipleNegativesRankingLoss支持按mini_batch_num_tokens打包以减少训练内存。

进一步阅读:多向量模型的输入格式说明见 docs/input_formats.rst,自定义多向量模型指南见 docs/multi_vector_encoder/usage/custom_models.rst,损失函数总览见 docs/multi_vector_encoder/loss_overview.md。

  • 人工智能
  • NLP
  • Embedding
  • 微调

【免费下载链接】sentence-transformers

State-of-the-Art Embeddings, Retrieval, and Reranking

项目地址:https://gitcode.com/gh_mirrors/se/sentence-transformers
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/20 22:12:59

Yakit 源码安装指南:4 站从克隆仓库到跑通 MITM 渗透测试平台

Yakit 源码安装指南:4 站从克隆仓库到跑通 MITM 渗透测试平台 【免费下载链接】yakit Cyber Security ALL-IN-ONE Platform 项目地址: https://gitcode.com/GitHub_Trending/ya/yakit Yakit 是基于 Electron 的网络安全一体化平台,集成 MITM 交互…

作者头像 李华
网站建设 2026/9/20 22:06:14

R2R本地部署教程:一条命令跑起你的私有AI文档系统

R2R本地部署教程:一条命令跑起你的私有AI文档系统 【免费下载链接】R2R SoTA production-ready AI retrieval system. Agentic Retrieval-Augmented Generation (RAG) with a RESTful API. 项目地址: https://gitcode.com/GitHub_Trending/r2/R2R R2R 是一个…

作者头像 李华
网站建设 2026/9/20 22:04:30

CEF自定义编译包实战:Windows 64位支持MP3/MP4/H264集成指南

简介:面向Windows 64位平台的CEF二进制开发包,基于Chromium 134.0.6998.178内核,特别适配CEF4Delphi等桌面开发框架,专为需要在Delphi或C Builder应用中嵌入现代浏览器界面的开发者提供一站式解决方案。该版本在标准编译基础上额外…

作者头像 李华