ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

FlagEmbedding 评估器 AbsEvaluator 深度解析:从检索、重排到指标计算的完整评估流水线

FlagEmbedding 评估器 AbsEvaluator 深度解析:从检索、重排到指标计算的完整评估流水线 FlagEmbedding 评估器 AbsEvaluator 深度解析从检索、重排到指标计算的完整评估流水线【免费下载链接】FlagEmbeddingRetrieval and Retrieval-augmented LLMs项目地址: https://gitcode.com/GitHub_Trending/fl/FlagEmbedding本篇技术指南围绕 FlagEmbedding 中抽象评估组件FlagEmbedding.abc.evaluation.AbsEvaluator展开系统讲解如何在 BEIR、MS MARCO、MIRACL、MKQA、MLDR、AIR-Bench 等检索评测任务上通过统一的数据加载 → 稠密检索 → 重排 → 指标计算 → 结果输出流水线完成模型评估。读完本文你将掌握AbsEvaluator的核心调用流程、每项参数的真实作用以及如何自定义评估器、复用检索结果缓存、选择正确的评测指标并输出对比报告。本文对应的 API 文档入口为 docs/source/API/abc/evaluation/evaluator.rst。一、AbsEvaluator 在 FlagEmbedding 评估体系中的定位FlagEmbedding 在FlagEmbedding/abc/evaluation/目录下提供了一套面向信息检索IR评估的抽象基类abc用于把评估什么数据、用什么模型检索、怎么重排、算什么指标这四件事解耦成四个可替换的组件。AbsEvaluator正是其中负责编排整条评估流水线的核心类源码位于 FlagEmbedding/abc/evaluation/evaluator.py。从 FlagEmbedding/abc/evaluation/init.py 的导出列表可以看出这套评估体系由以下组件协同工作组件类职责源码评估器AbsEvaluator编排检索、重排、评估全过程管理结果读写evaluator.py数据加载器AbsEvalDataLoader加载语料corpus、查询queries与相关性标注qrelsdata_loader.py检索器EvalRetriever/EvalDenseRetriever编码语料与查询、建 Faiss 索引并返回 Top-k 结果searcher.py重排器EvalReranker对检索结果做交叉编码重排searcher.py运行器AbsEvalRunner组装上述组件并驱动整个评估runner.py参数类AbsEvalArgs/AbsEvalModelArgs声明评估与模型相关的全部命令行参数arguments.py这一设计直接服务于FlagEmbedding/evaluation/下的各评测入口如FlagEmbedding/evaluation/beir、msmarco、miracl、mkqa、mldr、air_bench、bright、mteb、custom等。这些下游评测模块要么直接复用AbsEvaluator要么像BEIREvaluator见 FlagEmbedding/evaluation/beir/evaluator.py和MKQAEvaluator见 FlagEmbedding/evaluation/mkqa/evaluator.py那样继承它并重写部分方法以适应多子数据集聚合、跨语言共享语料等特殊场景。注本文档对应的 Sphinx API 页面 evaluator.rst 通过.. autoclass:: FlagEmbedding.abc.evaluation.AbsEvaluator指令自动生成AbsEvaluator的 API 说明因此其权威内容即源码中的 docstring 与实现本文在此基础上展开。二、AbsEvaluator 的构造与三个核心入参AbsEvaluator的构造函数非常精简evaluator.py#L27-L35class AbsEvaluator: def __init__( self, eval_name: str, data_loader: AbsEvalDataLoader, overwrite: bool False, ): self.eval_name eval_name self.data_loader data_loader self.overwrite overwrite参数类型默认值作用eval_namestr必填当前评估实验的名称如beir、msmarco、miracl。它会写入每个结果文件的元数据并在读取结果时用于一致性校验data_loaderAbsEvalDataLoader必填负责加载语料、查询、相关性标注的数据加载器通常由AbsEvalRunner.load_data_loader()创建overwriteboolFalse若为True即使磁盘上已存在检索/重排结果文件也会重新计算并覆盖否则会复用已有缓存实现断点续跑在实际运行中AbsEvaluator通常不是被直接实例化的而是由AbsEvalRunnerFlagEmbedding/abc/evaluation/runner.py在__init__阶段装配好。从 runner.py#L127-L138 可以看到AbsEvalRunner.load_evaluator()正是用eval_args.eval_name、data_loader和eval_args.overwrite构造评估器随后在run()中调用self.evaluator(...)触发完整评估流程。三、评估主流程call中的检索与重排两阶段AbsEvaluator的全部核心逻辑都在__call__方法中evaluator.py#L102-L264它把一个数据集上的评估划分为检索阶段与重排阶段。先看完整的参数签名def __call__( self, splits: Union[str, List[str]], search_results_save_dir: str, retriever: EvalRetriever, reranker: Optional[EvalReranker] None, corpus_embd_save_dir: Optional[str] None, ignore_identical_ids: bool False, k_values: List[int] [1, 3, 5, 10, 100, 1000], dataset_name: Optional[str] None, **kwargs, ):参数类型默认值说明splitsstr/List[str]必填要评估的数据切分如test、dev会被data_loader.check_splits()过滤掉不存在的切分search_results_save_dirstr必填检索/重排结果的保存根目录retrieverEvalRetriever必填稠密检索器实例rerankerEvalRerankerNone可选的交叉编码重排器为None时跳过重排阶段corpus_embd_save_dirstrNone语料向量缓存目录None表示不保存向量ignore_identical_idsboolFalse是否在结果中剔除与查询 id 相同的文档如 MS MARCO 等需要排除MIRACL 等则不应开启k_valuesList[int][1, 3, 5, 10, 100, 1000]计算指标时采用的截断点集合dataset_namestrNone数据集名称为None时评估默认数据集3.1 前置准备切分校验与保存路径规划__call__首先调用data_loader.check_splits(splits, dataset_namedataset_name)过滤无效切分若全部切分都不可用则直接告警并跳过评估。随后根据dataset_name决定结果文件命名模板if dataset_name is not None: save_name f{dataset_name}- {split}.json else: save_name {split}.json即评估单个数据集时每个切分生成一个dataset-split.json文件评估默认数据集时生成split.json。接下来通过get_corpus_embd_save_dir()evaluator.py#L80-L100计算语料向量缓存目录。基类实现中若指定了corpus_embd_save_dir会在此基础上追加retriever_name若有dataset_name再追加一层dataset_name最终形如corpus_embd_save_dir/retriever/dataset/。这一方法的设计意图在 docstring 中写明对于 MKQA 这类多语言查询共享同一语料的评测子类可以重写该方法把不同dataset_name的语料向量存到同一目录以复用。MKQAEvaluator正是这样做的——见 FlagEmbedding/evaluation/mkqa/evaluator.py#L14-L33它省略了dataset_name层级所有语言变体共享一份doc.npy。3.2 检索阶段Retrieval Stage检索阶段的目录约定为search_results_save_dir/retriever/NoReranker/其中retriever取自str(retriever)。需要说明的是EvalRetriever.__str__返回的是os.path.basename(self.embedder.model.config._name_or_path)见 searcher.py#L27-L31即模型名因此结果目录天然按模型名组织。流程如下缓存判断遍历所有切分若任一切分的结果文件split_no_reranker_search_results_save_path不存在或self.overwrite为True则置flag True进入重新检索分支。重新检索分支通过data_loader.load_corpus()加载语料通过data_loader.load_queries()为每个切分加载查询把所有切分的查询合并后一次性交给retriever(corpus, all_queries, corpus_embd_save_dir..., ignore_identical_ids..., **kwargs)避免重复编码查询将合并结果按切分拆分回no_reranker_search_results_dict[split]调用save_search_results()把每个切分的结果写入NoReranker 目录/save_name.json。复用缓存分支若结果文件已存在且overwriteFalse则通过load_search_results()读回结果并用check_data_info()校验元数据。回收资源检索完成后调用retriever.stop_multi_process_pool()其内部转发给embedder.stop_self_pool()释放多进程/显存资源见 searcher.py#L33-L40。指标落盘若NoReranker/EVAL/eval_results.json不存在、或overwrite、或本次重新检索过flag则调用evaluate_results()计算指标并写入该EVAL子目录。3.3 重排阶段Reranking Stage仅当传入的reranker不为None时执行。重排结果的目录约定为search_results_save_dir/retriever/reranker/即检索器名 重排器名双层目录二者可直接对比是否重排带来的收益。重排阶段的流程与检索阶段对称对每个切分若结果文件已存在且overwriteFalse则跳过continue否则调用reranker(corpus, queries, search_resultsno_reranker_search_results_dict[split], ignore_identical_ids..., **kwargs)进行重排save_search_results()保存结果其中reranker_name字段写入str(reranker)即重排器模型名全部切分完成后调用reranker.stop_multi_process_pool()释放资源若reranker 目录/EVAL/eval_results.json缺失、overwrite或本次有重排过则计算并写入重排后的指标。从 searcher.py#L183-L249 可以看到EvalReranker的内部逻辑先把检索结果截断到rerank_top_k默认 100构造(query, doc)句子对调用reranker.compute_score(pairs)批量打分再按新分数重新组织{qid: {docid: score}}返回。这也解释了AbsEvaluator的rerank_top_k参数arguments.py#L49与重排阶段的关系——重排只对检索 Top-k 的子集进行以控制计算开销。四、数据校验机制check_data_info 与结果元数据为了确保读回的缓存结果与当前评估配置严格一致AbsEvaluator在复用结果时会调用check_data_info()evaluator.py#L37-L78逐项比对元数据任何一项不匹配都会抛出ValueErrordata_info[eval_name] ! self.eval_name→eval_name mismatchdata_info[model_name] ! model_name或data_info[reranker_name] ! reranker_name→model_name or reranker_name mismatchdata_info[split] ! split→split mismatchdataset_name is not None且data_info[dataset_name] ! dataset_name→dataset_name mismatch这些元数据由save_search_results()evaluator.py#L266-L299写入每个结果 JSON 的结构固定为{ eval_name: beir, model_name: bge-large-en-v1.5, reranker_name: NoReranker, split: test, dataset_name: fiqa, search_results: { qid_1: {docid_a: 0.912, docid_b: 0.873} } }load_search_results()evaluator.py#L301-L315读取该文件时会把search_results字段弹出返回(data_info, search_results)两个值供后续校验与指标计算使用。子类可以像BEIREvaluator那样重写check_data_info与save_search_results增加额外的sub_dataset_name元数据字段见 FlagEmbedding/evaluation/beir/evaluator.py#L16-L64 与 #L418-L454。五、指标计算compute_metrics 与 evaluate_resultsAbsEvaluator将指标计算拆成两层单文件粒度的compute_metrics()和目录粒度的evaluate_results()。5.1 compute_metrics六类检索指标一次算齐compute_metrics()evaluator.py#L317-L356接受qrels、search_results与k_values三个输入内部调用 utils.py 中的三个指标函数evaluate_metrics()utils.py#L95-L147基于pytrec_eval.RelevanceEvaluator计算NDCGk、MAPk、Recallk、PrecisionkPk其对每个查询在给定 k 截断点下求均值结果保留 5 位小数evaluate_mrr()utils.py#L14-L52计算MRRk平均倒数排名该实现改编自 BEIR 的自定义指标evaluate_recall_cap()utils.py#L56-L91计算R_capk封顶召回率分母取min(相关文档数, k)同样改编自 BEIR。最终compute_metrics把六个字典统一拼装成如下形式的分数表scores { ndcg_at_1: 0.31, ndcg_at_3: 0.42, ..., # 对应 NDCGk map_at_1: 0.18, map_at_3: 0.25, ..., # 对应 MAPk recall_at_1: 0.10, recall_at_10: 0.55, ..., # 对应 Recallk precision_at_1: 0.31, ..., # 对应 Pk mrr_at_1: 0.31, ..., # 对应 MRRk recall_cap_at_1: 0.10, ..., # 对应 R_capk }注意命名风格字典键用下划线形式如ndcg_at_10、recall_at_100这与你后续在--eval_metrics命令行参数中填写的指标名一一对应。5.2 evaluate_results遍历结果目录聚合指标evaluate_results()evaluator.py#L358-L400遍历search_results_save_dir下所有.json文件对每个文件load_search_results()读回data_info与search_results断言data_info[eval_name] self.eval_name不一致直接assert失败防止混入其他实验的结果从data_info取split与dataset_name通过data_loader.load_qrels(dataset_name..., split...)加载该切分的相关性标注调用compute_metrics()计算指标并以f{dataset_name}-{split}有数据集名时或split作为键写入结果字典。需要重点说明的是evaluate_results的输入目录就是某个 (检索器, 重排器) 组合下的结果目录例如output_dir/retriever/NoReranker/。这意味着每对检索器 × 重排器组合只产出一份EVAL/eval_results.json天然支持在同一套检索结果上测试多个重排器的实验设计。MKQAEvaluator还演示了如何重写evaluate_results由于 MKQA 的答案召回需要把语料拼成title text文本后交给问答侧评估evaluate_qa_recall见 FlagEmbedding/evaluation/mkqa/utils/compute_metrics.py其评估逻辑与通用的pytrec_eval指标不同因此整体重写了该方法FlagEmbedding/evaluation/mkqa/evaluator.py#L35-L117。六、结果输出JSON、DataFrame 与 Markdown 报告评估完成后AbsEvaluator提供三个输出相关的方法覆盖机器可读与人类可读两类场景。6.1 输出 JSONoutput_eval_results_to_json()evaluator.py#L402-L414把eval_results_dict按indent4写入指定路径并在日志中打印Results saved to path。它是检索/重排阶段写EVAL/eval_results.json所用的方法。6.2 生成 DataFrame 透视表get_results_df()evaluator.py#L416-L464用于把多模型 × 多重排器 × 多切分的三层嵌套结果字典展平为一个透视表。它遍历eval_results_dict[model_name][reranker_name][split]构建以(Model, Reranker)为MultiIndex、以各切分为列、末尾附average平均列的 DataFrame。某切分缺失时该单元格为None且只要任一列缺失average也会置为None避免用不完整数据平均。其数据结构对应AbsEvalRunner.evaluate_metrics从output_dir/model/reranker/EVAL/eval_results.json汇总出来的eval_results_dictrunner.py#L159-L177。6.3 输出 Markdown 对比报告output_eval_results_to_markdown()evaluator.py#L466-L499按指标逐个生成 Markdown 表格每个指标生成一个## metric章节表格列为Model | Reranker | split1 | ... | average每行对应一个(Model, Reranker)组合数值以*100的百分比形式保留 3 位小数输出每列最高分用**加粗**标出便于快速定位每个切分上的最优组合。这是AbsEvalRunner.run()末尾生成最终报告所走的路径runner.py#L223-L229先汇总各组合的EVAL/eval_results.json再按eval_output_methodjson或markdown与eval_metrics指定的指标列表输出到eval_output_path。七、端到端实战命令行参数与一次完整评估7.1 命令行参数速查评估的全部行为由AbsEvalArgs与AbsEvalModelArgs两个 dataclass 声明FlagEmbedding/abc/evaluation/arguments.py。与AbsEvaluator直接相关的评估侧参数如下参数默认值说明--eval_name必填评估任务名如beir、msmarco--dataset_dirNone本地数据集目录需含corpus.jsonl、split_queries.jsonl、split_qrels.jsonl或包含多个此类子目录传None时数据集仅下载到缓存--dataset_namesNone要评估的数据集/语言名称列表None表示评估全部可用数据集--splitstest要评估的切分可多个--corpus_embd_save_dirNone语料向量缓存目录None则不保存--output_dir./search_results检索/重排结果保存根目录--search_top_k1000检索阶段每个查询保留的候选数--rerank_top_k100重排阶段每个查询处理的候选数--overwriteFalse是否覆盖已有结果--ignore_identical_idsFalse是否剔除与查询同 id 的文档--k_values1 3 5 10 100 1000指标截断点集合--eval_output_methodmarkdown结果输出方式json或markdown--eval_output_path./eval_results.md最终报告输出路径--eval_metricsndcg_at_10 recall_at_10报告中要展示的指标需与compute_metrics输出的键一致模型侧AbsEvalModelArgs的关键参数包括--embedder_name_or_path必填、--embedder_model_class如encoder-only-m3、decoder-only-base、decoder-only-icl等、--reranker_name_or_path、--reranker_model_class、--devices、--use_fp16/--use_bf16、--normalize_embeddings默认True、各类 instruction 参数、--embedder_batch_size默认 3000、--embedder_query_max_length/--embedder_passage_max_length默认 512以及--truncate_dim用于 Matryoshka 截断。这些参数会经 runner.py#L37-L93 的get_models()传入FlagAutoModel.from_finetuned()与FlagAutoReranker.from_finetuned()完成模型加载。7.2 真实运行脚本解读仓库提供了各评测任务的官方示例脚本例如 BEIR 评测脚本 examples/evaluation/beir/eval_beir.shdataset_namesfiqa arguana cqadupstack eval_args\ --eval_name beir \ --dataset_dir ./beir/data \ --dataset_names $dataset_names \ --splits test dev \ --corpus_embd_save_dir ./beir/corpus_embd \ --output_dir ./beir/search_results \ --search_top_k 1000 --rerank_top_k 100 \ --cache_path $HF_HUB_CACHE \ --overwrite False \ --k_values 10 100 \ --eval_output_method markdown \ --eval_output_path ./beir/beir_eval_results.md \ --eval_metrics ndcg_at_10 recall_at_100 \ --ignore_identical_ids True \ model_args\ --embedder_name_or_path BAAI/bge-large-en-v1.5 \ --reranker_name_or_path BAAI/bge-reranker-v2-m3 \ --devices cuda:0 cuda:1 \ --cache_dir $HF_MODEL_CACHE \ --reranker_max_length 1024 \ cmdpython -m FlagEmbedding.evaluation.beir \ $eval_args \ $model_args \ echo $cmd eval $cmd要点拆解--dataset_names fiqa arguana cqadupstack指定三个 BEIR 子数据集其中cqadupstack是特殊的子数据集族BEIREvaluator.evaluate_results会把其多个子集如cqadupstack-android、cqadupstack-english等的指标平均后合并为cqadupstack-test一行见 FlagEmbedding/evaluation/beir/evaluator.py#L400-L414。--ignore_identical_ids True剔除与查询同 id 的文档BEIR 惯例注意 dense 检索器在ignore_identical_idsTrue时会告警提示类似 MIRACL 的数据集不应开启该选项searcher.py#L101-L102。--overwrite False让二次运行直接复用./beir/search_results与./beir/corpus_embd中的缓存实现增量评估与断点续跑。指定了--reranker_name_or_path因此评估会同时产出NoReranker与bge-reranker-v2-m3两个目录并各自生成EVAL/eval_results.json最终在beir_eval_results.md中对比。脚本最终通过python -m FlagEmbedding.evaluation.beir调用对应评测模块其运行入口位于 FlagEmbedding/evaluation/beir/main.py。其他评测任务MS MARCO、MIRACL、MKQA、MLDR、AIR-Bench、MTEB、BRIGHT 等的脚本分别位于 examples/evaluation/结构完全一致可照此扩展。八、如何自定义评估器继承 AbsEvaluator 的三种模式从BEIREvaluator与MKQAEvaluator的实际用法可以总结出三种自定义模式供你在自己的评测任务中参考模式一重写get_corpus_embd_save_dir共享语料向量。当多个dataset_name共享同一份语料时如 MKQA 的多语言变体重写该方法让它们写入同一目录避免重复编码与重复存储。示例见 FlagEmbedding/evaluation/mkqa/evaluator.py#L14-L33。模式二重写check_data_info/save_search_results增加元数据维度。当结果需要按子数据集如sub_dataset_name区分时在元数据中追加字段并同步扩展校验逻辑。示例见 FlagEmbedding/evaluation/beir/evaluator.py#L16-L64 与 #L418-L454。模式三重写evaluate_results定制指标聚合逻辑。当评测指标不是标准检索指标时如 MKQA 的答案召回率、BEIR 的 CQADupstack 子集平均整体重写该方法。示例见 FlagEmbedding/evaluation/mkqa/evaluator.py#L35-L117 与 FlagEmbedding/evaluation/beir/evaluator.py#L351-L416。若你需要评估自定义格式的本地数据集AbsEvalDataLoader已内置corpus.jsonlsplit_queries.jsonlsplit_qrels.jsonl的本地加载逻辑data_loader.py#L232-L317只需按此格式组织数据目录并通过--dataset_dir传入对于远程数据集则需要继承AbsEvalDataLoader并实现_load_remote_corpus、_load_remote_qrels、_load_remote_queries三个抽象方法。九、使用注意事项与最佳实践结果文件即缓存output_dir/retriever/NoReranker/与output_dir/retriever/reranker/下的 JSON 既是结果也是缓存。修改了k_values等只影响指标计算的参数时无需重新检索直接重跑指标计算即可修改了模型、指令或search_top_k时务必设置--overwrite True或更换--output_dir否则旧缓存可能被误用——check_data_info的元数据校验会在此时抛出ValueError提醒你配置不匹配。指标名与--eval_metrics对齐compute_metrics输出的键是ndcg_at_10、recall_at_100等下划线格式--eval_metrics必须严格匹配这些键否则get_results_df中该列会全部为None。ignore_identical_ids需按数据集谨慎选择MS MARCO 等数据集建议开启以排除查询-文档同 id 的干扰MIRACL 等数据集不应开启searcher.py#L101-L102。多切分查询合并检索__call__会把所有切分的查询合并后一次性检索再从总结果中按切分拆回避免重复编码但要求各切分查询 id 不冲突。corpus_embd_save_dir是加速关键语料向量doc.npy与 Faiss 索引可在多次实验间复用对大型语料如 MS MARCO、BEIR而言这是避免重复编码的主要手段。底层索引构建与检索实现在 utils.py#L150-L228默认使用Flat索引与内积度量faiss.METRIC_INNER_PRODUCT在 GPU 可用时自动尝试index_cpu_to_all_gpus分片构建useFloat16半精度。重排只作用于检索 Top-krerank_top_k默认 100会先截断检索结果再打分控制交叉编码器开销compute_score采用批量打分默认 batch size 3000见 arguments.py#L153-L154。十、与其他 API 的关系AbsEvaluator并不是孤立组件。它在评估流水线中的上下游关系如下上游AbsEvalRunnerFlagEmbedding/abc/evaluation/runner.py负责从AbsEvalArgs/AbsEvalModelArgs解析参数、加载模型与数据加载器并在run()中调用evaluator(...)。数据侧AbsEvalDataLoaderFlagEmbedding/abc/evaluation/data_loader.py为评估器提供语料、查询与 qrels。检索/重排侧EvalRetriever/EvalDenseRetriever/EvalRerankerFlagEmbedding/abc/evaluation/searcher.py包装FlagEmbedding.abc.inference中的AbsEmbedder与AbsReranker通过encode_corpus/encode_queries/compute_score完成向量化与打分。指标侧FlagEmbedding/abc/evaluation/utils.py 提供基于pytrec_eval与 Faiss 的底层指标与索引实现。下游评测FlagEmbedding/evaluation/下的 BEIR、MS MARCO、MIRACL、MKQA、MLDR、AIR-Bench 等模块通过继承AbsEvaluator或直接复用其流程构建各自的评测入口对应的命令行示例见 examples/evaluation/。综上AbsEvaluator通过两阶段检索 重排、双目录模型名分层、四要素语料/查询/qrels/指标的统一设计为 FlagEmbedding 覆盖的多语言、多领域检索评测提供了可复用、可缓存、可扩展的评估骨架——无论是跑一份官方 benchmark 报告还是为自定义数据集编写新的评测器它都是最直接的起点。延伸阅读评估器基类源码FlagEmbedding/abc/evaluation/evaluator.py评估参数定义FlagEmbedding/abc/evaluation/arguments.py数据加载器FlagEmbedding/abc/evaluation/data_loader.py检索器与重排器FlagEmbedding/abc/evaluation/searcher.py指标与索引工具FlagEmbedding/abc/evaluation/utils.py评估运行器FlagEmbedding/abc/evaluation/runner.pyAPI 文档目录docs/source/API/abc/evaluation/命令行示例examples/evaluation/【免费下载链接】FlagEmbeddingRetrieval and Retrieval-augmented LLMs项目地址: https://gitcode.com/GitHub_Trending/fl/FlagEmbedding创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表