本文档介绍 BGE-M3 模型的完整服务化部署与精度评测实践,涵盖以下两个核心环节:
/v1/embeddings 格式的在线推理服务。MKQA 是一个多语言问答数据集,涵盖 26 种语言,所有语言共享同一英文语料库(BeIR/nq,约 268 万篇文档)。评测目标是验证模型在不同语言 query 下的跨语言检索能力。
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-m3export IMAGE=swr.cn-south-1.myhuaweicloud.com/ascendhub/mis-tei:26.0.0-800I-A2-aarch64
export NAME=bge-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-m3 0.0.0.0 8003curl 127.0.0.1:8003/v1/embeddings \
-X POST \
-d '{
"input": ["The capital of China is Beijing."]
}' \
-H 'Content-Type: application/json'| 属性 | 说明 |
|---|---|
| 全称 | Multilingual Knowledge Questions & Answers |
| 语言数 | 26 种 |
| 共享语料 | BeIR/nq(英文维基百科,2,681,468 篇文档) |
| Query 数 | 约 10,000(每种语言) |
| 答案形式 | 短文本答案(非多项选择) |
| 评测指标 | QA Recall@k |
MKQA 数据集涵盖 26 种语言,各语言代码如下表所示:
| 序号 | 语言 | 代码 | 序号 | 语言 | 代码 |
|---|---|---|---|---|---|
| 1 | 阿拉伯语 | ar | 14 | 日语 | ja |
| 2 | 丹麦语 | da | 15 | 高棉语 | km |
| 3 | 德语 | de | 16 | 韩语 | ko |
| 4 | 英语 | en | 17 | 马来语 | ms |
| 5 | 西班牙语 | es | 18 | 荷兰语 | nl |
| 6 | 芬兰语 | fi | 19 | 挪威语 | no |
| 7 | 法语 | fr | 20 | 波兰语 | pl |
| 8 | 希伯来语 | he | 21 | 葡萄牙语 | pt |
| 9 | 匈牙利语 | hu | 22 | 俄语 | ru |
| 10 | 意大利语 | it | 23 | 瑞典语 | sv |
| 11 | 简体中文 | zh_cn | 24 | 泰语 | th |
| 12 | 繁体中文(香港) | zh_hk | 25 | 土耳其语 | tr |
| 13 | 繁体中文(台湾) | zh_tw | 26 | 越南语 | vi |
与传统 IR Recall 不同,MKQA 的 QA Recall 检查的是:检索到的 top-k 文档中是否包含答案文本。
# 核心逻辑:答案字符串是否出现在文档中
def has_answer(answers, text) -> bool:
for answer in answers:
if answer.lower() in text.lower():
return True
return False# 克隆 FlagEmbedding 仓库
git clone https://github.com/FlagOpen/FlagEmbedding.git
cd FlagEmbedding
# 切换到 patch 对应的 commit
git checkout 7ed43d6
# 安装 FlagEmbedding
pip install -e .
# 安装评测依赖
pip install faiss-cpu datasets pytrec_eval从当前仓库中获取Patch 文件,其中包含所有代码修改和新增文件:
# 将 patch 复制到仓库根目录后应用
git apply mkqa_api_eval.patchgit status应看到以下变更:
| 文件 | 状态 | 说明 |
|---|---|---|
api_embedder.py | 新增 | API Embedder 实现 |
run_mkqa_api.py | 新增 | 评测主入口 |
quick_verify.py | 新增 | 快速验证脚本 |
FlagEmbedding/evaluation/mkqa/utils/compute_metrics.py | 修改 | 添加 tqdm 进度条 |
FlagEmbedding 默认流程:
本地加载 HuggingFace 模型 → Corpus Encoding → FAISS Index → Query Encoding → Search → Evaluate适配后流程:
调用远程 Embedding API → Corpus Encoding → FAISS Index → Query Encoding → Search → Evaluate各阶段详细说明:
| 阶段 | 说明 | 输入/输出 |
|---|---|---|
| Corpus Encoding | 通过 Embedding API 对 268 万篇英文语料进行向量编码,生成 doc.npy(约 10.3 GB) | 输入:BeIR/nq 语料文本;输出:(2681468, 1024) 维 float32 数组 |
| FAISS Index | 基于余弦相似度构建平面内积索引(IndexFlatIP),支持 top-k 最近邻检索 | 输入:Corpus embeddings;输出:可查询的 FAISS 索引对象 |
| Query Encoding | 对评测语言的 query 文本进行向量编码(每种语言约 6,600 条 query) | 输入:MKQA query 文本;输出:(n_queries, 1024) 维向量 |
| Search | 在 FAISS 索引中检索与 query 最相似的 top-k 文档 | 输入:Query embeddings + FAISS 索引;输出:(n_queries, top_k) 文档 ID 及相似度分数 |
| Evaluate | 使用 has_answer 判断检索结果中是否包含答案文本,计算 QA Recall@k | 输入:Search 结果 + 标准答案;输出:qa_recall@{1,3,5,10,100,1000} |
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), ...):
...run_mkqa_api.py)适配后的评测入口,支持命令行参数:
def parse_args():
parser.add_argument("--dataset_names", nargs="+", default=None) # 语言代码
parser.add_argument("--api_url", default="http://127.0.0.1:8003/v1/embeddings")
parser.add_argument("--api_model", default="bge-m3")
parser.add_argument("--api_batch_size", type=int, default=64)
parser.add_argument("--search_top_k", type=int, default=1000)
parser.add_argument("--corpus_embd_save_dir", default=None) # Corpus 缓存目录
parser.add_argument("--overwrite", action="store_true") # 覆盖已有结果
...quick_verify.py)用于端到端 pipeline 验证,使用小规模数据(默认 1000 文档 + 100 query),数分钟内完成。所有参数均通过命令行传入:
def parse_args():
# data
parser.add_argument("--cache_path", default="/tmp/hf_cache") # HF 数据集缓存目录
parser.add_argument("--dataset_name", default="en") # MKQA 语言代码
parser.add_argument("--max_docs", type=int, default=1000) # 采样的语料文档数
parser.add_argument("--max_queries", type=int, default=100) # 采样的 query 数
...# 预创建缓存子目录(避免 wget 因目录缺失失败)
mkdir -p /tmp/hf_cache/mkqa
# 测试 1000 文档 + 100 query,验证 pipeline 完整性
HF_ENDPOINT=https://hf-mirror.com python quick_verify.py \
--cache_path /tmp/hf_cache \
--dataset_name en \
--max_docs 1000 \
--max_queries 100 \
--api_url "http://127.0.0.1:8003/v1/embeddings" \
--api_model "bge-m3" \
--api_batch_size 32 \
--top_k 100mkdir -p /tmp/hf_cache/mkqa
nohup env HF_ENDPOINT=https://hf-mirror.com python run_mkqa_api.py \
--dataset_names en \
--cache_path /tmp/hf_cache \
--api_url "http://127.0.0.1:8003/v1/embeddings" \
--api_model "bge-m3" \
--api_batch_size 32 \
--search_top_k 1000 \
--output_dir ./search_results_api \
--corpus_embd_save_dir ./corpus_embd_api \
--eval_output_path ./eval_results_api.md \
--log_level INFO \
> run_en_eval.log 2>&1 &mkdir -p /tmp/hf_cache/mkqa
nohup env HF_ENDPOINT=https://hf-mirror.com python run_mkqa_api.py \
--dataset_names zh_cn \
--cache_path /tmp/hf_cache \
--api_url "http://127.0.0.1:8003/v1/embeddings" \
--api_model "bge-m3" \
--api_batch_size 32 \
--search_top_k 1000 \
--output_dir ./search_results_api \
--corpus_embd_save_dir ./corpus_embd_api \
--eval_output_path ./eval_results_api.md \
--log_level INFO \
> run_zh_eval.log 2>&1 &mkdir -p /tmp/hf_cache/mkqa
nohup env HF_ENDPOINT=https://hf-mirror.com python run_mkqa_api.py \
--cache_path /tmp/hf_cache \
--api_url "http://127.0.0.1:8003/v1/embeddings" \
--api_model "bge-m3" \
--api_batch_size 32 \
--search_top_k 1000 \
--output_dir ./search_results_api \
--corpus_embd_save_dir ./corpus_embd_api \
--eval_output_path ./eval_results_api.md \
--log_level INFO \
> run_all_eval.log 2>&1 &quick_verify.py(快速验证脚本)与 run_mkqa_api.py(完整评测脚本)支持的命令行参数分为三类:通用参数、quick_verify.py 独有参数、run_mkqa_api.py 独有参数。
通用参数(两个脚本都支持):
| 参数 | 说明 | quick_verify 默认值 | run_mkqa_api 默认值 |
|---|---|---|---|
--cache_path | HuggingFace 数据集缓存目录 | /tmp/hf_cache | None(需手动指定) |
--api_url | Embedding API 地址 | http://127.0.0.1:8003/v1/embeddings | http://127.0.0.1:8005/v1/embeddings |
--api_model | API 请求体中的 model 字段 | bge-m3 | bge-m3 |
--api_batch_size | 每次 API 请求批量大小 | 32 | 64 |
--log_level | 日志级别(DEBUG/INFO/WARNING/ERROR) | INFO | INFO |
快速验证参数(仅quick_verify.py):
| 参数 | 默认值 | 说明 |
|---|---|---|
--dataset_name | en | 验证使用的 MKQA 语言代码(单语言,如 en、zh_cn) |
--max_docs | 1000 | 从 BeIR/nq 语料中采样的文档数量(小规模快速验证) |
--max_queries | 100 | 从 MKQA 中采样的 query 数量 |
--top_k | 100 | 每个 query 检索的 top-k 文档数(实际值不超过语料规模) |
完整评测参数(仅run_mkqa_api.py ):
| 参数 | 默认值 | 说明 |
|---|---|---|
--dataset_dir | None | 本地 MKQA 数据集目录(为空时仅使用缓存) |
--dataset_names | None(全部 26 种) | 评测语言代码列表(多语言,可空格分隔多个,如 en zh_cn) |
--output_dir | ./search_results_api | 搜索结果保存目录 |
--corpus_embd_save_dir | None | Corpus embeddings 缓存目录(用于跨语言复用) |
--overwrite | False | 是否覆盖已有的搜索结果与 embeddings 缓存 |
--search_top_k | 1000 | 每个 query 检索的 top-k 文档数 |
--eval_output_path | ./eval_results_api.md | 多语言汇总评测结果的 Markdown 输出路径 |
注意事项:
quick_verify.py 的 --top_k 与 run_mkqa_api.py 的 --search_top_k 作用一致,但参数名不同quick_verify.py 的 --dataset_name(单数)只接受单语言,run_mkqa_api.py 的 --dataset_names(复数)接受多语言列表8003,使用 run_mkqa_api.py 时需通过 --api_url 显式覆盖默认值| 阶段 | 英文 | 中文 | 说明 |
|---|---|---|---|
| Corpus Loading | ~3.5 min | ~4 min | 加载 268 万文档 |
| Corpus API Encoding | ~58 min | 0(缓存复用) | 首次需编码,后续复用 |
| FAISS Index 构建 | ~1.5 min | ~1.5 min | 构建向量索引 |
| Query API Encoding | ~8 sec | ~7 sec | 编码 query |
| FAISS Search | ~41 min | ~44 min | 检索 |
| Evaluate | ~42 min | ~44 min | 计算 qa_recall@k |
| 总计 | ~2.5 小时 | ~1.7 小时 |
{
"en-test": {
"qa_recall_at_1": 0.4687,
"qa_recall_at_3": 0.6073,
"qa_recall_at_5": 0.6542,
"qa_recall_at_10": 0.6989,
"qa_recall_at_100": 0.7840,
"qa_recall_at_1000": 0.8234
},
"zh_cn-test": {
"qa_recall_at_1": 0.3650,
"qa_recall_at_3": 0.5170,
"qa_recall_at_5": 0.5653,
"qa_recall_at_10": 0.6256,
"qa_recall_at_100": 0.7500,
"qa_recall_at_1000": 0.8127
}
}| 指标 | 英文 (en) | 中文 (zh_cn) | 差距 |
|---|---|---|---|
| qa_recall@1 | 46.87% | 36.50% | -10.37 |
| qa_recall@3 | 60.73% | 51.70% | -9.03 |
| qa_recall@5 | 65.42% | 56.53% | -8.89 |
| qa_recall@10 | 69.89% | 62.56% | -7.33 |
| qa_recall@100 | 78.40% | 75.00% | -3.40 |
| qa_recall@1000 | 82.34% | 81.27% | -1.07 |
M3-Embedding 结果:
| 语言 | 官方 Dense Recall@100 | 本次服务化 qa_recall@100 | 差异 |
|---|---|---|---|
| 中文 (zh_cn) | 74.6% | 75.00% | +0.40 |
说明:
API 化适配可行:通过 APIEmbedder 将 FlagEmbedding 框架适配为远程 API 调用,无需修改框架核心逻辑,完整保留了评测流程的准确性。
Corpus embedding 可复用:26 种语言共享同一语料库,首次编码后缓存 doc.npy,后续语言无需重复编码,大幅节省时间和 API 调用成本。
中文检索能力:中文 qa_recall@1000 达 81.27%,与英文 82.34% 差距仅 1.07 个百分点,说明 BGE-M3 的跨语言检索能力较强。