本文档介绍 bge-reranker-v2-m3 模型的服务化部署与精度评测实践。该模型是 BGE 系列的重排序模型,官方 CMTEB 检索评测中将其与 bge-large-zh-v1.5(Embedding 模型)搭配使用:先由 bge-large-zh-v1.5 生成向量进行初筛检索,再由 bge-reranker-v2-m3 对 Top-K 结果进行精排。
本实践基于 FlagEmbedding 框架,通过 API 调用方式完成两阶段评测,验证服务化部署后的精度是否与官方结果一致。
评测目标:
说明:Embedding 模型(bge-large-zh-v1.5)与 Reranker 模型(bge-reranker-v2-m3)均基于昇腾(Ascend)NPU 与 TEI(Text Embeddings Inference)框架进行服务化部署,步骤完全相同,仅权重文件不同。以下以 bge-reranker-v2-m3 为例说明,bge-large-zh-v1.5 只需替换对应的模型名称和端口即可。
docker pull swr.cn-south-1.myhuaweicloud.com/ascendhub/mis-tei:26.0.0-800I-A2-aarch64TORCH_DEVICE_BACKEND_AUTOLOAD=0 modelscope download --model BAAI/bge-reranker-v2-m3模型权重已下载到 /opt/data/models 目录,通过 -v 挂载到容器内。
export IMAGE=swr.cn-south-1.myhuaweicloud.com/ascendhub/mis-tei:26.0.0-800I-A2-aarch64
export NAME=bge-reranker-v2-m3
docker run -u root -e ENABLE_BOOST=True -itd --privileged \
--name $NAME \
--net=host \
--shm-size=40g \
--device /dev/davinci_manager \
--device /dev/devmm_svm \
--device /dev/hisi_hdc \
-w /workspace \
-v /usr/local/Ascend/driver:/usr/local/Ascend/driver:ro \
-v /usr/local/sbin:/usr/local/sbin:ro \
-v /opt/data/models:/home/HwHiAiUser/model/ \
$IMAGE BAAI/bge-reranker-v2-m3 0.0.0.0 8007curl 127.0.0.1:8007/v1/rerank \
-X POST \
-d '{
"query": "What is Deep Learning?",
"documents": ["Deep Learning is not...", "Deep learning is..."]
}' \
-H 'Content-Type: application/json'| 属性 | 说明 |
|---|---|
| 全称 | T2Retrieval (CMTEB 检索任务之一) |
| 语料规模 | 118,605 篇中文文档 |
| Query 数 | 22,812 个查询 |
| Qrels 数 | 118,932 条相关性标注 |
| 评测指标 | NDCG@k, Recall@k, MAP@k, MRR@k, Precision@k |
| 数据格式 | MTEB 2.x BEIR 格式(corpus/queries/qrels 分离) |
# HuggingFace Hub
"mteb/T2Retrieval"
# 三个子配置
corpus → 118,605 篇文档(title + text)
queries → 22,812 个查询
default → 118,932 条 qrels(query-id + corpus-id + score)| 指标 | 说明 |
|---|---|
| NDCG@k | 归一化折损累计增益,衡量排序质量 |
| Recall@k | top-k 中召回的相关文档比例 |
| MAP@k | 平均精度均值 |
| MRR@k | 第一个相关文档排名的倒数均值 |
| Precision@k | top-k 中相关文档的精确率 |
# 基础依赖
pip install faiss-cpu datasets pytrec_eval
# 克隆 FlagEmbedding 仓库
git clone https://github.com/FlagOpen/FlagEmbedding.git
cd FlagEmbedding
# 切换到 patch 对应的 commit
git checkout 7ed43d6
# FlagEmbedding(本地安装)
pip install -e .从当前仓库中获取Patch 文件,其中包含所有代码修改和新增文件:
# 将 patch 复制到仓库根目录后应用
git apply cmteb_api_eval.patchgit status应看到以下变更:
| 文件 | 状态 | 说明 |
|---|---|---|
api_embedder.py | 新增 | Embedding API 封装,ThreadPoolExecutor 并发调用 |
api_reranker.py | 新增 | Reranker API 封装,按 query 分组调用 |
run_cmteb_api.py | 新增 | CMTEB 评测主入口 |
quick_verify_cmteb.py | 新增 | 快速验证脚本,加载真实 T2Retrieval 子集进行端到端验证 |
FlagEmbedding 默认流程:
本地加载 HuggingFace 模型 → Corpus Encoding → FAISS Index → Query Encoding → Search → Evaluate适配后流程:
调用远程 Embedding API → Corpus Encoding → FAISS Index → Query Encoding → Search → Reranker API → Evaluate各阶段详细说明:
| 阶段 | 说明 | 输入/输出 |
|---|---|---|
| Corpus API Encoding | 通过 Embedding API 对 11.8 万篇文档进行向量编码,生成 doc.npy(约 464MB) | 输入:118,605 篇文档;输出:(118605, 1024) 维 float32 数组 |
| FAISS Index | 基于内积相似度构建平面内积索引(IndexFlatIP),支持 top-k 最近邻检索 | 输入:Corpus embeddings;输出:可查询的 FAISS 索引对象 |
| Query API Encoding | 对 22,812 个查询进行向量编码(带 query instruction) | 输入:Query 文本;输出:(22812, 1024) 维向量 |
| Search | 在 FAISS 索引中检索与 query 最相似的 top-k 文档 | 输入:Query embeddings + FAISS 索引;输出:(22812, 100) 文档 ID 及相似度分数 |
| Reranker API | 对 top-100 文档调用 Reranker API 重新排序 | 每 query 4 次 API 调用(100 docs ÷ batch_size 32) |
| Evaluate | 基于 qrels 计算 NDCG/Recall/MAP/MRR/Precision | 输入:Search 结果 + 标准答案;输出:各 k 值指标 |
api_embedder.py)核心逻辑:继承 AbsEmbedder,将 encode_single_device 重载为 HTTP API 调用。
class APIEmbedder(AbsEmbedder):
def __init__(self, ..., max_workers=8, **kwargs):
kwargs["devices"] = ["cpu"]
super().__init__(...)
self.max_workers = max_workers
def encode_single_device(self, sentences, batch_size=256, ...):
# 使用 ThreadPoolExecutor 并发调用 API
with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
# 提交所有 batch 任务
future_to_idx = {
executor.submit(self._call_api, batch): batch_idx
...
}
# 收集结果
for future in tqdm(as_completed(future_to_idx), ...):
...api_reranker.py)核心逻辑:继承 AbsReranker,按查询分组调用重排序器 API。
class APIReranker(AbsReranker):
def compute_score_single_gpu(self, sentence_pairs, ...):
# Group by query to batch call API per query
query_to_docs = {}
for idx, (query, doc) in enumerate(sentence_pairs):
...
scores = [0.0] * len(sentence_pairs)
for query in tqdm(query_to_docs, desc="API reranking"):
docs = query_to_docs[query]
# TEI API may have max batch size limit; chunk if needed
for start in range(0, len(docs), self.batch_size):
chunk_scores = self._call_api(query, doc_chunk)
...
return np.array(scores, dtype=np.float32)关键设计点:
batch_size=32 切分 chunkrun_cmteb_api.py)适配后的评测入口,支持命令行参数:
def parse_args():
parser.add_argument("--dataset_names", nargs="+", default=None) # 评测任务名称
parser.add_argument("--embedder_api_url", default="http://127.0.0.1:8009/v1/embeddings")
parser.add_argument("--embedder_api_model", default="bge-large-zh-v1.5")
parser.add_argument("--embedder_api_batch_size", type=int, default=32)
parser.add_argument("--search_top_k", type=int, default=100)
parser.add_argument("--rerank_top_k", type=int, default=100)
parser.add_argument("--corpus_embd_save_dir", default=None) # Corpus 缓存目录
parser.add_argument("--overwrite", action="store_true") # 覆盖已有结果
...quick_verify_cmteb.py)用于端到端 pipeline 验证,使用小规模真实数据(默认 50 文档 + 5 查询),所有参数均通过命令行传入,与 run_cmteb_api.py 保持一致。
HF_ENDPOINT=https://hf-mirror.com python quick_verify_cmteb.py \
--embedder_api_url "http://127.0.0.1:8009/v1/embeddings" \
--embedder_api_model "bge-large-zh-v1.5" \
--reranker_api_url "http://127.0.0.1:8007/rerank" \
--reranker_api_model "bge-reranker-v2-m3" \
--max_corpus 50 \
--max_queries 5HF_ENDPOINT=https://hf-mirror.com python quick_verify_cmteb.py \
--embedder_api_url "http://127.0.0.1:8009/v1/embeddings" \
--embedder_api_model "bge-large-zh-v1.5" \
--reranker_api_url "http://127.0.0.1:8007/rerank" \
--reranker_api_model "bge-reranker-v2-m3" \
--max_corpus 50 \
--max_queries 5 \
--search_top_k 10 \
--rerank_top_k 10mkdir -p /tmp/hf_cache/cmteb
cd /workspace/FlagEmbedding
HF_ENDPOINT=https://hf-mirror.com nohup python run_cmteb_api.py \
--dataset_names T2Retrieval \
--cache_path /tmp/hf_cache/cmteb \
--embedder_api_url "http://127.0.0.1:8009/v1/embeddings" \
--embedder_api_model "bge-large-zh-v1.5" \
--embedder_api_batch_size 32 \
--embedder_max_workers 8 \
--reranker_api_url "http://127.0.0.1:8007/rerank" \
--reranker_api_model "bge-reranker-v2-m3" \
--reranker_api_batch_size 32 \
--search_top_k 100 \
--rerank_top_k 100 \
--output_dir ./cmteb_search_results_api \
--corpus_embd_save_dir ./cmteb_corpus_embd_api \
--eval_output_path ./cmteb_eval_results_api.md \
--log_level INFO \
> run_t2retrieval.log 2>&1 &run_cmteb_api.py(正式评测)与 quick_verify_cmteb.py(快速验证)均通过命令行参数配置,参数风格保持一致。
通用参数(两个脚本均支持):
| 参数 | 默认值 | 说明 |
|---|---|---|
--embedder_api_url | http://127.0.0.1:8009/v1/embeddings | 嵌入模型 API 端点 |
--embedder_api_model | bge-large-zh-v1.5 | 嵌入模型名称 |
--embedder_api_batch_size | 32 / 8 | 嵌入模型 API 每批文本数量(正式 / 验证) |
--embedder_max_workers | 8 / 2 | 嵌入模型 API 并发线程数(正式 / 验证) |
--query_instruction | 为这个句子生成表示以用于检索相关文章: | 查询指令前缀 |
--reranker_api_url | http://127.0.0.1:8007/rerank | 重排模型 API 端点 |
--reranker_api_model | bge-reranker-v2-m3 | 重排模型名称 |
--reranker_api_batch_size | 32 / 8 | 重排模型 API 每批文档数(正式 / 验证) |
--search_top_k | 100 / 10 | 检索返回前 k 条结果(正式 / 验证) |
--rerank_top_k | 100 / 10 | 重排返回前 k 条结果(正式 / 验证) |
--k_values | [1,3,5,10,100,1000] / [1,3,5,10] | 评测指标截断点(正式 / 验证) |
--cache_path | None | HuggingFace 数据集缓存目录 |
正式评测参数(仅 run_cmteb_api.py):
| 参数 | 默认值 | 说明 |
|---|---|---|
--dataset_names | None(全部任务) | 评测数据集名称列表,如 T2Retrieval |
--dataset_dir | None | 本地数据集目录 |
--splits | ["dev"] | 数据集划分列表 |
--output_dir | ./cmteb_search_results_api | 检索结果保存目录 |
--corpus_embd_save_dir | None | 语料嵌入缓存目录 |
--eval_output_path | ./cmteb_eval_results_api.md | 评估报告输出路径 |
--overwrite | False | 是否覆盖已有结果 |
--log_level | INFO | 日志级别 |
快速验证参数(仅 quick_verify_cmteb.py):
| 参数 | 默认值 | 说明 |
|---|---|---|
--max_corpus | 50 | 验证用文档数 |
--max_queries | 5 | 验证用查询数 |
首次运行(无缓存)真实耗时(基于 run_t2retrieval.log 时间戳):
| 阶段 | 耗时 | 说明 |
|---|---|---|
| 数据加载 | ~15秒 | 从 HuggingFace Hub 下载并加载语料库(Corpus)+ 相关性判断(Qrels)+ 查询集(Queries) |
| 语料库 API 编码 | ~6分钟 | 118,605 篇文档通过 Embedding API 进行编码(批处理大小=32,8个工作进程) |
| FAISS 索引构建 | ~1分钟 | 构建 118,605 个向量的内积索引 |
| 查询 API 编码 | ~2分钟 | 22,812 个查询通过 Embedding API 进行编码 |
| FAISS 检索 | ~8分钟 | 在 CPU 上对 22,812 × 118,605 进行 top-100 检索(使用 faiss-cpu) |
| 保存检索结果 | ~1分钟 | 保存 93MB 大小的检索结果 JSON 文件 |
| 检索评估 | ~1分钟 | 无重排序器(Reranker)阶段的指标计算 |
| 重排序器 API | ~5小时40分钟 | 22,812 个查询 × 每个查询4次调用,单线程串行调用 |
| 最终评估 | ~1分钟 | 重排序器(Reranker)阶段的指标计算 |
| 总计 | ~6小时 |
检索阶段(无重排序器):
{
"T2Retrieval-dev": {
"ndcg_at_1": 0.89383,
"ndcg_at_3": 0.85389,
"ndcg_at_5": 0.83960,
"ndcg_at_10": 0.83975,
"ndcg_at_100": 0.87592,
"ndcg_at_1000": 0.87592,
"map_at_1": 0.27132,
"map_at_3": 0.53632,
"map_at_5": 0.65916,
"map_at_10": 0.76314,
"map_at_100": 0.79939,
"map_at_1000": 0.79939,
"recall_at_1": 0.27132,
"recall_at_3": 0.55350,
"recall_at_5": 0.69370,
"recall_at_10": 0.82932,
"recall_at_100": 0.94673,
"recall_at_1000": 0.94673,
"precision_at_1": 0.89383,
"precision_at_3": 0.74744,
"precision_at_5": 0.62641,
"precision_at_10": 0.41775,
"precision_at_100": 0.04990,
"precision_at_1000": 0.00499,
"mrr_at_1": 0.89383,
"mrr_at_3": 0.91594,
"mrr_at_5": 0.91883,
"mrr_at_10": 0.92034,
"mrr_at_100": 0.92123,
"mrr_at_1000": 0.92123
}
}Reranker 重排阶段:
{
"T2Retrieval-dev": {
"ndcg_at_1": 0.90430,
"ndcg_at_3": 0.86471,
"ndcg_at_5": 0.84912,
"ndcg_at_10": 0.84676,
"ndcg_at_100": 0.88214,
"ndcg_at_1000": 0.88214,
"map_at_1": 0.27684,
"map_at_3": 0.54535,
"map_at_5": 0.66852,
"map_at_10": 0.77114,
"map_at_100": 0.80765,
"map_at_1000": 0.80765,
"recall_at_1": 0.27684,
"recall_at_3": 0.56159,
"recall_at_5": 0.70095,
"recall_at_10": 0.83255,
"recall_at_100": 0.94673,
"recall_at_1000": 0.94673,
"precision_at_1": 0.90430,
"precision_at_3": 0.75554,
"precision_at_5": 0.63160,
"precision_at_10": 0.41910,
"precision_at_100": 0.04990,
"precision_at_1000": 0.00499,
"mrr_at_1": 0.90430,
"mrr_at_3": 0.92438,
"mrr_at_5": 0.92679,
"mrr_at_10": 0.92810,
"mrr_at_100": 0.92889,
"mrr_at_1000": 0.92889
}
}本次评测 vs 官方 CMTEB 结果:
| 指标 | 本次(无 Reranker) | 本次(Reranker) | 官方(无 Reranker) | 官方(Reranker) |
|---|---|---|---|---|
| NDCG@10 | 83.98 | 84.68 | 83.99 | 84.57 |
官方数据来源:ModelScope - bge-reranker-v2-m3 CMTEB 检索评测表
完整指标对比:
| 指标 | 本次(无 Reranker) | 本次(Reranker) | 提升 |
|---|---|---|---|
| NDCG@1 | 89.38 | 90.43 | +1.05 |
| NDCG@3 | 85.39 | 86.47 | +1.08 |
| NDCG@5 | 83.96 | 84.91 | +0.95 |
| NDCG@10 | 83.98 | 84.68 | +0.70 |
| NDCG@100 | 87.59 | 88.21 | +0.62 |
| Recall@100 | 94.67 | 94.67 | — |
| MRR@10 | 92.03 | 92.81 | +0.78 |
| MAP@100 | 79.94 | 80.77 | +0.83 |
与官方结果高度一致:
Reranker 对排序质量有正向优化:NDCG@10 从 83.98 提升到 84.68,提升约 0.7 个百分点
低 k 值提升更明显:NDCG@1/3/5 提升 0.95-1.08,说明 Reranker 能有效将更相关文档排到前列
Recall 不变:Recall@100 均为 94.67,符合预期(Reranker 只在已有 top-100 内重排,不引入新文档)
API 化适配可行:通过 APIEmbedder + APIReranker 将 FlagEmbedding 框架适配为远程 API 调用,完整保留了评测流程的准确性。
Reranker 有正向优化效果:NDCG@10 从 83.98 提升到 84.68,低 k 值(NDCG@1/3/5)提升更明显,说明 bge-reranker-v2-m3 能有效优化排序质量。
与官方结果高度一致: