BM25 是什么
BM25 是经过几十年验证的词频检索算法,打分由三部分构成:词频饱和、文档长度归一与逆文档频率。它有两个可调参数,k1 控制词频增长的饱和程度,b 控制长文档的惩罚力度。对错误码、型号、版本号、专有名词这类精确匹配查询,它至今仍然可靠,而且延迟低、打分可解释、不需要训练。中文场景的成败几乎全在分词上:不配分词器就退化成单字匹配,效果会差到让人误判关键词检索没有价值。
基本信息
| 打分构成 | 词频饱和项加文档长度归一加逆文档频率 |
|---|---|
| 核心参数 | k1 控制词频饱和,b 控制长度惩罚,常见取值 1.2 与 0.75 附近 |
| 中文处理 | 必须配分词器,如 Elasticsearch 的 ik、或 jieba 等分词库 |
| 常见实现 | Elasticsearch 与 OpenSearch、Lucene、PostgreSQL 全文检索、SQLite FTS5 |
| 主要优势 | 可解释、低延迟、无训练成本、精确匹配能力强 |
| 主要局限 | 不理解同义改写与语义近似,换个说法就搜不到 |
典型使用场景
- 作为向量检索的对照基线:先跑通 BM25,再判断引入向量到底带来多少增益
- 精确术语查询:错误码、设备型号、接口路径、版本号等高区分度内容
- 混合检索的一路:与向量召回并列,用融合算法合并两路结果
- 冷启动阶段内容少:没有足够数据评估 embedding 效果时先用关键词顶住
- 需要可解释打分的场景:能说清某条结果为什么排前面,便于审计与调参
Elasticsearch 查询与调试
Elasticsearch 查询与调试
{
"query": {
"match": {
"content": { "query": "SSE 断线重连", "operator": "or" }
}
},
"size": 20,
"_source": ["doc_id", "title"]
}查看分词结果
查看分词结果
# 中文索引必须在 mapping 中指定分词器,否则默认按单字切分
curl -s 'localhost:9200/kb/_analyze' -H 'Content-Type: application/json' \
-d '{"analyzer": "ik_max_word", "text": "SSE 断线重连"}'
# 检查点:确认关键词被切成有意义的词,而不是一个字一个词
curl -s 'localhost:9200/kb/_search?explain=true' -d @query.json # 看打分构成,定位异常排序
# 分数只在本查询内可比,跨查询或跨索引比较没有意义常见误区与处理
| 情形 | 表现 | 处理 |
|---|---|---|
| 未配中文分词器 | 检索退化成单字匹配,噪声大 | 指定 ik 或等价分词器并重建索引 |
| k1 与 b 用默认值不调 | 长文档或短文档排序失衡 | 按语料长度分布实测调参 |
| 与向量分数直接比较 | 阈值完全失真 | 用排名融合,不要混比原始分数 |
| 同义词未配置 | 换个说法搜不到 | 维护同义词表并定期从搜索日志补充 |
| 只依赖关键词检索 | 语义近似问题漏检严重 | 与向量召回并行,按问题类型路由 |
BM25 分数是相对值,只在本查询的结果集内可比:把它和余弦相似度放在同一个阈值下判断,或者拿两次查询的分数绝对值比较,都会得出错误结论。
先跑通 BM25 基线再上向量是很划算的顺序:如果关键词检索已经满足大部分真实问题,向量化带来的切分、embedding 与索引维护成本可以先不引入。