sentence-transformers 多向量编码器评估指南:MultiVectorEncoder 的 MaxSim 评测体系与 NanoBEIR 实战

原创2026-09-20 10:34:54935 阅读
文章标签:人工智能NLPEmbedding微调

导读

本指南围绕 sentence-transformers 仓库中多向量编码器(MultiVectorEncoder,即 ColBERT 风格 late-interaction 模型)的评估体系展开,核心场景是检索质量(IR)、重排序(Reranking)、三元组排序与知识蒸馏跟踪等任务的离线评测。读完本文,你将掌握评估器基于 MaxSim 打分端到端运行的原理,能用 nano_beir.py 在 13 个 NanoBEIR 子集上快速评估预训练模型,并熟练配置 dataset_namescorpus_chunk_sizechunk_elements 等关键参数,同时了解全套评估器的数据格式与指标含义。

一、多向量编码器为什么需要专门的评估器

与 SentenceTransformer 将每个输入编码为单个向量不同,多向量编码器把每个输入编码为一串 token 向量(每个 token 一个向量),查询与文档的比较采用 ColBERT 风格的 MaxSim(late-interaction)打分:对每个查询 token,取其与文档所有 token 的最大相似度,再对所有查询 token 求和,即 sum_i max_j (a_i · b_j)。该公式的权威实现见 sentence_transformers/util/similarity.py 中的 maxsim 函数,它在 sentence_transformers/multi_vector_encoder 之外同样被多处复用。

正是这种"查询一组 token 向量 vs 文档一组 token 向量"的形态,决定了普通的余弦/点积评估器无法直接套用——它们定义在单向量之上,对非齐次(ragged)的 per-token 嵌入无法正确计算。sentence_transformers/multi_vector_encoder/evaluation/ 下的评估器把打分链路端到端封装好了:

  • 编码阶段使用 encode_queryencode_document(而非统一的 encode),因此模型的 [Q] / [D] 前缀、查询扩展(query expansion)以及文档 skiplist 都会在评估时生效;
  • 打分阶段使用模型自身的 similarity_fn_name"maxsim""meanmaxsim");
  • 指标阶段在分数之上计算标准的检索/排序指标。

MultiVectorEncoder 只支持这两种相似度函数,见 sentence_transformers/multi_vector_encoder/model.py 中的 SUPPORTED_SIMILARITY_FN_NAMES = ("maxsim", "meanmaxsim")。两者的区别在于 maxsim 会随查询长度累积分数(归一化嵌入下约每个查询 token 贡献 1 分),而 meanmaxsim 将总分除以真实查询 token 数,把分数拉回余弦的 [-1, 1] 区间——后者用于以长度归一化打分训练的模型。

一个必须注意的约束:这些评估器不支持 truncate_dim。原因在源码 docstring 中写得很清楚——多向量 token 嵌入没有 Matryoshka 式截断,任何非 Nonetruncate_dim 都会直接抛出 ValueError(见 nano_beir.pyinformation_retrieval.py 的构造函数校验)。

二、运行评估脚本的通用流程

目录下的每个评估脚本都遵循统一的五步流程:

  1. 加载预训练的多向量模型(MultiVectorEncoder(...));
  2. 准备评估数据集;
  3. 配置合适的评估器;
  4. 运行评估;
  5. 报告结果。

评估器与示例脚本的对应关系如下:

评估器 示例脚本
MultiVectorNanoBEIREvaluator examples/multi_vector_encoder/evaluation/nano_beir.py

运行方式很简单:直接执行 Python 脚本即可,无需任何额外的前置数据处理步骤(NanoBEIR 子集由评估器自行加载)。

三、NanoBEIR 快速评测实战

3.1 什么是 NanoBEIR

NanoBEIR 是 BEIR)。

3.2 完整示例代码

以下是 nano_beir.py 的完整内容,评估 lightonai/LateOn 模型在全部 13 个 Nano-* 检索数据集上的 MaxSim 表现:

"""Evaluate a pretrained multi-vector model on NanoBEIR.

NanoBEIR is a fast benchmarking suite of 13 small BEIR subsets, useful for quickly comparing models
without running the full BEIR evaluation. This script loads a model from the Hub and runs all 13
Nano-* IR datasets with MaxSim scoring.
"""

from __future__ import annotations

from pprint import pprint

from sentence_transformers import MultiVectorEncoder
from sentence_transformers.multi_vector_encoder.evaluation import MultiVectorNanoBEIREvaluator


def main() -> None:
    model = MultiVectorEncoder("lightonai/LateOn")
    evaluator = MultiVectorNanoBEIREvaluator(batch_size=16)
    results = evaluator(model)
    print(f"Primary metric: {evaluator.primary_metric} = {results[evaluator.primary_metric]:.4f}")
    pprint({k: v for k, v in results.items() if "ndcg@10" in k})


if __name__ == "__main__":
    main()

要点拆解:

  • MultiVectorEncoder("lightonai/LateOn") 从 Hub 加载预训练模型,等价于用任意多向量模型(如 lightonai/GTE-ModernColBERT-v1,见评估器 docstring 中的示例)替换;
  • MultiVectorNanoBEIREvaluator(batch_size=16) 配置评估器,batch_size 控制编码时每次处理的文本数;
  • evaluator(model) 触发端到端评估,返回一个 dict[str, float]
  • 默认配置下 evaluator.primary_metric 对应各子集主指标的均值聚合(aggregate_fn 默认 np.meanaggregate_key 默认 "mean"),示例中的过滤条件 "ndcg@10" in k 会打印每个子集的 NDCG@10 以及聚合均值。

3.3 报告哪些指标

对每个子集,评估器报告 MRR@k、NDCG@k、Recall@k、Precision@k、Accuracy@k、MAP@k,并在最后跨子集聚合这些指标。各 k 值的默认配置(可覆盖)为:MRR@k = [10]、NDCG@k = [10]、Accuracy@k = [1, 3, 5, 10]、Precision/Recall@k = [1, 3, 5, 10]、MAP@k = [100]

四、MultiVectorNanoBEIREvaluator 关键参数详解

结合 sentence_transformers/multi_vector_encoder/evaluation/nano_beir.py 的 docstring,以下参数最值得掌握:

参数 默认值 说明
dataset_names 全部 13 个子集 限制评测范围,如 ["msmarco", "nq", "fiqa2018"]。13 个子集为:climatefeverdbpediafeverfiqa2018hotpotqamsmarconfcorpusnqquoraretrievalscidocsarguanascifacttouche2020
dataset_id "sentence-transformers/NanoBEIR-en" 指向具备相同布局(corpus / queries / qrels)的其他数据集,例如 NanoBEIR 集合中的翻译变体,用于非英语评估
corpus_chunk_size 5000 每轮往返(round-trip)编码并打分的文档数量。越大则同时驻留内存的文档嵌入越多,但编码轮次越少
chunk_elements None MaxSim 打分中间结果的元素预算上限。调低可降低打分阶段内存占用
batch_size 32 编码时的每批输入数量
mrr_at_k / ndcg_at_k [10] MRR 与 NDCG 的 k 值
accuracy_at_k [1, 3, 5, 10] Accuracy 的 k 值
precision_recall_at_k [1, 3, 5, 10] Precision 与 Recall 的 k 值
map_at_k [100] MAP 的 k 值
show_progress_bar False 评估时是否显示进度条
write_csv True 是否把每次调用(按 epoch/steps 一行)追加写入 CSV
write_predictions False 是否将每查询 top-k 预测写入 JSONL,可直接作为 ReciprocalRankFusionEvaluator 的输入

典型使用建议:训练过程中常用 dataset_names=["msmarco", "nq", "fiqa2018"] 这类子集做快速迭代评估,完整 13 子集留到训练收尾再跑。

4.1 chunk_elements 背后的内存原理

chunk_elements 直接透传给 similarity.py 中的 maxsim 函数,它约束的是补零后的 (chunk, d_tokens, dim) 文档张量与 4D 打分中间张量 (batch_q, chunk, q_tokens, d_tokens) 的总元素数。文档按预算贪心打包进块(逐块补零,因此单个超长文档只会撑大自己所在块),默认 None 时采用 maxsim 内置的 1 亿元素预算(最多约 400 MB,bf16/fp16 下减半)。在非常大的查询批量下,单文档兜底下限仍然可能很大,此时需要在外部对查询分片。

另外注意:MultiVectorNanoBEIREvaluator 构造时会把 corpus_chunk_sizechunk_elements 注入到每个子集内部构造的 IR 评估器(见源码 _ir_extra_kwargs_load_dataset 的合并逻辑),因此这两个参数对全部子集统一生效。

五、其他 MaxSim 评估器:数据格式与指标

NanoBEIR 是本目录唯一带示例脚本的任务,但包内还内置了其他任务的 MaxSim 评估器(全部导出自 sentence_transformers/multi_vector_encoder/evaluation/init.py),每个类的 docstring 都附有可运行示例:

评估器 必需数据
MultiVectorInformationRetrievalEvaluator 查询(qid => 问题文本)、语料(cid => 文档文本)、相关文档(qid => set[cid])
MultiVectorRerankingEvaluator 形如 {'query': ..., 'positive': [...], 'negative': [...]} 的字典列表
MultiVectorTripletEvaluator (anchor, positive, negative) 三元组
MultiVectorDistillationEvaluator 查询 + 候选文档 + teacher 分数

5.1 MultiVectorInformationRetrievalEvaluator:自建语料的检索评估

MultiVectorNanoBEIREvaluator 内部就是逐子集运行 MultiVectorInformationRetrievalEvaluator,因此它接受与上面相同的指标与内存选项(corpus_chunk_sizechunk_elements、各 *_at_kwrite_predictions 等),用于你自己的语料。实现细节(见 information_retrieval.py):

  • 未显式传入 score_functions 时,打分函数在每次调用时根据 model.similarity_fn_name 动态解析(_model_score_functions),因此模型换用 "meanmaxsim" 时评估自动跟随;若显式传入 chunk_elements,则会以 functools.partial 把它绑定到默认打分函数上;
  • 查询嵌入会被预补零并跨语料块复用(embed_inputspad_sequence),每个块只重新编码文档,降低重复开销;
  • 自定义 score_functions 时,若其中混入 XTR 打分(xtr_scores / XTRScores),会直接抛 ValueError——XTR 做的是跨整个候选集的全局 top-k,与评估器"逐块打分语料"的机制不兼容,逐块取 top-k 会静默出错,因此源码主动拒绝;
  • 显式 prompt 与模型注册 prompt 不匹配时会发出 warning_once 提示,避免显式 prompt 悄悄替换掉模型训练时的 marker prompt。

5.2 MultiVectorRerankingEvaluator:二阶重排

MultiVectorRerankingEvaluator 对每个查询的固定候选列表打分,报告 MAP、MRR@k、NDCG@kat_k 默认 10)。这正是把多向量模型用作**二阶重排器(second-stage reranker)**的评测形态:一阶段检索器返回每个查询的候选(正例与干扰项混合),多向量模型再对其重打分排序。实现上(见 reranking.py),查询与文档分别经 encode_query / encode_document 非对称编码,打分默认回退到 model.similarity(会把单查询归一化为 one-query batch)。

5.3 MultiVectorTripletEvaluator:三元组排序准确率

MultiVectorTripletEvaluator 检查 anchor 对 positive 的分数高于对 negative 的次数比例,判定条件为 MaxSim(anchor, positive) > MaxSim(anchor, negative) + margin。anchor 经 encode_query 编码(带查询前缀与长度),positive / negative 经 encode_document 编码。

margin 有一个容易踩坑的细节:margin 字典必须按相似度类型(maxsimmeanmaxsim)分别指定(传入 float 则对两者同时生效),因为两种打分的量纲不同——maxsim 分数随查询长度累积(归一化嵌入下约每查询 token 一分),而 meanmaxsim 除以 token 数后落在余弦的 [-1, 1] 区间,两者需要不同的 margin 值。源码通过 MultiVectorEncoder.SUPPORTED_SIMILARITY_FN_NAMES 枚举生成全部受支持的成对打分函数,默认选用模型当前的 similarity_fn_name

5.4 MultiVectorDistillationEvaluator:蒸馏过程跟踪

MultiVectorDistillationEvaluatorKL 散度(越低越好)与 Spearman 秩相关(越高越好,主指标)比较学生分数与 teacher 分数,用于跟踪知识蒸馏训练。它支持两种数据形态(见 distillation.py):

  • 逐查询候选集(KD 训练格式)documents 为每查询一个 N 路候选列表,scores 为对应的 2 维 teacher 分数。两个指标都按查询计算,直接对齐训练损失:KL 使用与 MultiVectorDistillKLDivLoss 相同的温度处理(temperaturestudent_temperatureteacher_temperature 三个参数,KL 还会乘上学生温度平方),Spearman 为各查询秩相关的均值;若训练使用了非默认的 similarity_fct(如 MeanMaxSim 打分),评估器也支持传入 similarity_fct 镜像训练设置,否则逐查询 KL 无法与训练损失对齐;
  • 扁平配对:每查询一个文档、1 维分数。此时逐查询分布无定义,KL 把整个数据集 softmax 成单个分布报告总散度(不做配对数量归一,因此不可与逐查询 KL 或 PyLate 直接比较),Spearman 则是全体配对的单一全局相关。

细节上:teacher 或学生分数为常量时秩相关无定义,对应查询会被跳过(全部跳过则报 0.0);因为 MaxSim 分数跨查询不可比(随查询长度累积),逐查询相关才是能跟踪损失的那个信号——这也是 Spearman 被设为主指标的原因。

六、评估实践要点小结

  1. 训练中快速迭代:用 dataset_names 挑 3 个子集(如 ["msmarco", "nq", "fiqa2018"]),完整 13 子集留到训练结束;
  2. 内存控制:打分内存优先调 chunk_elements(默认 1 亿元素预算、约 400 MB,bf16/fp16 减半);编码内存用 corpus_chunk_size 控制同时驻留的文档嵌入数;
  3. 保持一致打分:若模型以 "meanmaxsim"(长度归一化)训练,评估器会自动按 model.similarity_fn_name 解析打分,无需额外配置;蒸馏评估则务必把 temperature 与训练损失对齐;
  4. 非英语评测:把 dataset_id 换成 NanoBEIR 集合中的翻译变体即可,无需改动其余代码;
  5. 结果落盘:默认 write_csv=True 会把每次调用(epoch/steps 一行)追加到 CSV;write_predictions=True 输出的 JSONL 可作为稀疏检索融合评估器(ReciprocalRankFusionEvaluator)的输入做下游分析。

如需深入实现,可继续阅读 nano_beir.py(子集加载与 truncate_dim 校验)、information_retrieval.py(动态打分解析与逐块打分)、similarity.pymaxsim / meanmaxsim 的底层实现与内存预算逻辑),以及配套测试 tests/multi_vector_encoder/test_evaluators.py 验证各评估器的行为。

登录后查看全文
sentence-transformers