- 人工智能
- NLP
- Embedding
- 微调
【免费下载链接】sentence-transformers
State-of-the-Art Embeddings, Retrieval, and Reranking
本文是 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_path | str | None | 磁盘路径则从本地加载;否则尝试下载预训练多向量模型;再失败则尝试用该名称从 Hugging Face Hub 构造模型 |
modules | list[nn.Module] | None | 按顺序串行调用的 torch 模块列表,可用于从零搭建自定义多向量模型 |
device | "cuda"/"cpu"/"mps"/"npu" | None | 计算设备;为None时自动检测可用 GPU |
prompts | dict[str, str] | None | 标准 prompts 字典,由 encode 方法前置到输入。ColBERT 风格模型需提供{"query": "[Q] ", "document": "[D] "}(或模型自身的前缀 token) |
default_prompt_name | str | None | 默认使用的 prompt 名称;未设置则不应用任何 prompt |
cache_folder | str | None | 模型存储路径,也可通过环境变量SENTENCE_TRANSFORMERS_HOME设置 |
trust_remote_code | bool | False | 是否允许加载 Hub 上自带建模代码的自定义模型 |
revision | str | None | 指定的模型版本 |
local_files_only | bool | False | 是否仅使用本地文件 |
token | bool/str | None | Hugging Face 认证 token |
model_kwargs | dict | None | 透传给底层 Transformers 模型的关键字参数 |
processor_kwargs | dict | None | 透传给 HF processor / tokenizer 的关键字参数 |
config_kwargs | dict | None | 透传给 HF config 的关键字参数 |
model_card_data | MultiVectorEncoderModelCardData | None | 模型卡数据对象 |
backend | "torch"/"onnx"/"openvino" | "torch" | 推理后端 |
similarity_fn_name | "maxsim"/"meanmaxsim" | "maxsim" | 相似度函数名 |
注意:query_length、document_length、query_expansion、skiplist_words等长度 / 扩展 / 掩码旋钮并不直接挂在模型上,而是位于底层模块Transformer与MultiVectorMask上,并在保存时写入 checkpoint 配置,见 model.py。
默认模块流水线
从测试 tests/multi_vector_encoder/test_model.py 可以看到,一个从裸 HF backbone 构建的MultiVectorEncoder默认由 4 个模块组成:
Transformer(backbone,产出上下文化 token 嵌入);Dense(token 级投影,将隐藏维度投影到多向量维度,默认输出 128 维,bias=False、activation_function=nn.Identity()、module_input_name="token_embeddings");MultiVectorMask(计算打分掩码,见下节);Normalize(token 级 L2 归一化,module_input_name="token_embeddings")。
默认情况下query_expansion=None(经典 ColBERT 技巧属于显式配方选择而非默认行为),skiplist_words=[](空掩码列表)。你可以在构造时传入modules=...自定义投影(如不同的输出维度)。
编码:encode、encode_query 与 encode_document
为什么检索要用不对称的 query / document 拆分
多向量检索中,查询与文档的预处理策略刻意不同,这正是encode_query与encode_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/prompt | None | 使用的 prompt;prompt字符串优先于prompt_name |
batch_size | 32 | 前向传播批大小,必须为正整数 |
show_progress_bar | None(自动) | 是否显示进度条 |
output_value | "token_embeddings" | "token_embeddings"(默认)返回按打分掩码切片的逐 token 嵌入;None返回原始逐输入模块输出字典(含token_embeddings、attention_mask及自定义模块写入的额外键),不做归一化与转换 |
convert_to_numpy | False | 为True时返回numpy.ndarray列表并逐批移到 CPU;多进程编码(pool或 device 列表)总是返回 CPU 结果 |
device | None | 单个设备、设备列表(多进程编码)或None(使用模型当前设备) |
normalize_embeddings | False | 返回前对每个 token 向量做 L2 归一化;流水线中已有 token 级Normalize时无操作 |
pool | None | 通过start_multi_process_pool创建的多进程池 |
chunk_size | None | 多进程编码的块大小 |
token_pooling | None | 按次调用的 token 池化,应用于tasks中匹配的 task(默认仅 document);若模型流水线已内置池化则会叠加 |
task | None | "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.modalities与model.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.py与text_similarity_map.py利用 MaxSim 分数可追溯到具体 token / 图像 patch 的特性做可解释性可视化;token_pooling.py用HierarchicalTokenPooling压缩文档索引。
前缀 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 ColBERT:
config.json中architectures == ["HF_ColBERT"]触发专用路径——从仓库根的linear.weight读取内联投影权重、从artifact.metadata恢复特殊 token / 长度 / 扩展配置,并默认以string.punctuation预置 skiplist(与原始mask_punctuation默认行为一致);旧格式的query_expansion平面字段(do_query_expansion、attend_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_only | True/False | 是否不访问 Hub 查询数据集与基础模型信息 |
generate_widget_examples | True/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后模型卡可自动记录碳排放信息。
实战要点与进阶阅读
- 检索一定用不对称 API:
encode_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
相关推荐
sentence-transformers MultiVectorEncoder 使用指南:MaxSim 多向量编码、视觉文档检索与模型加载实战
sentence transformers MultiVectorEncoder 使用指南:MaxSim 多向量编码、视觉文档检索与模型加载实战 本文以 doc
人工智能NLPEmbedding微调sentence-transformers 多向量编码器评估指南:MultiVectorEncoder 的 MaxSim 评测体系与 NanoBEIR 实战
sentence transformers 多向量编码器评估指南:MultiVectorEncoder 的 MaxSim 评测体系与 NanoBEIR 实战 导
人工智能NLPEmbedding微调基于 CocoIndex 与 ColPali 多向量检索的图像搜索:Qdrant MaxSim 延迟交互式匹配实战
基于 CocoIndex 与 ColPali 多向量检索的图像搜索:Qdrant MaxSim 延迟交互式匹配实战 本文以 CocoIndex 仓库中的 ima
人工智能大模型RAGAI AgentAgent 记忆数据工程流处理
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考