2224 字
约 7 分钟
17
S20-A26 · 多路召回:为什么"加了一路"反而更差

一个检索系统先只有"向量召回",后来加了"关键词召回"做补充。上线后"命中率"升了 3 个点,而"答案质量"降了——因为两路结果的"合并方式"是"简单拼接",于是"关键词召回里那些'字面命中但语义无关'"的结果"排到了前面".

多路召回的价值在"互补":向量擅长"同义/近义",关键词擅长"精确/生僻"。而"合并"才是决定成败的一步——这个"吃掉或放大"取决于"两路的分数量级":"向量相似度是 0–1 的小数,BM25 是 0–30 的实数"——**于是"直接相加"等于"关键词一路说了算".
加一路的收益,会被"合并方式"吃掉或放大.

1. 两路召回的三种"互补关系"

关系 例子
互补(各覆盖一部分) 语义问法 vs 型号编码
重叠(都命中) 常见问题的热门文档
互斥(一路总为空) 生僻词/纯口语

第三项是"该警惕的":"关键词一路几乎不命中"第三项的"识别"很便宜:"抽样 50 条查询,统计"仅关键词命中"的比例"——**低于 5%,那一路基本"只贡献噪声".
——**那它带来的只有"排序噪声",没有"召回增益".

"简单拼接"为什么会"更差",用一条查询就能看清:

// 三步"复现问题"
1 查询:"怎么改发票抬头"(语义型)
2 向量路:命中"抬头变更流程"(对)
3 关键词路:命中"发票抬头字段说明"(字面命中、语义不对)→ 拼接后被排到前面
// 判据:"拼接口"吃掉的是"跨路的相对质量"

2. "合并"的三种做法

做法 特点
简单拼接(先 A 后 B) 最差(B 全在后)
交错(交替取) 简单(位置不公平)
融合打分(RRF/加权) 最好(可比)

第三项的关键是"把两路分数换成"可比的排名":RRF(Reciprocal Rank Fusion)只用"名次",不用"原始分"——于是"两路分数尺度不同"这件事不再重要.

"RRF"的实现只有五行,而它"把两路的结果"变成"一张分数表":

def rrf(rank_lists, k=60):
    score = {}
    for rl in rank_lists:                    # 每一路是一个"按名次排好的 id 列表"
        for r, doc in enumerate(rl, start=1):
            score[doc] = score.get(doc, 0) + 1.0 / (k + r)
    return sorted(score.items(), key=lambda x: -x[1])
// 判据:合并后"分数与'出现在几路、名次多前'成正比"

3. "RRF"为什么"几乎不用调参"

优点 说明
只用名次 不依赖"分数归一化"
常数 k 平滑 抑制"第一名独大"
可加任意多路 每路贡献一份"1/(k+rank)"

第二项是"稳"的来源:"k 取 60"是常见默认第二项的"数学"很直观:"k=0"时第一名拿 1.0、第二名 0.5、第三名 0.33——**"名次差一点、分数差很多";而"k=60"时"前三名是 0.016、0.016、0.015",于是"头部不再压倒性".
——**于是"某一路的第一名"也只是"多一票",而不是"压倒其它".

"权重"要"能回滚",否则"调参"会变成"玄学":

// 三次"权重"实验(同一回归集)
1 等权:命中率 81.2,答案质量 3.9/5
2 关键词 1.5:命中率 83.0,质量 4.1/5   ← 采纳
3 关键词 2.5:命中率 83.4,质量 3.8/5   ← 过头
// 判据:每次只改一个参数,且"两组指标"一起看

4. "权重"的三种给法

给法 说明
等权 最简单(RRF 的默认)
手工权重 靠"经验"(要"能解释")
学习权重 靠"标注数据"(要"回归集")

第二项的"证据"要"来自数据":"抽样里'含编号'的查询占 30%,而这类查询"关键词命中率 95%、向量 40%"——于是"给关键词 1.5 倍"是"有据的",而不是"拍脑袋".
第二项要"写清理由":
"关键词权重 1.5,因为'型号/编号'类查询占 30%"**——**否则"权重"会变成"谁都说不清的神秘参数".

"去重"是"三个检查"的前置,而它常被"漏在合并之前":

// 三步"合并前处理"
1 每路"先过滤"(阈值/黑名单),再进融合
2 按"文档 id"去重(同一文档只留"最高名次")
3 记录"来源位图"(哪些路命中了它)——用于"来源分布"统计
// 判据:融合的输入"干净且可追溯"

5. "合并后"的三个必做检查

检查 目的
两路"各覆盖多少"(去重前后) 判断"是否真互补"
"仅 A 命中"与"仅 B 命中"的质量 判断"谁在拉低"
最终 Top10 的"来源分布" 判断"是否被一路垄断"

第三项最直观:"Top10 里 9 条来自向量"第三项的"修法"很具体:"若 9/1",先"查'仅关键词命中'的质量"——**"质量好 → 提高其权重;质量差 → 那一路该'降权或去掉'".
——说明"关键词那一路"在"合并后"没起作用.

"阈值"是"最便宜的护栏",而它要"按路"分别设:

// 三种"过滤"(按路)
1 向量路:相似度 < 0.35 → 丢弃(防"勉强相似")
2 关键词路:BM25 < 3 → 丢弃(防"只匹配一个常用词")
3 融合后:只保留"来源 ≥ 2 路"的(保守,召回会降)
// 判据:先"保召回",再用"来源数"做"重排特征"

6. 排错:五种"多路召回问题"

现象 成因 处理方向
加一路后"更差" 拼接顺序 改 RRF
Top10 被一路垄断 分数尺度差 用名次融合
命中率升、质量降 噪声进前排 加"过滤/阈值"
两路"几乎重合" 不互补 换一路/去掉
"生僻词"仍差 关键词路"没生效" 查分词/字段

7. 边界:什么情况"一路就够"

场景 为什么 做法
语料小、问法集中 单路召回率已高 单路
只需"精确匹配"(编号/错误码) 关键词更准 单路
只需"语义相似"(问答) 向量更合适 单路
延迟极敏感(< 20ms) 两路=两倍耗时 单路 + 缓存

最后一条与"延迟"有关的边界:"多路召回"通常是"并行发两条查询",于是"延迟 ≈ max(两路)"而不是"和"——但"融合 + 重排"要"额外几十毫秒";而"重排模型"通常比"多一路召回"更"吃延迟".
判断口径一句话:"两路的结果,会不会"覆盖不同的用户意图""。会(既有"型号编码"又有"口语描述"),多路值得做,且必须"用名次融合";不会(只有一类问法),"加一路"只是"加一份噪声"——而"加了一路更差"这件事,通常不是"那一路不好",而是"合并方式"把它们"放在同一条起跑线上".

8. 自测

  1. 三种"互补关系"里,哪一种"该警惕"?它带来的只有什么?
  2. 三种"合并"做法里,为什么"简单拼接"最差?
  3. RRF 为什么"几乎不用调参"?它只用"什么信息"?
  4. "权重"为什么必须"写清理由"?不写会怎样?
  5. "Top10 来源分布"能看出什么?9/1 说明什么?
  6. 判断"该不该加一路"的那个问题是什么?

补一句"顺序"建议:"加一路之前,先把'单路'调到最好"——因为"多路"会"掩盖单路的问题"(如"分词错"被"向量那路补上"),而"掩盖"会让"下次排查更难";而"单路先做好、再多路"的顺序,能让每次改动"归因清楚".

而"归因清楚"这件事,是多路系统能被长期维护的前提。

否则"多路"迟早变成"没人敢动的一团参数"。

9. 一句话总结

多路召回的关键在"合并",不在"多加":两路分数"尺度不同",所以"不能用原始分相加",而要用"名次融合(RRF)"——它让每路"只贡献一票",于是"哪一路更该在前"由"排名"决定,而不是由"分数的大小"决定。而"加了一路更差"的诊断,第一步永远是"看 Top10 的来源分布"。

S20-A26 · 多路召回:为什么"加了一路"反而更差
https://www.clxhxhhr.top/posts/6710/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。